Selaa lähdekoodia

Fixes #986 Affiliation changes aren't displayed in the chat

JC Brand 6 vuotta sitten
vanhempi
commit
014354d075
4 muutettua tiedostoa jossa 194 lisäystä ja 50 poistoa
  1. 1 0
      CHANGES.md
  2. 42 8
      dist/converse.js
  3. 113 33
      spec/chatroom.js
  4. 38 9
      src/converse-muc-views.js

+ 1 - 0
CHANGES.md

@@ -10,6 +10,7 @@
 - #421 XEP-0308: Last Message Correction
 - #497 XEP-0384: OMEMO encrypted messaging
 - #968 Use nickname from VCard when joining a room
+- #986 Affiliation changes aren't displayed in the chat
 - #1081 Allow for shift-enter to insert newlines
 - #1091 There's now only one CSS file for all view modes.
 - #1094 Show room members who aren't currently online

+ 42 - 8
dist/converse.js

@@ -68264,6 +68264,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
           this.model.occupants.on('add', this.showJoinNotification, this);
           this.model.occupants.on('remove', this.showLeaveNotification, this);
           this.model.occupants.on('change:show', this.showJoinOrLeaveNotification, this);
+          this.model.occupants.on('change:role', this.informOfOccupantsRoleChange, this);
+          this.model.occupants.on('change:affiliation', this.informOfOccupantsAffiliationChange, this);
           this.createEmojiPicker();
           this.createOccupantsView();
           this.render().insertIntoDOM();
@@ -68382,10 +68384,34 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
           this.occupantsview = new _converse.ChatRoomOccupantsView({
             'model': this.model.occupants
           });
-          this.occupantsview.model.on('change:role', this.informOfOccupantsRoleChange, this);
           return this;
         },
 
+        informOfOccupantsAffiliationChange(occupant, changed) {
+          const previous_affiliation = occupant._previousAttributes.affiliation,
+                current_affiliation = occupant.get('affiliation');
+
+          if (previous_affiliation === 'admin') {
+            this.showChatEvent(__("%1$s is no longer an admin of this groupchat", occupant.get('nick')));
+          } else if (previous_affiliation === 'owner') {
+            this.showChatEvent(__("%1$s is no longer an owner of this groupchat", occupant.get('nick')));
+          } else if (previous_affiliation === 'outcast') {
+            this.showChatEvent(__("%1$s is no longer banned from this groupchat", occupant.get('nick')));
+          }
+
+          if (current_affiliation === 'none' && previous_affiliation === 'member') {
+            this.showChatEvent(__("%1$s is no longer a permanent member of this groupchat", occupant.get('nick')));
+          }
+
+          if (current_affiliation === 'member') {
+            this.showChatEvent(__("%1$s is now a permanent member of this groupchat", occupant.get('nick')));
+          } else if (current_affiliation === 'outcast') {
+            this.showChatEvent(__("%1$s has been banned from this groupchat", occupant.get('nick')));
+          } else if (current_affiliation === 'admin' || current_affiliation == 'owner') {
+            this.showChatEvent(__(`%1$s is now an ${current_affiliation} of this groupchat`, occupant.get('nick')));
+          }
+        },
+
         informOfOccupantsRoleChange(occupant, changed) {
           const previous_role = occupant._previousAttributes.role;
 
@@ -68605,12 +68631,20 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
           /* Check that a command to change a groupchat user's role or
            * affiliation has anough arguments.
            */
-          // TODO check if first argument is valid
           if (args.length < 1 || args.length > 2) {
             this.showErrorMessage(__('Error: the "%1$s" command takes two arguments, the user\'s nickname and optionally a reason.', command));
             return false;
           }
 
+          if (!this.model.occupants.findWhere({
+            'nick': args[0]
+          }) && !this.model.occupants.findWhere({
+            'jid': args[0]
+          })) {
+            this.showErrorMessage(__('Error: couldn\'t find a groupchat participant "%1$s"', args[0]));
+            return false;
+          }
+
           return true;
         },
 
@@ -68692,13 +68726,9 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
 
                 const occupant = this.model.occupants.findWhere({
                   'nick': args[0]
+                }) || this.model.occupants.findWhere({
+                  'jid': args[0]
                 });
