Only send presence status update when a MUC is entered, and when joining
a MUC, include the `<show>` element.
This commit is contained in:
JC Brand 2022-02-18 10:23:54 +01:00
parent 20cd90855f
commit beb220f188
5 changed files with 92 additions and 23 deletions

View File

@ -11,6 +11,7 @@
- #2733: Fix OMEMO race condition related to automatic reconnection and SMACKS
- #2733: Wait for decrypted/parsed message before queuing to UI
- #2751: Media not rendered when Converse runs in a browser extension
- #2781: Flashing error when connecting to a room
- #2786: Fix webpack configuration not working on Windows OS
- #2788: `TypeError` when trying to use `@converse/headless`
- #2789: Implement new hook `parseMessageForCommands` for plugins to add custom commands

View File

@ -175,7 +175,7 @@ const ChatRoomMixin = {
}
return this;
}
api.send(await this.constructPresence(password));
api.send(await this.constructJoinPresence(password));
return this;
},
@ -191,7 +191,7 @@ const ChatRoomMixin = {
return this.join();
},
async constructPresence (password) {
async constructJoinPresence (password) {
let stanza = $pres({
'id': getUniqueId(),
'from': _converse.connection.jid,
@ -205,6 +205,17 @@ const ChatRoomMixin = {
if (password) {
stanza.cnode(Strophe.xmlElement('password', [], password));
}
stanza.up(); // Go one level up, out of the `x` element.
const status = _converse.xmppstatus.get('status');
if (['away', 'chat', 'dnd', 'online', 'xa'].includes(status)) {
stanza.c('show').t(status).up();
}
const status_message = _converse.xmppstatus.get('status_message');
if (status_message) {
stanza.c('status').t(status_message).up();
}
stanza = await api.hook('constructedMUCPresence', null, stanza);
return stanza;
},
@ -271,10 +282,9 @@ const ChatRoomMixin = {
if (conn_status === roomstatus.ENTERED &&
api.settings.get('muc_subscribe_to_rai') &&
this.getOwnAffiliation() !== 'none') {
if (conn_status !== roomstatus.DISCONNECTED && conn_status !== roomstatus.CLOSING) {
this.sendMarkerForLastMessage('received', true);
await this.leave();
}
this.enableRAI();
}
} else {
@ -869,7 +879,7 @@ const ChatRoomMixin = {
await new Promise(resolve =>
this.features.destroy({
'success': resolve,
'error': (m, e) => { log.error(e); resolve(); }
'error': (_, e) => { log.error(e); resolve(); }
})
);
}
@ -878,7 +888,7 @@ const ChatRoomMixin = {
if (disco_entity) {
await new Promise(resolve => disco_entity.destroy({
'success': resolve,
'error': (m, e) => { log.error(e); resolve(); }
'error': (_, e) => { log.error(e); resolve(); }
}));
}
u.safeSave(this.session, { 'connection_status': converse.ROOMSTATUS.DISCONNECTED });
@ -899,7 +909,7 @@ const ChatRoomMixin = {
await new Promise(resolve =>
this.session.destroy({
'success': resolve,
'error': (m, e) => { log.error(e); resolve(); }
'error': (_, e) => { log.error(e); resolve(); }
})
);
return _converse.ChatBox.prototype.close.call(this);
@ -1918,14 +1928,19 @@ const ChatRoomMixin = {
},
/**
* When sending a status update presence (i.e. based on the `<show>`
* element), we need to first make sure that the MUC is connected,
* otherwise we will get an error from the MUC service.
* Sends a status update presence (i.e. based on the `<show>` element)
* @method _converse.ChatRoom#sendStatusPresence
* @param { String } type
* @param { String } [status] - An optional status message
* @param { Element[]|Strophe.Builder[]|Element|Strophe.Builder } [child_nodes]
* Nodes(s) to be added as child nodes of the `presence` XML element.
*/
async sendStatusPresence (presence) {
await this.rejoinIfNecessary();
async sendStatusPresence (type, status, child_nodes) {
if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
const presence = await _converse.xmppstatus.constructPresence(type, this.getRoomJIDAndNick(), status);
child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
api.send(presence);
}
},
/**

View File

@ -37,7 +37,59 @@ describe("Groupchats", function () {
expect(model.get('num_unread')).toBe(0);
}));
describe("An groupchat", function () {
describe("A groupchat", function () {
it("sends the user status when joining and when it changes",
mock.initConverse(['statusInitialized'], {}, async function (_converse) {
const muc_jid = 'coven@chat.shakespeare.lit';
_converse.xmppstatus.set('status', 'away');
const sent_stanzas = _converse.connection.sent_stanzas;
while (sent_stanzas.length) sent_stanzas.pop();
const muc = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
let pres = await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'presence').pop());
expect(Strophe.serialize(pres)).toBe(
`<presence from="${_converse.jid}" id="${pres.getAttribute('id')}" to="${muc_jid}/romeo" xmlns="jabber:client">`+
`<x xmlns="http://jabber.org/protocol/muc"><history maxstanzas="0"/></x>`+
`<show>away</show>`+
`<c hash="sha-1" node="https://conversejs.org" ver="TfHz9vOOfqIG0Z9lW5CuPaWGnrQ=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`);
expect(muc.getOwnOccupant().get('show')).toBe('away');
while (sent_stanzas.length) sent_stanzas.pop();
_converse.xmppstatus.set('status', 'xa');
pres = await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'presence').pop());
expect(Strophe.serialize(pres)).toBe(
`<presence to="${muc_jid}/romeo" xmlns="jabber:client">`+
`<show>xa</show>`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="TfHz9vOOfqIG0Z9lW5CuPaWGnrQ=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`)
_converse.xmppstatus.set('status', 'dnd');
_converse.xmppstatus.set('status_message', 'Do not disturb');
while (sent_stanzas.length) sent_stanzas.pop();
const muc2_jid = 'cave@chat.shakespeare.lit';
const muc2 = await mock.openAndEnterChatRoom(_converse, muc2_jid, 'romeo');
pres = await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'presence').pop());
expect(Strophe.serialize(pres)).toBe(
`<presence from="${_converse.jid}" id="${pres.getAttribute('id')}" to="${muc2_jid}/romeo" xmlns="jabber:client">`+
`<x xmlns="http://jabber.org/protocol/muc"><history maxstanzas="0"/></x>`+
`<show>dnd</show>`+
`<status>Do not disturb</status>`+
`<c hash="sha-1" node="https://conversejs.org" ver="TfHz9vOOfqIG0Z9lW5CuPaWGnrQ=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`);
expect(muc2.getOwnOccupant().get('show')).toBe('dnd');
}));
it("reconnects when no-acceptable error is returned when sending a message",
mock.initConverse([], {}, async function (_converse) {
@ -87,6 +139,7 @@ describe("Groupchats", function () {
expect(Strophe.serialize(pres)).toBe(
`<presence from="${_converse.jid}" id="${pres.getAttribute('id')}" to="coven@chat.shakespeare.lit/romeo" xmlns="jabber:client">`+
`<x xmlns="http://jabber.org/protocol/muc"><history maxstanzas="0"/></x>`+
`<show>online</show>`+
`<c hash="sha-1" node="https://conversejs.org" ver="TfHz9vOOfqIG0Z9lW5CuPaWGnrQ=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`);
}));

View File

@ -28,11 +28,7 @@ export default {
if (['away', 'chat', 'dnd', 'online', 'xa', undefined].includes(type)) {
const mucs = await api.rooms.get();
mucs.forEach(async muc => {
const presence = await model.constructPresence(type, muc.getRoomJIDAndNick(), status);
child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
muc.sendStatusPresence(presence);
});
mucs.forEach(muc => muc.sendStatusPresence(type, status, child_nodes));
}
}
},

View File

@ -292,7 +292,11 @@ async function receiveOwnMUCPresence (_converse, muc_jid, nick, affiliation='own
id: u.getUniqueId()
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
.c('item').attrs({ affiliation, role, 'jid': _converse.bare_jid }).up()
.c('status').attrs({code:'110'});
.c('status').attrs({code:'110'}).up().up()
if (_converse.xmppstatus.get('status')) {
presence.c('show').t(_converse.xmppstatus.get('status'));
}
_converse.connection._dataRecv(createRequest(presence));
}