Skip to content

Commit 72241c9

Browse files
committed
chatboxes: wait until messages are fixed before returning new chatbox
Fixes #1691
1 parent fdb2412 commit 72241c9

9 files changed

+75
-58
lines changed

CHANGES.md

+17-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
# Changelog
22

3-
## 5.0.4
3+
## 6.0.0 (Unreleased)
4+
5+
- #1691 Fix `collection.chatbox is undefined` errors
6+
7+
### Breaking changes
8+
9+
- The following API methods now return promises:
10+
* `_converse.api.chats.get`
11+
* `_converse.api.chats.create`
12+
* `_converse.api.rooms.get`
13+
* `_converse.api.rooms.create`
14+
15+
## 5.0.4 (Unreleased)
416

517
- Bugfix: Don't treat every duplicate message ID as a message correction; since some
618
clients don't use globally unique ID's this causes false positives.
7-
- #1712: `TypeError: plugin._features is not a function`
8-
- Bugfix: process stanzas from mam one-by-one in order to correctly process message
9-
receipts
10-
- #1714 Bugfix: Don't notify the user in case we're receiving a message delivery receipt only
11-
- add config option [allow_message_corrections](https://conversejs.org/docs/html/configuration.html#allow_message_corrections)
19+
- Bugfix: process stanzas from mam one-by-one in order to correctly process message receipts
20+
- Add config option [allow_message_corrections](https://conversejs.org/docs/html/configuration.html#allow_message_corrections)
1221
which, if set to `last`, limits editing of sent messages to the last message sent
22+
- #1712 `TypeError: plugin._features is not a function`
23+
- #1714 Bugfix: Don't notify the user in case we're receiving a message delivery receipt only
1324

1425
## 5.0.3 (2019-09-13)
1526

spec/bookmarks.js

+1
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
'nick': ' Othello'
271271
});
272272
expect(_converse.bookmarks.length).toBe(1);
273+
await u.waitUntil(() => _converse.chatboxes.length >= 1);
273274
expect(view.model.get('bookmarked')).toBeTruthy();
274275
let bookmark_icon = await u.waitUntil(() => view.el.querySelector('.toggle-bookmark'));
275276
expect(u.hasClass('button-on', bookmark_icon)).toBeTruthy();

spec/converse.js

+14-12
Original file line numberDiff line numberDiff line change
@@ -262,26 +262,27 @@
262262
_converse.api.trigger('rosterContactsFetched');
263263

264264
// Test on chat that doesn't exist.
265-
expect(_converse.api.chats.get('[email protected]')).toBeFalsy();
265+
let chat = await _converse.api.chats.get('[email protected]');
266+
expect(chat).toBeFalsy();
266267
const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
267268
const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
268269

269270
// Test on chat that's not open
270-
let box = _converse.api.chats.get(jid);
271-
expect(typeof box === 'undefined').toBeTruthy();
271+
chat = await _converse.api.chats.get(jid);
272+
expect(typeof chat === 'undefined').toBeTruthy();
272273
expect(_converse.chatboxes.length).toBe(1);
273274

274275
// Test for one JID
275-
box = await _converse.api.chats.open(jid);
276-
expect(box instanceof Object).toBeTruthy();
277-
expect(box.get('box_id')).toBe(`box-${btoa(jid)}`);
276+
chat = await _converse.api.chats.open(jid);
277+
expect(chat instanceof Object).toBeTruthy();
278+
expect(chat.get('box_id')).toBe(`box-${btoa(jid)}`);
278279

279280
const view = _converse.chatboxviews.get(jid);
280281
await u.waitUntil(() => u.isVisible(view.el));
281282
// Test for multiple JIDs
282283
test_utils.openChatBoxFor(_converse, jid2);
283284
await u.waitUntil(() => _converse.chatboxes.length == 2);
284-
const list = _converse.api.chats.get([jid, jid2]);
285+
const list = await _converse.api.chats.get([jid, jid2]);
285286
expect(Array.isArray(list)).toBeTruthy();
286287
expect(list[0].get('box_id')).toBe(`box-${btoa(jid)}`);
287288
expect(list[1].get('box_id')).toBe(`box-${btoa(jid2)}`);
@@ -299,13 +300,14 @@
299300
const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
300301
const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
301302
// Test on chat that doesn't exist.
302-
expect(_converse.api.chats.get('[email protected]')).toBeFalsy();
303+
let chat = await _converse.api.chats.get('[email protected]');
304+
expect(chat).toBeFalsy();
303305

