MUC: Queue messages received before we're ready

This commit is contained in:
JC Brand 2020-02-19 16:46:56 +01:00
parent 58f6d36564
commit 49817a850f
5 changed files with 132 additions and 21 deletions

89
package-lock.json generated
View File

@ -9988,6 +9988,11 @@
"dev": true,
"optional": true
},
"filesize": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-4.2.1.tgz",
"integrity": "sha512-bP82Hi8VRZX/TUBKfE24iiUGsB/sfm2WUrwTQyAzQrhO3V9IhcBBNBXMyzLY5orACxRyYJ3d2HeRVX+eFv4lmA=="
},
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@ -10299,7 +10304,6 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
@ -10309,8 +10313,7 @@
"graceful-fs": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"dev": true
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
}
}
},
@ -11535,8 +11538,7 @@
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"handle-thing": {
"version": "2.0.0",
@ -12004,6 +12006,11 @@
"minimatch": "^3.0.4"
}
},
"immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
},
"import-cwd": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
@ -12567,6 +12574,11 @@
"integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
"dev": true
},
"jed": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/jed/-/jed-1.1.1.tgz",
"integrity": "sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ="
},
"jest-worker": {
"version": "25.1.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz",
@ -12726,7 +12738,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6"
}
@ -12963,6 +12974,14 @@
"type-check": "~0.3.2"
}
},
"lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
"requires": {
"immediate": "~3.0.5"
}
},
"linkify-it": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
@ -13025,6 +13044,14 @@
"json5": "^0.5.0"
}
},
"localforage": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz",
"integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==",
"requires": {
"lie": "3.1.1"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
@ -19160,6 +19187,14 @@
}
}
},
"pluggable.js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-2.0.1.tgz",
"integrity": "sha512-SBt6v6Tbp20Jf8hU0cpcc/+HBHGMY8/Q+yA6Ih0tBQE8tfdZ6U4PRG0iNvUUjLx/hVyOP53n0UfGBymlfaaXCg==",
"requires": {
"lodash": "^4.17.11"
}
},
"po-loader": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/po-loader/-/po-loader-0.5.0.tgz",
@ -21068,6 +21103,13 @@
"integrity": "sha512-Mf37VjirD7RqCVeYgI8jb5K0DymIho/jNJqDgIkMs4cgKbEkvsow8Q6hpvF7Zmys9iEif0oW41hgbeWVZwABJw==",
"dev": true
},
"skeletor.js": {
"version": "github:skeletorjs/skeletor#dea2d5791ee894493e30b92662c953efec0e58f6",
"from": "github:skeletorjs/skeletor#dea2d5791ee894493e30b92662c953efec0e58f6",
"requires": {
"lodash": "^4.17.14"
}
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
@ -21742,6 +21784,11 @@
"through": "^2.3.4"
}
},
"strophe.js": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.3.4.tgz",
"integrity": "sha512-jSLDG8jolhAwGOSgiJ7DTMSYK3wVoEJHKtpVRyEacQZ6CWA6z2WRPJpcFMjsIweq5aP9/XIvKUQqHBu/ZhvESA=="
},
"style-loader": {
"version": "0.23.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
@ -22195,6 +22242,33 @@
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true
},
"twemoji": {
"version": "12.1.5",
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-12.1.5.tgz",
"integrity": "sha512-B0PBVy5xomwb1M/WZxf/IqPZfnoIYy1skXnlHjMwLwTNfZ9ljh8VgWQktAPcJXu8080WoEh6YwQGPVhDVqvrVQ==",
"requires": {
"fs-extra": "^8.0.1",
"jsonfile": "^5.0.0",
"twemoji-parser": "12.1.3",
"universalify": "^0.1.2"
},
"dependencies": {
"jsonfile": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz",
"integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^0.1.2"
}
}
}
},
"twemoji-parser": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-12.1.3.tgz",
"integrity": "sha512-ND4LZXF4X92/PFrzSgGkq6KPPg8swy/U0yRw1k/+izWRVmq1HYi3khPwV3XIB6FRudgVICAaBhJfW8e8G3HC7Q=="
},
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@ -22415,8 +22489,7 @@
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
},
"unpipe": {
"version": "1.0.0",

View File

@ -580,12 +580,15 @@
it("shows join/leave messages when users enter or exit a groupchat",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
['rosterGroupsFetched', 'chatBoxesFetched'], {'muc_fetch_members': false},
async function (done, _converse) {
const muc_jid = 'coven@chat.shakespeare.lit';
await test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1');
const nick = 'some1';
const room_creation_promise = await _converse.api.rooms.open(muc_jid, {nick});
await test_utils.getRoomFeatures(_converse, muc_jid);
const sent_stanzas = _converse.connection.sent_stanzas;
await u.waitUntil(() => sent_stanzas.filter(iq => sizzle('presence history', iq).length).pop());
const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
const chat_content = view.el.querySelector('.chat-content');
@ -627,6 +630,10 @@
expect(sizzle('div.chat-info:first', chat_content).pop().textContent.trim())
.toBe("some1 has entered the groupchat");
await room_creation_promise;
await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED));
await view.model.messages.fetched;
presence = $pres({
to: 'romeo@montague.lit/_converse.js-29092160',
from: 'coven@chat.shakespeare.lit/newguy'
@ -664,7 +671,7 @@
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
await u.waitUntil(() => chat_content.querySelectorAll('div.chat-info').length === 3);
expect(sizzle('div.chat-info:last', chat_content).pop().textContent.trim())
.toBe("newgirl has entered the groupchat");
@ -5184,13 +5191,14 @@
});
describe("A paused notification", function () {
it("will be shown if received",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1');
const muc_jid = 'coven@chat.shakespeare.lit';
await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'some1');
const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
const chat_content = view.el.querySelector('.chat-content');

View File

@ -185,9 +185,10 @@
mock.initConverse(
['chatBoxesInitialized'],
{ 'auto_login': false,
'enable_smacks': true,
'show_controlbox_by_default': true,
'blacklisted_plugins': 'converse-mam',
'enable_smacks': true,
'muc_fetch_members': false,
'show_controlbox_by_default': true,
'smacks_max_unacked_stanzas': 2
},
async function (done, _converse) {
@ -243,9 +244,11 @@
_converse.connection._dataRecv(test_utils.createRequest(result));
expect(_converse.session.get('smacks_enabled')).toBe(true);
const nick = 'romeo';
const func = _converse.chatboxes.onChatBoxesFetched;
spyOn(_converse.chatboxes, 'onChatBoxesFetched').and.callFake(collection => {
const muc = new _converse.ChatRoom({'jid': muc_jid, 'id': muc_jid}, {'collection': _converse.chatboxes});
const muc = new _converse.ChatRoom({'jid': muc_jid, 'id': muc_jid, nick}, {'collection': _converse.chatboxes});
_converse.chatboxes.add(muc);
func.call(_converse.chatboxes, collection);
});
@ -262,7 +265,15 @@
await _converse.api.waitUntil('chatBoxesFetched');
const muc = _converse.chatboxes.get(muc_jid);
await u.waitUntil(() => muc.messages.length === 1);
await u.waitUntil(() => muc.message_queue.length === 1);
const view = _converse.chatboxviews.get(muc_jid);
await test_utils.getRoomFeatures(_converse, muc_jid);
await test_utils.receiveOwnMUCPresence(_converse, muc_jid, nick);
await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED));
await view.model.messages.fetched;
await u.waitUntil(() => muc.messages.length);
expect(muc.messages.at(0).get('message')).toBe('First message')
done();
}));

