ソースを参照

muc: Render role change messages as ephemeral notifications

JC Brand 5 年 前
コミット
958379682a
5 ファイル変更82 行追加99 行削除
  1. 0 4
      sass/_chatbox.scss
  2. 27 42
      spec/muc.js
  3. 15 21
      spec/retractions.js
  4. 27 11
      src/converse-muc-views.js
  5. 13 21
      src/headless/converse-muc.js

+ 0 - 4
sass/_chatbox.scss

@@ -235,10 +235,6 @@
                 font-style: italic;
                 line-height: var(--line-height-small);
                 padding: 0 1em 0.3em;
-
-                &:before {
-                    content: " ";
-                }
             }
 
             video {

+ 27 - 42
spec/muc.js

@@ -918,7 +918,7 @@ describe("Groupchats", function () {
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "some1 and newgirl have entered the groupchat\n newguy has left the groupchat");
+                "some1 and newgirl have entered the groupchat\nnewguy has left the groupchat");
 
             // When the user immediately joins again, we collapse the
             // multiple join/leave messages.
@@ -949,7 +949,7 @@ describe("Groupchats", function () {
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "some1 and newgirl have entered the groupchat\n newguy has left the groupchat");
+                "some1 and newgirl have entered the groupchat\nnewguy has left the groupchat");
 
             presence = $pres({
                     to: 'romeo@montague.lit/_converse.js-29092160',
@@ -963,7 +963,7 @@ describe("Groupchats", function () {
                 });
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "some1, newgirl and nomorenicks have entered the groupchat\n newguy has left the groupchat");
+                "some1, newgirl and nomorenicks have entered the groupchat\nnewguy has left the groupchat");
 
             presence = $pres({
                     to: 'romeo@montague.lit/_converse.js-290918392',
@@ -977,7 +977,7 @@ describe("Groupchats", function () {
                 });
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "some1 and newgirl have entered the groupchat\n newguy and nomorenicks have left the groupchat");
+                "some1 and newgirl have entered the groupchat\nnewguy and nomorenicks have left the groupchat");
 
             presence = $pres({
                     to: 'romeo@montague.lit/_converse.js-29092160',
@@ -991,7 +991,7 @@ describe("Groupchats", function () {
                 });
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "some1, newgirl and nomorenicks have entered the groupchat\n newguy has left the groupchat");
+                "some1, newgirl and nomorenicks have entered the groupchat\nnewguy has left the groupchat");
 
             // Test a member joining and leaving
             presence = $pres({
@@ -1031,7 +1031,7 @@ describe("Groupchats", function () {
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "some1, newgirl and nomorenicks have entered the groupchat\n newguy and insider have left the groupchat");
+                "some1, newgirl and nomorenicks have entered the groupchat\nnewguy and insider have left the groupchat");
 
             expect(view.model.occupants.length).toBe(5);
             expect(view.model.occupants.findWhere({'jid': 'insider@montague.lit'}).get('show')).toBe('offline');
@@ -1051,7 +1051,7 @@ describe("Groupchats", function () {
 
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "some1 and nomorenicks have entered the groupchat\n newguy, insider and newgirl have left the groupchat");
+                "some1 and nomorenicks have entered the groupchat\nnewguy, insider and newgirl have left the groupchat");
             expect(view.model.occupants.length).toBe(4);
             done();
         }));
