Commit 95837968 authored by JC Brand's avatar JC Brand

muc: Render role change messages as ephemeral notifications

parent c6f8ef0c
...@@ -235,10 +235,6 @@ ...@@ -235,10 +235,6 @@
font-style: italic; font-style: italic;
line-height: var(--line-height-small); line-height: var(--line-height-small);
padding: 0 1em 0.3em; padding: 0 1em 0.3em;
&:before {
content: " ";
}
} }
video { video {
......
This diff is collapsed.
...@@ -648,7 +648,7 @@ describe("Message Retractions", function () { ...@@ -648,7 +648,7 @@ describe("Message Retractions", function () {
_converse.connection._dataRecv(mock.createRequest(reflection)); _converse.connection._dataRecv(mock.createRequest(reflection));
await u.waitUntil(() => view.model.handleRetraction.calls.count() === 1); 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('retracted')).toBeTruthy();
expect(view.model.messages.last().get('is_ephemeral')).toBe(false); expect(view.model.messages.last().get('is_ephemeral')).toBe(false);
expect(view.model.messages.last().get('editable')).toBe(false); expect(view.model.messages.last().get('editable')).toBe(false);
...@@ -670,14 +670,11 @@ describe("Message Retractions", function () { ...@@ -670,14 +670,11 @@ describe("Message Retractions", function () {
const occupant = view.model.getOwnOccupant(); const occupant = view.model.getOwnOccupant();
expect(occupant.get('role')).toBe('moderator'); expect(occupant.get('role')).toBe('moderator');
occupant.save('role', 'member'); occupant.save('role', 'member');
await u.waitUntil(() => await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.includes("romeo is no longer a moderator"));
Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
"romeo is no longer a moderator"
);
const retraction_stanza = await sendAndThenRetractMessage(_converse, view); const retraction_stanza = await sendAndThenRetractMessage(_converse, view);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1); 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(); expect(view.model.messages.last().get('retracted')).toBeTruthy();
const el = view.el.querySelector('.chat-msg--retracted .chat-msg__message div'); const el = view.el.querySelector('.chat-msg--retracted .chat-msg__message div');
expect(el.textContent.trim()).toBe('romeo has removed this message'); expect(el.textContent.trim()).toBe('romeo has removed this message');
...@@ -700,14 +697,14 @@ describe("Message Retractions", function () { ...@@ -700,14 +697,14 @@ describe("Message Retractions", function () {
_converse.connection._dataRecv(mock.createRequest(error)); _converse.connection._dataRecv(mock.createRequest(error));
await u.waitUntil(() => view.el.querySelectorAll('.chat-error').length === 1); await u.waitUntil(() => view.el.querySelectorAll('.chat-error').length === 1);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0);
expect(view.model.messages.length).toBe(3); expect(view.model.messages.length).toBe(2);
expect(view.model.messages.at(1).get('retracted')).toBeFalsy(); expect(view.model.messages.at(0).get('retracted')).toBeFalsy();
expect(view.model.messages.at(1).get('is_ephemeral')).toBeFalsy(); expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy();
expect(view.model.messages.at(1).get('editable')).toBeTruthy(); expect(view.model.messages.at(0).get('editable')).toBeTruthy();
const err_msg = "Sorry, something went wrong while trying to retract your message." 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(1).get('message')).toBe(err_msg);
expect(view.model.messages.at(2).get('type')).toBe('error'); expect(view.model.messages.at(1).get('type')).toBe('error');
expect(view.el.querySelectorAll('.chat-error').length).toBe(1); expect(view.el.querySelectorAll('.chat-error').length).toBe(1);
const errmsg = view.el.querySelector('.chat-error'); const errmsg = view.el.querySelector('.chat-error');
...@@ -729,14 +726,11 @@ describe("Message Retractions", function () { ...@@ -729,14 +726,11 @@ describe("Message Retractions", function () {
const occupant = view.model.getOwnOccupant(); const occupant = view.model.getOwnOccupant();
expect(occupant.get('role')).toBe('moderator'); expect(occupant.get('role')).toBe('moderator');
occupant.save('role', 'member'); occupant.save('role', 'member');
await u.waitUntil(() => await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.includes("romeo is no longer a moderator"))
Array.from(view.el.querySelectorAll('.chat-info__message')).pop()?.textContent.trim() ===
"romeo is no longer a moderator"
);
await sendAndThenRetractMessage(_converse, view); await sendAndThenRetractMessage(_converse, view);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1); 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(); expect(view.model.messages.last().get('retracted')).toBeTruthy();
const el = view.el.querySelector('.chat-msg--retracted .chat-msg__message div'); const el = view.el.querySelector('.chat-msg--retracted .chat-msg__message div');
expect(el.textContent.trim()).toBe('romeo has removed this message'); expect(el.textContent.trim()).toBe('romeo has removed this message');
...@@ -744,10 +738,10 @@ describe("Message Retractions", function () { ...@@ -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').length === 1);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0);
expect(view.model.messages.length).toBe(4); expect(view.model.messages.length).toBe(3);
expect(view.model.messages.at(1).get('retracted')).toBeFalsy(); expect(view.model.messages.at(0).get('retracted')).toBeFalsy();
expect(view.model.messages.at(1).get('is_ephemeral')).toBeFalsy(); expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy();
expect(view.model.messages.at(1).get('editable')).toBeTruthy(); expect(view.model.messages.at(0).get('editable')).toBeTruthy();
const error_messages = view.el.querySelectorAll('.chat-error'); const error_messages = view.el.querySelectorAll('.chat-error');
expect(error_messages.length).toBe(2); expect(error_messages.length).toBe(2);
......
...@@ -725,7 +725,7 @@ converse.plugins.add('converse-muc-views', { ...@@ -725,7 +725,7 @@ converse.plugins.add('converse-muc-views', {
renderNotifications () { renderNotifications () {
const actors_per_state = this.model.notifications.toJSON(); const actors_per_state = this.model.notifications.toJSON();
const states = api.settings.get('muc_show_join_leave') ? 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; converse.CHAT_STATES;
const message = states.reduce((result, state) => { const message = states.reduce((result, state) => {
...@@ -736,15 +736,23 @@ converse.plugins.add('converse-muc-views', { ...@@ -736,15 +736,23 @@ converse.plugins.add('converse-muc-views', {
const actors = existing_actors.map(a => this.model.getOccupant(a)?.getDisplayName() || a); const actors = existing_actors.map(a => this.model.getOccupant(a)?.getDisplayName() || a);
if (actors.length === 1) { if (actors.length === 1) {
if (state === 'composing') { 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') { } 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) { } 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') { } 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') { } 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) { } else if (actors.length > 1) {
let actors_str; let actors_str;
...@@ -756,15 +764,23 @@ converse.plugins.add('converse-muc-views', { ...@@ -756,15 +764,23 @@ converse.plugins.add('converse-muc-views', {
} }
if (state === 'composing') { 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') { } 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) { } 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') { } 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') { } 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; return result;
......
...@@ -17,6 +17,7 @@ import st from "./utils/stanza"; ...@@ -17,6 +17,7 @@ import st from "./utils/stanza";
import u from "./utils/form"; import u from "./utils/form";
converse.MUC_TRAFFIC_STATES = ['entered', 'exited']; converse.MUC_TRAFFIC_STATES = ['entered', 'exited'];
converse.MUC_ROLE_CHANGES = ['op', 'deop', 'voice', 'mute'];
const MUC_ROLE_WEIGHTS = { const MUC_ROLE_WEIGHTS = {
'moderator': 1, 'moderator': 1,
...@@ -1938,7 +1939,12 @@ converse.plugins.add('converse-muc', { ...@@ -1938,7 +1939,12 @@ converse.plugins.add('converse-muc', {
}; };
const actors_per_chat_state = converse.CHAT_STATES.reduce(reducer, {}); const actors_per_chat_state = converse.CHAT_STATES.reduce(reducer, {});
const actors_per_traffic_state = converse.MUC_TRAFFIC_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); window.setTimeout(() => this.removeNotification(actor, state), 10000);
}, },
...@@ -2109,31 +2115,17 @@ converse.plugins.add('converse-muc', { ...@@ -2109,31 +2115,17 @@ converse.plugins.add('converse-muc', {
} }
const previous_role = occupant._previousAttributes.role; const previous_role = occupant._previousAttributes.role;
if (previous_role === 'moderator') { if (previous_role === 'moderator') {
this.createMessage({ this.updateNotifications(occupant.get('nick'), 'deop');
'type': 'info', } else if (previous_role === 'visitor') {
'message': __("%1$s is no longer a moderator", occupant.get('nick')) this.updateNotifications(occupant.get('nick'), 'voice');
});
}
if (previous_role === 'visitor') {
this.createMessage({
'type': 'info',
'message': __("%1$s has been given a voice", occupant.get('nick'))
});
} }
if (occupant.get('role') === 'visitor') { if (occupant.get('role') === 'visitor') {
this.createMessage({ this.updateNotifications(occupant.get('nick'), 'mute');
'type': 'info', } else if (occupant.get('role') === 'moderator') {
'message': __("%1$s has been muted", occupant.get('nick'))
});
}
if (occupant.get('role') === 'moderator') {
if (!['owner', 'admin'].includes(occupant.get('affiliation'))) { if (!['owner', 'admin'].includes(occupant.get('affiliation'))) {
// Oly show this message if the user isn't already // Oly show this message if the user isn't already
// an admin or owner, otherwise this isn't new information. // an admin or owner, otherwise this isn't new information.
this.createMessage({ this.updateNotifications(occupant.get('nick'), 'op');
'type': 'info',
'message': __("%1$s is now a moderator", occupant.get('nick'))
});
} }
} }
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment