Explorar o código

Allow messages to be edited by clicking a pencil icon

updates #421
JC Brand %!s(int64=7) %!d(string=hai) anos
pai
achega
87406f5f93
Modificáronse 5 ficheiros con 178 adicións e 26 borrados
  1. 36 8
      dist/converse.js
  2. 109 9
      spec/messages.js
  3. 22 3
      src/converse-chatview.js
  4. 1 2
      src/converse-message-view.js
  5. 10 4
      src/templates/message.html

+ 36 - 8
dist/converse.js

@@ -69620,6 +69620,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
         // Leaky abstraction from MUC
         events: {
           'change input.fileupload': 'onFileSelection',
+          'click .chat-msg__action-edit': 'onMessageEditButtonClicked',
           'click .chatbox-navback': 'showControlBox',
           'click .close-chatbox-button': 'close',
           'click .new-msgs-indicator': 'viewUnreadMessages',
@@ -69632,8 +69633,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
           'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
           'click .toggle-smiley': 'toggleEmojiMenu',
           'click .upload-file': 'toggleFileUpload',
-          'keydown .chat-textarea': 'keyPressed',
-          'input .chat-textarea': 'inputChanged'
+          'input .chat-textarea': 'inputChanged',
+          'keydown .chat-textarea': 'keyPressed'
         },
 
         initialize() {
@@ -70240,6 +70241,27 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
           }));
         },
 
