From 725a382e3b25fd91f652faf9e72ce2dfbce147ab Mon Sep 17 00:00:00 2001 From: JC Brand Date: Wed, 17 Apr 2019 11:52:41 +0200 Subject: [PATCH] Refactor initialization and defaults for chat boxes - Let box_id start with char for valid HTML. - No need to use SHA1 for box id - No need for the user_id attribute. - Set nickname when we set the roster contact. Also... - _converse.api.contacts.get is now async - _converse.api.chats.create is now async --- CHANGES.md | 1 + dist/converse.js | 309 +++++++++++++------------ spec/chatbox.js | 7 +- spec/converse.js | 27 ++- spec/messages.js | 2 +- spec/user-details-modal.js | 3 +- src/converse-chatboxviews.js | 14 +- src/converse-chatview.js | 22 +- src/converse-controlbox.js | 22 +- src/converse-headline.js | 30 ++- src/converse-muc-views.js | 7 +- src/headless/converse-chatboxes.js | 114 +++++---- src/headless/converse-muc.js | 58 +++-- src/headless/converse-roster.js | 52 ++--- src/headless/dist/converse-headless.js | 195 +++++++--------- src/templates/chatbox_head.html | 4 +- 16 files changed, 426 insertions(+), 441 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bb8fb5a82..67d87942e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ this was the default behavior. - `_converse.api.emit` has been removed in favor of [\_converse.api.trigger](https://conversejs.org/docs/html/api/-_converse.api.html#.trigger) - `_converse.updateSettings` has been removed in favor of [\_converse.api.settings.update](https://conversejs.org/docs/html/api/-_converse.api.settings.html#.update) +- `_converse.api.roster.get` now returns a promise. ## 4.2.0 (2019-04-04) diff --git a/dist/converse.js b/dist/converse.js index 768101fae..4eb274735 100644 --- a/dist/converse.js +++ b/dist/converse.js @@ -42258,6 +42258,7 @@ Strophe.Websocket.prototype = { }); //# sourceMappingURL=strophe.js.map + /***/ }), /***/ "./node_modules/strophejs-plugin-ping/strophe.ping.js": @@ -48638,19 +48639,17 @@ const AvatarMixin = { return; } - const data = { - 'classes': canvas_el.getAttribute('class'), - 'width': canvas_el.width, - 'height': canvas_el.height - }; - if (this.model.vcard) { + const data = { + 'classes': canvas_el.getAttribute('class'), + 'width': canvas_el.width, + 'height': canvas_el.height + }; const image_type = this.model.vcard.get('image_type'), image = this.model.vcard.get('image'); data['image'] = "data:" + image_type + ";base64," + image; + canvas_el.outerHTML = templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default()(data); } - - canvas_el.outerHTML = templates_avatar_svg__WEBPACK_IMPORTED_MODULE_4___default()(data); } }; @@ -48761,9 +48760,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins /* This method gets overridden in src/converse-controlbox.js if * the controlbox plugin is active. */ - this.each(function (view) { - view.close(); - }); + this.each(v => v.close()); return this; } @@ -49008,7 +49005,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins initialize() { this.model.on('change:status', this.onStatusMessageChanged, this); this.debouncedRender = _.debounce(this.render, 50); - this.model.vcard.on('change', this.debouncedRender, this); + + if (this.model.vcard) { + this.model.vcard.on('change', this.debouncedRender, this); + } + this.model.on('rosterContactAdded', () => { this.model.contact.on('change:nickname', this.debouncedRender, this); this.debouncedRender(); @@ -49016,7 +49017,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins }, render() { - this.el.innerHTML = templates_chatbox_head_html__WEBPACK_IMPORTED_MODULE_8___default()(_.extend(this.model.vcard.toJSON(), this.model.toJSON(), { + const vcard = _.get(this.model, 'vcard'), + vcard_json = vcard ? vcard.toJSON() : {}; + + this.el.innerHTML = templates_chatbox_head_html__WEBPACK_IMPORTED_MODULE_8___default()(_.extend(vcard_json, this.model.toJSON(), { '_converse': _converse, 'info_close': __('Close this chat box'), 'display_name': this.model.getDisplayName() @@ -49053,7 +49057,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins initialize() { _converse.BootstrapModal.prototype.initialize.apply(this, arguments); - this.model.on('contactAdded', this.registerContactEventHandlers, this); + this.model.on('rosterContactAdded', this.registerContactEventHandlers, this); this.model.on('change', this.render, this); this.registerContactEventHandlers(); /** @@ -49067,7 +49071,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins }, toHTML() { - return templates_user_details_modal_html__WEBPACK_IMPORTED_MODULE_20___default()(_.extend(this.model.toJSON(), this.model.vcard.toJSON(), { + const vcard = _.get(this.model, 'vcard'), + vcard_json = vcard ? vcard.toJSON() : {}; + + return templates_user_details_modal_html__WEBPACK_IMPORTED_MODULE_20___default()(_.extend(this.model.toJSON(), vcard_json, { '_': _, '__': __, 'view': this, @@ -50301,11 +50308,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins }); _converse.api.listen.on('chatBoxViewsInitialized', () => { - const that = _converse.chatboxviews; + const views = _converse.chatboxviews; _converse.chatboxes.on('add', item => { - if (!that.get(item.get('id')) && item.get('type') === _converse.PRIVATE_CHAT_TYPE) { - that.add(item.get('id'), new _converse.ChatBoxView({ + if (!views.get(item.get('id')) && item.get('type') === _converse.PRIVATE_CHAT_TYPE) { + views.add(item.get('id'), new _converse.ChatBoxView({ model: item })); } @@ -50345,7 +50352,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins */ 'get'(jids) { if (_.isUndefined(jids)) { - _converse.log("chats.create: You need to provide at least one JID", Strophe.LogLevel.ERROR); + _converse.log("chatviews.get: You need to provide at least one JID", Strophe.LogLevel.ERROR); return null; } @@ -50595,20 +50602,17 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins }); _converse.ControlBox = _converse.ChatBox.extend({ - defaults: { - 'bookmarked': false, - 'box_id': 'controlbox', - 'chat_state': undefined, - 'closed': !_converse.show_controlbox_by_default, - 'num_unread': 0, - 'type': _converse.CONTROLBOX_TYPE, - 'url': '' - }, - - initialize() { - u.safeSave(this, { - 'time_opened': this.get('time_opened') || moment().valueOf() - }); + defaults() { + return { + 'bookmarked': false, + 'box_id': 'controlbox', + 'chat_state': undefined, + 'closed': !_converse.show_controlbox_by_default, + 'num_unread': 0, + 'time_opened': this.get('time_opened') || moment().valueOf(), + 'type': _converse.CONTROLBOX_TYPE, + 'url': '' + }; } }); @@ -51720,6 +51724,7 @@ __webpack_require__.r(__webpack_exports__); const _converse$env = _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].env, _ = _converse$env._, + moment = _converse$env.moment, utils = _converse$env.utils; _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins.add('converse-headline', { /* Plugin dependencies are other plugins which might be @@ -51760,13 +51765,24 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins const _converse = this._converse, __ = _converse.__; _converse.HeadlinesBox = _converse.ChatBox.extend({ - defaults: { - 'type': _converse.HEADLINES_TYPE, - 'bookmarked': false, - 'chat_state': undefined, - 'num_unread': 0, - 'url': '' + defaults() { + return { + 'bookmarked': false, + 'hidden': _.includes(['mobile', 'fullscreen'], _converse.view_mode), + 'message_type': 'headline', + 'num_unread': 0, + 'time_opened': this.get('time_opened') || moment().valueOf(), + 'type': _converse.HEADLINES_TYPE + }; + }, + + initialize() { + this.initMessages(); + this.set({ + 'box_id': `box-${btoa(this.get('jid'))}` + }); } + }); _converse.HeadlinesBoxView = _converse.ChatBoxView.extend({ className: 'chatbox headlines', @@ -51815,7 +51831,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins if (utils.isHeadlineMessage(_converse, message)) { const from_jid = message.getAttribute('from'); - if (_.includes(from_jid, '@') && !_converse.api.contacts.get(from_jid) && !_converse.allow_non_roster_messaging) { + if (_.includes(from_jid, '@') && !_converse.roster.get(from_jid) && !_converse.allow_non_roster_messaging) { return; } @@ -51854,11 +51870,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins _converse.api.listen.on('reconnected', registerHeadlineHandler); _converse.api.listen.on('chatBoxViewsInitialized', () => { - const that = _converse.chatboxviews; + const views = _converse.chatboxviews; _converse.chatboxes.on('add', item => { - if (!that.get(item.get('id')) && item.get('type') === _converse.HEADLINES_TYPE) { - that.add(item.get('id'), new _converse.HeadlinesBoxView({ + if (!views.get(item.get('id')) && item.get('type') === _converse.HEADLINES_TYPE) { + views.add(item.get('id'), new _converse.HeadlinesBoxView({ model: item })); } @@ -53404,10 +53420,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins _converse.api.settings.update({ 'auto_list_rooms': false, + 'cache_muc_messages': true, + 'locked_muc_domain': false, + 'locked_muc_nickname': false, 'muc_disable_moderator_commands': false, 'muc_domain': undefined, - 'locked_muc_nickname': false, - 'locked_muc_domain': false, 'muc_show_join_leave': true, 'roomconfig_whitelist': [], 'visible_toolbar_buttons': { @@ -55566,7 +55583,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins /* Upon a reconnection event from converse, join again * all the open groupchats. */ - _converse.chatboxviews.each(function (view) { + _converse.chatboxviews.each(view => { if (view.model.get('type') === _converse.CHATROOMS_TYPE) { view.model.save('connection_status', _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].ROOMSTATUS.DISCONNECTED); view.model.registerHandlers(); @@ -61743,7 +61760,6 @@ const _converse$env = _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].env Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, Strophe = _converse$env.Strophe, - b64_sha1 = _converse$env.b64_sha1, moment = _converse$env.moment, sizzle = _converse$env.sizzle, utils = _converse$env.utils, @@ -61786,12 +61802,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha const ModelWithContact = Backbone.Model.extend({ async setRosterContact(jid) { - await _converse.api.waitUntil('rosterContactsFetched'); - - const contact = _converse.roster.get(jid); + const contact = await _converse.api.contacts.get(jid); if (contact) { this.contact = contact; + this.set('nickname', contact.get('nickname')); this.trigger('rosterContactAdded'); } } @@ -62019,6 +62034,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha 'message_type': 'chat', 'nickname': undefined, 'num_unread': 0, + 'time_opened': this.get('time_opened') || moment().valueOf(), 'type': _converse.PRIVATE_CHAT_TYPE, 'url': '' }; @@ -62036,15 +62052,10 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha // This happens when the controlbox is in browser storage, // but we're in embedded mode. return; - } // XXX: this creates a dependency on converse-roster, which we - // probably shouldn't have here, so we should probably move - // ChatBox out of converse-chatboxes + } - - this.presence = _converse.presences.findWhere({ - 'jid': jid - }) || _converse.presences.create({ - 'jid': jid + this.set({ + 'box_id': `box-${btoa(jid)}` }); if (_converse.vcards) { @@ -62056,31 +62067,30 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha } if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) { + this.presence = _converse.presences.findWhere({ + 'jid': jid + }) || _converse.presences.create({ + 'jid': jid + }); this.setRosterContact(jid); } + this.on('change:chat_state', this.sendChatState, this); + this.initMessages(); + }, + + initMessages() { this.messages = new _converse.Messages(); const storage = _converse.config.get('storage'); - this.messages.browserStorage = new Backbone.BrowserStorage[storage](b64_sha1(`converse.messages${jid}${_converse.bare_jid}`)); + this.messages.browserStorage = new Backbone.BrowserStorage[storage](`converse.messages${this.get('jid')}${_converse.bare_jid}`); this.messages.chatbox = this; this.messages.on('change:upload', message => { if (message.get('upload') === _converse.SUCCESS) { _converse.api.send(this.createMessageStanza(message)); } }); - this.on('change:chat_state', this.sendChatState, this); // Models get saved immediately after creation, so no need to - // call `save` here. - - this.set({ - // The chat_state will be set to ACTIVE once the chat box is opened - // and we listen for change:chat_state, so shouldn't set it to ACTIVE here. - 'box_id': b64_sha1(this.get('jid')), - 'time_opened': this.get('time_opened') || moment().valueOf(), - 'user_id': Strophe.getNodeFromJid(this.get('jid')), - 'nickname': _.get(_converse.api.contacts.get(this.get('jid')), 'attributes.nickname') - }); }, validate(attrs, options) { @@ -62812,8 +62822,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha const from_bare_jid = Strophe.getBareJidFromJid(from_jid), from_resource = Strophe.getResourceFromJid(from_jid), is_me = from_bare_jid === _converse.bare_jid; - let contact_jid, - is_roster_contact = false; + let contact_jid; if (is_me) { // I am the sender, so this must be a forwarded message... @@ -62824,17 +62833,18 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha contact_jid = Strophe.getBareJidFromJid(to_jid); } else { contact_jid = from_bare_jid; - await _converse.api.waitUntil('rosterContactsFetched'); - is_roster_contact = !_.isUndefined(_converse.roster.get(contact_jid)); + } - if (!is_roster_contact && !_converse.allow_non_roster_messaging) { - return; - } + const contact = await _converse.api.contacts.get(contact_jid); + const is_roster_contact = !_.isUndefined(contact); + + if (!is_me && !is_roster_contact && !_converse.allow_non_roster_messaging) { + return; } // Get chat box, but only create when the message has something to show to the user const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0, - roster_nick = _.get(_converse.api.contacts.get(contact_jid), 'attributes.nickname'), + roster_nick = _.get(contact, 'attributes.nickname'), chatbox = this.getChatBox(contact_jid, { 'nickname': roster_nick }, has_body); @@ -62985,16 +62995,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * @param {string|string[]} jid|jids An jid or array of jids * @param {object} [attrs] An object containing configuration attributes. */ - 'create'(jids, attrs) { - if (_.isUndefined(jids)) { - _converse.log("chats.create: You need to provide at least one JID", Strophe.LogLevel.ERROR); - - return null; - } - + async create(jids, attrs) { if (_.isString(jids)) { if (attrs && !_.get(attrs, 'fullname')) { - attrs.fullname = _.get(_converse.api.contacts.get(jids), 'attributes.fullname'); + const contact = await _converse.api.contacts.get(jids); + attrs.fullname = _.get(contact, 'attributes.fullname'); } const chatbox = _converse.chatboxes.getChatBox(jids, attrs, true); @@ -63008,10 +63013,17 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha return chatbox; } - return _.map(jids, jid => { - attrs.fullname = _.get(_converse.api.contacts.get(jid), 'attributes.fullname'); - return _converse.chatboxes.getChatBox(jid, attrs, true).maybeShow(); - }); + if (_.isArray(jids)) { + return Promise.all(jids.forEach(async jid => { + const contact = await _converse.api.contacts.get(jids); + attrs.fullname = _.get(contact, 'attributes.fullname'); + return _converse.chatboxes.getChatBox(jid, attrs, true).maybeShow(); + })); + } + + _converse.log("chats.create: You need to provide at least one JID", Strophe.LogLevel.ERROR); + + return null; }, /** @@ -63020,6 +63032,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * @method _converse.api.chats.open * @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com'] * @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model. + * @param {Boolean} [attrs.minimized] - Should the chat be + * created in minimized state. * @param {Boolean} [force=false] - By default, a minimized * chat won't be maximized (in `overlayed` view mode) and in * `fullscreen` view mode a newly opened chat won't replace @@ -63053,22 +63067,21 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * } * }); */ - 'open'(jids, attrs, force) { - return new Promise((resolve, reject) => { - Promise.all([_converse.api.waitUntil('rosterContactsFetched'), _converse.api.waitUntil('chatBoxesFetched')]).then(() => { - if (_.isUndefined(jids)) { - const err_msg = "chats.open: You need to provide at least one JID"; + async open(jids, attrs, force) { + await Promise.all([_converse.api.waitUntil('rosterContactsFetched'), _converse.api.waitUntil('chatBoxesFetched')]); - _converse.log(err_msg, Strophe.LogLevel.ERROR); + if (_.isString(jids)) { + const chat = await _converse.api.chats.create(jids, attrs); + return chat.maybeShow(force); + } else if (_.isArray(jids)) { + return Promise.all(jids.map(j => _converse.api.chats.create(j, attrs).then(c => c.maybeShow(force)))); + } - reject(new Error(err_msg)); - } else if (_.isString(jids)) { - resolve(_converse.api.chats.create(jids, attrs).maybeShow(force)); - } else { - resolve(_.map(jids, jid => _converse.api.chats.create(jid, attrs).maybeShow(force))); - } - }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); - }); + const err_msg = "chats.open: You need to provide at least one JID"; + + _converse.log(err_msg, Strophe.LogLevel.ERROR); + + throw new Error(err_msg); }, /** @@ -63091,7 +63104,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * const models = _converse.api.chats.get(); * */ - 'get'(jids) { + get(jids) { if (_.isUndefined(jids)) { const result = []; @@ -66506,7 +66519,6 @@ const _converse$env = _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].env $build = _converse$env.$build, $msg = _converse$env.$msg, $pres = _converse$env.$pres, - b64_sha1 = _converse$env.b64_sha1, sizzle = _converse$env.sizzle, f = _converse$env.f, moment = _converse$env.moment, @@ -66647,7 +66659,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc */ settings.type = _converse.CHATROOMS_TYPE; settings.id = jid; - settings.box_id = b64_sha1(jid); const chatbox = _converse.chatboxes.getChatBox(jid, settings, true); @@ -66665,7 +66676,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc _converse.ChatRoom = _converse.ChatBox.extend({ defaults() { - return _.assign(_.clone(_converse.ChatBox.prototype.defaults), { + return { // For group chats, we distinguish between generally unread // messages and those ones that specifically mention the // user. @@ -66676,19 +66687,33 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc // generally unread messages (which *includes* mentions!). 'num_unread_general': 0, 'affiliation': null, + 'bookmarked': false, + 'chat_state': undefined, 'connection_status': _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.DISCONNECTED, + 'description': '', + 'hidden': _.includes(['mobile', 'fullscreen'], _converse.view_mode), + 'message_type': 'groupchat', 'name': '', 'nick': _converse.xmppstatus.get('nickname') || _converse.nickname, - 'description': '', + 'num_unread': 0, 'roomconfig': {}, - 'type': _converse.CHATROOMS_TYPE, - 'message_type': 'groupchat' - }); + 'time_opened': this.get('time_opened') || moment().valueOf(), + 'type': _converse.CHATROOMS_TYPE + }; }, initialize() { - this.constructor.__super__.initialize.apply(this, arguments); + if (_converse.vcards) { + this.vcard = _converse.vcards.findWhere({ + 'jid': this.get('jid') + }) || _converse.vcards.create({ + 'jid': this.get('jid') + }); + } + this.set('box_id', `box-${btoa(this.get('jid'))}`); + this.initMessages(); + this.on('change:chat_state', this.sendChatState, this); this.on('change:connection_status', this.onConnectionStatusChanged, this); const storage = _converse.config.get('storage'); @@ -68056,7 +68081,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc jid = jid.toLowerCase(); attrs.type = _converse.CHATROOMS_TYPE; attrs.id = jid; - attrs.box_id = b64_sha1(jid); return _converse.chatboxes.getChatBox(jid, attrs, create); }; @@ -69607,28 +69631,6 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins /********** Event Handlers *************/ - function addRelatedContactToChatbox(chatbox, contact) { - if (!_.isUndefined(contact)) { - chatbox.contact = contact; - chatbox.trigger('contactAdded', contact); - } - } - - _converse.api.waitUntil('rosterContactsFetched').then(() => { - _converse.roster.on('add', contact => { - /* When a new contact is added, check if we already have a - * chatbox open for it, and if so attach it to the chatbox. - */ - const chatbox = _converse.chatboxes.findWhere({ - 'jid': contact.get('jid') - }); - - if (chatbox) { - addRelatedContactToChatbox(chatbox, contact); - } - }); - }); - function updateUnreadCounter(chatbox) { const contact = _converse.roster.findWhere({ 'jid': chatbox.get('jid') @@ -69644,11 +69646,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins _converse.api.listen.on('chatBoxesInitialized', () => { _converse.chatboxes.on('change:num_unread', updateUnreadCounter); - _converse.chatboxes.on('add', async chatbox => { - await _converse.api.waitUntil('rosterContactsFetched'); - addRelatedContactToChatbox(chatbox, _converse.roster.findWhere({ - 'jid': chatbox.get('jid') - })); + _converse.chatboxes.on('add', chatbox => { + if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) { + chatbox.setRosterContact(chatbox.get('jid')); + } }); }); @@ -69664,7 +69665,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins }); if (chatbox) { - addRelatedContactToChatbox(chatbox, contact); + chatbox.setRosterContact(contact.get('jid')); } }); }); @@ -69751,20 +69752,20 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins * @method _converse.api.contacts.get * @params {(string[]|string)} jid|jids The JID or JIDs of * the contacts to be returned. - * @returns {(RosterContact[]|RosterContact)} [Backbone.Model](http://backbonejs.org/#Model) - * (or an array of them) representing the contact. + * @returns {promise} Promise which resolves with the + * _converse.RosterContact (or an array of them) representing the contact. * * @example * // Fetch a single contact * _converse.api.listen.on('rosterContactsFetched', function () { - * const contact = _converse.api.contacts.get('buddy@example.com') + * const contact = await _converse.api.contacts.get('buddy@example.com') * // ... * }); * * @example * // To get multiple contacts, pass in an array of JIDs: * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = _converse.api.contacts.get( + * const contacts = await _converse.api.contacts.get( * ['buddy1@example.com', 'buddy2@example.com'] * ) * // ... @@ -69773,14 +69774,14 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins * @example * // To return all contacts, simply call ``get`` without any parameters: * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = _converse.api.contacts.get(); + * const contacts = await _converse.api.contacts.get(); * // ... * }); */ - 'get'(jids) { - const _getter = function _getter(jid) { - return _converse.roster.get(Strophe.getBareJidFromJid(jid)) || null; - }; + async get(jids) { + await _converse.api.waitUntil('rosterContactsFetched'); + + const _getter = jid => _converse.roster.get(Strophe.getBareJidFromJid(jid)); if (_.isUndefined(jids)) { jids = _converse.roster.pluck('jid'); @@ -93084,7 +93085,11 @@ var _ = {escape:__webpack_require__(/*! ./node_modules/lodash/escape.js */ "./no module.exports = function(o) { var __t, __p = '', __e = _.escape, __j = Array.prototype.join; function print() { __p += __j.call(arguments, '') } -__p += '\n
\n
\n
\n
\n \n
\n
\n
\n
\n '; + if (o.type !== o._converse.HEADLINES_TYPE) { ; +__p += '\n \n '; + } ; +__p += '\n
\n '; if (o.url) { ; diff --git a/spec/chatbox.js b/spec/chatbox.js index eb9d88822..a4c85a43e 100644 --- a/spec/chatbox.js +++ b/spec/chatbox.js @@ -241,9 +241,8 @@ await test_utils.waitForRoster(_converse, 'current'); const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; - const chat = _converse.api.chats.create(sender_jid, { - minimized: true - }); + const chat = await _converse.api.chats.create(sender_jid, {'minimized': true}); + await test_utils.waitUntil(() => _converse.chatboxes.length > 1); const chatBoxView = _converse.chatboxviews.get(sender_jid); expect(u.isVisible(chatBoxView.el)).toBeFalsy(); @@ -1389,7 +1388,7 @@ msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too'); await _converse.chatboxes.onMessage(msg); - await test_utils.waitUntil(() => chatbox.messages.length > 1); + await test_utils.waitUntil(() => chatbox.messages.length === 2); indicator_el = sizzle(selector, _converse.rosterview.el).pop(); expect(indicator_el.textContent).toBe('2'); done(); diff --git a/spec/converse.js b/spec/converse.js index 9b54e9f8f..aebfcdbf9 100644 --- a/spec/converse.js +++ b/spec/converse.js @@ -247,23 +247,24 @@ describe("The \"contacts\" API", function () { - it("has a method 'get' which returns wrapped contacts", mock.initConverse((done, _converse) => { + it("has a method 'get' which returns wrapped contacts", mock.initConverse(async (done, _converse) => { // Check that it returns nothing if a non-existing JID is given test_utils.createContacts(_converse, 'current'); - expect(_converse.api.contacts.get('non-existing@jabber.org')).toBeFalsy(); + let contact = await _converse.api.contacts.get('non-existing@jabber.org'); + expect(contact).toBeFalsy(); // Check when a single jid is given const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; - const contact = _converse.api.contacts.get(jid); + contact = await _converse.api.contacts.get(jid); expect(contact.get('fullname')).toBe(mock.cur_names[0]); expect(contact.get('jid')).toBe(jid); // You can retrieve multiple contacts by passing in an array const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost'; - let list = _converse.api.contacts.get([jid, jid2]); + let list = await _converse.api.contacts.get([jid, jid2]); expect(_.isArray(list)).toBeTruthy(); expect(list[0].get('fullname')).toBe(mock.cur_names[0]); expect(list[1].get('fullname')).toBe(mock.cur_names[1]); // Check that all JIDs are returned if you call without any parameters - list = _converse.api.contacts.get(); + list = await _converse.api.contacts.get(); expect(list.length).toBe(mock.cur_names.length); done(); })); @@ -301,11 +302,9 @@ expect(_converse.chatboxes.length).toBe(1); // Test for one JID - test_utils.openChatBoxFor(_converse, jid); - await test_utils.waitUntil(() => _converse.chatboxes.length == 1); - box = _converse.api.chats.get(jid); + box = await _converse.api.chats.open(jid); expect(box instanceof Object).toBeTruthy(); - expect(box.get('box_id')).toBe(b64_sha1(jid)); + expect(box.get('box_id')).toBe(`box-${btoa(jid)}`); const chatboxview = _converse.chatboxviews.get(jid); expect(u.isVisible(chatboxview.el)).toBeTruthy(); @@ -314,8 +313,8 @@ await test_utils.waitUntil(() => _converse.chatboxes.length == 2); const list = _converse.api.chats.get([jid, jid2]); expect(_.isArray(list)).toBeTruthy(); - expect(list[0].get('box_id')).toBe(b64_sha1(jid)); - expect(list[1].get('box_id')).toBe(b64_sha1(jid2)); + expect(list[0].get('box_id')).toBe(`box-${btoa(jid)}`); + expect(list[1].get('box_id')).toBe(`box-${btoa(jid2)}`); done(); })); @@ -334,7 +333,7 @@ const box = await _converse.api.chats.open(jid); expect(box instanceof Object).toBeTruthy(); - expect(box.get('box_id')).toBe(b64_sha1(jid)); + expect(box.get('box_id')).toBe(`box-${btoa(jid)}`); expect( _.keys(box), ['close', 'endOTR', 'focus', 'get', 'initiateOTR', 'is_chatroom', 'maximize', 'minimize', 'open', 'set'] @@ -344,8 +343,8 @@ // Test for multiple JIDs const list = await _converse.api.chats.open([jid, jid2]); expect(_.isArray(list)).toBeTruthy(); - expect(list[0].get('box_id')).toBe(b64_sha1(jid)); - expect(list[1].get('box_id')).toBe(b64_sha1(jid2)); + expect(list[0].get('box_id')).toBe(`box-${btoa(jid)}`); + expect(list[1].get('box_id')).toBe(`box-${btoa(jid2)}`); done(); })); }); diff --git a/spec/messages.js b/spec/messages.js index 2d8070064..58888e9bc 100644 --- a/spec/messages.js +++ b/spec/messages.js @@ -741,7 +741,7 @@ let msg_obj = chatbox.messages.models[0]; expect(msg_obj.get('message')).toEqual(message); expect(msg_obj.get('fullname')).toBeUndefined(); - expect(msg_obj.get('nickname')).toBeUndefined(); + expect(msg_obj.get('nickname')).toBe(null); expect(msg_obj.get('sender')).toEqual('them'); expect(msg_obj.get('is_delayed')).toEqual(true); await test_utils.waitUntil(() => chatbox.vcard.get('fullname') === 'Candice van der Knijff') diff --git a/spec/user-details-modal.js b/spec/user-details-modal.js index 5afb9b078..f0c933fcb 100644 --- a/spec/user-details-modal.js +++ b/spec/user-details-modal.js @@ -24,9 +24,8 @@ const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; test_utils.openChatBoxFor(_converse, contact_jid); - await test_utils.waitUntil(() => _converse.chatboxes.length); + await test_utils.waitUntil(() => _converse.chatboxes.length > 1); const view = _converse.chatboxviews.get(contact_jid); - await new Promise((resolve) => view.model.once('contactAdded', resolve)); let show_modal_button = view.el.querySelector('.show-user-details-modal'); expect(u.isVisible(show_modal_button)).toBeTruthy(); show_modal_button.click(); diff --git a/src/converse-chatboxviews.js b/src/converse-chatboxviews.js index 48dd46d7b..4a721b645 100644 --- a/src/converse-chatboxviews.js +++ b/src/converse-chatboxviews.js @@ -23,17 +23,17 @@ const AvatarMixin = { if (_.isNull(canvas_el)) { return; } - const data = { - 'classes': canvas_el.getAttribute('class'), - 'width': canvas_el.width, - 'height': canvas_el.height, - } if (this.model.vcard) { + const data = { + 'classes': canvas_el.getAttribute('class'), + 'width': canvas_el.width, + 'height': canvas_el.height, + } const image_type = this.model.vcard.get('image_type'), image = this.model.vcard.get('image'); data['image'] = "data:" + image_type + ";base64," + image; + canvas_el.outerHTML = tpl_avatar(data); } - canvas_el.outerHTML = tpl_avatar(data); }, }; @@ -143,7 +143,7 @@ converse.plugins.add('converse-chatboxviews', { /* This method gets overridden in src/converse-controlbox.js if * the controlbox plugin is active. */ - this.each(function (view) { view.close(); }); + this.each(v => v.close()); return this; } }); diff --git a/src/converse-chatview.js b/src/converse-chatview.js index 8070023cc..a07fdb96c 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -163,7 +163,9 @@ converse.plugins.add('converse-chatview', { this.model.on('change:status', this.onStatusMessageChanged, this); this.debouncedRender = _.debounce(this.render, 50); - this.model.vcard.on('change', this.debouncedRender, this); + if (this.model.vcard) { + this.model.vcard.on('change', this.debouncedRender, this); + } this.model.on('rosterContactAdded', () => { this.model.contact.on('change:nickname', this.debouncedRender, this); this.debouncedRender(); @@ -171,9 +173,11 @@ converse.plugins.add('converse-chatview', { }, render () { + const vcard = _.get(this.model, 'vcard'), + vcard_json = vcard ? vcard.toJSON() : {}; this.el.innerHTML = tpl_chatbox_head( _.extend( - this.model.vcard.toJSON(), + vcard_json, this.model.toJSON(), { '_converse': _converse, 'info_close': __('Close this chat box'), @@ -213,7 +217,7 @@ converse.plugins.add('converse-chatview', { initialize () { _converse.BootstrapModal.prototype.initialize.apply(this, arguments); - this.model.on('contactAdded', this.registerContactEventHandlers, this); + this.model.on('rosterContactAdded', this.registerContactEventHandlers, this); this.model.on('change', this.render, this); this.registerContactEventHandlers(); /** @@ -226,9 +230,11 @@ converse.plugins.add('converse-chatview', { }, toHTML () { + const vcard = _.get(this.model, 'vcard'), + vcard_json = vcard ? vcard.toJSON() : {}; return tpl_user_details_modal(_.extend( this.model.toJSON(), - this.model.vcard.toJSON(), { + vcard_json, { '_': _, '__': __, 'view': this, @@ -1380,10 +1386,10 @@ converse.plugins.add('converse-chatview', { }); _converse.api.listen.on('chatBoxViewsInitialized', () => { - const that = _converse.chatboxviews; + const views = _converse.chatboxviews; _converse.chatboxes.on('add', item => { - if (!that.get(item.get('id')) && item.get('type') === _converse.PRIVATE_CHAT_TYPE) { - that.add(item.get('id'), new _converse.ChatBoxView({model: item})); + if (!views.get(item.get('id')) && item.get('type') === _converse.PRIVATE_CHAT_TYPE) { + views.add(item.get('id'), new _converse.ChatBoxView({model: item})); } }); }); @@ -1421,7 +1427,7 @@ converse.plugins.add('converse-chatview', { 'get' (jids) { if (_.isUndefined(jids)) { _converse.log( - "chats.create: You need to provide at least one JID", + "chatviews.get: You need to provide at least one JID", Strophe.LogLevel.ERROR ); return null; diff --git a/src/converse-controlbox.js b/src/converse-controlbox.js index e250638b2..abb8b2ddf 100644 --- a/src/converse-controlbox.js +++ b/src/converse-controlbox.js @@ -188,18 +188,18 @@ converse.plugins.add('converse-controlbox', { const addControlBox = () => _converse.chatboxes.add({'id': 'controlbox'}); _converse.ControlBox = _converse.ChatBox.extend({ - defaults: { - 'bookmarked': false, - 'box_id': 'controlbox', - 'chat_state': undefined, - 'closed': !_converse.show_controlbox_by_default, - 'num_unread': 0, - 'type': _converse.CONTROLBOX_TYPE, - 'url': '' - }, - initialize () { - u.safeSave(this, {'time_opened': this.get('time_opened') || moment().valueOf()}); + defaults () { + return { + 'bookmarked': false, + 'box_id': 'controlbox', + 'chat_state': undefined, + 'closed': !_converse.show_controlbox_by_default, + 'num_unread': 0, + 'time_opened': this.get('time_opened') || moment().valueOf(), + 'type': _converse.CONTROLBOX_TYPE, + 'url': '' + } } }); diff --git a/src/converse-headline.js b/src/converse-headline.js index 8f24e0004..da2d989de 100644 --- a/src/converse-headline.js +++ b/src/converse-headline.js @@ -8,7 +8,7 @@ import "converse-chatview"; import converse from "@converse/headless/converse-core"; import tpl_chatbox from "templates/chatbox.html"; -const { _, utils } = converse.env; +const { _, moment, utils } = converse.env; converse.plugins.add('converse-headline', { @@ -52,13 +52,21 @@ converse.plugins.add('converse-headline', { { __ } = _converse; _converse.HeadlinesBox = _converse.ChatBox.extend({ - defaults: { - 'type': _converse.HEADLINES_TYPE, - 'bookmarked': false, - 'chat_state': undefined, - 'num_unread': 0, - 'url': '' + defaults () { + return { + 'bookmarked': false, + 'hidden': _.includes(['mobile', 'fullscreen'], _converse.view_mode), + 'message_type': 'headline', + 'num_unread': 0, + 'time_opened': this.get('time_opened') || moment().valueOf(), + 'type': _converse.HEADLINES_TYPE + } }, + + initialize () { + this.initMessages(); + this.set({'box_id': `box-${btoa(this.get('jid'))}`}); + } }); @@ -110,7 +118,7 @@ converse.plugins.add('converse-headline', { if (utils.isHeadlineMessage(_converse, message)) { const from_jid = message.getAttribute('from'); if (_.includes(from_jid, '@') && - !_converse.api.contacts.get(from_jid) && + !_converse.roster.get(from_jid) && !_converse.allow_non_roster_messaging) { return; } @@ -142,10 +150,10 @@ converse.plugins.add('converse-headline', { _converse.api.listen.on('chatBoxViewsInitialized', () => { - const that = _converse.chatboxviews; + const views = _converse.chatboxviews; _converse.chatboxes.on('add', item => { - if (!that.get(item.get('id')) && item.get('type') === _converse.HEADLINES_TYPE) { - that.add(item.get('id'), new _converse.HeadlinesBoxView({model: item})); + if (!views.get(item.get('id')) && item.get('type') === _converse.HEADLINES_TYPE) { + views.add(item.get('id'), new _converse.HeadlinesBoxView({model: item})); } }); }); diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index d23f52328..463efd657 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -108,10 +108,11 @@ converse.plugins.add('converse-muc-views', { // configuration settings. _converse.api.settings.update({ 'auto_list_rooms': false, + 'cache_muc_messages': true, + 'locked_muc_domain': false, + 'locked_muc_nickname': false, 'muc_disable_moderator_commands': false, 'muc_domain': undefined, - 'locked_muc_nickname': false, - 'locked_muc_domain': false, 'muc_show_join_leave': true, 'roomconfig_whitelist': [], 'visible_toolbar_buttons': { @@ -2130,7 +2131,7 @@ converse.plugins.add('converse-muc-views', { /* Upon a reconnection event from converse, join again * all the open groupchats. */ - _converse.chatboxviews.each(function (view) { + _converse.chatboxviews.each(view => { if (view.model.get('type') === _converse.CHATROOMS_TYPE) { view.model.save('connection_status', converse.ROOMSTATUS.DISCONNECTED); view.model.registerHandlers(); diff --git a/src/headless/converse-chatboxes.js b/src/headless/converse-chatboxes.js index 1626b6142..0ce6ba430 100644 --- a/src/headless/converse-chatboxes.js +++ b/src/headless/converse-chatboxes.js @@ -9,7 +9,7 @@ import "./utils/form"; import converse from "./converse-core"; import filesize from "filesize"; -const { $msg, Backbone, Promise, Strophe, b64_sha1, moment, sizzle, utils, _ } = converse.env; +const { $msg, Backbone, Promise, Strophe, moment, sizzle, utils, _ } = converse.env; const u = converse.env.utils; Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0'); @@ -59,10 +59,10 @@ converse.plugins.add('converse-chatboxes', { const ModelWithContact = Backbone.Model.extend({ async setRosterContact (jid) { - await _converse.api.waitUntil('rosterContactsFetched'); - const contact = _converse.roster.get(jid); + const contact = await _converse.api.contacts.get(jid); if (contact) { this.contact = contact; + this.set('nickname', contact.get('nickname')); this.trigger('rosterContactAdded'); } } @@ -259,6 +259,7 @@ converse.plugins.add('converse-chatboxes', { 'message_type': 'chat', 'nickname': undefined, 'num_unread': 0, + 'time_opened': this.get('time_opened') || moment().valueOf(), 'type': _converse.PRIVATE_CHAT_TYPE, 'url': '' } @@ -276,23 +277,24 @@ converse.plugins.add('converse-chatboxes', { // but we're in embedded mode. return; } - // XXX: this creates a dependency on converse-roster, which we - // probably shouldn't have here, so we should probably move - // ChatBox out of converse-chatboxes - this.presence = _converse.presences.findWhere({'jid': jid}) || _converse.presences.create({'jid': jid}); + 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}); this.setRosterContact(jid); } + this.on('change:chat_state', this.sendChatState, this); + this.initMessages(); + }, + initMessages () { this.messages = new _converse.Messages(); const storage = _converse.config.get('storage'); this.messages.browserStorage = new Backbone.BrowserStorage[storage]( - b64_sha1(`converse.messages${jid}${_converse.bare_jid}`)); + `converse.messages${this.get('jid')}${_converse.bare_jid}`); this.messages.chatbox = this; this.messages.on('change:upload', (message) => { @@ -300,19 +302,6 @@ converse.plugins.add('converse-chatboxes', { _converse.api.send(this.createMessageStanza(message)); } }); - - this.on('change:chat_state', this.sendChatState, this); - - // Models get saved immediately after creation, so no need to - // call `save` here. - this.set({ - // The chat_state will be set to ACTIVE once the chat box is opened - // and we listen for change:chat_state, so shouldn't set it to ACTIVE here. - 'box_id' : b64_sha1(this.get('jid')), - 'time_opened': this.get('time_opened') || moment().valueOf(), - 'user_id' : Strophe.getNodeFromJid(this.get('jid')), - 'nickname':_.get(_converse.api.contacts.get(this.get('jid')), 'attributes.nickname') - }); }, validate (attrs, options) { @@ -956,8 +945,7 @@ converse.plugins.add('converse-chatboxes', { from_resource = Strophe.getResourceFromJid(from_jid), is_me = from_bare_jid === _converse.bare_jid; - let contact_jid, - is_roster_contact = false; + let contact_jid; if (is_me) { // I am the sender, so this must be a forwarded message... if (_.isNull(to_jid)) { @@ -969,15 +957,17 @@ converse.plugins.add('converse-chatboxes', { contact_jid = Strophe.getBareJidFromJid(to_jid); } else { contact_jid = from_bare_jid; - await _converse.api.waitUntil('rosterContactsFetched'); - is_roster_contact = !_.isUndefined(_converse.roster.get(contact_jid)); - if (!is_roster_contact && !_converse.allow_non_roster_messaging) { - return; - } } + + const contact = await _converse.api.contacts.get(contact_jid); + const is_roster_contact = !_.isUndefined(contact); + if (!is_me && !is_roster_contact && !_converse.allow_non_roster_messaging) { + return; + } + // Get chat box, but only create when the message has something to show to the user const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0, - roster_nick = _.get(_converse.api.contacts.get(contact_jid), 'attributes.nickname'), + roster_nick = _.get(contact, 'attributes.nickname'), chatbox = this.getChatBox(contact_jid, {'nickname': roster_nick}, has_body); if (chatbox) { @@ -1105,17 +1095,11 @@ converse.plugins.add('converse-chatboxes', { * @param {string|string[]} jid|jids An jid or array of jids * @param {object} [attrs] An object containing configuration attributes. */ - 'create' (jids, attrs) { - if (_.isUndefined(jids)) { - _converse.log( - "chats.create: You need to provide at least one JID", - Strophe.LogLevel.ERROR - ); - return null; - } + async create (jids, attrs) { if (_.isString(jids)) { if (attrs && !_.get(attrs, 'fullname')) { - attrs.fullname = _.get(_converse.api.contacts.get(jids), 'attributes.fullname'); + const contact = await _converse.api.contacts.get(jids); + attrs.fullname = _.get(contact, 'attributes.fullname'); } const chatbox = _converse.chatboxes.getChatBox(jids, attrs, true); if (_.isNil(chatbox)) { @@ -1124,10 +1108,18 @@ converse.plugins.add('converse-chatboxes', { } return chatbox; } - return _.map(jids, (jid) => { - attrs.fullname = _.get(_converse.api.contacts.get(jid), 'attributes.fullname'); - return _converse.chatboxes.getChatBox(jid, attrs, true).maybeShow(); - }); + if (_.isArray(jids)) { + return Promise.all(jids.forEach(async jid => { + const contact = await _converse.api.contacts.get(jids); + attrs.fullname = _.get(contact, 'attributes.fullname'); + return _converse.chatboxes.getChatBox(jid, attrs, true).maybeShow(); + })); + } + _converse.log( + "chats.create: You need to provide at least one JID", + Strophe.LogLevel.ERROR + ); + return null; }, /** @@ -1136,6 +1128,8 @@ converse.plugins.add('converse-chatboxes', { * @method _converse.api.chats.open * @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com'] * @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model. + * @param {Boolean} [attrs.minimized] - Should the chat be + * created in minimized state. * @param {Boolean} [force=false] - By default, a minimized * chat won't be maximized (in `overlayed` view mode) and in * `fullscreen` view mode a newly opened chat won't replace @@ -1169,23 +1163,21 @@ converse.plugins.add('converse-chatboxes', { * } * }); */ - 'open' (jids, attrs, force) { - return new Promise((resolve, reject) => { - Promise.all([ - _converse.api.waitUntil('rosterContactsFetched'), - _converse.api.waitUntil('chatBoxesFetched') - ]).then(() => { - if (_.isUndefined(jids)) { - const err_msg = "chats.open: You need to provide at least one JID"; - _converse.log(err_msg, Strophe.LogLevel.ERROR); - reject(new Error(err_msg)); - } else if (_.isString(jids)) { - resolve(_converse.api.chats.create(jids, attrs).maybeShow(force)); - } else { - resolve(_.map(jids, jid => _converse.api.chats.create(jid, attrs).maybeShow(force))); - } - }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); - }); + async open (jids, attrs, force) { + await Promise.all([ + _converse.api.waitUntil('rosterContactsFetched'), + _converse.api.waitUntil('chatBoxesFetched') + ]); + + if (_.isString(jids)) { + const chat = await _converse.api.chats.create(jids, attrs); + return chat.maybeShow(force); + } else if (_.isArray(jids)) { + return Promise.all(jids.map(j => _converse.api.chats.create(j, attrs).then(c => c.maybeShow(force)))); + } + const err_msg = "chats.open: You need to provide at least one JID"; + _converse.log(err_msg, Strophe.LogLevel.ERROR); + throw new Error(err_msg); }, /** @@ -1208,7 +1200,7 @@ converse.plugins.add('converse-chatboxes', { * const models = _converse.api.chats.get(); * */ - 'get' (jids) { + get (jids) { if (_.isUndefined(jids)) { const result = []; _converse.chatboxes.each(function (chatbox) { diff --git a/src/headless/converse-muc.js b/src/headless/converse-muc.js index ad40359cc..3584d16b6 100644 --- a/src/headless/converse-muc.js +++ b/src/headless/converse-muc.js @@ -19,7 +19,7 @@ const MUC_ROLE_WEIGHTS = { 'none': 2, }; -const { Strophe, Backbone, Promise, $iq, $build, $msg, $pres, b64_sha1, sizzle, f, moment, _ } = converse.env; +const { Strophe, Backbone, Promise, $iq, $build, $msg, $pres, sizzle, f, moment, _ } = converse.env; // Add Strophe Namespaces Strophe.addNamespace('MUC_ADMIN', Strophe.NS.MUC + "#admin"); @@ -162,7 +162,6 @@ converse.plugins.add('converse-muc', { */ settings.type = _converse.CHATROOMS_TYPE; settings.id = jid; - settings.box_id = b64_sha1(jid) const chatbox = _converse.chatboxes.getChatBox(jid, settings, true); chatbox.maybeShow(true); return chatbox; @@ -178,32 +177,42 @@ converse.plugins.add('converse-muc', { _converse.ChatRoom = _converse.ChatBox.extend({ defaults () { - return _.assign( - _.clone(_converse.ChatBox.prototype.defaults), { - // For group chats, we distinguish between generally unread - // messages and those ones that specifically mention the - // user. - // - // To keep things simple, we reuse `num_unread` from - // _converse.ChatBox to indicate unread messages which - // mention the user and `num_unread_general` to indicate - // generally unread messages (which *includes* mentions!). - 'num_unread_general': 0, + return { + // For group chats, we distinguish between generally unread + // messages and those ones that specifically mention the + // user. + // + // To keep things simple, we reuse `num_unread` from + // _converse.ChatBox to indicate unread messages which + // mention the user and `num_unread_general` to indicate + // generally unread messages (which *includes* mentions!). + 'num_unread_general': 0, - 'affiliation': null, - 'connection_status': converse.ROOMSTATUS.DISCONNECTED, - 'name': '', - 'nick': _converse.xmppstatus.get('nickname') || _converse.nickname, - 'description': '', - 'roomconfig': {}, - 'type': _converse.CHATROOMS_TYPE, - 'message_type': 'groupchat' - } - ); + 'affiliation': null, + 'bookmarked': false, + 'chat_state': undefined, + 'connection_status': converse.ROOMSTATUS.DISCONNECTED, + 'description': '', + 'hidden': _.includes(['mobile', 'fullscreen'], _converse.view_mode), + 'message_type': 'groupchat', + 'name': '', + 'nick': _converse.xmppstatus.get('nickname') || _converse.nickname, + 'num_unread': 0, + 'roomconfig': {}, + 'time_opened': this.get('time_opened') || moment().valueOf(), + 'type': _converse.CHATROOMS_TYPE + } }, initialize() { - this.constructor.__super__.initialize.apply(this, arguments); + if (_converse.vcards) { + this.vcard = _converse.vcards.findWhere({'jid': this.get('jid')}) || + _converse.vcards.create({'jid': this.get('jid')}); + } + this.set('box_id', `box-${btoa(this.get('jid'))}`); + + this.initMessages(); + this.on('change:chat_state', this.sendChatState, this); this.on('change:connection_status', this.onConnectionStatusChanged, this); const storage = _converse.config.get('storage'); @@ -1391,7 +1400,6 @@ converse.plugins.add('converse-muc', { jid = jid.toLowerCase(); attrs.type = _converse.CHATROOMS_TYPE; attrs.id = jid; - attrs.box_id = b64_sha1(jid) return _converse.chatboxes.getChatBox(jid, attrs, create); }; diff --git a/src/headless/converse-roster.js b/src/headless/converse-roster.js index e672a122f..15c90821e 100644 --- a/src/headless/converse-roster.js +++ b/src/headless/converse-roster.js @@ -880,44 +880,25 @@ converse.plugins.add('converse-roster', { /********** Event Handlers *************/ - function addRelatedContactToChatbox (chatbox, contact) { - if (!_.isUndefined(contact)) { - chatbox.contact = contact; - chatbox.trigger('contactAdded', contact); - } - } - - _converse.api.waitUntil('rosterContactsFetched').then(() => { - _converse.roster.on('add', (contact) => { - /* When a new contact is added, check if we already have a - * chatbox open for it, and if so attach it to the chatbox. - */ - const chatbox = _converse.chatboxes.findWhere({'jid': contact.get('jid')}); - if (chatbox) { - addRelatedContactToChatbox(chatbox, contact); - } - }); - }); - - function updateUnreadCounter (chatbox) { const contact = _converse.roster.findWhere({'jid': chatbox.get('jid')}); if (!_.isUndefined(contact)) { contact.save({'num_unread': chatbox.get('num_unread')}); } } - _converse.api.listen.on('chatBoxesInitialized', () => { - _converse.chatboxes.on('change:num_unread', updateUnreadCounter) - _converse.chatboxes.on('add', async chatbox => { - await _converse.api.waitUntil('rosterContactsFetched'); - addRelatedContactToChatbox(chatbox, _converse.roster.findWhere({'jid': chatbox.get('jid')})); + _converse.api.listen.on('chatBoxesInitialized', () => { + _converse.chatboxes.on('change:num_unread', updateUnreadCounter); + + _converse.chatboxes.on('add', chatbox => { + if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) { + chatbox.setRosterContact(chatbox.get('jid')); + } }); }); _converse.api.listen.on('beforeTearDown', _converse.unregisterPresenceHandler()); - _converse.api.waitUntil('rosterContactsFetched').then(() => { _converse.roster.on('add', (contact) => { /* When a new contact is added, check if we already have a @@ -925,7 +906,7 @@ converse.plugins.add('converse-roster', { */ const chatbox = _converse.chatboxes.findWhere({'jid': contact.get('jid')}); if (chatbox) { - addRelatedContactToChatbox(chatbox, contact); + chatbox.setRosterContact(contact.get('jid')); } }); }); @@ -999,20 +980,20 @@ converse.plugins.add('converse-roster', { * @method _converse.api.contacts.get * @params {(string[]|string)} jid|jids The JID or JIDs of * the contacts to be returned. - * @returns {(RosterContact[]|RosterContact)} [Backbone.Model](http://backbonejs.org/#Model) - * (or an array of them) representing the contact. + * @returns {promise} Promise which resolves with the + * _converse.RosterContact (or an array of them) representing the contact. * * @example * // Fetch a single contact * _converse.api.listen.on('rosterContactsFetched', function () { - * const contact = _converse.api.contacts.get('buddy@example.com') + * const contact = await _converse.api.contacts.get('buddy@example.com') * // ... * }); * * @example * // To get multiple contacts, pass in an array of JIDs: * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = _converse.api.contacts.get( + * const contacts = await _converse.api.contacts.get( * ['buddy1@example.com', 'buddy2@example.com'] * ) * // ... @@ -1021,14 +1002,13 @@ converse.plugins.add('converse-roster', { * @example * // To return all contacts, simply call ``get`` without any parameters: * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = _converse.api.contacts.get(); + * const contacts = await _converse.api.contacts.get(); * // ... * }); */ - 'get' (jids) { - const _getter = function (jid) { - return _converse.roster.get(Strophe.getBareJidFromJid(jid)) || null; - }; + async get (jids) { + await _converse.api.waitUntil('rosterContactsFetched'); + const _getter = jid => _converse.roster.get(Strophe.getBareJidFromJid(jid)); if (_.isUndefined(jids)) { jids = _converse.roster.pluck('jid'); } else if (_.isString(jids)) { diff --git a/src/headless/dist/converse-headless.js b/src/headless/dist/converse-headless.js index 441810cbd..ee0a17567 100644 --- a/src/headless/dist/converse-headless.js +++ b/src/headless/dist/converse-headless.js @@ -38790,6 +38790,7 @@ Strophe.Websocket.prototype = { }); //# sourceMappingURL=strophe.js.map + /***/ }), /***/ "./node_modules/strophejs-plugin-ping/strophe.ping.js": @@ -40248,7 +40249,6 @@ const _converse$env = _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].env Backbone = _converse$env.Backbone, Promise = _converse$env.Promise, Strophe = _converse$env.Strophe, - b64_sha1 = _converse$env.b64_sha1, moment = _converse$env.moment, sizzle = _converse$env.sizzle, utils = _converse$env.utils, @@ -40291,12 +40291,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha const ModelWithContact = Backbone.Model.extend({ async setRosterContact(jid) { - await _converse.api.waitUntil('rosterContactsFetched'); - - const contact = _converse.roster.get(jid); + const contact = await _converse.api.contacts.get(jid); if (contact) { this.contact = contact; + this.set('nickname', contact.get('nickname')); this.trigger('rosterContactAdded'); } } @@ -40524,6 +40523,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha 'message_type': 'chat', 'nickname': undefined, 'num_unread': 0, + 'time_opened': this.get('time_opened') || moment().valueOf(), 'type': _converse.PRIVATE_CHAT_TYPE, 'url': '' }; @@ -40541,15 +40541,10 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha // This happens when the controlbox is in browser storage, // but we're in embedded mode. return; - } // XXX: this creates a dependency on converse-roster, which we - // probably shouldn't have here, so we should probably move - // ChatBox out of converse-chatboxes + } - - this.presence = _converse.presences.findWhere({ - 'jid': jid - }) || _converse.presences.create({ - 'jid': jid + this.set({ + 'box_id': `box-${btoa(jid)}` }); if (_converse.vcards) { @@ -40561,31 +40556,30 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha } if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) { + this.presence = _converse.presences.findWhere({ + 'jid': jid + }) || _converse.presences.create({ + 'jid': jid + }); this.setRosterContact(jid); } + this.on('change:chat_state', this.sendChatState, this); + this.initMessages(); + }, + + initMessages() { this.messages = new _converse.Messages(); const storage = _converse.config.get('storage'); - this.messages.browserStorage = new Backbone.BrowserStorage[storage](b64_sha1(`converse.messages${jid}${_converse.bare_jid}`)); + this.messages.browserStorage = new Backbone.BrowserStorage[storage](`converse.messages${this.get('jid')}${_converse.bare_jid}`); this.messages.chatbox = this; this.messages.on('change:upload', message => { if (message.get('upload') === _converse.SUCCESS) { _converse.api.send(this.createMessageStanza(message)); } }); - this.on('change:chat_state', this.sendChatState, this); // Models get saved immediately after creation, so no need to - // call `save` here. - - this.set({ - // The chat_state will be set to ACTIVE once the chat box is opened - // and we listen for change:chat_state, so shouldn't set it to ACTIVE here. - 'box_id': b64_sha1(this.get('jid')), - 'time_opened': this.get('time_opened') || moment().valueOf(), - 'user_id': Strophe.getNodeFromJid(this.get('jid')), - 'nickname': _.get(_converse.api.contacts.get(this.get('jid')), 'attributes.nickname') - }); }, validate(attrs, options) { @@ -41317,8 +41311,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha const from_bare_jid = Strophe.getBareJidFromJid(from_jid), from_resource = Strophe.getResourceFromJid(from_jid), is_me = from_bare_jid === _converse.bare_jid; - let contact_jid, - is_roster_contact = false; + let contact_jid; if (is_me) { // I am the sender, so this must be a forwarded message... @@ -41329,17 +41322,18 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha contact_jid = Strophe.getBareJidFromJid(to_jid); } else { contact_jid = from_bare_jid; - await _converse.api.waitUntil('rosterContactsFetched'); - is_roster_contact = !_.isUndefined(_converse.roster.get(contact_jid)); + } - if (!is_roster_contact && !_converse.allow_non_roster_messaging) { - return; - } + const contact = await _converse.api.contacts.get(contact_jid); + const is_roster_contact = !_.isUndefined(contact); + + if (!is_me && !is_roster_contact && !_converse.allow_non_roster_messaging) { + return; } // Get chat box, but only create when the message has something to show to the user const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0, - roster_nick = _.get(_converse.api.contacts.get(contact_jid), 'attributes.nickname'), + roster_nick = _.get(contact, 'attributes.nickname'), chatbox = this.getChatBox(contact_jid, { 'nickname': roster_nick }, has_body); @@ -41490,16 +41484,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * @param {string|string[]} jid|jids An jid or array of jids * @param {object} [attrs] An object containing configuration attributes. */ - 'create'(jids, attrs) { - if (_.isUndefined(jids)) { - _converse.log("chats.create: You need to provide at least one JID", Strophe.LogLevel.ERROR); - - return null; - } - + async create(jids, attrs) { if (_.isString(jids)) { if (attrs && !_.get(attrs, 'fullname')) { - attrs.fullname = _.get(_converse.api.contacts.get(jids), 'attributes.fullname'); + const contact = await _converse.api.contacts.get(jids); + attrs.fullname = _.get(contact, 'attributes.fullname'); } const chatbox = _converse.chatboxes.getChatBox(jids, attrs, true); @@ -41513,10 +41502,17 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha return chatbox; } - return _.map(jids, jid => { - attrs.fullname = _.get(_converse.api.contacts.get(jid), 'attributes.fullname'); - return _converse.chatboxes.getChatBox(jid, attrs, true).maybeShow(); - }); + if (_.isArray(jids)) { + return Promise.all(jids.forEach(async jid => { + const contact = await _converse.api.contacts.get(jids); + attrs.fullname = _.get(contact, 'attributes.fullname'); + return _converse.chatboxes.getChatBox(jid, attrs, true).maybeShow(); + })); + } + + _converse.log("chats.create: You need to provide at least one JID", Strophe.LogLevel.ERROR); + + return null; }, /** @@ -41525,6 +41521,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * @method _converse.api.chats.open * @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com'] * @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model. + * @param {Boolean} [attrs.minimized] - Should the chat be + * created in minimized state. * @param {Boolean} [force=false] - By default, a minimized * chat won't be maximized (in `overlayed` view mode) and in * `fullscreen` view mode a newly opened chat won't replace @@ -41558,22 +41556,21 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * } * }); */ - 'open'(jids, attrs, force) { - return new Promise((resolve, reject) => { - Promise.all([_converse.api.waitUntil('rosterContactsFetched'), _converse.api.waitUntil('chatBoxesFetched')]).then(() => { - if (_.isUndefined(jids)) { - const err_msg = "chats.open: You need to provide at least one JID"; + async open(jids, attrs, force) { + await Promise.all([_converse.api.waitUntil('rosterContactsFetched'), _converse.api.waitUntil('chatBoxesFetched')]); - _converse.log(err_msg, Strophe.LogLevel.ERROR); + if (_.isString(jids)) { + const chat = await _converse.api.chats.create(jids, attrs); + return chat.maybeShow(force); + } else if (_.isArray(jids)) { + return Promise.all(jids.map(j => _converse.api.chats.create(j, attrs).then(c => c.maybeShow(force)))); + } - reject(new Error(err_msg)); - } else if (_.isString(jids)) { - resolve(_converse.api.chats.create(jids, attrs).maybeShow(force)); - } else { - resolve(_.map(jids, jid => _converse.api.chats.create(jid, attrs).maybeShow(force))); - } - }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); - }); + const err_msg = "chats.open: You need to provide at least one JID"; + + _converse.log(err_msg, Strophe.LogLevel.ERROR); + + throw new Error(err_msg); }, /** @@ -41596,7 +41593,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha * const models = _converse.api.chats.get(); * */ - 'get'(jids) { + get(jids) { if (_.isUndefined(jids)) { const result = []; @@ -45011,7 +45008,6 @@ const _converse$env = _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].env $build = _converse$env.$build, $msg = _converse$env.$msg, $pres = _converse$env.$pres, - b64_sha1 = _converse$env.b64_sha1, sizzle = _converse$env.sizzle, f = _converse$env.f, moment = _converse$env.moment, @@ -45152,7 +45148,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc */ settings.type = _converse.CHATROOMS_TYPE; settings.id = jid; - settings.box_id = b64_sha1(jid); const chatbox = _converse.chatboxes.getChatBox(jid, settings, true); @@ -45170,7 +45165,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc _converse.ChatRoom = _converse.ChatBox.extend({ defaults() { - return _.assign(_.clone(_converse.ChatBox.prototype.defaults), { + return { // For group chats, we distinguish between generally unread // messages and those ones that specifically mention the // user. @@ -45181,19 +45176,33 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc // generally unread messages (which *includes* mentions!). 'num_unread_general': 0, 'affiliation': null, + 'bookmarked': false, + 'chat_state': undefined, 'connection_status': _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.DISCONNECTED, + 'description': '', + 'hidden': _.includes(['mobile', 'fullscreen'], _converse.view_mode), + 'message_type': 'groupchat', 'name': '', 'nick': _converse.xmppstatus.get('nickname') || _converse.nickname, - 'description': '', + 'num_unread': 0, 'roomconfig': {}, - 'type': _converse.CHATROOMS_TYPE, - 'message_type': 'groupchat' - }); + 'time_opened': this.get('time_opened') || moment().valueOf(), + 'type': _converse.CHATROOMS_TYPE + }; }, initialize() { - this.constructor.__super__.initialize.apply(this, arguments); + if (_converse.vcards) { + this.vcard = _converse.vcards.findWhere({ + 'jid': this.get('jid') + }) || _converse.vcards.create({ + 'jid': this.get('jid') + }); + } + this.set('box_id', `box-${btoa(this.get('jid'))}`); + this.initMessages(); + this.on('change:chat_state', this.sendChatState, this); this.on('change:connection_status', this.onConnectionStatusChanged, this); const storage = _converse.config.get('storage'); @@ -46561,7 +46570,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc jid = jid.toLowerCase(); attrs.type = _converse.CHATROOMS_TYPE; attrs.id = jid; - attrs.box_id = b64_sha1(jid); return _converse.chatboxes.getChatBox(jid, attrs, create); }; @@ -48112,28 +48120,6 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins /********** Event Handlers *************/ - function addRelatedContactToChatbox(chatbox, contact) { - if (!_.isUndefined(contact)) { - chatbox.contact = contact; - chatbox.trigger('contactAdded', contact); - } - } - - _converse.api.waitUntil('rosterContactsFetched').then(() => { - _converse.roster.on('add', contact => { - /* When a new contact is added, check if we already have a - * chatbox open for it, and if so attach it to the chatbox. - */ - const chatbox = _converse.chatboxes.findWhere({ - 'jid': contact.get('jid') - }); - - if (chatbox) { - addRelatedContactToChatbox(chatbox, contact); - } - }); - }); - function updateUnreadCounter(chatbox) { const contact = _converse.roster.findWhere({ 'jid': chatbox.get('jid') @@ -48149,11 +48135,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins _converse.api.listen.on('chatBoxesInitialized', () => { _converse.chatboxes.on('change:num_unread', updateUnreadCounter); - _converse.chatboxes.on('add', async chatbox => { - await _converse.api.waitUntil('rosterContactsFetched'); - addRelatedContactToChatbox(chatbox, _converse.roster.findWhere({ - 'jid': chatbox.get('jid') - })); + _converse.chatboxes.on('add', chatbox => { + if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) { + chatbox.setRosterContact(chatbox.get('jid')); + } }); }); @@ -48169,7 +48154,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins }); if (chatbox) { - addRelatedContactToChatbox(chatbox, contact); + chatbox.setRosterContact(contact.get('jid')); } }); }); @@ -48256,20 +48241,20 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins * @method _converse.api.contacts.get * @params {(string[]|string)} jid|jids The JID or JIDs of * the contacts to be returned. - * @returns {(RosterContact[]|RosterContact)} [Backbone.Model](http://backbonejs.org/#Model) - * (or an array of them) representing the contact. + * @returns {promise} Promise which resolves with the + * _converse.RosterContact (or an array of them) representing the contact. * * @example * // Fetch a single contact * _converse.api.listen.on('rosterContactsFetched', function () { - * const contact = _converse.api.contacts.get('buddy@example.com') + * const contact = await _converse.api.contacts.get('buddy@example.com') * // ... * }); * * @example * // To get multiple contacts, pass in an array of JIDs: * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = _converse.api.contacts.get( + * const contacts = await _converse.api.contacts.get( * ['buddy1@example.com', 'buddy2@example.com'] * ) * // ... @@ -48278,14 +48263,14 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins * @example * // To return all contacts, simply call ``get`` without any parameters: * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = _converse.api.contacts.get(); + * const contacts = await _converse.api.contacts.get(); * // ... * }); */ - 'get'(jids) { - const _getter = function _getter(jid) { - return _converse.roster.get(Strophe.getBareJidFromJid(jid)) || null; - }; + async get(jids) { + await _converse.api.waitUntil('rosterContactsFetched'); + + const _getter = jid => _converse.roster.get(Strophe.getBareJidFromJid(jid)); if (_.isUndefined(jids)) { jids = _converse.roster.pluck('jid'); diff --git a/src/templates/chatbox_head.html b/src/templates/chatbox_head.html index bc888e1a2..3e4e4c73f 100644 --- a/src/templates/chatbox_head.html +++ b/src/templates/chatbox_head.html @@ -2,7 +2,9 @@
- + {[ if (o.type !== o._converse.HEADLINES_TYPE) { ]} + + {[ } ]}
{[ if (o.url) { ]}