Newer
Older
/**
* Add RAI support to MUCs.
* @see https://xmpp.org/extensions/inbox/room-activity-indicators.html
*/
converse.plugins.add('converse-rai', {
dependencies: [
'converse-chatboxes',
],
overrides: {
ChatRoom: {
async initialize() {
await this.__super__.initialize.apply(this, arguments);
this.listenTo(this, 'change:hidden', this.onHiddenChange);
},
/**
* Handler that gets called when the 'hidden' flag is toggled.
* @private
* @method _converse.ChatRoom#onHiddenChange
*/
async onHiddenChange() {
const conn_status = this.session.get('connection_status');
const { api } = this.__super__._converse;
if (this.get('hidden')
&& conn_status === converse.ROOMSTATUS.ENTERED
&& api.settings.get('muc_subscribe_to_rai')
) {
this.sendMarkerForLastMessage('received', true);
await this.leave();
await this.close();
} else if (conn_status === converse.ROOMSTATUS.DISCONNECTED) {
/**
* Subscribe to RAI if connected to the room, but it's hidden.
* @private
* @method _converse.ChatRoom#onConnectionStatusChanged
*/
async onConnectionStatusChanged() {
const conn_status = this.session.get('connection_status');
const { api } = this.__super__._converse;
if (this.get('hidden')
&& conn_status === converse.ROOMSTATUS.ENTERED
&& api.settings.get('muc_subscribe_to_rai')
) {
await this.leave();
await this.close();
this.enableRAI();
} else {
await this.__super__.onConnectionStatusChanged.apply(this, arguments);
}
},
/**
* 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() {
const { api } = this.__super__._converse;
if (api.settings.get('muc_subscribe_to_rai')) {
api.rooms.subscribe(this.get('jid'));
}
},
},
ChatBox: {
/**
* Finds the last eligible message and then sends a XEP-0333 chat marker for it.
* @param { ('received'|'displayed'|'acknowledged') } [type='displayed']
* @param { Boolean } force - Whether a marker should be sent for the
* message, even if it didn't include a `markable` element.
*/
sendMarkerForLastMessage(type = 'displayed', force = false) {
const msgs = Array.from(this.messages.models);
msgs.reverse();
const msg = msgs.find(m => m.get('sender') === 'them' && (force || m.get('is_markable')));
msg && this.sendMarkerForMessage(msg, type, force);
},
/**
* Given the passed in message object, send a XEP-0333 chat marker.
* @param { _converse.Message } msg
* @param { ('received'|'displayed'|'acknowledged') } [type='displayed']
* @param { Boolean } force - Whether a marker should be sent for the
* message, even if it didn't include a `markable` element.
*/
sendMarkerForMessage(msg, type = 'displayed', force = false) {
const _converse = this.__super__._converse;
// Markers should be sent for message with a stanza_id
const is_groupchat = this.get('type') === _converse.CHATROOMS_TYPE;
const by_jid = is_groupchat ? this.get('jid') : _converse.bare_jid;
const stanza_id = msg.get('stanza_id '.concat(by_jid));
if (msg && (msg?.get('is_markable') || force) && stanza_id) {
const from_jid = Strophe.getBareJidFromJid(msg.get('from'));
this.sendMarker(from_jid, stanza_id, type, msg.get('type'));
},
initialize() {
const { Strophe, _, $pres, u, sizzle, log } = converse.env;
const _converse = this._converse;
Strophe.addNamespace('RAI', 'urn:xmpp:rai:0');
/**
* Send an RAI stanza for the given jids.
* The presence stanza is sent for a whole muc domain.
*
* @param {string|Array} jids
* @returns {void}
*/
log.error(`You must enable the 'muc_subscribe_to_rai' option before subscribing to a MUC.`);
return;
if (typeof jids === 'string') {
jids = [jids];
const muc_domains = _.uniq(jids.map(jid => Strophe.getDomainFromJid(jid)));
for (let domain of muc_domains) {
const rai = $pres({ to: domain, id: u.getUniqueId() }).c('rai', {
await _converse.connection.send(rai);
log.debug(`Sent RAI stanza for domain "${domain}"`);
}
/**
* Send a displayed marker once the chatroom is scrolled down.
* FIXME: This is sending two requests (for the same message)
*/
api.listen.on('chatBoxScrolledDown', data => {
if (data.chatbox.get('type') === _converse.CHATROOMS_TYPE && api.settings.get('muc_subscribe_to_rai')) {
data.chatbox.sendMarkerForLastMessage('displayed', true);
}
});
/**
* Triggers an event if a jid has activity.
*
* @param message
* @returns {boolean}
*/
function mucActivityHandler(message) {
const rai = sizzle(`rai[xmlns="${Strophe.NS.RAI}"]`, message).pop();
if (rai) {
rai.querySelectorAll('activity').forEach(activity => {
const jid = activity.textContent;
log.debug(`Received activity for the room: ${jid}`);
_converse.connection.addHandler(mucActivityHandler, null, 'message');