+        onMessageEditButtonClicked(ev) {
+          const idx = this.model.messages.findLastIndex('correcting'),
+                currently_correcting = idx >= 0 ? this.model.messages.at(idx) : null,
+                message_el = u.ancestor(ev.target, '.chat-msg'),
+                message = this.model.messages.findWhere({
+            'msgid': message_el.getAttribute('data-msgid')
+          });
+
+          if (currently_correcting !== message) {
+            if (!_.isNil(currently_correcting)) {
+              currently_correcting.save('correcting', false);
+            }
+
+            message.save('correcting', true);
+            this.insertIntoTextArea(message.get('message'), true);
+          } else {
+            message.save('correcting', false);
+            this.insertIntoTextArea('', true);
+          }
+        },
+
         editLaterMessage() {
           let message;
           let idx = this.model.messages.findLastIndex('correcting');
@@ -74647,7 +74669,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
       });
       _converse.MessageView = _converse.ViewWithAvatar.extend({
         events: {
-          'click .chat-msg-edited': 'showMessageVersionsModal'
+          'click .chat-msg__edit-modal': 'showMessageVersionsModal'
         },
 
         initialize() {
@@ -85733,17 +85755,17 @@ __p += '\n        </span>\n        ';
  if (!o.is_me_message) { ;
 __p += '<div class="chat-msg__body">';
  } ;
-__p += ' \n            ';
+__p += '\n            ';
  if (o.edited) { ;
 __p += ' <i title="' +
 __e(o.__('This message has been edited')) +
-'" class="fa fa-edit chat-msg-edited"></i> ';
+'" class="fa fa-edit chat-msg__edit-modal"></i> ';
  } ;
 __p += '\n            ';
  if (!o.is_me_message) { ;
 __p += '<div class="chat-msg__message">';
  } ;
-__p += ' \n                ';
+__p += '\n                ';
  if (o.is_spoiler) { ;
 __p += '\n                    <div class="chat-msg__spoiler-hint">\n                        <span class="spoiler-hint">' +
 __e(o.spoiler_hint) +
@@ -85759,11 +85781,17 @@ __p += '"><!-- message gets added here via renderMessage --></div>\n
  if (!o.is_me_message) { ;
 __p += '</div>';
  } ;
-__p += '\n        ';
+__p += '\n            ';
+ if (o.type !== 'headline' && !o.is_me_message && o.sender === 'me') { ;
+__p += '\n            <div class="chat-msg__actions">\n                <button class="chat-msg__action chat-msg__action-edit fa fa-pencil" title="' +
+__e(o.__('Edit this message')) +
+'">&nbsp;</button>\n            </div>\n            ';
+ } ;
+__p += '\n\n        ';
  if (!o.is_me_message) { ;
 __p += '</div>';
  } ;
-__p += ' \n    </div>\n</div>\n';
+__p += '\n    </div>\n</div>\n';
 return __p
 };
 

+ 109 - 9
spec/messages.js

@@ -19,7 +19,106 @@
 
     describe("A Chat Message", function () {
 
-        it("can be sent as a correction",
+        it("can be sent as a correction by clicking the pencil icon",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
+            test_utils.createContacts(_converse, 'current', 1);
+            test_utils.openControlBox();
+            const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+            test_utils.openChatBoxFor(_converse, contact_jid);
+
+            const view = _converse.chatboxviews.get(contact_jid);
+            const textarea = view.el.querySelector('textarea.chat-textarea');
+
+            textarea.value = 'But soft, what light through yonder airlock breaks?';
+            view.keyPressed({
+                target: textarea,
+                preventDefault: _.noop,
+                keyCode: 13 // Enter
+            });
+            expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
+            expect(view.el.querySelector('.chat-msg__text').textContent)
+                .toBe('But soft, what light through yonder airlock breaks?');
+            expect(textarea.value).toBe('');
+
+            const first_msg = view.model.messages.findWhere({'message': 'But soft, what light through yonder airlock breaks?'});
+
+            expect(view.el.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(1);
+            let action = view.el.querySelector('.chat-msg .chat-msg__action');
+            expect(action.getAttribute('title')).toBe('Edit this message');
+
+            action.style.opacity = 1;
+            action.click();
+
+            expect(textarea.value).toBe('But soft, what light through yonder airlock breaks?');
+            expect(view.model.messages.at(0).get('correcting')).toBe(true);
+            expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
+            expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(true);
+
+            spyOn(_converse.connection, 'send');
+            textarea.value = 'But soft, what light through yonder window breaks?';
+            view.keyPressed({
+                target: textarea,
+                preventDefault: _.noop,
+                keyCode: 13 // Enter
+            });
+            expect(_converse.connection.send).toHaveBeenCalled();
+
+            const msg = _converse.connection.send.calls.all()[0].args[0];
+            expect(msg.toLocaleString())
+            .toBe(`<message from='dummy@localhost/resource' `+
+                    `to='max.frankfurter@localhost' type='chat' id='${msg.nodeTree.getAttribute('id')}' `+
+                    `xmlns='jabber:client'>`+
+                        `<body>But soft, what light through yonder window breaks?</body>`+
+                        `<active xmlns='http://jabber.org/protocol/chatstates'/>`+
+                        `<replace xmlns='urn:xmpp:message-correct:0' id='${first_msg.get('msgid')}'/>`+
+                `</message>`);
+            expect(view.model.messages.models.length).toBe(1);
+            const corrected_message = view.model.messages.at(0);
+            expect(corrected_message.get('msgid')).toBe(first_msg.get('msgid'));
+            expect(corrected_message.get('correcting')).toBe(false);
+            expect(corrected_message.get('older_versions').length).toBe(1);
+            expect(corrected_message.get('older_versions')[0]).toBe('But soft, what light through yonder airlock breaks?');
+
+            expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
+            expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
+
+            // Test that clicking the pencil icon a second time cancels editing.
+            action = view.el.querySelector('.chat-msg .chat-msg__action');
+            action.style.opacity = 1;
+            action.click();
+
+            expect(textarea.value).toBe('But soft, what light through yonder window breaks?');
+            expect(view.model.messages.at(0).get('correcting')).toBe(true);
+            expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
+            expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(true);
+
+            action = view.el.querySelector('.chat-msg .chat-msg__action');
+            action.style.opacity = 1;
+            action.click();
+            expect(textarea.value).toBe('');
+            expect(view.model.messages.at(0).get('correcting')).toBe(false);
+            expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
+            expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
+
+            // Test that messages from other users don't have the pencil icon
+            _converse.chatboxes.onMessage(
+                $msg({
+                    'from': contact_jid,
+                    'to': _converse.connection.jid,
+                    'type': 'chat',
+                    'id': (new Date()).getTime()
+                }).c('body').t('Hello').up()
+                .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
+            );
+            expect(view.el.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(1);
+            done();
+        }));
+
+
+        it("can be sent as a correction by using the up arrow",
             mock.initConverseWithPromises(
                 null, ['rosterGroupsFetched'], {},
                 function (done, _converse) {
@@ -180,19 +279,20 @@
                     spyOn(_converse, 'emit');
                     const message = 'This is a received message';
                     const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                    const msg = $msg({
+
+                    // We don't already have an open chatbox for this user
+                    expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
+
+                    _converse.chatboxes.onMessage(
+                        $msg({
                             'from': sender_jid,
                             'to': _converse.connection.jid,
                             'type': 'chat',
                             'id': (new Date()).getTime()
                         }).c('body').t(message).up()
-                        .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
+                        .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
+                    );
 
-                    // We don't already have an open chatbox for this user
-                    expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
-
-                    // onMessage is a handler for received XMPP messages
-                    _converse.chatboxes.onMessage(msg);
                     expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
 
                     // Check that the chatbox and its view now exist
@@ -1804,7 +1904,7 @@
             }).catch(_.partial(console.error, _));
         }));
 
-        it("can be sent as a correction",
+        it("can be sent as a correction by using the up arrow",
             mock.initConverseWithPromises(
                 null, ['rosterGroupsFetched'], {},
                 function (done, _converse) {

+ 22 - 3
src/converse-chatview.js

@@ -237,7 +237,7 @@
 
             _converse.UserDetailsModal = _converse.BootstrapModal.extend({
 
-                events: { 
+                events: {
                     'click button.remove-contact': 'removeContact',
                     'click button.refresh-contact': 'refreshContact'
                 },
@@ -321,6 +321,7 @@
 
                 events: {
                     'change input.fileupload': 'onFileSelection',
+                    'click .chat-msg__action-edit': 'onMessageEditButtonClicked',
                     'click .chatbox-navback': 'showControlBox',
                     'click .close-chatbox-button': 'close',
                     'click .new-msgs-indicator': 'viewUnreadMessages',
@@ -333,8 +334,8 @@
                     'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
                     'click .toggle-smiley': 'toggleEmojiMenu',
                     'click .upload-file': 'toggleFileUpload',
-                    'keydown .chat-textarea': 'keyPressed',
-                    'input .chat-textarea': 'inputChanged'
+                    'input .chat-textarea': 'inputChanged',
+                    'keydown .chat-textarea': 'keyPressed'
                 },
 
                 initialize () {
@@ -931,6 +932,24 @@
                     return f(this.model.messages.filter({'sender': 'me'}));
                 },
 
+                onMessageEditButtonClicked (ev) {
+                    const idx = this.model.messages.findLastIndex('correcting'),
+                          currently_correcting = idx >=0 ? this.model.messages.at(idx) : null,
+                          message_el = u.ancestor(ev.target, '.chat-msg'),
+                          message = this.model.messages.findWhere({'msgid': message_el.getAttribute('data-msgid')});
+
+                    if (currently_correcting !== message) {
+                        if (!_.isNil(currently_correcting)) {
+                            currently_correcting.save('correcting', false);
+                        }
+                        message.save('correcting', true);
+                        this.insertIntoTextArea(message.get('message'), true);
+                    } else {
+                        message.save('correcting', false);
+                        this.insertIntoTextArea('', true);
+                    }
+                },
+
                 editLaterMessage () {
                     let message;
                     let idx = this.model.messages.findLastIndex('correcting');

+ 1 - 2
src/converse-message-view.js

@@ -80,9 +80,8 @@
 
 
             _converse.MessageView = _converse.ViewWithAvatar.extend({
-
                 events: {
-                    'click .chat-msg-edited': 'showMessageVersionsModal'
+                    'click .chat-msg__edit-modal': 'showMessageVersionsModal'
                 },
 
                 initialize () {

+ 10 - 4
src/templates/message.html

@@ -10,9 +10,9 @@
             </span>
             {[ if (!o.is_me_message) { ]}<time timestamp="{{{o.isodate}}}" class="chat-msg__time">{{{o.pretty_time}}}</time>{[ } ]}
         </span>
-        {[ if (!o.is_me_message) { ]}<div class="chat-msg__body">{[ } ]} 
-            {[ if (o.edited) { ]} <i title="{{{o.__('This message has been edited')}}}" class="fa fa-edit chat-msg-edited"></i> {[ } ]}
-            {[ if (!o.is_me_message) { ]}<div class="chat-msg__message">{[ } ]} 
+        {[ if (!o.is_me_message) { ]}<div class="chat-msg__body">{[ } ]}
+            {[ if (o.edited) { ]} <i title="{{{o.__('This message has been edited')}}}" class="fa fa-edit chat-msg__edit-modal"></i> {[ } ]}
+            {[ if (!o.is_me_message) { ]}<div class="chat-msg__message">{[ } ]}
                 {[ if (o.is_spoiler) { ]}
                     <div class="chat-msg__spoiler-hint">
                         <span class="spoiler-hint">{{{o.spoiler_hint}}}</span>
@@ -22,6 +22,12 @@
                 <div class="chat-msg__text{[ if (o.is_spoiler) { ]} spoiler collapsed{[ } ]}"><!-- message gets added here via renderMessage --></div>
                 <div class="chat-msg__media"></div>
             {[ if (!o.is_me_message) { ]}</div>{[ } ]}
-        {[ if (!o.is_me_message) { ]}</div>{[ } ]} 
+            {[ if (o.type !== 'headline' && !o.is_me_message && o.sender === 'me') { ]}
+            <div class="chat-msg__actions">
+                <button class="chat-msg__action chat-msg__action-edit fa fa-pencil" title="{{{o.__('Edit this message')}}}">&nbsp;</button>
+            </div>
+            {[ } ]}
+
+        {[ if (!o.is_me_message) { ]}</div>{[ } ]}
     </div>
 </div>