Browse Source

MUC: Add support for status code 333

JC Brand 5 years ago
parent
commit
1419b4fc96
2 changed files with 108 additions and 41 deletions
  1. 51 0
      spec/muc.js
  2. 57 41
      src/headless/converse-muc.js

+ 51 - 0
spec/muc.js

@@ -2778,6 +2778,57 @@ describe("Groupchats", function () {
             done();
             done();
         }));
         }));
 
 
+        it("informs users if they have exited the groupchat due to a technical reason",
+            mock.initConverse(
+                ['rosterGroupsFetched'], {},
+                async function (done, _converse) {
+
+            /*  <presence
+             *      from='harfleur@chat.shakespeare.lit/pistol'
+             *      to='pistol@shakespeare.lit/harfleur'
+             *      type='unavailable'>
+             *  <x xmlns='http://jabber.org/protocol/muc#user'>
+             *      <item affiliation='none' role='none'>
+             *          <actor nick='Fluellen'/>
+             *          <reason>Avaunt, you cullion!</reason>
+             *      </item>
+             *      <status code='110'/>
+             *      <status code='307'/>
+             *  </x>
+             *  </presence>
+             */
+            await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
+            var presence = $pres().attrs({
+                    from:'lounge@montague.lit/romeo',
+                    to:'romeo@montague.lit/pda',
+                    type:'unavailable'
+                })
+                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
+                .c('item').attrs({
+                    affiliation: 'none',
+                    jid: 'romeo@montague.lit/pda',
+                    role: 'none'
+                })
+                .c('reason').t('Flux capacitor overload!').up()
+                .up()
+                .c('status').attrs({code:'110'}).up()
+                .c('status').attrs({code:'333'}).up()
+                .c('status').attrs({code:'307'}).nodeTree;
+
+            _converse.connection._dataRecv(mock.createRequest(presence));
+
+            const view = _converse.chatboxviews.get('lounge@montague.lit');
+            expect(u.isVisible(view.el.querySelector('.chat-area'))).toBeFalsy();
+            expect(u.isVisible(view.el.querySelector('.occupants'))).toBeFalsy();
+            const chat_body = view.el.querySelector('.chatroom-body');
+            expect(chat_body.querySelectorAll('.disconnect-msg').length).toBe(2);
+            expect(chat_body.querySelector('.disconnect-msg:first-child').textContent.trim()).toBe(
+                'You have exited this groupchat due to a technical problem');
+            expect(chat_body.querySelector('.disconnect-msg:nth-child(2)').textContent.trim()).toBe(
+                'The reason given is: "Flux capacitor overload!".');
+            done();
+        }));
+
 
 
         it("can be saved to, and retrieved from, browserStorage",
         it("can be saved to, and retrieved from, browserStorage",
             mock.initConverse(
             mock.initConverse(

+ 57 - 41
src/headless/converse-muc.js

@@ -23,6 +23,8 @@ export const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none'];
 converse.MUC_TRAFFIC_STATES = ['entered', 'exited'];
 converse.MUC_TRAFFIC_STATES = ['entered', 'exited'];
 converse.MUC_ROLE_CHANGES = ['op', 'deop', 'voice', 'mute'];
 converse.MUC_ROLE_CHANGES = ['op', 'deop', 'voice', 'mute'];
 
 
+const ACTION_INFO_CODES = ['301', '303', '333', '307', '321', '322'];
+
 const MUC_ROLE_WEIGHTS = {
 const MUC_ROLE_WEIGHTS = {
     'moderator':    1,
     'moderator':    1,
     'participant':  2,
     'participant':  2,
@@ -183,13 +185,13 @@ converse.plugins.add('converse-muc', {
 
 
             disconnect_messages: {
             disconnect_messages: {
                 301: __('You have been banned from this groupchat'),
                 301: __('You have been banned from this groupchat'),
+                333: __('You have exited this groupchat due to a technical problem'),
                 307: __('You have been kicked from this groupchat'),
                 307: __('You have been kicked from this groupchat'),
                 321: __("You have been removed from this groupchat because of an affiliation change"),
                 321: __("You have been removed from this groupchat because of an affiliation change"),
                 322: __("You have been removed from this groupchat because the groupchat has changed to members-only and you're not a member"),
                 322: __("You have been removed from this groupchat because the groupchat has changed to members-only and you're not a member"),
                 332: __("You have been removed from this groupchat because the service hosting it is being shut down")
                 332: __("You have been removed from this groupchat because the service hosting it is being shut down")
             },
             },
 
 
-            action_info_codes: ['301', '303', '307', '321', '322']
         }
         }
 
 
 
 
@@ -2061,6 +2063,8 @@ converse.plugins.add('converse-muc', {
                     return actor ? __("%1$s has been banned by %2$s", nick, actor) : __("%1$s has been banned", nick);
                     return actor ? __("%1$s has been banned by %2$s", nick, actor) : __("%1$s has been banned", nick);
                 } else if (code === '303') {
                 } else if (code === '303') {
                     return ___("%1$s\'s nickname has changed", nick);
                     return ___("%1$s\'s nickname has changed", nick);
+                } else if (code === '333') {
+                    return ___("%1$s has exited the room due to a technical issue");
                 } else  if (code === '307') {
                 } else  if (code === '307') {
                     return actor ? __("%1$s has been kicked out by %2$s", nick, actor) : __("%1$s has been kicked out", nick);
                     return actor ? __("%1$s has been kicked out by %2$s", nick, actor) : __("%1$s has been kicked out", nick);
                 } else if (code === '321') {
                 } else if (code === '321') {
@@ -2147,6 +2151,52 @@ converse.plugins.add('converse-muc', {
                 }
                 }
             },
             },
 
 
+
+            /**
+             * Create an info message based on a received MUC status code
+             * @private
+             * @method _converse.ChatRoom#createInfoMessage
+             * @param { string } code - The MUC status code
+             * @param { XMLElement } stanza - The original stanza that contains the code
+             * @param { Boolean } is_self - Whether this stanza refers to our own presence
+             */
+            createInfoMessage (code, stanza, is_self) {
+                const data = { 'type': 'info', };
+
+                if (code === '110' || (code === '100' && !is_self)) {
+                    return;
+                } else if (code in _converse.muc.info_messages) {
+                    data.message = _converse.muc.info_messages[code];
+                } else if (!is_self && ACTION_INFO_CODES.includes(code)) {
+                    const nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
+                    const item = stanza.querySelector(`x[xmlns="${Strophe.NS.MUC_USER}"] item`);
+                    data.actor = item ? item.querySelector('actor')?.getAttribute('nick') : undefined;
+                    data.reason = item ? item.querySelector('reason')?.textContent : undefined;
+                    data.message = this.getActionInfoMessage(code, nick, data.actor);
+                } else if (is_self && (code in _converse.muc.new_nickname_messages)) {
+                    let nick;
+                    if (is_self && code === "210") {
+                        nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
+                    } else if (is_self && code === "303") {
+                        nick = stanza.querySelector(`x[xmlns="${Strophe.NS.MUC_USER}"] item`).getAttribute('nick');
+                    }
+                    this.save('nick', nick);
+                    data.message = __(_converse.muc.new_nickname_messages[code], nick);
+                }
+                if (data.message) {
+                    if (code === "201" && this.messages.findWhere(data)) {
+                        return;
+                    } else if (code in _converse.muc.info_messages &&
+                            this.messages.length &&
+                            this.messages.pop().get('message') === data.message) {
+                        // XXX: very naive duplication checking
+                        return;
+                    }
+                    this.createMessage(data);
+                }
+            },
+
+
             /**
             /**
              * Create info messages based on a received presence or message stanza
              * Create info messages based on a received presence or message stanza
              * @private
              * @private
@@ -2154,47 +2204,13 @@ converse.plugins.add('converse-muc', {
              * @param { XMLElement } stanza
              * @param { XMLElement } stanza
              */
              */
             createInfoMessages (stanza) {
             createInfoMessages (stanza) {
-                const is_self = stanza.querySelector("status[code='110']") !== null;
-                const x = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"]`, stanza).pop();
-                if (!x) {
-                    return;
+                const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code'));
+                if (codes.includes('333') && codes.includes('307')) {
+                    // See: https://github.com/xsf/xeps/pull/969/files#diff-ac5113766e59219806793c1f7d967f1bR4966
+                    codes.splice(codes.indexOf('307'), 1);
                 }
                 }
-                sizzle('status', x).map(s => s.getAttribute('code')).forEach(code => {
-                    const data = {
-                        'type': 'info',
-                    };
-                    if (code === '110' || (code === '100' && !is_self)) {
-                        return;
-                    } else if (code in _converse.muc.info_messages) {
-                        data.message = _converse.muc.info_messages[code];
-                    } else if (!is_self && _converse.muc.action_info_codes.includes(code)) {
-                        const nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
-                        const item = x.querySelector('item');
-                        data.actor = item ? invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined;
-                        data.reason = item ? item.querySelector('reason')?.textContent : undefined;
-                        data.message = this.getActionInfoMessage(code, nick, data.actor);
-                    } else if (is_self && (code in _converse.muc.new_nickname_messages)) {
-                        let nick;
-                        if (is_self && code === "210") {
-                            nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
-                        } else if (is_self && code === "303") {
-                            nick = stanza.querySelector('x item').getAttribute('nick');
-                        }
-                        this.save('nick', nick);
-                        data.message = __(_converse.muc.new_nickname_messages[code], nick);
-                    }
-                    if (data.message) {
-                        if (code === "201" && this.messages.findWhere(data)) {
-                            return;
-                        } else if (code in _converse.muc.info_messages &&
-                                this.messages.length &&
-                                this.messages.pop().get('message') === data.message) {
-                            // XXX: very naive duplication checking
-                            return;
-                        }
-                        this.createMessage(data);
-                    }
-                });
+                const is_self = codes.includes('110');
+                codes.forEach(code => this.createInfoMessage(code, stanza, is_self));
             },
             },