@@ -1102,7 +1102,7 @@ describe("Groupchats", function () {
                 </presence>`);
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "romeo, fabio and jcbrand have entered the groupchat\n Dele Olajide has left the groupchat");
+                "romeo, fabio and jcbrand have entered the groupchat\nDele Olajide has left the groupchat");
 
             presence = u.toStanza(
                 `<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/Dele Olajide">
@@ -1134,7 +1134,7 @@ describe("Groupchats", function () {
                 </presence>`);
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "romeo, fabio and others have entered the groupchat\n fuvuv has left the groupchat");
+                "romeo, fabio and others have entered the groupchat\nfuvuv has left the groupchat");
 
             presence = u.toStanza(
                 `<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/fabio">
@@ -1145,7 +1145,7 @@ describe("Groupchats", function () {
                 </presence>`);
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "romeo, jcbrand and Dele Olajide have entered the groupchat\n fuvuv and fabio have left the groupchat");
+                "romeo, jcbrand and Dele Olajide have entered the groupchat\nfuvuv and fabio have left the groupchat");
 
             presence = u.toStanza(
                 `<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fabio">
@@ -1157,7 +1157,7 @@ describe("Groupchats", function () {
                 </presence>`);
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "romeo, jcbrand and others have entered the groupchat\n fuvuv has left the groupchat");
+                "romeo, jcbrand and others have entered the groupchat\nfuvuv has left the groupchat");
 
             // XXX: hack so that we can test leave/enter of occupants
             // who were already in the room when we joined.
@@ -1172,7 +1172,7 @@ describe("Groupchats", function () {
                 </presence>`);
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "romeo, jcbrand and Dele Olajide have entered the groupchat\n fuvuv and fabio have left the groupchat");
+                "romeo, jcbrand and Dele Olajide have entered the groupchat\nfuvuv and fabio have left the groupchat");
 
             presence = u.toStanza(
                 `<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" type="unavailable" from="coven@chat.shakespeare.lit/Dele Olajide">
@@ -1182,7 +1182,7 @@ describe("Groupchats", function () {
                 </presence>`);
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "romeo and jcbrand have entered the groupchat\n fuvuv, fabio and Dele Olajide have left the groupchat");
+                "romeo and jcbrand have entered the groupchat\nfuvuv, fabio and Dele Olajide have left the groupchat");
 
             presence = u.toStanza(
                 `<presence xmlns="jabber:client" to="romeo@montague.lit/orchard" from="coven@chat.shakespeare.lit/fabio">
@@ -1193,7 +1193,7 @@ describe("Groupchats", function () {
                 </presence>`);
             _converse.connection._dataRecv(mock.createRequest(presence));
             await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
-                "romeo, jcbrand and fabio have entered the groupchat\n fuvuv and Dele Olajide have left the groupchat");
+                "romeo, jcbrand and fabio have entered the groupchat\nfuvuv and Dele Olajide have left the groupchat");
 
             expect(1).toBe(1);
             done();
@@ -2802,8 +2802,8 @@ describe("Groupchats", function () {
                         'role': 'visitor'
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
-            const info_msg = await u.waitUntil(() => view.el.querySelector('.chat-info__message'));
-            expect(info_msg.textContent.trim()).toBe("annoyingGuy has been muted");
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
+                "romeo has entered the groupchat\nannoyingGuy has been muted");
 
             presence = $pres({
                     'from': 'lounge@montague.lit/annoyingGuy',
@@ -2816,10 +2816,8 @@ describe("Groupchats", function () {
                         'role': 'participant'
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                    "annoyingGuy has been given a voice"
-            );
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() ===
+                "romeo has entered the groupchat\nannoyingGuy has been given a voice");
 
             // Check that we don't see an info message concerning the role,
             // if the affiliation has changed.
@@ -3626,10 +3624,10 @@ describe("Groupchats", function () {
                         'role': 'moderator'
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                "trustworthyguy is now a moderator"
-            );
+            // Check now that things get restored when the user is given a voice
+            await u.waitUntil(
+                () => view.el.querySelector('.chat-content__notifications').textContent.split('\n', 2).pop()?.trim() ===
+                    "trustworthyguy is now a moderator");
 
             // Call now with the correct amount of arguments.
             // XXX: Calling onFormSubmitted directly, trying
@@ -3669,10 +3667,7 @@ describe("Groupchats", function () {
                         'role': 'participant'
             });
             _converse.connection._dataRecv(mock.createRequest(presence));
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                "trustworthyguy is no longer a moderator"
-            );
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.includes("trustworthyguy is no longer a moderator"));
             done();
         }));
 
@@ -3769,10 +3764,7 @@ describe("Groupchats", function () {
                         'role': 'visitor'
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                "annoyingGuy has been muted"
-            );
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.includes("annoyingGuy has been muted"));
 
             // Call now with the correct of arguments.
             // XXX: Calling onFormSubmitted directly, trying