View File

@ -384,6 +384,7 @@ converse.plugins.add('converse-muc', {
await new Promise(resolve => this.features.fetch({'success': resolve, 'error': resolve}));
await this.fetchOccupants();
await this.fetchMessages();
await this.clearMessageQueue();
return true;
} else {
await this.clearCache();
@ -451,12 +452,23 @@ converse.plugins.add('converse-muc', {
return this.join();
},
initMessages () {
_converse.ChatBox.prototype.initMessages.call(this);
this.message_queue = [];
},
async clearMessageQueue () {
await Promise.all(this.message_queue.map(m => this.onMessage(m)));
this.message_queue = [];
},
async onConnectionStatusChanged () {
if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
if (_converse.muc_fetch_members) {
await this.occupants.fetchMembers();
}
await this.fetchMessages();
await this.clearMessageQueue();
/**
* Triggered when the user has entered a new MUC
* @event _converse#enteredNewRoom
@ -1780,6 +1792,13 @@ converse.plugins.add('converse-muc', {
* @param { XMLElement } stanza - The message stanza.
*/
async onMessage (stanza) {
if (!this.messages.fetched || this.messages.fetched.isPending) {
// We're not ready to accept messages before we've fetched
// from our store, so we stuff them into a queue.
this.message_queue.push(stanza);
return;
}
if (sizzle(`message > forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).length) {
return log.warn('onMessage: Ignoring unencapsulated forwarded groupchat message');
}

View File

@ -140,9 +140,7 @@
};
utils.openChatRoom = async function (_converse, room, server) {
const model = await _converse.api.rooms.open(`${room}@${server}`);
await model.messages.fetched;
return model;
return _converse.api.rooms.open(`${room}@${server}`);
};
utils.getRoomFeatures = async function (_converse, muc_jid, features=[]) {
@ -273,7 +271,9 @@
return new Promise(resolve => _converse.api.listen.on('membersFetched', resolve));
};
utils.receiveOwnMUCPresence = function (_converse, muc_jid, nick) {
utils.receiveOwnMUCPresence = async function (_converse, muc_jid, nick) {
const sent_stanzas = _converse.connection.sent_stanzas;
await u.waitUntil(() => sent_stanzas.filter(iq => sizzle('presence history', iq).length).pop());
const presence = $pres({
to: _converse.connection.jid,
from: `${muc_jid}/${nick}`,
@ -297,7 +297,7 @@
// The user has just entered the room (because join was called)
// and receives their own presence from the server.
// See example 24: https://xmpp.org/extensions/xep-0045.html#enter-pres
utils.receiveOwnMUCPresence(_converse, muc_jid, nick);
await utils.receiveOwnMUCPresence(_converse, muc_jid, nick);
await room_creation_promise;
const view = _converse.chatboxviews.get(muc_jid);