304-
const box = await _converse.api.chats.open(jid);
305-
expect(box instanceof Object).toBeTruthy();
306-
expect(box.get('box_id')).toBe(`box-${btoa(jid)}`);
306+
chat = await _converse.api.chats.open(jid);
307+
expect(chat instanceof Object).toBeTruthy();
308+
expect(chat.get('box_id')).toBe(`box-${btoa(jid)}`);
307309
expect(
308-
_.keys(box),
310+
Object.keys(chat),
309311
['close', 'endOTR', 'focus', 'get', 'initiateOTR', 'is_chatroom', 'maximize', 'minimize', 'open', 'set']
310312
);
311313
const view = _converse.chatboxviews.get(jid);

spec/messages.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@
10471047
const message = 'This message is sent from this chatbox';
10481048
await test_utils.sendMessage(view, message);
10491049

1050-
const chatbox = _converse.api.chats.get(contact_jid);
1050+
const chatbox = await _converse.api.chats.get(contact_jid);
10511051
expect(chatbox.messages.models.length, 1);
10521052
const msg_object = chatbox.messages.models[0];
10531053

spec/muc.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group .group-toggle').length, 300);
5757
let muc_jid = '[email protected]';
5858
await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
59-
let room = _converse.api.rooms.get(muc_jid);
59+
let room = await _converse.api.rooms.get(muc_jid);
6060
expect(room instanceof Object).toBeTruthy();
6161

6262
let chatroomview = _converse.chatboxviews.get(muc_jid);
@@ -68,27 +68,27 @@
6868
// Test with mixed case
6969
muc_jid = '[email protected]';
7070
await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
71-
room = _converse.api.rooms.get(muc_jid);
71+
room = await _converse.api.rooms.get(muc_jid);
7272
expect(room instanceof Object).toBeTruthy();
7373
chatroomview = _converse.chatboxviews.get(muc_jid.toLowerCase());
7474
expect(u.isVisible(chatroomview.el)).toBeTruthy();
7575

7676
muc_jid = '[email protected]';
77-
room = _converse.api.rooms.get(muc_jid);
77+
room = await _converse.api.rooms.get(muc_jid);
7878
expect(room instanceof Object).toBeTruthy();
7979
chatroomview = _converse.chatboxviews.get(muc_jid.toLowerCase());
8080
expect(u.isVisible(chatroomview.el)).toBeTruthy();
8181

8282
muc_jid = '[email protected]';
83-
room = _converse.api.rooms.get(muc_jid);
83+
room = await _converse.api.rooms.get(muc_jid);
8484
expect(room instanceof Object).toBeTruthy();
8585
chatroomview = _converse.chatboxviews.get(muc_jid.toLowerCase());
8686
expect(u.isVisible(chatroomview.el)).toBeTruthy();
8787
chatroomview.close();
8888

8989
// Non-existing room
9090
muc_jid = '[email protected]';
91-
room = _converse.api.rooms.get(muc_jid);
91+
room = await _converse.api.rooms.get(muc_jid);
9292
expect(typeof room === 'undefined').toBeTruthy();
9393
done();
9494
}));