@@ -3813,10 +3805,7 @@ describe("Groupchats", function () {
                         'role': 'participant'
                     });
             _converse.connection._dataRecv(mock.createRequest(presence));
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                "annoyingGuy has been given a voice"
-            );
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.includes("annoyingGuy has been given a voice"));
             done();
         }));
 
@@ -5180,7 +5169,7 @@ describe("Groupchats", function () {
                         type: 'groupchat'
                     }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                 await view.model.queueMessage(msg);
-                await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim()  == 'nomorenicks is typing\n newguy has stopped typing');
+                await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim()  == 'nomorenicks is typing\nnewguy has stopped typing');
                 done();
             }));
         });
@@ -5301,11 +5290,7 @@ describe("Groupchats", function () {
             expect(textarea === null).toBe(false);
 
             // Check now that things get restored when the user is given a voice
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                "troll has been given a voice"
-            );
-            expect(view.el.querySelectorAll('.chat-info__message').length).toBe(2);
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() === "troll has been given a voice");
             done();
         }));
     });

+ 15 - 21
spec/retractions.js

@@ -648,7 +648,7 @@ describe("Message Retractions", function () {
             _converse.connection._dataRecv(mock.createRequest(reflection));
             await u.waitUntil(() => view.model.handleRetraction.calls.count() === 1);
 
-            expect(view.model.messages.length).toBe(2);
+            expect(view.model.messages.length).toBe(1);
             expect(view.model.messages.last().get('retracted')).toBeTruthy();
             expect(view.model.messages.last().get('is_ephemeral')).toBe(false);
             expect(view.model.messages.last().get('editable')).toBe(false);
@@ -670,14 +670,11 @@ describe("Message Retractions", function () {
             const occupant = view.model.getOwnOccupant();
             expect(occupant.get('role')).toBe('moderator');
             occupant.save('role', 'member');
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                "romeo is no longer a moderator"
-            );
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.includes("romeo is no longer a moderator"));
             const retraction_stanza = await sendAndThenRetractMessage(_converse, view);
             await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1);
 
-            expect(view.model.messages.length).toBe(2);
+            expect(view.model.messages.length).toBe(1);
             expect(view.model.messages.last().get('retracted')).toBeTruthy();
             const el = view.el.querySelector('.chat-msg--retracted .chat-msg__message div');
             expect(el.textContent.trim()).toBe('romeo has removed this message');
@@ -700,14 +697,14 @@ describe("Message Retractions", function () {
             _converse.connection._dataRecv(mock.createRequest(error));
             await u.waitUntil(() => view.el.querySelectorAll('.chat-error').length === 1);
             await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0);
-            expect(view.model.messages.length).toBe(3);
-            expect(view.model.messages.at(1).get('retracted')).toBeFalsy();
-            expect(view.model.messages.at(1).get('is_ephemeral')).toBeFalsy();
-            expect(view.model.messages.at(1).get('editable')).toBeTruthy();
+            expect(view.model.messages.length).toBe(2);
+            expect(view.model.messages.at(0).get('retracted')).toBeFalsy();
+            expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy();
+            expect(view.model.messages.at(0).get('editable')).toBeTruthy();
 
             const err_msg = "Sorry, something went wrong while trying to retract your message."
-            expect(view.model.messages.at(2).get('message')).toBe(err_msg);
-            expect(view.model.messages.at(2).get('type')).toBe('error');
+            expect(view.model.messages.at(1).get('message')).toBe(err_msg);
+            expect(view.model.messages.at(1).get('type')).toBe('error');
 
             expect(view.el.querySelectorAll('.chat-error').length).toBe(1);
             const errmsg = view.el.querySelector('.chat-error');
@@ -729,14 +726,11 @@ describe("Message Retractions", function () {
             const occupant = view.model.getOwnOccupant();
             expect(occupant.get('role')).toBe('moderator');
             occupant.save('role', 'member');
-            await u.waitUntil(() =>
-                Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
-                "romeo is no longer a moderator"
-            );
+            await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.includes("romeo is no longer a moderator"))
             await sendAndThenRetractMessage(_converse, view);
             await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1);
 
-            expect(view.model.messages.length).toBe(2);
+            expect(view.model.messages.length).toBe(1);
             expect(view.model.messages.last().get('retracted')).toBeTruthy();
             const el = view.el.querySelector('.chat-msg--retracted .chat-msg__message div');
             expect(el.textContent.trim()).toBe('romeo has removed this message');
@@ -744,10 +738,10 @@ describe("Message Retractions", function () {
             await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1);
 
             await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0);
-            expect(view.model.messages.length).toBe(4);
-            expect(view.model.messages.at(1).get('retracted')).toBeFalsy();
-            expect(view.model.messages.at(1).get('is_ephemeral')).toBeFalsy();
-            expect(view.model.messages.at(1).get('editable')).toBeTruthy();
+            expect(view.model.messages.length).toBe(3);
+            expect(view.model.messages.at(0).get('retracted')).toBeFalsy();
+            expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy();
+            expect(view.model.messages.at(0).get('editable')).toBeTruthy();
 
             const error_messages = view.el.querySelectorAll('.chat-error');
             expect(error_messages.length).toBe(2);

+ 27 - 11
src/converse-muc-views.js

@@ -725,7 +725,7 @@ converse.plugins.add('converse-muc-views', {
             renderNotifications () {
                 const actors_per_state = this.model.notifications.toJSON();
                 const states = api.settings.get('muc_show_join_leave') ?
-                    [...converse.CHAT_STATES, ...converse.MUC_TRAFFIC_STATES] :
+                    [...converse.CHAT_STATES, ...converse.MUC_TRAFFIC_STATES, ...converse.MUC_ROLE_CHANGES] :
                     converse.CHAT_STATES;
 
                 const message = states.reduce((result, state) => {
@@ -736,15 +736,23 @@ converse.plugins.add('converse-muc-views', {
                     const actors = existing_actors.map(a => this.model.getOccupant(a)?.getDisplayName() || a);
                     if (actors.length === 1) {
                         if (state === 'composing') {
-                            return `${result} ${__('%1$s is typing', actors[0])}\n`;
+                            return `${result}${__('%1$s is typing', actors[0])}\n`;
                         } else if (state === 'paused') {
-                            return `${result} ${__('%1$s has stopped typing', actors[0])}\n`;
+                            return `${result}${__('%1$s has stopped typing', actors[0])}\n`;
                         } else if (state === _converse.GONE) {
-                            return `${result} ${__('%1$s has gone away', actors[0])}\n`;
+                            return `${result}${__('%1$s has gone away', actors[0])}\n`;
                         } else if (state === 'entered') {
-                            return `${result} ${__('%1$s has entered the groupchat', actors[0])}\n`;
+                            return `${result}${__('%1$s has entered the groupchat', actors[0])}\n`;
                         } else if (state === 'exited') {
-                            return `${result} ${__('%1$s has left the groupchat', actors[0])}\n`;
+                            return `${result}${__('%1$s has left the groupchat', actors[0])}\n`;
+                        } else if (state === 'op') {
+                            return `${result}${__("%1$s is now a moderator", actors[0])}\n`;
+                        } else if (state === 'deop') {
+                            return `${result}${__("%1$s is no longer a moderator", actors[0])}\n`;
+                        } else if (state === 'voice') {
+                            return `${result}${__("%1$s has been given a voice", actors[0])}\n`;
+                        } else if (state === 'mute') {
+                            return `${result}${__("%1$s has been muted", actors[0])}\n`;
                         }
                     } else if (actors.length > 1) {
                         let actors_str;
@@ -756,15 +764,23 @@ converse.plugins.add('converse-muc-views', {
                         }
 
                         if (state === 'composing') {
-                            return `${result} ${__('%1$s are typing', actors_str)}\n`;
+                            return `${result}${__('%1$s are typing', actors_str)}\n`;
                         } else if (state === 'paused') {
-                            return `${result} ${__('%1$s have stopped typing', actors_str)}\n`;
+                            return `${result}${__('%1$s have stopped typing', actors_str)}\n`;
                         } else if (state === _converse.GONE) {
-                            return `${result} ${__('%1$s have gone away', actors_str)}\n`;
+                            return `${result}${__('%1$s have gone away', actors_str)}\n`;
                         } else if (state === 'entered') {
-                            return `${result} ${__('%1$s have entered the groupchat', actors_str)}\n`;
+                            return `${result}${__('%1$s have entered the groupchat', actors_str)}\n`;
                         } else if (state === 'exited') {
-                            return `${result} ${__('%1$s have left the groupchat', actors_str)}\n`;
+                            return `${result}${__('%1$s have left the groupchat', actors_str)}\n`;
+                        } else if (state === 'op') {
+                            return `${result}${__("%1$s are now moderators", actors[0])}\n`;
+                        } else if (state === 'deop') {
+                            return `${result}${__("%1$s are no longer moderator", actors[0])}\n`;
+                        } else if (state === 'voice') {
+                            return `${result}${__("%1$s have been given voices", actors[0])}\n`;
+                        } else if (state === 'mute') {
+                            return `${result}${__("%1$s have been muted", actors[0])}\n`;
                         }
                     }
                     return result;

+ 13 - 21
src/headless/converse-muc.js

@@ -17,6 +17,7 @@ import st from "./utils/stanza";
 import u from "./utils/form";
 
 converse.MUC_TRAFFIC_STATES = ['entered', 'exited'];
+converse.MUC_ROLE_CHANGES = ['op', 'deop', 'voice', 'mute'];
 
 const MUC_ROLE_WEIGHTS = {
     'moderator':    1,
@@ -1938,7 +1939,12 @@ converse.plugins.add('converse-muc', {
                 };
                 const actors_per_chat_state = converse.CHAT_STATES.reduce(reducer, {});
                 const actors_per_traffic_state = converse.MUC_TRAFFIC_STATES.reduce(reducer, {});
-                this.notifications.set(Object.assign(actors_per_chat_state, actors_per_traffic_state));
+                const actors_per_role_change = converse.MUC_ROLE_CHANGES.reduce(reducer, {});
+                this.notifications.set(Object.assign(
+                    actors_per_chat_state,
+                    actors_per_traffic_state,
+                    actors_per_role_change
+                ));
                 window.setTimeout(() => this.removeNotification(actor, state), 10000);
             },
 
@@ -2109,31 +2115,17 @@ converse.plugins.add('converse-muc', {
                 }
                 const previous_role = occupant._previousAttributes.role;
                 if (previous_role === 'moderator') {
-                    this.createMessage({
-                        'type': 'info',
-                        'message': __("%1$s is no longer a moderator", occupant.get('nick'))
-                    });
-                }
-                if (previous_role === 'visitor') {
-                    this.createMessage({
-                        'type': 'info',
-                        'message': __("%1$s has been given a voice", occupant.get('nick'))
-                    });
+                    this.updateNotifications(occupant.get('nick'), 'deop');
+                } else if (previous_role === 'visitor') {
+                    this.updateNotifications(occupant.get('nick'), 'voice');
                 }
                 if (occupant.get('role') === 'visitor') {
-                    this.createMessage({
-                        'type': 'info',
-                        'message': __("%1$s has been muted", occupant.get('nick'))
-                    });
-                }
-                if (occupant.get('role') === 'moderator') {
+                    this.updateNotifications(occupant.get('nick'), 'mute');
+                } else if (occupant.get('role') === 'moderator') {
                     if (!['owner', 'admin'].includes(occupant.get('affiliation'))) {
                         // Oly show this message if the user isn't already
                         // an admin or owner, otherwise this isn't new information.
-                        this.createMessage({
-                            'type': 'info',
-                            'message': __("%1$s is now a moderator", occupant.get('nick'))
-                        });
+                        this.updateNotifications(occupant.get('nick'), 'op');
                     }
                 }
             },