-
-                if (!occupant) {
-                  this.showErrorMessage(__(`Error: Can't find a groupchat participant with the nickname "${args[0]}"`));
-                  break;
-                }
-
                 this.model.setAffiliation('member', [{
                   'jid': occupant.get('jid'),
                   'reason': args[1]
@@ -69275,6 +69305,10 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
         },
 
         showLeaveNotification(occupant) {
+          if (_.includes(occupant.get('states'), '303') || _.includes(occupant.get('states'), '307')) {
+            return;
+          }
+
           const nick = occupant.get('nick'),
                 stat = occupant.get('status'),
                 last_el = this.content.lastElementChild;

+ 113 - 33
spec/chatroom.js

@@ -2107,7 +2107,7 @@
                     expect(_converse.connection.send).not.toHaveBeenCalled();
                     expect(view.el.querySelectorAll('.chat-error').length).toBe(1);
                     expect(view.el.querySelector('.chat-error').textContent.trim())
-                        .toBe(`Error: Can't find a groupchat participant with the nickname "chris"`)
+                        .toBe(`Error: couldn't find a groupchat participant "chris"`)
 
                     // Now test with an existing nick
                     textarea.value = '/member marc Welcome to the club!';
@@ -2312,18 +2312,33 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
+                let sent_IQ, IQ_id;
+                const sendIQ = _converse.connection.sendIQ;
+                spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
+                    sent_IQ = iq;
+                    IQ_id = sendIQ.bind(this)(iq, callback, errback);
+                });
+
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
-                    var sent_IQ, IQ_id;
-                    var sendIQ = _converse.connection.sendIQ;
-                    spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
-                        sent_IQ = iq;
-                        IQ_id = sendIQ.bind(this)(iq, callback, errback);
-                    });
                     var view = _converse.chatboxviews.get('lounge@localhost');
                     spyOn(view, 'onMessageSubmitted').and.callThrough();
                     spyOn(view.model, 'setAffiliation').and.callThrough();
                     spyOn(view, 'showErrorMessage').and.callThrough();
                     spyOn(view, 'validateRoleChangeCommand').and.callThrough();
+
+                    let presence = $pres({
+                            'from': 'lounge@localhost/annoyingGuy',
+                            'id':'27C55F89-1C6A-459A-9EB5-77690145D624',
+                            'to': 'dummy@localhost/desktop'
+                        })
+                        .c('x', { 'xmlns': 'http://jabber.org/protocol/muc#user'})
+                            .c('item', {
+                                'jid': 'annoyingguy@localhost',
+                                'affiliation': 'member',
+                                'role': 'participant'
+                            });
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+
                     var textarea = view.el.querySelector('.chat-textarea')
                     textarea.value = '/owner';
                     view.keyPressed({
@@ -2337,23 +2352,42 @@
                         "Error: the \"owner\" command takes two arguments, the user's nickname and optionally a reason.");
                     expect(view.model.setAffiliation).not.toHaveBeenCalled();
 
+                    view.onMessageSubmitted('/owner nobody You\'re responsible');
+                    expect(view.showErrorMessage).toHaveBeenCalledWith(
+                        'Error: couldn\'t find a groupchat participant "nobody"');
+                    expect(view.model.setAffiliation).not.toHaveBeenCalled();
+
                     // Call now with the correct amount of arguments.
                     // XXX: Calling onMessageSubmitted directly, trying
                     // again via triggering Event doesn't work for some weird
                     // reason.
-                    view.onMessageSubmitted('/owner annoyingGuy@localhost You\'re responsible');
-                    expect(view.validateRoleChangeCommand.calls.count()).toBe(2);
-                    expect(view.showErrorMessage.calls.count()).toBe(1);
+                    view.onMessageSubmitted('/owner annoyingGuy You\'re responsible');
+                    expect(view.validateRoleChangeCommand.calls.count()).toBe(3);
                     expect(view.model.setAffiliation).toHaveBeenCalled();
+                    expect(view.showErrorMessage.calls.count()).toBe(2);
                     // Check that the member list now gets updated
                     expect(sent_IQ.toLocaleString()).toBe(
                         "<iq to='lounge@localhost' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
                             "<query xmlns='http://jabber.org/protocol/muc#admin'>"+
-                                "<item affiliation='owner' jid='annoyingGuy@localhost'>"+
+                                "<item affiliation='owner' jid='annoyingGuy'>"+
                                     "<reason>You&apos;re responsible</reason>"+
                                 "</item>"+
                             "</query>"+
                         "</iq>");
+
+                    presence = $pres({
+                            'from': 'lounge@localhost/annoyingGuy',
+                            'id':'27C55F89-1C6A-459A-9EB5-77690145D628',
+                            'to': 'dummy@localhost/desktop'
+                        })
+                        .c('x', { 'xmlns': 'http://jabber.org/protocol/muc#user'})
+                            .c('item', {
+                                'jid': 'annoyingguy@localhost',
+                                'affiliation': 'owner',
+                                'role': 'participant'
+                            });
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(view.el.querySelectorAll('.chat-info')[4].textContent).toBe("annoyingGuy is now an owner of this groupchat");
                     done();
                 }).catch(_.partial(console.error, _));
             }));
