Commit 1419b4fc authored by JC Brand's avatar JC Brand

MUC: Add support for status code 333

parent 945d7e98
...@@ -2778,6 +2778,57 @@ describe("Groupchats", function () { ...@@ -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(
......
...@@ -23,6 +23,8 @@ export const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none']; ...@@ -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', { ...@@ -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', { ...@@ -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', { ...@@ -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', { ...@@ -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 codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code'));
const x = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"]`, stanza).pop(); if (codes.includes('333') && codes.includes('307')) {
if (!x) { // See: https://github.com/xsf/xeps/pull/969/files#diff-ac5113766e59219806793c1f7d967f1bR4966
return; codes.splice(codes.indexOf('307'), 1);
} }
sizzle('status', x).map(s => s.getAttribute('code')).forEach(code => { const is_self = codes.includes('110');
const data = { codes.forEach(code => this.createInfoMessage(code, stanza, is_self));
'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);
}
});
}, },
......
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