Browse Source

Also squash leave/join messages

And fix an HTML rendering bug for info messages and nicks that contain spaces
JC Brand 6 years ago
parent
commit
7b9c97dfd3
6 changed files with 6571 additions and 7735 deletions
  1. 6375 7702
      dist/converse.js
  2. 156 2
      spec/chatroom.js
  3. 1 2
      src/converse-chatview.js
  4. 1 2
      src/converse-message-view.js
  5. 36 23
      src/converse-muc-views.js
  6. 2 4
      src/templates/info.html

File diff suppressed because it is too large
+ 6375 - 7702
dist/converse.js


+ 156 - 2
spec/chatroom.js

@@ -558,7 +558,7 @@
                     expect($chat_content.find('div.chat-info').length).toBe(4);
                     var $msg_el = $chat_content.find('div.chat-info:last');
                     expect($msg_el.html()).toBe("newguy has left and re-entered the groupchat");
-                    expect($msg_el.data('leavejoin')).toBe('"newguy"');
+                    expect($msg_el.data('leavejoin')).toBe('newguy');
 
                     presence = $pres({
                             to: 'dummy@localhost/_converse.js-29092160',
@@ -575,7 +575,7 @@
                     expect($chat_content.find('div.chat-info').length).toBe(4);
                     const msg_el = sizzle('div.chat-info', chat_content).pop();
                     expect(msg_el.textContent).toBe('newguy has left the groupchat');
-                    expect(msg_el.getAttribute('data-leave')).toBe('"newguy"');
+                    expect(msg_el.getAttribute('data-leave')).toBe('newguy');
 
                     presence = $pres({
                             to: 'dummy@localhost/_converse.js-29092160',
@@ -687,6 +687,160 @@
                 }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
 
+            it("combines subsequent join/leave messages when users enter or exit a groupchat",
+                    mock.initConverseWithPromises(
+                        null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                        function (done, _converse) {
+
+                test_utils.openAndEnterChatRoom(_converse, 'coven', 'chat.shakespeare.lit', 'dummy')
+                .then(() => {
+                    const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
+                    const chat_content = view.el.querySelector('.chat-content');
+
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(1);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("dummy has entered the groupchat");
+                    
+                    let presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fabio">
+                            <c xmlns="http://jabber.org/protocol/caps" node="http://conversations.im" ver="INI3xjRUioclBTP/aACfWi5m9UY=" hash="sha-1"/>
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="participant"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(2);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fabio has entered the groupchat");
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/Dele Olajide">
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-39320524" role="participant"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(3);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/jcbrand">
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="owner" jid="jc@opkode.com/converse.js-30645022" role="moderator"/>
+                                <status code="110"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(4);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("jcbrand has entered the groupchat");
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-39320524" role="none"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(4);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered and left the groupchat");
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/Dele Olajide">
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-74567907" role="participant"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(4);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("Dele Olajide has entered the groupchat");
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fuvuv" xml:lang="en">
+                            <c xmlns="http://jabber.org/protocol/caps" node="http://jabber.pix-art.de" ver="5tOurnuFnp2h50hKafeUyeN4Yl8=" hash="sha-1"/>
+                            <x xmlns="vcard-temp:x:update"/>
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="fuvuv@blabber.im/Pix-Art Messenger.8zoB" role="participant"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(5);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fuvuv has entered the groupchat");
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/fuvuv">
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="fuvuv@blabber.im/Pix-Art Messenger.8zoB" role="none"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(5);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("fuvuv has entered and left the groupchat");
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
+                            <status>Disconnected: Replaced by new connection</status>
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="none"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(5);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
+                        `fabio has entered and left the groupchat. "Disconnected: Replaced by new connection"`);
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fabio">
+                            <c xmlns="http://jabber.org/protocol/caps" node="http://conversations.im" ver="INI3xjRUioclBTP/aACfWi5m9UY=" hash="sha-1"/>
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="participant"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(5);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
+                        `fabio has entered the groupchat`);
+
+                    // XXX: hack so that we can test leave/enter of occupants
+                    // who were already in the room when we joined.
+                    chat_content.innerHTML = '';
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
+                            <status>Disconnected: closed</status>
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="none"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(1);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
+                        `fabio has left the groupchat. "Disconnected: closed"`);
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="deleo@traderlynk.4ng.net/converse.js-74567907" role="none"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(2);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
+                        `Dele Olajide has left the groupchat`);
+
+                    presence = Strophe.xmlHtmlNode(
+                        `<presence xmlns="jabber:client" to="dummy@localhost/resource" from="coven@chat.shakespeare.lit/fabio">
+                            <c xmlns="http://jabber.org/protocol/caps" node="http://conversations.im" ver="INI3xjRUioclBTP/aACfWi5m9UY=" hash="sha-1"/>
+                            <x xmlns="http://jabber.org/protocol/muc#user">
+                                <item affiliation="none" jid="fabio@montefuscolo.com.br/Conversations.ZvLu" role="participant"/>
+                            </x>
+                        </presence>`).firstElementChild;
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(sizzle('div.chat-info', chat_content).length).toBe(2);
+                    expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe(
+                        `fabio has left and re-entered the groupchat`);
+
+                    done();
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
+            }));
+
             it("shows a new day indicator if a join/leave message is received on a new day",
                 mock.initConverseWithPromises(
                     null, ['rosterGroupsFetched'], {},

+ 1 - 2
src/converse-chatview.js

@@ -499,7 +499,7 @@
                     return this;
                 },
 
-                showChatEvent (message, data='') {
+                showChatEvent (message) {
                     const isodate = moment().format();
                     this.content.insertAdjacentHTML(
                         'beforeend',
@@ -507,7 +507,6 @@
                             'extra_classes': 'chat-event',
                             'message': message,
                             'isodate': isodate,
-                            'data': data
                         }));
                     this.insertDayIndicator(this.content.lastElementChild);
                     this.scrollDown();

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

@@ -163,8 +163,7 @@
                           msg = u.stringToElement(
                         tpl_info(_.extend(this.model.toJSON(), {
                             'extra_classes': 'chat-error',
-                            'isodate': moment_time.format(),
-                            'data': ''
+                            'isodate': moment_time.format()
                         })));
                     return this.replaceElement(msg);
                 },

