Set VCards via events, thereby reducing coupling

- Rename `chatRoomOpened` event to `chatRoomViewInitialized`
- Rename `chatBoxInitialized` to `chatBoxViewInitialized` and trigger only for `ChatBoxView` instances.
- New event `headlinesBoxViewInitialized`
- Trigger the `chatBoxInitialized` event when a new `_converse.ChatBox` is opened.
This commit is contained in:
JC Brand 2019-12-18 12:42:40 +01:00
parent c3102561f4
commit 8b6c902c4c
19 changed files with 147 additions and 121 deletions

View File

@ -52,9 +52,14 @@
* `_converse.api.rooms.create`
* `_converse.api.roomviews.close`
- Changes the events:
* The `chatBoxInitialized` event now triggers when a `_converse.ChatBox` (not the view) is opened.
* Renamed the old `chatBoxInitialized` to `chatBoxViewInitialized` and trigger only for `ChatBoxView` instances.
* Renamed `chatRoomOpened` event to `chatRoomViewInitialized`
* The order of certain events have now changed: `statusInitialized` is now triggered after `initialized` and `connected` and `reconnected`.
- `_converse.api.chats.get()` now only returns one-on-one chats, not the control box or headline notifications.
- The `show_only_online_users` setting has been removed.
- The order of certain events have now changed: `statusInitialized` is now triggered after `initialized` and `connected` and `reconnected`.
- `_converse.api.alert.show` is now `_converse.api.show` and instead of taking
an integer for the `type`, "info", "warn" or "error" should be passed in.
- The `converse-headline` plugin has been split up into `converse-headlines` and `converse-headlines-view`.

View File

@ -143,8 +143,8 @@
it("will be automatically opened if 'autojoin' is set on the bookmark", mock.initConverse(
['rosterGroupsFetched'], {},
async function (done, _converse) {
['rosterGroupsFetched'], {},
async function (done, _converse) {
await test_utils.waitUntilDiscoConfirmed(
_converse, _converse.bare_jid,
@ -168,7 +168,7 @@
'name': 'The Play',
'nick': ' Othello'
});
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
await new Promise(resolve => _converse.api.listen.once('chatRoomViewInitialized', resolve));
expect(_.isUndefined(_converse.chatboxviews.get(jid))).toBeFalsy();
// Check that we don't auto-join if muc_respect_autojoin is false

View File

@ -136,7 +136,7 @@
const message_promise = new Promise(resolve => _converse.api.listen.on('message', resolve));
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
await new Promise(resolve => _converse.api.listen.once('chatBoxViewInitialized', resolve));
await u.waitUntil(() => message_promise);
expect(_converse.chatboxviews.keys().length).toBe(2);
done();
@ -390,7 +390,7 @@
await u.waitUntil(() => _converse.chatboxes.length == 7)
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
expect(_converse.chatboxes.length).toEqual(7);
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxInitialized', jasmine.any(Object));
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxViewInitialized', jasmine.any(Object));
await test_utils.closeAllChatBoxes(_converse);
expect(_converse.chatboxes.length).toEqual(1);
@ -1427,7 +1427,7 @@
await u.waitUntil(() => chatbox.messages.length > 1);
expect(select_msgs_indicator().textContent).toBe('2');
view.model.maximize();
expect(select_msgs_indicator()).toBeUndefined();
u.waitUntil(() => typeof select_msgs_indicator() === 'undefined');
done();
}));

View File

@ -106,8 +106,8 @@
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
chatview.model.set({'minimized': false});
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count')).toBe(null);
await u.waitUntil(() => _converse.rosterview.el.querySelector('.msgs-indicator') === null);
done();
}));
});

View File

@ -170,7 +170,7 @@
'id': _converse.connection.getUniqueId()
}).c('body').t('😇').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
await new Promise(resolve => _converse.on('chatBoxInitialized', resolve));
await new Promise(resolve => _converse.on('chatBoxViewInitialized', resolve));
const view = _converse.api.chatviews.get(sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));
const chat_content = view.el.querySelector('.chat-content');

View File

@ -1082,7 +1082,7 @@
'id': (new Date()).getTime()
}).c('body').t('A message').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
await new Promise(resolve => _converse.on('chatBoxInitialized', resolve));
await new Promise(resolve => _converse.on('chatBoxViewInitialized', resolve));
const view = _converse.api.chatviews.get(sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));

View File