src/headless/converse-bookmarks.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,23 @@ converse.plugins.add('converse-bookmarks', {
9999
comparator: (item) => item.get('name').toLowerCase(),
100100

101101
initialize () {
102-
this.on('add', _.flow(this.openBookmarkedRoom, this.markRoomAsBookmarked));
102+
this.on('add', bm => this.openBookmarkedRoom(bm)
103+
.then(bm => this.markRoomAsBookmarked(bm))
104+
.catch(e => _converse.log(e, Strophe.LogLevel.FATAL))
105+
);
106+
103107
this.on('remove', this.markRoomAsUnbookmarked, this);
104108
this.on('remove', this.sendBookmarkStanza, this);
105109

106-
const storage = _converse.config.get('storage'),
107-
cache_key = `converse.room-bookmarks${_converse.bare_jid}`;
110+
const storage = _converse.config.get('storage');
111+
const cache_key = `converse.room-bookmarks${_converse.bare_jid}`;
108112
this.fetched_flag = cache_key+'fetched';
109113
this.browserStorage = new BrowserStorage[storage](cache_key);
110114
},
111115

112-
openBookmarkedRoom (bookmark) {
116+
async openBookmarkedRoom (bookmark) {
113117
if ( _converse.muc_respect_autojoin && bookmark.get('autojoin')) {
114-
const groupchat = _converse.api.rooms.create(bookmark.get('jid'), bookmark.get('nick'));
118+
const groupchat = await _converse.api.rooms.create(bookmark.get('jid'), bookmark.get('nick'));
115119
groupchat.maybeShow();
116120
}
117121
return bookmark;

src/headless/converse-chatboxes.js

+13-18
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@ converse.plugins.add('converse-chatboxes', {
326326

327327
initMessages () {
328328
this.messages = new this.messagesCollection();
329+
this.messages.chatbox = this;
329330
const storage = _converse.config.get('storage');
330331
this.messages.browserStorage = new BrowserStorage[storage](this.getMessagesCacheKey());
331-
this.messages.chatbox = this;
332332
this.listenTo(this.messages, 'change:upload', message => {
333333
if (message.get('upload') === _converse.SUCCESS) {
334334
_converse.api.send(this.createMessageStanza(message));
@@ -1167,7 +1167,7 @@ converse.plugins.add('converse-chatboxes', {
11671167
if (utils.isSameBareJID(from_jid, _converse.bare_jid)) {
11681168
return;
11691169
}
1170-
const chatbox = this.getChatBox(from_jid);
1170+
const chatbox = await this.getChatBox(from_jid);
11711171
if (!chatbox) {
11721172
return;
11731173
}
@@ -1203,7 +1203,7 @@ converse.plugins.add('converse-chatboxes', {
12031203
/**
12041204
* Handler method for all incoming single-user chat "message" stanzas.
12051205
* @private
1206-
* @method _converse.ChatBox#onMessage
1206+
* @method _converse.ChatBoxes#onMessage
12071207
* @param { XMLElement } stanza - The incoming message stanza
12081208
*/
12091209
async onMessage (stanza) {
@@ -1279,7 +1279,7 @@ converse.plugins.add('converse-chatboxes', {
12791279
// Get chat box, but only create when the message has something to show to the user
12801280
const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0;
12811281
const roster_nick = get(contact, 'attributes.nickname');
1282-
const chatbox = this.getChatBox(contact_jid, {'nickname': roster_nick}, has_body);
1282+
const chatbox = await this.getChatBox(contact_jid, {'nickname': roster_nick}, has_body);
12831283

12841284
if (chatbox) {
12851285
const message = await chatbox.getDuplicateMessage(stanza);
@@ -1319,7 +1319,7 @@ converse.plugins.add('converse-chatboxes', {
13191319
* @param { object } attrs - Optional chat box atributes. If the
13201320
* chat box already exists, its attributes will be updated.
13211321
*/
1322-
getChatBox (jid, attrs={}, create) {
1322+
async getChatBox (jid, attrs={}, create) {
13231323
if (isObject(jid)) {
13241324
create = attrs;
13251325
attrs = jid;
@@ -1337,6 +1337,7 @@ converse.plugins.add('converse-chatboxes', {
13371337
_converse.log(response.responseText);
13381338
}
13391339
});
1340+
await chatbox.messages.fetched;
13401341
if (!chatbox.isValid()) {
13411342
chatbox.destroy();
13421343
return null;
@@ -1470,7 +1471,7 @@ converse.plugins.add('converse-chatboxes', {
14701471
* // To open a single chat, provide the JID of the contact you're chatting with in that chat:
14711472
* converse.plugins.add('myplugin', {
14721473
* initialize: function() {
1473-
* var _converse = this._converse;
1474+
* const _converse = this._converse;
14741475
* // Note, buddy@example.org must be in your contacts roster!
14751476
* _converse.api.chats.open('[email protected]').then(chat => {
14761477
* // Now you can do something with the chat model
@@ -1482,7 +1483,7 @@ converse.plugins.add('converse-chatboxes', {
14821483
* // To open an array of chats, provide an array of JIDs:
14831484
* converse.plugins.add('myplugin', {
14841485
* initialize: function () {
1485-
* var _converse = this._converse;
1486+
* const _converse = this._converse;
14861487
* // Note, these users must first be in your contacts roster!
14871488
* _converse.api.chats.open(['buddy1@example.com', '[email protected]']).then(chats => {
14881489
* // Now you can do something with the chat models
@@ -1518,7 +1519,7 @@ converse.plugins.add('converse-chatboxes', {
15181519
*
15191520
* @method _converse.api.chats.get
15201521
* @param {String|string[]} jids - e.g. '[email protected]' or ['[email protected]', '[email protected]']
1521-
* @returns {_converse.ChatBox}
1522+
* @returns { Promise<_converse.ChatBox> }
15221523
*
15231524
* @example
15241525
* // To return a single chat, provide the JID of the contact you're chatting with in that chat:
@@ -1535,19 +1536,13 @@ converse.plugins.add('converse-chatboxes', {
15351536
*/
15361537
get (jids) {
15371538
if (jids === undefined) {
1538-
const result = [];
1539-
_converse.chatboxes.each(function (chatbox) {
1540-
// FIXME: Leaky abstraction from MUC. We need to add a
1541-
// base type for chat boxes, and check for that.
1542-
if (chatbox.get('type') !== _converse.CHATROOMS_TYPE) {
1543-
result.push(chatbox);
1544-
}
1545-
});
1546-
return result;
1539+
// FIXME: Leaky abstraction from MUC. We need to add a
1540+
// base type for chat boxes, and check for that.
1541+
return _converse.chatboxes.filter(c => (c.get('type') !== _converse.CHATROOMS_TYPE));
15471542
} else if (isString(jids)) {
15481543
return _converse.chatboxes.getChatBox(jids);
15491544
}
1550-
return jids.map(jid => _converse.chatboxes.getChatBox(jid, {}, true));
1545+
return Promise.all(jids.map(jid => _converse.chatboxes.getChatBox(jid, {}, true)));
15511546
}
15521547
}
15531548
});

src/headless/converse-muc.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,14 @@ converse.plugins.add('converse-muc', {
230230
}
231231
}
232232

233-
function openChatRoom (jid, settings) {
233+
async function openChatRoom (jid, settings) {
234234
/* Opens a groupchat, making sure that certain attributes
235235
* are correct, for example that the "type" is set to
236236
* "chatroom".
237237
*/
238238
settings.type = _converse.CHATROOMS_TYPE;
239239
settings.id = jid;
240-
const chatbox = _converse.chatboxes.getChatBox(jid, settings, true);
240+
const chatbox = await _converse.chatboxes.getChatBox(jid, settings, true);
241241
chatbox.maybeShow(true);
242242
return chatbox;
243243
}
@@ -2042,7 +2042,7 @@ converse.plugins.add('converse-muc', {
20422042
* @method _converse.ChatRoom#onDirectMUCInvitation
20432043
* @param { XMLElement } message - The message stanza containing the invitation.
20442044
*/
2045-
_converse.onDirectMUCInvitation = function (message) {
2045+
_converse.onDirectMUCInvitation = async function (message) {
20462046
const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
20472047
from = Strophe.getBareJidFromJid(message.getAttribute('from')),
20482048
room_jid = x_el.getAttribute('jid'),
@@ -2068,7 +2068,7 @@ converse.plugins.add('converse-muc', {
20682068
}
20692069
}
20702070
if (result === true) {
2071-
const chatroom = openChatRoom(room_jid, {'password': x_el.getAttribute('password') });
2071+
const chatroom = await openChatRoom(room_jid, {'password': x_el.getAttribute('password') });
20722072
if (chatroom.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
20732073
_converse.chatboxes.get(room_jid).join();
20742074
}
@@ -2198,6 +2198,7 @@ converse.plugins.add('converse-muc', {
21982198
* @param {(string[]|string)} jid|jids The JID or array of
21992199
* JIDs of the chatroom(s) to create
22002200
* @param {object} [attrs] attrs The room attributes
2201+
* @returns {Promise} Promise which resolves with the Backbone.Model representing the chat.
22012202
*/
22022203
create (jids, attrs={}) {
22032204
attrs = _.isString(attrs) ? {'nick': attrs} : (attrs || {});
@@ -2241,6 +2242,7 @@ converse.plugins.add('converse-muc', {
22412242
* another chat already in the foreground.
22422243
* Set `force` to `true` if you want to force the room to be
22432244
* maximized or shown.
2245+
* @returns {Promise} Promise which resolves with the Backbone.Model representing the chat.
22442246
*
22452247
* @example
22462248
* this._converse.api.rooms.open('[email protected]')
@@ -2277,13 +2279,14 @@ converse.plugins.add('converse-muc', {
22772279
_converse.log(err_msg, Strophe.LogLevel.ERROR);
22782280
throw(new TypeError(err_msg));
22792281
} else if (_.isString(jids)) {
2280-
const room = _converse.api.rooms.create(jids, attrs);
2281-
if (room) {
2282-
room.maybeShow(force);
2283-
}
2282+
const room = await _converse.api.rooms.create(jids, attrs);
2283+
room && room.maybeShow(force);
22842284
return room;
22852285
} else {
2286-
return jids.map(jid => _converse.api.rooms.create(jid, attrs).maybeShow(force));
2286+
return jids.map(async jid => {
2287+
const room = await _converse.api.rooms.create(jid, attrs);
2288+
room.maybeShow(force);
2289+
});
22872290
}
22882291
},
22892292

@@ -2300,6 +2303,7 @@ converse.plugins.add('converse-muc', {
23002303
* the user's JID will be used.
23012304
* @param {boolean} create A boolean indicating whether the room should be created
23022305
* if not found (default: `false`)
2306+
* @returns {Promise} Promise which resolves with the Backbone.Model representing the chat.
23032307
* @example
23042308
* _converse.api.waitUntil('roomsAutoJoined').then(() => {
23052309
* const create_if_not_found = true;

0 commit comments

Comments
 (0)