+ 36 - 23
src/converse-muc-views.js

@@ -1462,7 +1462,6 @@
                         this.content.insertAdjacentHTML(
                             'beforeend',
                             tpl_info({
-                                'data': '',
                                 'isodate': moment().format(),
                                 'extra_classes': 'chat-event',
                                 'message': message
@@ -1505,19 +1504,22 @@
                     }
                     const nick = occupant.get('nick'),
                           stat = occupant.get('status'),
-                          last_el = this.content.lastElementChild;
+                          last_leave_el = this.getImmediateNotification(this.content.lastElementChild, nick, 'leave');
 
-                    if (_.includes(_.get(last_el, 'classList', []), 'chat-info') &&
-                        _.get(last_el, 'dataset', {}).leave === `"${nick}"`) {
+                    if (_.includes(_.get(last_leave_el, 'classList', []), 'chat-info') &&
+                            _.get(last_leave_el, 'dataset', {}).leave === nick) {
 
-                        last_el.outerHTML =
+                        let el = this.content.lastElementChild;
+                        el.insertAdjacentElement('afterend', last_leave_el);
+                        last_leave_el.outerHTML =
                             tpl_info({
-                                'data': `data-leavejoin="${nick}"`,
+                                'data_name': 'leavejoin',
+                                'data_value': nick,
                                 'isodate': moment().format(),
                                 'extra_classes': 'chat-event',
                                 'message': __('%1$s has left and re-entered the groupchat', nick)
                             });
-                        const el = this.content.lastElementChild;
+                        el = this.content.lastElementChild;
                         setTimeout(() => u.addClass('fade-out', el), 5000);
                         setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5250);
                     } else {
@@ -1528,15 +1530,16 @@
                             message = __('%1$s has entered the groupchat. "%2$s"', nick, stat);
                         }
                         const data = {
-                            'data': `data-join="${nick}"`,
+                            'data_name': 'join',
+                            'data_value': nick,
                             'isodate': moment().format(),
                             'extra_classes': 'chat-event',
                             'message': message
                         };
-                        if (_.includes(_.get(last_el, 'classList', []), 'chat-info') &&
-                            _.get(last_el, 'dataset', {}).joinleave === `"${nick}"`) {
+                        if (_.includes(_.get(last_leave_el, 'classList', []), 'chat-info') &&
+                                _.get(last_leave_el, 'dataset', {}).joinleave === nick) {
 
-                            last_el.outerHTML = tpl_info(data);
+                            last_leave_el.outerHTML = tpl_info(data);
                         } else {
                             const el = u.stringToElement(tpl_info(data));
                             this.content.insertAdjacentElement('beforeend', el);
@@ -1546,15 +1549,24 @@
                     this.scrollDown();
                 },
 
-                getImmediateJoinNotification (el, nick) {
+                getImmediateNotification (el, nick, type='join') {
                     while (!_.isNil(el)) {
                         const data = _.get(el, 'dataset', {});
                         if (!_.includes(_.get(el, 'classList', []), 'chat-info')) {
                             return;
                         }
-                        if (moment(el.getAttribute('data-isodate')).isSame(new Date(), "day") &&
-                                (data.join === `"${nick}"` || data.leavejoin === `"${nick}"`)) {
-                            return el;
+                        if (!moment(el.getAttribute('data-isodate')).isSame(new Date(), "day")) {
+                            el = el.previousElementSibling;
+                            continue;
+                        }
+                        if (type === 'join') {
+                            if (data.join === nick || data.leavejoin === nick) {
+                                return el;
+                            }
+                        } else {
+                            if (data.leave === nick || data.joinleave === nick) {
+                                return el;
+                            }
                         }
                         el = el.previousElementSibling;
                     }
@@ -1566,12 +1578,12 @@
                     }
                     const nick = occupant.get('nick'),
                           stat = occupant.get('status'),
-                          last_join_el = this.getImmediateJoinNotification(this.content.lastElementChild, nick),
+                          last_join_el = this.getImmediateNotification(this.content.lastElementChild, nick, 'join'),
                           data = _.get(last_join_el, 'dataset', {});
 
                     if (last_join_el) {
                         let message;
-                        if (data.join === `"${nick}"`) {
+                        if (data.join === nick) {
                             if (_.isNil(stat)) {
                                 message = __('%1$s has entered and left the groupchat', nick);
                             } else {
@@ -1581,7 +1593,8 @@
                             el.insertAdjacentElement('afterend', last_join_el);
                             last_join_el.outerHTML =
                                 tpl_info({
-                                    'data': `data-joinleave="${nick}"`,
+                                    'data_name': 'joinleave',
+                                    'data_value': nick,
                                     'isodate': moment().format(),
                                     'extra_classes': 'chat-event',
                                     'message': message
@@ -1589,7 +1602,7 @@
                             el = this.content.lastElementChild;
                             setTimeout(() => u.addClass('fade-out', el), 5000);
                             setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5250);
-                        } else if (data.leavejoin === `"${nick}"`) {
+                        } else if (data.leavejoin === nick) {
                             if (_.isNil(stat)) {
                                 message = __('%1$s has left the groupchat', nick);
                             } else {
@@ -1597,7 +1610,8 @@
                             }
                             last_join_el.outerHTML =
                                 tpl_info({
-                                    'data': `data-leave="${nick}"`,
+                                    'data_name': 'leave',
+                                    'data_value': nick,
                                     'isodate': moment().format(),
                                     'extra_classes': 'chat-event',
                                     'message': message
@@ -1614,7 +1628,8 @@
                             'message': message,
                             'isodate': moment().format(),
                             'extra_classes': 'chat-event',
-                            'data': `data-leave="${nick}"`
+                            'data_name': 'leave',
+                            'data_value': nick
                         }
                         const el = u.stringToElement(tpl_info(data));
                         this.content.insertAdjacentElement('beforeend', el);
@@ -1727,7 +1742,6 @@
                     this.content.insertAdjacentHTML(
                         'beforeend',
                         tpl_info({
-                            'data': '',
                             'isodate': date,
                             'extra_classes': 'chat-event',
                             'message': message
@@ -1737,7 +1751,6 @@
                         this.content.insertAdjacentHTML(
                             'beforeend',
                             tpl_info({
-                                'data': '',
                                 'isodate': date,
                                 'extra_classes': 'chat-topic',
                                 'message': u.addHyperlinks(xss.filterXSS(_.get(this.model.get('subject'), 'text'), {'whiteList': {}})),

+ 2 - 4
src/templates/info.html

@@ -1,10 +1,8 @@
 {[ if (o.render_message) { ]}
     <!-- XXX: Should only ever be rendered if the message text has been sanitized already -->
     <div class="message chat-info {{{o.extra_classes}}}"
-        data-isodate="{{{o.isodate}}}"
-        {{{o.data}}}>{{o.message}}</div>
+        data-isodate="{{{o.isodate}}}" {[ if (o.data_name) { ]} data-{{{o.data_name}}}="{{{o.data_value}}}"{[ } ]}>{{o.message}}</div>
 {[ } else { ]}
     <div class="message chat-info {{{o.extra_classes}}}"
-        data-isodate="{{{o.isodate}}}"
-        {{{o.data}}}>{{{o.message}}}</div>
+        data-isodate="{{{o.isodate}}}" {[ if (o.data_name) { ]} data-{{{o.data_name}}}="{{{o.data_value}}}"{[ } ]}>{{{o.message}}}</div>
 {[ } ]}

Some files were not shown because too many files changed in this diff