@ -360,7 +360,7 @@
expect(_converse.roster.updateContact).toHaveBeenCalled();
// The class on the contact will now have switched.
expect(u.hasClass('to', contacts[0])).toBe(false);
await u.waitUntil(() => !u.hasClass('to', contacts[0]));
expect(u.hasClass('both', contacts[0])).toBe(true);
done();

View File

@ -35,7 +35,7 @@
}).t(spoiler_hint)
.tree();
_converse.connection._dataRecv(test_utils.createRequest(msg));
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
await new Promise(resolve => _converse.api.listen.once('chatBoxViewInitialized', resolve));
const view = _converse.chatboxviews.get(sender_jid);
await new Promise(resolve => view.once('messageInserted', resolve));
await u.waitUntil(() => view.model.vcard.get('fullname') === 'Mercutio')
@ -70,7 +70,7 @@
'xmlns': 'urn:xmpp:spoiler:0',
}).tree();
_converse.connection._dataRecv(test_utils.createRequest(msg));
await new Promise(resolve => _converse.api.listen.once('chatBoxInitialized', resolve));
await new Promise(resolve => _converse.api.listen.once('chatBoxViewInitialized', resolve));
const view = _converse.chatboxviews.get(sender_jid);
await u.waitUntil(() => u.isVisible(view.el));
await u.waitUntil(() => view.model.vcard.get('fullname') === 'Mercutio')

View File

@ -308,8 +308,7 @@ converse.plugins.add('converse-bookmark-views', {
}
_converse.api.listen.on('bookmarksInitialized', initBookmarkViews);
_converse.api.listen.on('chatRoomOpened', view => view.setBookmarkState());
_converse.api.listen.on('chatRoomViewInitialized', view => view.setBookmarkState());
/************************ END Event Handlers ************************/
}
});

View File

