Commit f4b6b93b authored by Xavi Ferrer's avatar Xavi Ferrer Committed by JC Brand

update info messages visibility

parent 160ab345
......@@ -61,6 +61,7 @@ Soon we'll deprecate the latter, so prepare now.
- #2213: added CustomElement to converse.env
- #2220: fix rendering of emojis in case `use_system_emojis == false` (again).
- #2092: fixes room list update loop when having the `locked_muc_domain` truthy or `'hidden'`
- #2259: Rename configuration setting `muc_show_join_leave` to `muc_show_info_messages`. Now accepts a list of events to show instead of a boolean.
- #2285: Rename config option `muc_hats_from_vcard` to [muc_hats](https://conversejs.org/docs/html/configuration.html#muc-hats). Now accepts a list instead of a boolean and allows for more flexible choices regarding user badges.
- #2300: Fix message reorder issue after message correction.
- #2304: Custom emojis (stickers) images not shown
......
......@@ -1379,13 +1379,53 @@ Example:
muc_roomid_policy_hint: '<br><b>Policy for groupchat id:</b><br>- between 5 and 40 characters,<br>- lowercase from a to z (no special characters) or<br>- digits or<br>- dots (.) or<br>- underlines (_) or<br>- hyphens (-),<br>- no spaces<br>',
muc_show_join_leave
-------------------
muc_show_info_messages
----------------------
* Default; ``true``
* Default: List composed of MUC status codes, role changes, join and leave events
and affiliation changes. The values of converse.MUC_INFO_CODES below are joined to
build the default list:
.. code-block:: javascript
converse.MUC_AFFILIATION_CHANGES_LIST = ['owner', 'admin', 'member', 'exowner', 'exadmin', 'exmember', 'exoutcast']
converse.MUC_ROLE_CHANGES_LIST = ['op', 'deop', 'voice', 'mute'];
converse.MUC_TRAFFIC_STATES_LIST = ['entered', 'exited'];
converse.MUC_INFO_CODES = {
'visibility_changes': ['100', '102', '103', '172', '173', '174'],
'self': ['110'],
'non_privacy_changes': ['104', '201'],
'muc_logging_changes': ['170', '171'],
'nickname_changes': ['210', '303'],
'disconnect_messages': ['301', '307', '321', '322', '332', '333'],
'affiliation_changes': [...converse.AFFILIATION_CHANGES_LIST],
'join_leave_events': [...converse.MUC_TRAFFIC_STATES_LIST],
'role_changes': [...converse.MUC_ROLE_CHANGES_LIST],
};
This setting determines which info messages will Converse show inside a chatroom.
It is recommended to use the aforementioned Converse object in the following fashion
to build the list of desired info messages that will be shown:
.. code-block:: javascript
muc_show_info_messages: [
...converse.MUC_INFO_CODES.visibility_changes,
...converse.MUC_INFO_CODES.self,
...converse.MUC_INFO_CODES.non_privacy_changes,
...converse.MUC_INFO_CODES.muc_logging_changes,
...converse.MUC_INFO_CODES.nickname_changes,
...converse.MUC_INFO_CODES.disconnect_messages,
...converse.MUC_INFO_CODES.affiliation_changes,
...converse.MUC_INFO_CODES.join_leave_events,
...converse.MUC_INFO_CODES.role_changes,
]
By default all info messages are shown.
The behaviour of this setting is whitelisting, so if it is overriden all the desired
events must be specified.
Determines whether Converse will show info messages inside a chatroom
whenever a user joins or leaves it.
If an empty list is provided, no info message will be displayed at all.
muc_show_logs_before_join
-------------------------
......
......@@ -1302,9 +1302,9 @@ describe("Groupchats", function () {
done();
}));
it("doesn't show the disconnection messages when muc_show_join_leave is false",
it("doesn't show the disconnection messages when join_leave_events is not in muc_show_info_messages setting",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {'muc_show_join_leave': false},
['rosterGroupsFetched', 'chatBoxesFetched'], {'muc_show_info_messages': []},
async function (done, _converse) {
spyOn(_converse.ChatRoom.prototype, 'onOccupantAdded').and.callThrough();
......
......@@ -172,9 +172,14 @@ export const ChatRoomView = ChatBoxView.extend({
getNotifications () {
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.MUC_ROLE_CHANGES] :
converse.CHAT_STATES;
const role_changes = api.settings.get('muc_show_info_messages')
.filter(role_change => converse.MUC_ROLE_CHANGES_LIST.includes(role_change));
const join_leave_events = api.settings.get('muc_show_info_messages')
.filter(join_leave_event => converse.MUC_TRAFFIC_STATES_LIST.includes(join_leave_event));
const states = [...converse.CHAT_STATES, ...join_leave_events, ...role_changes];
return states.reduce((result, state) => {
const existing_actors = actors_per_state[state];
......
......@@ -20,12 +20,30 @@ import p from "./utils/parse-helpers";
export const ROLES = ['moderator', 'participant', 'visitor'];
export const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none'];
converse.MUC_TRAFFIC_STATES = ['entered', 'exited'];
converse.MUC_ROLE_CHANGES = ['op', 'deop', 'voice', 'mute'];
converse.AFFILIATION_CHANGES = {
OWNER: 'owner', ADMIN: 'admin', MEMBER: 'member', EXADMIN: 'exadmin',
EXOWNER: 'exowner', EXOUTCAST: 'exoutcast', EXMEMBER: 'exmember'
}
converse.AFFILIATION_CHANGES_LIST = Object.values(converse.AFFILIATION_CHANGES);
converse.MUC_TRAFFIC_STATES = { ENTERED: 'entered', EXITED: 'exited' };
converse.MUC_TRAFFIC_STATES_LIST = Object.values(converse.MUC_TRAFFIC_STATES);
converse.MUC_ROLE_CHANGES = {OP: 'op', DEOP: 'deop', VOICE: 'voice', MUTE: 'mute'}
converse.MUC_ROLE_CHANGES_LIST = Object.values(converse.MUC_ROLE_CHANGES);
const ACTION_INFO_CODES = ['301', '303', '333', '307', '321', '322'];
converse.MUC_INFO_CODES = {
'visibility_changes': ['100', '102', '103', '172', '173', '174'],
'self': ['110'],
'non_privacy_changes': ['104', '201'],
'muc_logging_changes': ['170', '171'],
'nickname_changes': ['210', '303'],
'disconnect_messages': ['301', '307', '321', '322', '332', '333'],
'affiliation_changes': [...converse.AFFILIATION_CHANGES_LIST],
'join_leave_events': [...converse.MUC_TRAFFIC_STATES_LIST],
'role_changes': [...converse.MUC_ROLE_CHANGES_LIST],
};
const MUC_ROLE_WEIGHTS = {
'moderator': 1,
'participant': 2,
......@@ -132,8 +150,18 @@ converse.plugins.add('converse-muc', {
'muc_instant_rooms': true,
'muc_nickname_from_jid': false,
'muc_send_probes': false,
'muc_show_join_leave': true,
'muc_show_logs_before_join': false
'muc_show_info_messages': [
...converse.MUC_INFO_CODES.visibility_changes,
...converse.MUC_INFO_CODES.self,
...converse.MUC_INFO_CODES.non_privacy_changes,
...converse.MUC_INFO_CODES.muc_logging_changes,
...converse.MUC_INFO_CODES.nickname_changes,
...converse.MUC_INFO_CODES.disconnect_messages,
...converse.MUC_INFO_CODES.affiliation_changes,
...converse.MUC_INFO_CODES.join_leave_events,
...converse.MUC_INFO_CODES.role_changes,
],
'muc_show_logs_before_join': false,
});
api.promises.add(['roomsAutoJoined']);
......@@ -196,6 +224,19 @@ converse.plugins.add('converse-muc', {
}
/**
* Determines info message visibility based on
* muc_show_info_messages configuration setting
* @param {*} code
* @memberOf _converse
*/
_converse.isInfoVisible = function (code) {
const info_messages = api.settings.get('muc_show_info_messages');
if (info_messages.includes(code)) {
return true;
}
return false;
}
async function openRoom (jid) {
if (!u.isValidMUCJID(jid)) {
......@@ -491,29 +532,31 @@ converse.plugins.add('converse-muc', {
},
onOccupantAdded (occupant) {
if (api.settings.get('muc_show_join_leave') &&
if (_converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.ENTERED) &&
this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED &&
occupant.get('show') === 'online') {
this.updateNotifications(occupant.get('nick'), 'entered');
this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.ENTERED);
}
},
onOccupantRemoved (occupant) {
if (api.settings.get('muc_show_join_leave') &&
if (_converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.EXITED) &&
this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED &&
occupant.get('show') === 'online') {
this.updateNotifications(occupant.get('nick'), 'exited');
this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.EXITED);
}
},
onOccupantShowChanged (occupant) {
if (occupant.get('states').includes('303') || !api.settings.get('muc_show_join_leave')) {
if (occupant.get('states').includes('303')) {
return;
}
if (occupant.get('show') === 'offline') {
this.updateNotifications(occupant.get('nick'), 'exited');
} else if (occupant.get('show') === 'online') {
this.updateNotifications(occupant.get('nick'), 'entered');
if (occupant.get('show') === 'offline' &&
_converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.EXITED)) {
this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.EXITED);
} else if (occupant.get('show') === 'online' &&
_converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.ENTERED)) {
this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.ENTERED);
}
},
......@@ -1975,8 +2018,8 @@ converse.plugins.add('converse-muc', {
return out;
};
const actors_per_chat_state = converse.CHAT_STATES.reduce(reducer, {});
const actors_per_traffic_state = converse.MUC_TRAFFIC_STATES.reduce(reducer, {});
const actors_per_role_change = converse.MUC_ROLE_CHANGES.reduce(reducer, {});
const actors_per_traffic_state = converse.MUC_TRAFFIC_STATES_LIST.reduce(reducer, {});
const actors_per_role_change = converse.MUC_ROLE_CHANGES_LIST.reduce(reducer, {});
this.notifications.set(Object.assign(
actors_per_chat_state,
actors_per_traffic_state,
......@@ -2093,36 +2136,42 @@ converse.plugins.add('converse-muc', {
}
const current_affiliation = occupant.get('affiliation');
if (previous_affiliation === 'admin') {
if (previous_affiliation === 'admin' &&
_converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXADMIN)) {
this.createMessage({
'type': 'info',
'message': __("%1$s is no longer an admin of this groupchat", occupant.get('nick'))
});
} else if (previous_affiliation === 'owner') {
} else if (previous_affiliation === 'owner' &&
_converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXOWNER)) {
this.createMessage({
'type': 'info',
'message': __("%1$s is no longer an owner of this groupchat", occupant.get('nick'))
});
} else if (previous_affiliation === 'outcast') {
} else if (previous_affiliation === 'outcast' &&
_converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXOUTCAST)) {
this.createMessage({
'type': 'info',
'message': __("%1$s is no longer banned from this groupchat", occupant.get('nick'))
});
}
if (current_affiliation === 'none' && previous_affiliation === 'member') {
if (current_affiliation === 'none' && previous_affiliation === 'member' &&
_converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXMEMBER)) {
this.createMessage({
'type': 'info',
'message': __("%1$s is no longer a member of this groupchat", occupant.get('nick'))
});
}
if (current_affiliation === 'member') {
if (current_affiliation === 'member' &&
_converse.isInfoVisible(converse.AFFILIATION_CHANGES.MEMBER)) {
this.createMessage({
'type': 'info',
'message': __("%1$s is now a member of this groupchat", occupant.get('nick'))
});
} else if (current_affiliation === 'admin' || current_affiliation == 'owner') {
} else if ((current_affiliation === 'admin' && _converse.isInfoVisible(converse.AFFILIATION_CHANGES.ADMIN))
|| (current_affiliation == 'owner' && _converse.isInfoVisible(converse.AFFILIATION_CHANGES.OWNER))) {
// For example: AppleJack is now an (admin|owner) of this groupchat
this.createMessage({
'type': 'info',
......@@ -2141,18 +2190,18 @@ converse.plugins.add('converse-muc', {
return;
}
const previous_role = occupant._previousAttributes.role;
if (previous_role === 'moderator') {
this.updateNotifications(occupant.get('nick'), 'deop');
} else if (previous_role === 'visitor') {
this.updateNotifications(occupant.get('nick'), 'voice');
if (previous_role === 'moderator' && _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.DEOP)) {
this.updateNotifications(occupant.get('nick'), converse.MUC_ROLE_CHANGES.DEOP);
} else if (previous_role === 'visitor' && _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.VOICE)) {
this.updateNotifications(occupant.get('nick'), converse.MUC_ROLE_CHANGES.VOICE);
}
if (occupant.get('role') === 'visitor') {
this.updateNotifications(occupant.get('nick'), 'mute');
if (occupant.get('role') === 'visitor' && _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.MUTE)) {
this.updateNotifications(occupant.get('nick'), converse.MUC_ROLE_CHANGES.MUTE);
} else if (occupant.get('role') === 'moderator') {
if (!['owner', 'admin'].includes(occupant.get('affiliation'))) {
if (!['owner', 'admin'].includes(occupant.get('affiliation')) && _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.OP)) {
// Oly show this message if the user isn't already
// an admin or owner, otherwise this isn't new information.
this.updateNotifications(occupant.get('nick'), 'op');
this.updateNotifications(occupant.get('nick'), converse.MUC_ROLE_CHANGES.OP);
}
}
},
......@@ -2167,8 +2216,10 @@ converse.plugins.add('converse-muc', {
* @param { Boolean } is_self - Whether this stanza refers to our own presence
*/
createInfoMessage (code, stanza, is_self) {
const data = { 'type': 'info', };
const data = { 'type': 'info' };
if (!_converse.isInfoVisible(code)){
return;
}
if (code === '110' || (code === '100' && !is_self)) {
return;
} else if (code in _converse.muc.info_messages) {
......@@ -2176,26 +2227,32 @@ converse.plugins.add('converse-muc', {
} 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.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)) {
} else if (is_self && code in _converse.muc.new_nickname_messages) {
// XXX: Side-effect of setting the nick. Should ideally be refactored out of this method
let nick;
if (is_self && code === "210") {
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');
} 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)) {
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) {
} else if (
code in _converse.muc.info_messages &&
this.messages.length &&
this.messages.pop().get('message') === data.message
) {
// XXX: very naive duplication checking
return;
}
......
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