@@ -2363,19 +2397,35 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
-                    var sent_IQ, IQ_id;
-                    var sendIQ = _converse.connection.sendIQ;
-                    spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
-                        sent_IQ = iq;
-                        IQ_id = sendIQ.bind(this)(iq, callback, errback);
-                    });
-                    var view = _converse.chatboxviews.get('lounge@localhost');
+                let sent_IQ, IQ_id;
+                const sendIQ = _converse.connection.sendIQ;
+                spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
+                    sent_IQ = iq;
+                    IQ_id = sendIQ.bind(this)(iq, callback, errback);
+                });
+
+                test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy')
+                .then(() => {
+                    const view = _converse.chatboxviews.get('lounge@localhost');
                     spyOn(view, 'onMessageSubmitted').and.callThrough();
                     spyOn(view.model, 'setAffiliation').and.callThrough();
                     spyOn(view, 'showErrorMessage').and.callThrough();
                     spyOn(view, 'validateRoleChangeCommand').and.callThrough();
-                    var textarea = view.el.querySelector('.chat-textarea')
+
+                    let presence = $pres({
+                            'from': 'lounge@localhost/annoyingGuy',
+                            'id':'27C55F89-1C6A-459A-9EB5-77690145D624',
+                            'to': 'dummy@localhost/desktop'
+                        })
+                        .c('x', { 'xmlns': 'http://jabber.org/protocol/muc#user'})
+                            .c('item', {
+                                'jid': 'annoyingguy@localhost',
+                                'affiliation': 'member',
+                                'role': 'participant'
+                            });
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+
+                    const textarea = view.el.querySelector('.chat-textarea')
                     textarea.value = '/ban';
                     view.keyPressed({
                         target: textarea,
@@ -2388,10 +2438,11 @@
                         "Error: the \"ban\" command takes two arguments, the user's nickname and optionally a reason.");
                     expect(view.model.setAffiliation).not.toHaveBeenCalled();
                     // Call now with the correct amount of arguments.
+
                     // XXX: Calling onMessageSubmitted directly, trying
                     // again via triggering Event doesn't work for some weird
                     // reason.
-                    view.onMessageSubmitted('/ban annoyingGuy@localhost You\'re annoying');
+                    view.onMessageSubmitted('/ban annoyingGuy You\'re annoying');
                     expect(view.validateRoleChangeCommand.calls.count()).toBe(2);
                     expect(view.showErrorMessage.calls.count()).toBe(1);
                     expect(view.model.setAffiliation).toHaveBeenCalled();
@@ -2399,11 +2450,27 @@
                     expect(sent_IQ.toLocaleString()).toBe(
                         "<iq to='lounge@localhost' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
                             "<query xmlns='http://jabber.org/protocol/muc#admin'>"+
-                                "<item affiliation='outcast' jid='annoyingGuy@localhost'>"+
+                                "<item affiliation='outcast' jid='annoyingGuy'>"+
                                     "<reason>You&apos;re annoying</reason>"+
                                 "</item>"+
                             "</query>"+
                         "</iq>");
+
+                    presence = $pres({
+                            'from': 'lounge@localhost/annoyingGuy',
+                            'id':'27C55F89-1C6A-459A-9EB5-77690145D628',
+                            'to': 'dummy@localhost/desktop'
+                        })
+                        .c('x', { 'xmlns': 'http://jabber.org/protocol/muc#user'})
+                            .c('item', {
+                                'jid': 'annoyingguy@localhost',
+                                'affiliation': 'outcast',
+                                'role': 'participant'
+                            });
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+                    expect(
+                        view.el.querySelectorAll('.chat-info')[3].textContent).toBe(
+                        "annoyingGuy has been banned from this groupchat");
                     done();
                 }).catch(_.partial(console.error, _));
             }));
@@ -2413,19 +2480,33 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
+                let sent_IQ, IQ_id;
+                const sendIQ = _converse.connection.sendIQ;
+                spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
+                    sent_IQ = iq;
+                    IQ_id = sendIQ.bind(this)(iq, callback, errback);
+                });
+
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
-                    var sent_IQ, IQ_id;
-                    var sendIQ = _converse.connection.sendIQ;
-                    spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
-                        sent_IQ = iq;
-                        IQ_id = sendIQ.bind(this)(iq, callback, errback);
-                    });
-                    var view = _converse.chatboxviews.get('lounge@localhost');
+                    const view = _converse.chatboxviews.get('lounge@localhost');
                     spyOn(view, 'onMessageSubmitted').and.callThrough();
                     spyOn(view, 'modifyRole').and.callThrough();
                     spyOn(view, 'showErrorMessage').and.callThrough();
                     spyOn(view, 'validateRoleChangeCommand').and.callThrough();
 
+                    let presence = $pres({
+                            'from': 'lounge@localhost/annoyingGuy',
+                            'id':'27C55F89-1C6A-459A-9EB5-77690145D624',
+                            'to': 'dummy@localhost/desktop'
+                        })
+                        .c('x', { 'xmlns': 'http://jabber.org/protocol/muc#user'})
+                            .c('item', {
+                                'jid': 'annoyingguy@localhost',
+                                'affiliation': 'none',
+                                'role': 'participant'
+                            });
+                    _converse.connection._dataRecv(test_utils.createRequest(presence));
+
                     var textarea = view.el.querySelector('.chat-textarea')
                     textarea.value = '/kick';
                     view.keyPressed({
@@ -2465,7 +2546,7 @@
                      *       </x>
                      *     </presence>
                      */
-                    var presence = $pres({
+                    presence = $pres({
                             'from': 'lounge@localhost/annoyingGuy',
                             'to': 'dummy@localhost/desktop',
                             'type': 'unavailable'
@@ -2477,9 +2558,8 @@
                             }).up()
                             .c('status', {'code': '307'});
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
-                    expect(
-                        view.el.querySelectorAll('.chat-info')[2].textContent).toBe(
-                        "annoyingGuy has been kicked out");
+                    expect(view.el.querySelectorAll('.chat-info')[3].textContent).toBe("annoyingGuy has been kicked out");
+                    expect(view.el.querySelectorAll('.chat-info').length).toBe(4);
                     done();
                 }).catch(_.partial(console.error, _));
             }));

+ 38 - 9
src/converse-muc-views.js

@@ -542,6 +542,8 @@
                     this.model.occupants.on('add', this.showJoinNotification, this);
                     this.model.occupants.on('remove', this.showLeaveNotification, this);
                     this.model.occupants.on('change:show', this.showJoinOrLeaveNotification, this);
+                    this.model.occupants.on('change:role', this.informOfOccupantsRoleChange, this);
+                    this.model.occupants.on('change:affiliation', this.informOfOccupantsAffiliationChange, this);
 
                     this.createEmojiPicker();
                     this.createOccupantsView();
@@ -643,10 +645,32 @@
                      */
                     this.model.occupants.chatroomview = this;
                     this.occupantsview = new _converse.ChatRoomOccupantsView({'model': this.model.occupants});
-                    this.occupantsview.model.on('change:role', this.informOfOccupantsRoleChange, this);
                     return this;
                 },
 
