UNPKG

converse.js

Version:
161 lines (141 loc) 5.51 kB
/** * @typedef {module:plugin-muc-parsers.MemberListItem} MemberListItem * @typedef {import('@converse/skeletor/src/types/collection').Attributes} Attributes * @typedef {import('@converse/skeletor/src/types/collection').CollectionOptions} CollectionOptions * @typedef {import('@converse/skeletor/src/types/collection').Options} Options */ import MUCOccupant from './occupant.js'; import _converse from '../../shared/_converse.js'; import api from '../../shared/api/index.js'; import converse from '../../shared/api/public.js'; import { Collection, Model } from '@converse/skeletor'; import { Strophe } from 'strophe.js'; import { getAffiliationList } from './affiliations/utils.js'; import { occupantsComparator } from './utils.js'; import { getUniqueId } from '../../utils/index.js'; const { u } = converse.env; /** * A list of {@link MUCOccupant} instances, representing participants in a MUC. * @class * @memberOf _converse */ class MUCOccupants extends Collection { constructor (attrs, options) { super( attrs, Object.assign({ comparator: occupantsComparator }, options) ); this.chatroom = null; } get model() { return MUCOccupant; } initialize() { this.on('change:nick', () => this.sort()); this.on('change:role', () => this.sort()); } static getAutoFetchedAffiliationLists () { const affs = api.settings.get('muc_fetch_members'); return Array.isArray(affs) ? affs : affs ? ['member', 'admin', 'owner'] : []; } /** * @param {Model|Attributes} attrs * @param {Options} [options] */ create (attrs, options) { if (attrs.id || attrs instanceof Model) { return super.create(attrs, options); } attrs.id = attrs.occupant_id || getUniqueId(); return super.create(attrs, options); } async fetchMembers () { if (!['member', 'admin', 'owner'].includes(this.getOwnOccupant()?.get('affiliation'))) { // https://xmpp.org/extensions/xep-0045.html#affil-priv return; } const affiliations = MUCOccupants.getAutoFetchedAffiliationLists(); if (affiliations.length === 0) { return; } const muc_jid = this.chatroom.get('jid'); const aff_lists = await Promise.all(affiliations.map(a => getAffiliationList(a, muc_jid))); const new_members = aff_lists.reduce( /** * @param {MemberListItem[]} acc * @param {MemberListItem[]|Error} val * @returns {MemberListItem[]} */ (acc, val) => { if (val instanceof Error) { return acc; } return [...val, ...acc]; }, [] ); const known_affiliations = affiliations.filter( a => !u.isErrorObject(aff_lists[affiliations.indexOf(a)]) ); const new_jids = /** @type {MemberListItem[]} */(new_members).map(m => m.jid).filter(m => m !== undefined); const new_nicks = /** @type {MemberListItem[]} */(new_members).map( (m) => (!m.jid && m.nick) || undefined).filter(m => m !== undefined); const removed_members = this.filter(m => { return ( known_affiliations.includes(m.get('affiliation')) && !new_nicks.includes(m.get('nick')) && !new_jids.includes(m.get('jid')) ); }); const bare_jid = _converse.session.get('bare_jid'); removed_members.forEach(occupant => { if (occupant.get('jid') === bare_jid) { return; } else if (occupant.get('show') === 'offline') { occupant.destroy(); } else { occupant.save('affiliation', null); } }); /** @type {MemberListItem[]} */(new_members).forEach(attrs => { const occupant = this.findOccupant(attrs); occupant ? occupant.save(attrs) : this.create(attrs); }); /** * Triggered once the member lists for this MUC have been fetched and processed. * @event _converse#membersFetched * @example _converse.api.listen.on('membersFetched', () => { ... }); */ api.trigger('membersFetched'); } /** * Try to find an existing occupant based on the provided {@link OccupantData} object. * Fetching the user by `occupant_id` is the quickest, O(1), * since it's a dictionary lookup. * Fetching by jid or nick is O(n), since it requires traversing an array. * Lookup by occupant_id is done first, then jid, and then nick. * * @param {import('./types').OccupantData} data */ findOccupant (data) { if (data.occupant_id) { return this.get(data.occupant_id); } const jid = data.jid && Strophe.getBareJidFromJid(data.jid); return jid && this.findWhere({ jid }) || data.nick && this.findWhere({ 'nick': data.nick }); } /** * Get the {@link MUCOccupant} instance which * represents the current user. * @method _converse.MUCOccupants#getOwnOccupant * @returns {MUCOccupant} */ getOwnOccupant () { return this.findOccupant({ jid: _converse.session.get('bare_jid'), occupant_id: this.chatroom.get('occupant_id') }); } } // @ts-ignore export default MUCOccupants;