Make sure that the occupant_id is also the id for occupants

insofar we have an `occupant_id`.

We do this by subclassing `create` on the `ChatRoomOccupants` collection
and `save` on the `ChatRoomOccupant` model, to make sure that whenever
an occupant is created or saved, that the `id` matches the `occupant_id`
value if it's available.

This lets us look up the occupant via `occupant_id` via dictionary lookup,
instead of array traversal.

Another change is to save `from_real_jid` when adding an occupant to a message
This commit is contained in:
JC Brand 2022-04-26 14:24:55 +02:00
parent b71a7ae2ac
commit d22c063ae5
3 changed files with 34 additions and 11 deletions

View File

@ -81,13 +81,14 @@ const ChatRoomMessageMixin = {
} else if (occupant.get('nick') !== Strophe.getResourceFromJid(this.get('from'))) {
return;
}
this.occupant = occupant;
this.trigger('occupantAdded');
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
const chatbox = this?.collection?.chatbox;
if (!chatbox) {
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
}
this.occupant = occupant;
this.trigger('occupantAdded');
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
this.stopListening(chatbox.occupants, 'add', this.onOccupantAdded);
},
@ -100,10 +101,11 @@ const ChatRoomMessageMixin = {
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
}
const nick = Strophe.getResourceFromJid(this.get('from'));
this.occupant = chatbox.occupants.findOccupant({ nick, 'occupant_id': this.get('occupant_id') });
const occupant_id = this.get('occupant_id');
this.occupant = chatbox.occupants.findOccupant({ nick, occupant_id });
if (!this.occupant && api.settings.get('muc_send_probes')) {
this.occupant = chatbox.occupants.create({ nick, 'type': 'unavailable' });
this.occupant = chatbox.occupants.create({ nick, occupant_id, 'type': 'unavailable' });
const jid = `${chatbox.get('jid')}/${nick}`;
api.user.presence.send('probe', jid);
}

View File

@ -1704,6 +1704,7 @@ const ChatRoomMixin = {
if (data.type === 'error' || (!data.jid && !data.nick && !data.occupant_id)) {
return true;
}
const occupant = this.occupants.findOccupant(data);
// Destroy an unavailable occupant if this isn't a nick change operation and if they're not affiliated
if (
@ -1717,11 +1718,14 @@ const ChatRoomMixin = {
occupant.destroy();
return;
}
const jid = data.jid || '';
const attributes = Object.assign(data, {
const attributes = {
...data,
'jid': Strophe.getBareJidFromJid(jid) || occupant?.attributes?.jid,
'resource': Strophe.getResourceFromJid(jid) || occupant?.attributes?.resource
});
}
if (occupant) {
occupant.save(attributes);
} else {

View File

@ -2,10 +2,12 @@ import ChatRoomOccupant from './occupant.js';
import u from '../../utils/form';
import { Collection } from '@converse/skeletor/src/collection.js';
import { MUC_ROLE_WEIGHTS } from './constants.js';
import { Model } from '@converse/skeletor/src/model.js';
import { Strophe } from 'strophe.js/src/strophe.js';
import { _converse, api } from '../../core.js';
import { getAffiliationList } from './affiliations/utils.js';
import { getAutoFetchedAffiliationLists } from './utils.js';
import { getUniqueId } from '@converse/headless/utils/core.js';
/**
@ -29,6 +31,14 @@ class ChatRoomOccupants extends Collection {
}
}
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);
}
/**
* Get the {@link _converse.ChatRoomOccupant} instance which
* represents the current user.
@ -94,16 +104,23 @@ class ChatRoomOccupants extends Collection {
* Try to find an existing occupant based on the passed in
* data object.
*
* If we have a JID, we use that as lookup variable,
* otherwise we use the nick. We don't always have both,
* but should have at least one or the other.
* 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.
*
* @method _converse.ChatRoomOccupants#findOccupant
* @param { OccupantData } data
*/
findOccupant (data) {
if (data.occupant_id && this.get(data.occupant_id)) {
return this.get(data.occupant_id);
}
const jid = data.jid && Strophe.getBareJidFromJid(data.jid);
return jid && this.findWhere({ jid }) ||
data.occupant_id && this.findWhere({ 'occupant_id': data.occupant_id }) ||
data.nick && this.findWhere({ 'nick': data.nick });
}
}