Commit 6b9c718d authored by JC Brand's avatar JC Brand

RAI: Handle MUCs that start out hidden

When a MUC starts out hidden, we first need to join in order to find out
whether we're affiliated before we can know whether we should enable RAI
or join the MUC as usual.
parent 986f7550
......@@ -313,10 +313,10 @@ window.addEventListener('converse-loaded', () => {
};
mock.openAndEnterChatRoom = async function (_converse, muc_jid, nick, features=[], members=[], force_open=true) {
mock.openAndEnterChatRoom = async function (_converse, muc_jid, nick, features=[], members=[], force_open=true, settings={}) {
const { api } = _converse;
muc_jid = muc_jid.toLowerCase();
const room_creation_promise = api.rooms.open(muc_jid, {}, force_open);
const room_creation_promise = api.rooms.open(muc_jid, settings, force_open);
await mock.getRoomFeatures(_converse, muc_jid, features);
await mock.waitForReservedNick(_converse, muc_jid, nick);
// The user has just entered the room (because join was called)
......
......@@ -114,6 +114,70 @@ describe("XEP-0437 Room Activity Indicators", function () {
done();
}));
it("will be activated for a MUC that starts out hidden",
mock.initConverse(
['rosterGroupsFetched'], {
'allow_bookmarks': false, // Hack to get the rooms list to render
'muc_subscribe_to_rai': true,
'view_mode': 'fullscreen'},
async function (done, _converse) {
const { api } = _converse;
expect(_converse.session.get('rai_enabled_domains')).toBe(undefined);
const muc_jid = 'lounge@montague.lit';
const nick = 'romeo';
const sent_stanzas = _converse.connection.sent_stanzas;
const muc_creation_promise = await api.rooms.open(muc_jid, {nick, 'hidden': true}, false);
await mock.getRoomFeatures(_converse, muc_jid, []);
await mock.receiveOwnMUCPresence(_converse, muc_jid, nick);
await muc_creation_promise;
const view = api.chatviews.get(muc_jid);
await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED));
expect(view.model.get('hidden')).toBe(true);
const getSentPresences = () => sent_stanzas.filter(s => s.nodeName === 'presence');
await u.waitUntil(() => getSentPresences().length === 3, 500);
const sent_presences = getSentPresences();
expect(Strophe.serialize(sent_presences[1])).toBe(
`<presence to="${muc_jid}/romeo" type="unavailable" xmlns="jabber:client">`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`
);
expect(Strophe.serialize(sent_presences[2])).toBe(
`<presence to="montague.lit" xmlns="jabber:client">`+
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
`<rai xmlns="urn:xmpp:rai:0"/>`+
`</presence>`
);
await u.waitUntil(() => view.model.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED);
expect(view.model.get('has_activity')).toBe(false);
const lview = _converse.rooms_list_view
const room_el = await u.waitUntil(() => lview.el.querySelector(".available-chatroom"));
expect(Array.from(room_el.classList).includes('unread-msgs')).toBeFalsy();
const activity_stanza = u.toStanza(`
<message from="${Strophe.getDomainFromJid(muc_jid)}">
<rai xmlns="urn:xmpp:rai:0">
<activity>${muc_jid}</activity>
</rai>
</message>
`);
_converse.connection._dataRecv(mock.createRequest(activity_stanza));
await u.waitUntil(() => view.model.get('has_activity'));
expect(Array.from(room_el.classList).includes('unread-msgs')).toBeTruthy();
done();
}));
it("may not be activated due to server resource constraints",
mock.initConverse(
......
......@@ -125,6 +125,7 @@ const ChatRoomMixin = {
// so we don't send out a presence stanza again.
return this;
}
// Set this early, so we don't rejoin in onHiddenChange
this.session.save('connection_status', converse.ROOMSTATUS.CONNECTING);
await this.refreshDiscoInfo();
......@@ -154,6 +155,17 @@ const ChatRoomMixin = {
return this;
},
/**
* Clear stale cache and re-join a MUC we've been in before.
* @private
* @method _converse.ChatRoom#rejoin
*/
rejoin () {
this.registerHandlers();
this.clearCache();
return this.join();
},
clearCache () {
this.session.save('connection_status', converse.ROOMSTATUS.DISCONNECTED);
if (this.occupants.length) {
......@@ -185,12 +197,18 @@ const ChatRoomMixin = {
}
},
async enableRAI () {
if (api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
this.sendMarkerForLastMessage('received', true);
if (this.session.get('connection_status') !== converse.ROOMSTATUS.DISCONNECTED) {
await this.leave();
}
/**
* Ensures that the user is subscribed to XEP-0437 Room Activity Indicators
* if `muc_subscribe_to_rai` is set to `true`.
* Only affiliated users can subscribe to RAI, but this method doesn't
* check whether the current user is affiliated because it's intended to be
* called after the MUC has been left and we don't have that information
* anymore.
* @private
* @method _converse.ChatRoom#enableRAI
*/
enableRAI () {
if (api.settings.get('muc_subscribe_to_rai')) {
const rai_enabled = _converse.session.get('rai_enabled_domains') || '';
const muc_domain = Strophe.getDomainFromJid(this.get('jid'));
if (!rai_enabled.includes(muc_domain)) {
......@@ -203,12 +221,19 @@ const ChatRoomMixin = {
/**
* Handler that gets called when the 'hidden' flag is toggled.
* @private
* @method _converse.ChatRoomView#onHiddenChange
*/
onHiddenChange () {
if (this.get('hidden')) {
this.enableRAI();
} else if (this.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
* @method _converse.ChatRoom#onHiddenChange
*/
async onHiddenChange () {
const conn_status = this.session.get('connection_status');
if (this.get('hidden') && conn_status === converse.ROOMSTATUS.ENTERED) {
if (api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
if (conn_status !== converse.ROOMSTATUS.DISCONNECTED) {
this.sendMarkerForLastMessage('received', true);
await this.leave();
}
this.enableRAI();
}
} else if (conn_status === converse.ROOMSTATUS.DISCONNECTED) {
this.rejoin();
}
},
......@@ -244,44 +269,40 @@ const ChatRoomMixin = {
}
},
/**
* Clear stale cache and re-join a MUC we've been in before.
* @private
* @method _converse.ChatRoom#rejoin
*/
rejoin () {
this.registerHandlers();
this.clearCache();
return this.join();
async onRoomEntered () {
await this.occupants.fetchMembers();
if (api.settings.get('clear_messages_on_reconnection')) {
// Don't call this.clearMessages because we don't want to
// recreate promises, since that will cause some existing
// awaiters to never proceed.
await this.messages.clearStore();
// A bit hacky. No need to fetch messages after clearing
this.messages.fetched.resolve();
} else {
await this.fetchMessages();
}
/**
* Triggered when the user has entered a new MUC
* @event _converse#enteredNewRoom
* @type { _converse.ChatRoom}
* @example _converse.api.listen.on('enteredNewRoom', model => { ... });
*/
api.trigger('enteredNewRoom', this);
if (
api.settings.get('auto_register_muc_nickname') &&
(await api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid')))
) {
this.registerNickname();
}
},
async onConnectionStatusChanged () {
if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
await this.occupants.fetchMembers();
if (api.settings.get('clear_messages_on_reconnection')) {
// Don't call this.clearMessages because we don't want to
// recreate promises, since that will cause some existing
// awaiters to never proceed.
await this.messages.clearStore();
// A bit hacky. No need to fetch messages after clearing
this.messages.fetched.resolve();
if (this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
await this.leave();
this.enableRAI();
} else {
await this.fetchMessages();
}
/**
* Triggered when the user has entered a new MUC
* @event _converse#enteredNewRoom
* @type { _converse.ChatRoom}
* @example _converse.api.listen.on('enteredNewRoom', model => { ... });
*/
api.trigger('enteredNewRoom', this);
if (
api.settings.get('auto_register_muc_nickname') &&
(await api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid')))
) {
this.registerNickname();
await this.onRoomEntered();
}
}
},
......
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