+                informOfOccupantsAffiliationChange(occupant, changed) {
+                    const previous_affiliation = occupant._previousAttributes.affiliation,
+                          current_affiliation = occupant.get('affiliation');
+
+                    if (previous_affiliation === 'admin') {
+                        this.showChatEvent(__("%1$s is no longer an admin of this groupchat", occupant.get('nick')))
+                    } else if (previous_affiliation === 'owner') {
+                        this.showChatEvent(__("%1$s is no longer an owner of this groupchat", occupant.get('nick')))
+                    } else if (previous_affiliation === 'outcast') {
+                        this.showChatEvent(__("%1$s is no longer banned from this groupchat", occupant.get('nick')))
+                    }
+
+                    if (current_affiliation === 'none' && previous_affiliation === 'member') {
+                        this.showChatEvent(__("%1$s is no longer a permanent member of this groupchat", occupant.get('nick')))
+                    } if (current_affiliation === 'member') {
+                        this.showChatEvent(__("%1$s is now a permanent member of this groupchat", occupant.get('nick')))
+                    } else if (current_affiliation === 'outcast') {
+                        this.showChatEvent(__("%1$s has been banned from this groupchat", occupant.get('nick')))
+                    } else if (current_affiliation === 'admin' || current_affiliation == 'owner') {
+                        this.showChatEvent(__(`%1$s is now an ${current_affiliation} of this groupchat`, occupant.get('nick')))
+                    }
+                },
+
                 informOfOccupantsRoleChange (occupant, changed) {
                     const previous_role = occupant._previousAttributes.role;
                     if (previous_role === 'moderator') {
@@ -831,13 +855,16 @@
                     /* Check that a command to change a groupchat user's role or
                      * affiliation has anough arguments.
                      */
-                    // TODO check if first argument is valid
                     if (args.length < 1 || args.length > 2) {
                         this.showErrorMessage(
                             __('Error: the "%1$s" command takes two arguments, the user\'s nickname and optionally a reason.', command)
                         );
                         return false;
                     }
+                    if (!this.model.occupants.findWhere({'nick': args[0]}) && !this.model.occupants.findWhere({'jid': args[0]})) {
+                        this.showErrorMessage(__('Error: couldn\'t find a groupchat participant "%1$s"', args[0]));
+                        return false;
+                    }
                     return true;
                 },
 
@@ -854,8 +881,8 @@
                         return false;
                     }
                     const match = text.replace(/^\s*/, "").match(/^\/(.*?)(?: (.*))?$/) || [false, '', ''],
-                        args = match[2] && match[2].splitOnce(' ') || [],
-                        command = match[1].toLowerCase();
+                          args = match[2] && match[2].splitOnce(' ') || [],
+                          command = match[1].toLowerCase();
                     switch (command) {
                         case 'admin':
                             if (!this.verifyAffiliations(['owner']) || !this.validateRoleChangeCommand(command, args)) {
@@ -873,6 +900,7 @@
                             if (!this.verifyAffiliations(['owner', 'admin']) || !this.validateRoleChangeCommand(command, args)) {
                                 break;
                             }
+
                             this.model.setAffiliation('outcast',
                                     [{ 'jid': args[0],
                                        'reason': args[1]
@@ -929,11 +957,9 @@
                             if (!this.verifyAffiliations(['admin', 'owner']) || !this.validateRoleChangeCommand(command, args)) {
                                 break;
                             }
-                            const occupant = this.model.occupants.findWhere({'nick': args[0]});
-                            if (!occupant) {
-                                this.showErrorMessage(__(`Error: Can't find a groupchat participant with the nickname "${args[0]}"`));
-                                break;
-                            }
+                            const occupant = this.model.occupants.findWhere({'nick': args[0]}) ||
+                                             this.model.occupants.findWhere({'jid': args[0]});
+
                             this.model.setAffiliation('member',
                                     [{ 'jid': occupant.get('jid'),
                                        'reason': args[1]
@@ -1485,6 +1511,9 @@
                 },
 
                 showLeaveNotification (occupant) {
+                    if (_.includes(occupant.get('states'), '303') || _.includes(occupant.get('states'), '307')) {
+                        return;
+                    }
                     const nick = occupant.get('nick'),
                           stat = occupant.get('status'),
                           last_el = this.content.lastElementChild;