@ -81,9 +81,8 @@ converse.plugins.add('converse-chatview', {
this.listenTo(this.model, 'change:status', this.onStatusMessageChanged);
this.debouncedRender = debounce(this.render, 50);
if (this.model.vcard) {
this.listenTo(this.model.vcard, 'change', this.debouncedRender);
}
this.listenTo(this.model, 'vcard:change', this.debouncedRender);
if (this.model.contact) {
this.listenTo(this.model.contact, 'destroy', this.debouncedRender);
}
@ -264,11 +263,11 @@ converse.plugins.add('converse-chatview', {
/**
* Triggered once the {@link _converse.ChatBoxView} has been initialized
* @event _converse#chatBoxInitialized
* @type { _converse.ChatBoxView | _converse.HeadlinesBoxView }
* @example _converse.api.listen.on('chatBoxInitialized', view => { ... });
* @event _converse#chatBoxViewInitialized
* @type { _converse.HeadlinesBoxView }
* @example _converse.api.listen.on('chatBoxViewInitialized', view => { ... });
*/
_converse.api.trigger('chatBoxInitialized', this);
_converse.api.trigger('chatBoxViewInitialized', this);
},
initDebounced () {

View File

@ -196,7 +196,6 @@ converse.plugins.add('converse-controlbox', {
* @example _converse.api.listen.on('controlBoxInitialized', view => { ... });
*/
_converse.api.trigger('controlBoxInitialized', this);
_converse.api.trigger('chatBoxInitialized', this);
},
render () {

View File

@ -53,7 +53,13 @@ converse.plugins.add('converse-headlines-view', {
this.render().insertHeading()
this.updateAfterMessagesFetched();
this.insertIntoDOM().hide();
_converse.api.trigger('chatBoxInitialized', this);
/**
* Triggered once the {@link _converse.HeadlinesBoxView} has been initialized
* @event _converse#headlinesBoxViewInitialized
* @type { _converse.HeadlinesBoxView }
* @example _converse.api.listen.on('headlinesBoxViewInitialized', view => { ... });
*/
_converse.api.trigger('headlinesBoxViewInitialized', this);
},
render () {

View File

@ -103,10 +103,6 @@ converse.plugins.add('converse-message-view', {
}
}, 50);
if (this.model.vcard) {
this.listenTo(this.model.vcard, 'change', this.debouncedRender);
}
if (this.model.rosterContactAdded) {
this.model.rosterContactAdded.then(() => {
this.listenTo(this.model.contact, 'change:nickname', this.debouncedRender);
@ -122,6 +118,7 @@ converse.plugins.add('converse-message-view', {
this.listenTo(this.model, 'change', this.onChanged);
this.listenTo(this.model, 'destroy', this.fadeOut);
this.listenTo(this.model, 'vcard:change', this.debouncedRender);
},
async render () {

View File

@ -688,13 +688,12 @@ converse.plugins.add('converse-muc-views', {
await this.updateAfterMessagesFetched();
this.onConnectionStatusChanged();
/**
* Triggered once a groupchat has been opened
* @event _converse#chatRoomOpened
* Triggered once a { @link _converse.ChatRoomView } has been opened
* @event _converse#chatRoomViewInitialized
* @type { _converse.ChatRoomView }
* @example _converse.api.listen.on('chatRoomOpened', view => { ... });
* @example _converse.api.listen.on('chatRoomViewInitialized', view => { ... });
*/
_converse.api.trigger('chatRoomOpened', this);
_converse.api.trigger('chatBoxInitialized', this);
_converse.api.trigger('chatRoomViewInitialized', this);
},
render () {
@ -1707,7 +1706,7 @@ converse.plugins.add('converse-muc-views', {
}
},
insertDayIndicator (next_msg_el) {
insertDayIndicator () {
this.removeEmptyHistoryFeedback();
return _converse.ChatBoxView.prototype.insertDayIndicator.apply(this, arguments);
},

View File

@ -339,14 +339,14 @@ converse.plugins.add('converse-rosterview', {
async initialize () {
await this.model.initialized;
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "highlight", this.highlight);
this.debouncedRender = debounce(this.render, 50);
this.listenTo(this.model, "change", this.debouncedRender);
this.listenTo(this.model, "destroy", this.remove);
this.listenTo(this.model, "highlight", this.highlight);
this.listenTo(this.model, "open", this.openChat);
this.listenTo(this.model, "remove", this.remove);
this.listenTo(this.model.presence, "change:show", this.render);
this.listenTo(this.model.vcard, 'change:fullname', this.render);
this.listenTo(this.model, 'vcard:change', this.debouncedRender);
this.listenTo(this.model.presence, "change:show", this.debouncedRender);
this.render();
},

View File

@ -89,6 +89,12 @@ converse.plugins.add('converse-chat', {
this.on('change:put', this.uploadFile, this);
}
this.setTimerForEphemeralMessage();
/**
* Triggered once a {@link _converse.Message} has been created and initialized.
* @event _converse#messageInitialized
* @type { _converse.Message}
* @example _converse.api.listen.on('messageInitialized', model => { ... });
*/
await _converse.api.trigger('messageInitialized', this, {'Synchronous': true});
this.initialized.resolve();
},
@ -298,9 +304,6 @@ converse.plugins.add('converse-chat', {
}
this.set({'box_id': `box-${btoa(jid)}`});
if (_converse.vcards) {
this.vcard = _converse.vcards.findWhere({'jid': jid}) || _converse.vcards.create({'jid': jid});
}
if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) {
this.presence = _converse.presences.findWhere({'jid': jid}) || _converse.presences.create({'jid': jid});
await this.setRosterContact(jid);
@ -308,6 +311,13 @@ converse.plugins.add('converse-chat', {
this.on('change:chat_state', this.sendChatState, this);
this.initMessages();
await this.fetchMessages();
/**
* Triggered once a {@link _converse.ChatBox} has been created and initialized.
* @event _converse#chatBoxInitialized
* @type { _converse.ChatBox}
* @example _converse.api.listen.on('chatBoxInitialized', model => { ... });
*/
await _converse.api.trigger('chatBoxInitialized', this, {'Synchronous': true});
this.initialized.resolve();
},

View File

@ -235,7 +235,7 @@ converse.plugins.add('converse-mam', {
/************************ BEGIN Event Handlers ************************/
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(Strophe.NS.MAM));
_converse.api.listen.on('serviceDiscovered', getMAMPrefsFromFeature);
_converse.api.listen.on('chatRoomOpened', view => {
_converse.api.listen.on('chatRoomViewInitialized', view => {
if (_converse.muc_show_logs_before_join) {
// If we want to show MAM logs before entering the MUC, we need
// to be informed once it's clear that this MUC supports MAM.

View File

@ -249,8 +249,14 @@ converse.plugins.add('converse-muc', {
}
if (!this.setTimerForEphemeralMessage()) {
this.setOccupant();
this.setVCard();
}
/**
* Triggered once a {@link _converse.ChatRoomMessageInitialized} has been created and initialized.
* @event _converse#chatRoomMessageInitialized
* @type { _converse.ChatRoomMessages}
* @example _converse.api.listen.on('chatRoomMessageInitialized', model => { ... });
*/
_converse.api.trigger('chatRoomMessageInitialized', this);
},
onOccupantRemoved () {
@ -288,37 +294,7 @@ converse.plugins.add('converse-muc', {
} else {
this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded);
}
},
getVCardForChatroomOccupant () {
const chatbox = get(this, 'collection.chatbox');
const nick = Strophe.getResourceFromJid(this.get('from'));
if (chatbox && chatbox.get('nick') === nick) {
return _converse.xmppstatus.vcard;
} else {
const jid = this.occupant && this.occupant.get('jid') || this.get('from');
if (jid) {
return _converse.vcards.findWhere({jid}) || _converse.vcards.create({jid});
} else {
log.error(`Could not assign VCard for message because no JID found! msgid: ${this.get('msgid')}`);
return;
}
}
},
async setVCard () {
await _converse.api.waitUntil('VCardsInitialized');
if (!_converse.vcards) {
return; // VCards aren't supported
}
if (['error', 'info'].includes(this.get('type'))) {
return;
} else {
this.vcard = this.getVCardForChatroomOccupant();
}
},
}
});
@ -378,10 +354,9 @@ converse.plugins.add('converse-muc', {
}
},
async initialize() {
async initialize () {
this.initialized = u.getResolveablePromise();
this.setVCard();
this.set('box_id', `box-${btoa(this.get('jid'))}`);
await this.restoreSession();
@ -397,17 +372,16 @@ converse.plugins.add('converse-muc', {
if (!restored) {
this.join();
}
/**
* Triggered once a {@link _converse.ChatRoom} has been created and initialized.
* @event _converse#chatRoomInitialized
* @type { _converse.ChatRoom }
* @example _converse.api.listen.on('chatRoomInitialized', model => { ... });
*/
await _converse.api.trigger('chatRoomInitialized', this, {'Synchronous': true});
this.initialized.resolve();
},
async setVCard () {
await _converse.api.waitUntil('VCardsInitialized');
if (_converse.vcards) {
this.vcard = _converse.vcards.findWhere({'jid': this.get('jid')}) ||
_converse.vcards.create({'jid': this.get('jid')});
}
},
/**
* Checks whether we're still joined and if so, restores the MUC state from cache.
* @private

View File

@ -9,8 +9,9 @@
import "./converse-status";
import converse from "./converse-core";
import tpl_vcard from "./templates/vcard.html";
import { get, has, isString } from "lodash";
const { Backbone, Strophe, _, $iq, dayjs, } = converse.env;
const { Backbone, Strophe, $iq, dayjs } = converse.env;
const u = converse.env.utils;
@ -84,7 +85,7 @@ converse.plugins.add('converse-vcard', {
} else {
(attrs = {})[key] = val;
}
if (_.has(attrs, 'image') && !attrs['image']) {
if (has(attrs, 'image') && !attrs['image']) {
attrs['image'] = _converse.DEFAULT_IMAGE;
attrs['image_type'] = _converse.DEFAULT_IMAGE_TYPE;
return Backbone.Model.prototype.set.call(this, attrs, options);
@ -116,13 +117,13 @@ converse.plugins.add('converse-vcard', {
if (vcard !== null) {
result = {
'stanza': iq,
'fullname': _.get(vcard.querySelector('FN'), 'textContent'),
'nickname': _.get(vcard.querySelector('NICKNAME'), 'textContent'),
'image': _.get(vcard.querySelector('PHOTO BINVAL'), 'textContent'),
'image_type': _.get(vcard.querySelector('PHOTO TYPE'), 'textContent'),
'url': _.get(vcard.querySelector('URL'), 'textContent'),
'role': _.get(vcard.querySelector('ROLE'), 'textContent'),
'email': _.get(vcard.querySelector('EMAIL USERID'), 'textContent'),
'fullname': get(vcard.querySelector('FN'), 'textContent'),
'nickname': get(vcard.querySelector('NICKNAME'), 'textContent'),
'image': get(vcard.querySelector('PHOTO BINVAL'), 'textContent'),
'image_type': get(vcard.querySelector('PHOTO TYPE'), 'textContent'),
'url': get(vcard.querySelector('URL'), 'textContent'),
'role': get(vcard.querySelector('ROLE'), 'textContent'),
'email': get(vcard.querySelector('EMAIL USERID'), 'textContent'),
'vcard_updated': (new Date()).toISOString(),
'vcard_error': undefined
};
@ -135,6 +136,7 @@ converse.plugins.add('converse-vcard', {
return result;
}
function createStanza (type, jid, vcard_el) {
const iq = $iq(jid ? {'type': type, 'to': jid} : {'type': type});
if (!vcard_el) {
@ -145,6 +147,7 @@ converse.plugins.add('converse-vcard', {
return iq;
}
async function getVCard (_converse, jid) {
const to = Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid;
let iq;
@ -160,7 +163,54 @@ converse.plugins.add('converse-vcard', {
return onVCardData(jid, iq);
}
/************************ BEGIN Event Handlers ************************/
async function setVCardOnModel (model) {
let jid;
if (model instanceof _converse.Message) {
if (model.get('type') === 'error') {
return;
}
jid = model.get('from');
} else {
jid = model.get('jid');
}
await _converse.api.waitUntil('VCardsInitialized');
model.vcard = _converse.vcards.findWhere({'jid': jid});
if (!model.vcard) {
model.vcard = _converse.vcards.create({'jid': jid});
}
model.vcard.on('change', () => model.trigger('vcard:change'));
}
function getVCardForChatroomOccupant (message) {
const chatbox = get(message, 'collection.chatbox');
const nick = Strophe.getResourceFromJid(message.get('from'));
if (chatbox && chatbox.get('nick') === nick) {
return _converse.xmppstatus.vcard;
} else {
const jid = message.occupant && message.occupant.get('jid') || message.get('from');
if (jid) {
return _converse.vcards.findWhere({jid}) || _converse.vcards.create({jid});
} else {
log.error(`Could not assign VCard for message because no JID found! msgid: ${message.get('msgid')}`);
return;
}
}
}
async function setVCardOnMUCMessage (message) {
await _converse.api.waitUntil('VCardsInitialized');
if (['error', 'info'].includes(message.get('type'))) {
return;
} else {
message.vcard = getVCardForChatroomOccupant(message);
}
}
_converse.initVCardCollection = async function () {
_converse.vcards = new _converse.VCards();
_converse.vcards.browserStorage = _converse.createStore(`${_converse.bare_jid}-converse.vcards`);
@ -182,9 +232,8 @@ converse.plugins.add('converse-vcard', {
_converse.api.trigger('VCardsInitialized');
}
_converse.api.listen.on('statusInitialized', _converse.initVCardCollection);
_converse.api.listen.on('clearSession', () => {
function clearVCardsSession () {
if (_converse.shouldClearCache()) {
_converse.api.promises.add('VCardsInitialized');
if (_converse.vcards) {
@ -192,30 +241,19 @@ converse.plugins.add('converse-vcard', {
delete _converse.vcards;
}
}
});
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(Strophe.NS.VCARD));
async function setVCardOnModel (model) {
let jid;
if (model instanceof _converse.Message) {
if (model.get('type') === 'error') {
return;
}
jid = model.get('from');
} else {
jid = model.get('jid');
}
await _converse.api.waitUntil('VCardsInitialized');
model.vcard = _converse.vcards.findWhere({'jid': jid});
if (!model.vcard) {
model.vcard = _converse.vcards.create({'jid': jid});
}
}
_converse.api.listen.on('rosterContactInitialized', m => setVCardOnModel(m));
/************************ BEGIN Event Handlers ************************/
_converse.api.listen.on('chatBoxInitialized', m => setVCardOnModel(m));
_converse.api.listen.on('chatRoomInitialized', m => setVCardOnModel(m));
_converse.api.listen.on('chatRoomMessageInitialized', m => setVCardOnMUCMessage(m));
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(Strophe.NS.VCARD));
_converse.api.listen.on('clearSession', () => clearVCardsSession());
_converse.api.listen.on('messageInitialized', m => setVCardOnModel(m));
_converse.api.listen.on('rosterContactInitialized', m => setVCardOnModel(m));
_converse.api.listen.on('statusInitialized', _converse.initVCardCollection);
/************************ BEGIN API ************************/
@ -275,7 +313,7 @@ converse.plugins.add('converse-vcard', {
* });
*/
get (model, force) {
if (_.isString(model)) {
if (isString(model)) {
return getVCard(_converse, model);
} else if (force ||
!model.get('vcard_updated') ||