Commit b5eea12d authored by JC Brand's avatar JC Brand

Refactor so that message attributes are parsed early

It's better to parse an incoming message stanza early, than to have
all kinds of methods throughout the codebase that does querySelector
etc.

Firstly, it allows us to catch and report errors and malicious stanzas early on.
It also simplifies programming because you don't need to try and
remember how to properly parse a stanza, all the work is done upfront
for you.
parent 27008aff
...@@ -37,7 +37,7 @@ describe("The nickname autocomplete feature", function () { ...@@ -37,7 +37,7 @@ describe("The nickname autocomplete feature", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t('Hello world').tree(); }).c('body').t('Hello world').tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
// Test that pressing @ brings up all options // Test that pressing @ brings up all options
const textarea = view.el.querySelector('textarea.chat-textarea'); const textarea = view.el.querySelector('textarea.chat-textarea');
......
...@@ -6,7 +6,7 @@ describe("A headlines box", function () { ...@@ -6,7 +6,7 @@ describe("A headlines box", function () {
mock.initConverse( mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {}, function (done, _converse) { ['rosterGroupsFetched', 'chatBoxesFetched'], {}, function (done, _converse) {
const { u, $msg} = converse.env; const { $msg } = converse.env;
/* XMPP spam message: /* XMPP spam message:
* *
* <message xmlns="jabber:client" * <message xmlns="jabber:client"
...@@ -17,7 +17,6 @@ describe("A headlines box", function () { ...@@ -17,7 +17,6 @@ describe("A headlines box", function () {
* <body>SORRY FOR THIS ADVERT</body * <body>SORRY FOR THIS ADVERT</body
* </message * </message
*/ */
sinon.spy(u, 'isHeadlineMessage');
const stanza = $msg({ const stanza = $msg({
'xmlns': 'jabber:client', 'xmlns': 'jabber:client',
'to': 'romeo@montague.lit', 'to': 'romeo@montague.lit',
...@@ -27,10 +26,7 @@ describe("A headlines box", function () { ...@@ -27,10 +26,7 @@ describe("A headlines box", function () {
.c('nick', {'xmlns': "http://jabber.org/protocol/nick"}).t("-wwdmz").up() .c('nick', {'xmlns': "http://jabber.org/protocol/nick"}).t("-wwdmz").up()
.c('body').t('SORRY FOR THIS ADVERT'); .c('body').t('SORRY FOR THIS ADVERT');
_converse.connection._dataRecv(mock.createRequest(stanza)); _converse.connection._dataRecv(mock.createRequest(stanza));
expect(u.isHeadlineMessage.called).toBeTruthy();
expect(u.isHeadlineMessage.returned(false)).toBeTruthy();
expect(_converse.api.headlines.get().length === 0); expect(_converse.api.headlines.get().length === 0);
u.isHeadlineMessage.restore();
done(); done();
})); }));
...@@ -51,7 +47,6 @@ describe("A headlines box", function () { ...@@ -51,7 +47,6 @@ describe("A headlines box", function () {
* </x> * </x>
* </message> * </message>
*/ */
sinon.spy(u, 'isHeadlineMessage');
const stanza = $msg({ const stanza = $msg({
'type': 'headline', 'type': 'headline',
'from': 'notify.example.com', 'from': 'notify.example.com',
...@@ -65,9 +60,6 @@ describe("A headlines box", function () { ...@@ -65,9 +60,6 @@ describe("A headlines box", function () {
_converse.connection._dataRecv(mock.createRequest(stanza)); _converse.connection._dataRecv(mock.createRequest(stanza));
await u.waitUntil(() => _converse.chatboxviews.keys().includes('notify.example.com')); await u.waitUntil(() => _converse.chatboxviews.keys().includes('notify.example.com'));
expect(u.isHeadlineMessage.called).toBeTruthy();
expect(u.isHeadlineMessage.returned(true)).toBeTruthy();
u.isHeadlineMessage.restore(); // unwraps
const view = _converse.chatboxviews.get('notify.example.com'); const view = _converse.chatboxviews.get('notify.example.com');
expect(view.model.get('show_avatar')).toBeFalsy(); expect(view.model.get('show_avatar')).toBeFalsy();
expect(view.el.querySelector('img.avatar')).toBe(null); expect(view.el.querySelector('img.avatar')).toBe(null);
...@@ -155,9 +147,8 @@ describe("A headlines box", function () { ...@@ -155,9 +147,8 @@ describe("A headlines box", function () {
mock.initConverse( mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {}, function (done, _converse) { ['rosterGroupsFetched', 'chatBoxesFetched'], {}, function (done, _converse) {
const { u, $msg, _ } = converse.env; const { $msg, _ } = converse.env;
_converse.allow_non_roster_messaging = false; _converse.allow_non_roster_messaging = false;
sinon.spy(u, 'isHeadlineMessage');
const stanza = $msg({ const stanza = $msg({
'type': 'headline', 'type': 'headline',
'from': 'andre5114@jabber.snc.ru/Spark', 'from': 'andre5114@jabber.snc.ru/Spark',
...@@ -168,9 +159,6 @@ describe("A headlines box", function () { ...@@ -168,9 +159,6 @@ describe("A headlines box", function () {
.c('body').t('Здравствуйте друзья'); .c('body').t('Здравствуйте друзья');
_converse.connection._dataRecv(mock.createRequest(stanza)); _converse.connection._dataRecv(mock.createRequest(stanza));
expect(_.without('controlbox', _converse.chatboxviews.keys()).length).toBe(0); expect(_.without('controlbox', _converse.chatboxviews.keys()).length).toBe(0);
expect(u.isHeadlineMessage.called).toBeTruthy();
expect(u.isHeadlineMessage.returned(true)).toBeTruthy();
u.isHeadlineMessage.restore(); // unwraps
done(); done();
})); }));
}); });
...@@ -294,7 +294,7 @@ describe("Message Archive Management", function () { ...@@ -294,7 +294,7 @@ describe("Message Archive Management", function () {
</message>`); </message>`);
spyOn(view.model, 'getDuplicateMessage').and.callThrough(); spyOn(view.model, 'getDuplicateMessage').and.callThrough();
spyOn(view.model, 'updateMessage').and.callThrough(); spyOn(view.model, 'updateMessage').and.callThrough();
view.model.queueMessage(stanza); view.model.handleMAMResult({ 'messages': [stanza] });
await u.waitUntil(() => view.model.getDuplicateMessage.calls.count()); await u.waitUntil(() => view.model.getDuplicateMessage.calls.count());
expect(view.model.getDuplicateMessage.calls.count()).toBe(1); expect(view.model.getDuplicateMessage.calls.count()).toBe(1);
const result = view.model.getDuplicateMessage.calls.all()[0].returnValue const result = view.model.getDuplicateMessage.calls.all()[0].returnValue
...@@ -338,7 +338,7 @@ describe("Message Archive Management", function () { ...@@ -338,7 +338,7 @@ describe("Message Archive Management", function () {
</result> </result>
</message>`); </message>`);
spyOn(view.model, 'getDuplicateMessage').and.callThrough(); spyOn(view.model, 'getDuplicateMessage').and.callThrough();
view.model.queueMessage(stanza); view.model.handleMAMResult({ 'messages': [stanza] });
await u.waitUntil(() => view.model.getDuplicateMessage.calls.count()); await u.waitUntil(() => view.model.getDuplicateMessage.calls.count());
expect(view.model.getDuplicateMessage.calls.count()).toBe(1); expect(view.model.getDuplicateMessage.calls.count()).toBe(1);
const result = await view.model.getDuplicateMessage.calls.all()[0].returnValue const result = await view.model.getDuplicateMessage.calls.all()[0].returnValue
...@@ -368,7 +368,7 @@ describe("Message Archive Management", function () { ...@@ -368,7 +368,7 @@ describe("Message Archive Management", function () {
</forwarded> </forwarded>
</result> </result>
</message>`); </message>`);
view.model.queueMessage(stanza); view.model.handleMAMResult({ 'messages': [stanza] });
await u.waitUntil(() => view.content.querySelectorAll('.chat-msg').length); await u.waitUntil(() => view.content.querySelectorAll('.chat-msg').length);
expect(view.content.querySelectorAll('.chat-msg').length).toBe(1); expect(view.content.querySelectorAll('.chat-msg').length).toBe(1);
...@@ -388,7 +388,7 @@ describe("Message Archive Management", function () { ...@@ -388,7 +388,7 @@ describe("Message Archive Management", function () {
</message>`); </message>`);
spyOn(view.model, 'getDuplicateMessage').and.callThrough(); spyOn(view.model, 'getDuplicateMessage').and.callThrough();
view.model.queueMessage(stanza); view.model.handleMAMResult({ 'messages': [stanza] });
await u.waitUntil(() => view.model.getDuplicateMessage.calls.count()); await u.waitUntil(() => view.model.getDuplicateMessage.calls.count());
expect(view.model.getDuplicateMessage.calls.count()).toBe(1); expect(view.model.getDuplicateMessage.calls.count()).toBe(1);
const result = await view.model.getDuplicateMessage.calls.all()[0].returnValue const result = await view.model.getDuplicateMessage.calls.all()[0].returnValue
......
...@@ -512,9 +512,8 @@ describe("A Chat Message", function () { ...@@ -512,9 +512,8 @@ describe("A Chat Message", function () {
// Ideally we wouldn't have to filter out headline // Ideally we wouldn't have to filter out headline
// messages, but Prosody gives them the wrong 'type' :( // messages, but Prosody gives them the wrong 'type' :(
sinon.spy(converse.env.log, 'info'); spyOn(converse.env.log, 'info');
sinon.spy(_converse.api.chatboxes, 'get'); sinon.spy(_converse.api.chatboxes, 'get');
sinon.spy(u, 'isHeadlineMessage');
const msg = $msg({ const msg = $msg({
from: 'montague.lit', from: 'montague.lit',
to: _converse.bare_jid, to: _converse.bare_jid,
...@@ -522,16 +521,12 @@ describe("A Chat Message", function () { ...@@ -522,16 +521,12 @@ describe("A Chat Message", function () {
id: u.getUniqueId() id: u.getUniqueId()
}).c('body').t("This headline message will not be shown").tree(); }).c('body').t("This headline message will not be shown").tree();
await _converse.handleMessageStanza(msg); await _converse.handleMessageStanza(msg);
expect(converse.env.log.info.calledWith( expect(converse.env.log.info).toHaveBeenCalledWith(
"handleMessageStanza: Ignoring incoming headline message from JID: montague.lit" "handleMessageStanza: Ignoring incoming server message from JID: montague.lit"
)).toBeTruthy(); );
expect(u.isHeadlineMessage.called).toBeTruthy();
expect(u.isHeadlineMessage.returned(true)).toBeTruthy();
expect(_converse.api.chatboxes.get.called).toBeFalsy(); expect(_converse.api.chatboxes.get.called).toBeFalsy();
// Remove sinon spies // Remove sinon spies
converse.env.log.info.restore();
_converse.api.chatboxes.get.restore(); _converse.api.chatboxes.get.restore();
u.isHeadlineMessage.restore();
done(); done();
})); }));
...@@ -1561,7 +1556,7 @@ describe("A Chat Message", function () { ...@@ -1561,7 +1556,7 @@ describe("A Chat Message", function () {
* </message> * </message>
*/ */
const error_txt = 'Server-to-server connection failed: Connecting failed: connection timeout'; const error_txt = 'Server-to-server connection failed: Connecting failed: connection timeout';
const sender_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@montague.lit'; const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
let fullname = _converse.xmppstatus.get('fullname'); // eslint-disable-line no-unused-vars let fullname = _converse.xmppstatus.get('fullname'); // eslint-disable-line no-unused-vars
fullname = _.isEmpty(fullname) ? _converse.bare_jid: fullname; fullname = _.isEmpty(fullname) ? _converse.bare_jid: fullname;
await _converse.api.chats.open(sender_jid) await _converse.api.chats.open(sender_jid)
...@@ -1757,7 +1752,7 @@ describe("A Chat Message", function () { ...@@ -1757,7 +1752,7 @@ describe("A Chat Message", function () {
await mock.waitForRoster(_converse, 'current'); await mock.waitForRoster(_converse, 'current');
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length) await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length)
// Send a message from a different resource // Send a message from a different resource
spyOn(converse.env.log, 'info'); spyOn(converse.env.log, 'error');
spyOn(_converse.api.chatboxes, 'create').and.callThrough(); spyOn(_converse.api.chatboxes, 'create').and.callThrough();
_converse.filter_by_resource = true; _converse.filter_by_resource = true;
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
...@@ -1770,8 +1765,8 @@ describe("A Chat Message", function () { ...@@ -1770,8 +1765,8 @@ describe("A Chat Message", function () {
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.handleMessageStanza(msg); await _converse.handleMessageStanza(msg);
expect(converse.env.log.info).toHaveBeenCalledWith( expect(converse.env.log.error.calls.all().pop().args[0]).toBe(
"handleMessageStanza: Ignoring incoming message intended for a different resource: romeo@montague.lit/some-other-resource", "Ignoring incoming message intended for a different resource: romeo@montague.lit/some-other-resource",
); );
expect(_converse.api.chatboxes.create).not.toHaveBeenCalled(); expect(_converse.api.chatboxes.create).not.toHaveBeenCalled();
_converse.filter_by_resource = false; _converse.filter_by_resource = false;
......
...@@ -156,7 +156,7 @@ describe("The Minimized Chats Widget", function () { ...@@ -156,7 +156,7 @@ describe("The Minimized Chats Widget", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
view.model.queueMessage(msg); view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.model.messages.length); await u.waitUntil(() => view.model.messages.length);
expect(u.isVisible(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count'))).toBeTruthy(); expect(u.isVisible(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count'))).toBeTruthy();
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe('1'); expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe('1');
......
...@@ -518,8 +518,7 @@ describe("Groupchats", function () { ...@@ -518,8 +518,7 @@ describe("Groupchats", function () {
_converse.connection._dataRecv(mock.createRequest(stanza)); _converse.connection._dataRecv(mock.createRequest(stanza));
const view = _converse.chatboxviews.get('jdev@conference.jabber.org'); const view = _converse.chatboxviews.get('jdev@conference.jabber.org');
await new Promise(resolve => view.model.once('change:subject', resolve)); await new Promise(resolve => view.model.once('change:subject', resolve));
const head_desc = await u.waitUntil(() => view.el.querySelector('.chat-head__desc'), 1000);
const head_desc = await u.waitUntil(() => view.el.querySelector('.chat-head__desc'));
expect(head_desc?.textContent.trim()).toBe(text); expect(head_desc?.textContent.trim()).toBe(text);
stanza = u.toStanza( stanza = u.toStanza(
...@@ -701,7 +700,7 @@ describe("Groupchats", function () { ...@@ -701,7 +700,7 @@ describe("Groupchats", function () {
'type': 'groupchat' 'type': 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
spyOn(view.model, 'clearMessages').and.callThrough(); spyOn(view.model, 'clearMessages').and.callThrough();
await view.model.close(); await view.model.close();
...@@ -732,7 +731,7 @@ describe("Groupchats", function () { ...@@ -732,7 +731,7 @@ describe("Groupchats", function () {
'type': 'groupchat' 'type': 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelector('.chat-msg__text a')); await u.waitUntil(() => view.el.querySelector('.chat-msg__text a'));
view.el.querySelector('.chat-msg__text a').click(); view.el.querySelector('.chat-msg__text a').click();
await u.waitUntil(() => _converse.chatboxes.length === 3) await u.waitUntil(() => _converse.chatboxes.length === 3)
...@@ -1269,7 +1268,7 @@ describe("Groupchats", function () { ...@@ -1269,7 +1268,7 @@ describe("Groupchats", function () {
'type': 'groupchat' 'type': 'groupchat'
}).c('body').t('Some message').tree(); }).c('body').t('Some message').tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => sizzle('.chat-msg:last .chat-msg__text', view.content).pop()); await u.waitUntil(() => sizzle('.chat-msg:last .chat-msg__text', view.content).pop());
let stanza = u.toStanza( let stanza = u.toStanza(
...@@ -1318,7 +1317,7 @@ describe("Groupchats", function () { ...@@ -1318,7 +1317,7 @@ describe("Groupchats", function () {
'to': 'romeo@montague.lit', 'to': 'romeo@montague.lit',
'type': 'groupchat' 'type': 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => sizzle('.chat-msg:last .chat-msg__text', view.content).pop()); await u.waitUntil(() => sizzle('.chat-msg:last .chat-msg__text', view.content).pop());
expect(_.includes(view.el.querySelector('.chat-msg__author').textContent, '**Dyon van de Wege')).toBeTruthy(); expect(_.includes(view.el.querySelector('.chat-msg__author').textContent, '**Dyon van de Wege')).toBeTruthy();
expect(view.el.querySelector('.chat-msg__text').textContent.trim()).toBe('is tired'); expect(view.el.querySelector('.chat-msg__text').textContent.trim()).toBe('is tired');
...@@ -1330,7 +1329,7 @@ describe("Groupchats", function () { ...@@ -1330,7 +1329,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 2); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 2);
expect(sizzle('.chat-msg__author:last', view.el).pop().textContent.includes('**Romeo Montague')).toBeTruthy(); expect(sizzle('.chat-msg__author:last', view.el).pop().textContent.includes('**Romeo Montague')).toBeTruthy();
expect(sizzle('.chat-msg__text:last', view.el).pop().textContent.trim()).toBe('is as well'); expect(sizzle('.chat-msg__text:last', view.el).pop().textContent.trim()).toBe('is as well');
...@@ -2016,7 +2015,7 @@ describe("Groupchats", function () { ...@@ -2016,7 +2015,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
await view.model.queueMessage(message.nodeTree); await view.model.handleMessageStanza(message.nodeTree);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length);
expect(view.content.querySelectorAll('.chat-msg').length).toBe(1); expect(view.content.querySelectorAll('.chat-msg').length).toBe(1);
expect(view.content.querySelector('.chat-msg__text').textContent.trim()).toBe(text); expect(view.content.querySelector('.chat-msg__text').textContent.trim()).toBe(text);
...@@ -2061,7 +2060,7 @@ describe("Groupchats", function () { ...@@ -2061,7 +2060,7 @@ describe("Groupchats", function () {
by="lounge@montague.lit"/> by="lounge@montague.lit"/>
<origin-id xmlns="urn:xmpp:sid:0" id="${view.model.messages.at(0).get('origin_id')}"/> <origin-id xmlns="urn:xmpp:sid:0" id="${view.model.messages.at(0).get('origin_id')}"/>
</message>`); </message>`);
await view.model.queueMessage(stanza); await view.model.handleMessageStanza(stanza);
expect(view.content.querySelectorAll('.chat-msg').length).toBe(1); expect(view.content.querySelectorAll('.chat-msg').length).toBe(1);
expect(sizzle('.chat-msg__text:last').pop().textContent.trim()).toBe(text); expect(sizzle('.chat-msg__text:last').pop().textContent.trim()).toBe(text);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
...@@ -2083,7 +2082,7 @@ describe("Groupchats", function () { ...@@ -2083,7 +2082,7 @@ describe("Groupchats", function () {
const promises = []; const promises = [];
for (let i=0; i<20; i++) { for (let i=0; i<20; i++) {
promises.push( promises.push(
view.model.queueMessage( view.model.handleMessageStanza(
$msg({ $msg({
from: 'lounge@montague.lit/someone', from: 'lounge@montague.lit/someone',
to: 'romeo@montague.lit.com', to: 'romeo@montague.lit.com',
...@@ -2096,7 +2095,7 @@ describe("Groupchats", function () { ...@@ -2096,7 +2095,7 @@ describe("Groupchats", function () {
// Give enough time for `markScrolled` to have been called // Give enough time for `markScrolled` to have been called
setTimeout(async () => { setTimeout(async () => {
view.content.scrollTop = 0; view.content.scrollTop = 0;
await view.model.queueMessage( await view.model.handleMessageStanza(
$msg({ $msg({
from: 'lounge@montague.lit/someone', from: 'lounge@montague.lit/someone',
to: 'romeo@montague.lit.com', to: 'romeo@montague.lit.com',
...@@ -4863,8 +4862,7 @@ describe("Groupchats", function () { ...@@ -4863,8 +4862,7 @@ describe("Groupchats", function () {
view.model.set({'minimized': true}); view.model.set({'minimized': true});
const nick = mock.chatroom_names[0]; const nick = mock.chatroom_names[0];
await view.model.handleMessageStanza($msg({
await view.model.queueMessage($msg({
from: muc_jid+'/'+nick, from: muc_jid+'/'+nick,
id: u.getUniqueId(), id: u.getUniqueId(),
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
...@@ -4875,7 +4873,7 @@ describe("Groupchats", function () { ...@@ -4875,7 +4873,7 @@ describe("Groupchats", function () {
expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1); expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
expect(roomspanel.el.querySelector('.msgs-indicator').textContent.trim()).toBe('1'); expect(roomspanel.el.querySelector('.msgs-indicator').textContent.trim()).toBe('1');
await view.model.queueMessage($msg({ await view.model.handleMessageStanza($msg({
'from': muc_jid+'/'+nick, 'from': muc_jid+'/'+nick,
'id': u.getUniqueId(), 'id': u.getUniqueId(),
'to': 'romeo@montague.lit', 'to': 'romeo@montague.lit',
...@@ -5027,7 +5025,7 @@ describe("Groupchats", function () { ...@@ -5027,7 +5025,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() === 'newguy and nomorenicks are typing'); await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() === 'newguy and nomorenicks are typing');
// <composing> state for a different occupant // <composing> state for a different occupant
...@@ -5037,7 +5035,7 @@ describe("Groupchats", function () { ...@@ -5037,7 +5035,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() === 'newguy, nomorenicks and majortom are typing'); await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() === 'newguy, nomorenicks and majortom are typing');
// <composing> state for a different occupant // <composing> state for a different occupant
...@@ -5047,7 +5045,7 @@ describe("Groupchats", function () { ...@@ -5047,7 +5045,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() === 'newguy, nomorenicks and others are typing'); await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() === 'newguy, nomorenicks and others are typing');
// Check that new messages appear under the chat state notifications // Check that new messages appear under the chat state notifications
...@@ -5057,7 +5055,7 @@ describe("Groupchats", function () { ...@@ -5057,7 +5055,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t('hello world').tree(); }).c('body').t('hello world').tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await new Promise(resolve => view.once('messageInserted', resolve)); await new Promise(resolve => view.once('messageInserted', resolve));
const messages = view.el.querySelectorAll('.message'); const messages = view.el.querySelectorAll('.message');
...@@ -5146,7 +5144,7 @@ describe("Groupchats", function () { ...@@ -5146,7 +5144,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent); await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent);
expect(view.el.querySelector('.chat-content__notifications').textContent.trim()).toBe('newguy is typing'); expect(view.el.querySelector('.chat-content__notifications').textContent.trim()).toBe('newguy is typing');
...@@ -5157,7 +5155,7 @@ describe("Groupchats", function () { ...@@ -5157,7 +5155,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() == 'newguy and nomorenicks are typing'); await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() == 'newguy and nomorenicks are typing');
...@@ -5168,7 +5166,7 @@ describe("Groupchats", function () { ...@@ -5168,7 +5166,7 @@ describe("Groupchats", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
await view.model.queueMessage(msg); await view.model.handleMessageStanza(msg);
await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() == 'nomorenicks is typing\nnewguy has stopped typing'); await u.waitUntil(() => view.el.querySelector('.chat-content__notifications').textContent.trim() == 'nomorenicks is typing\nnewguy has stopped typing');
done(); done();
})); }));
......
This diff is collapsed.
...@@ -95,10 +95,7 @@ describe("Notifications", function () { ...@@ -95,10 +95,7 @@ describe("Notifications", function () {
await u.waitUntil(() => _converse.chatboxviews.keys().length); await u.waitUntil(() => _converse.chatboxviews.keys().length);
const view = _converse.chatboxviews.get('notify.example.com'); const view = _converse.chatboxviews.get('notify.example.com');
await new Promise(resolve => view.once('messageInserted', resolve)); await new Promise(resolve => view.once('messageInserted', resolve));
expect( expect(_converse.chatboxviews.keys().includes('notify.example.com')).toBeTruthy();
_.includes(_converse.chatboxviews.keys(),
'notify.example.com')
).toBeTruthy();
expect(_converse.showMessageNotification).toHaveBeenCalled(); expect(_converse.showMessageNotification).toHaveBeenCalled();
done(); done();
})); }));
...@@ -175,7 +172,7 @@ describe("Notifications", function () { ...@@ -175,7 +172,7 @@ describe("Notifications", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
await view.model.queueMessage(message.nodeTree); await view.model.handleMessageStanza(message.nodeTree);
await u.waitUntil(() => _converse.playSoundNotification.calls.count()); await u.waitUntil(() => _converse.playSoundNotification.calls.count());
expect(_converse.playSoundNotification).toHaveBeenCalled(); expect(_converse.playSoundNotification).toHaveBeenCalled();
...@@ -186,7 +183,7 @@ describe("Notifications", function () { ...@@ -186,7 +183,7 @@ describe("Notifications", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
await view.model.queueMessage(message.nodeTree); await view.model.handleMessageStanza(message.nodeTree);
expect(_converse.playSoundNotification, 1); expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false; _converse.play_sounds = false;
...@@ -197,7 +194,7 @@ describe("Notifications", function () { ...@@ -197,7 +194,7 @@ describe("Notifications", function () {
to: 'romeo@montague.lit', to: 'romeo@montague.lit',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
await view.model.queueMessage(message.nodeTree); await view.model.handleMessageStanza(message.nodeTree);
expect(_converse.playSoundNotification, 1); expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false; _converse.play_sounds = false;
done(); done();
......
...@@ -19,7 +19,7 @@ async function sendAndThenRetractMessage (_converse, view) { ...@@ -19,7 +19,7 @@ async function sendAndThenRetractMessage (_converse, view) {
by="lounge@montague.lit"/> by="lounge@montague.lit"/>
<origin-id xmlns="urn:xmpp:sid:0" id="${msg_obj.get('origin_id')}"/> <origin-id xmlns="urn:xmpp:sid:0" id="${msg_obj.get('origin_id')}"/>
</message>`); </message>`);
await view.model.queueMessage(reflection_stanza); await view.model.handleMessageStanza(reflection_stanza);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length, 500); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length, 500);
const retract_button = await u.waitUntil(() => view.el.querySelector('.chat-msg__content .chat-msg__action-retract')); const retract_button = await u.waitUntil(() => view.el.querySelector('.chat-msg__content .chat-msg__action-retract'));
...@@ -52,7 +52,7 @@ describe("Message Retractions", function () { ...@@ -52,7 +52,7 @@ describe("Message Retractions", function () {
</message> </message>
`); `);
const view = _converse.api.chatviews.get(muc_jid); const view = _converse.api.chatviews.get(muc_jid);
await view.model.queueMessage(received_stanza); await view.model.handleMessageStanza(received_stanza);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1);
expect(view.model.messages.at(0).get('retracted')).toBeFalsy(); expect(view.model.messages.at(0).get('retracted')).toBeFalsy();
expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy(); expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy();
...@@ -394,7 +394,7 @@ describe("Message Retractions", function () { ...@@ -394,7 +394,7 @@ describe("Message Retractions", function () {
</message> </message>
`); `);
const view = _converse.api.chatviews.get(muc_jid); const view = _converse.api.chatviews.get(muc_jid);
await view.model.queueMessage(received_stanza); await view.model.handleMessageStanza(received_stanza);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1);
expect(view.model.messages.at(0).get('retracted')).toBeFalsy(); expect(view.model.messages.at(0).get('retracted')).toBeFalsy();
expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy(); expect(view.model.messages.at(0).get('is_ephemeral')).toBeFalsy();
...@@ -440,7 +440,7 @@ describe("Message Retractions", function () { ...@@ -440,7 +440,7 @@ describe("Message Retractions", function () {
<stanza-id xmlns='urn:xmpp:sid:0' id='stanza-id-1' by='${muc_jid}'/> <stanza-id xmlns='urn:xmpp:sid:0' id='stanza-id-1' by='${muc_jid}'/>
</message> </message>
`); `);
await view.model.queueMessage(received_stanza); await view.model.handleMessageStanza(received_stanza);
await u.waitUntil(() => view.model.messages.length === 1); await u.waitUntil(() => view.model.messages.length === 1);
expect(view.model.messages.at(0).get('retracted')).toBeFalsy(); expect(view.model.messages.at(0).get('retracted')).toBeFalsy();
...@@ -498,7 +498,7 @@ describe("Message Retractions", function () { ...@@ -498,7 +498,7 @@ describe("Message Retractions", function () {
</moderated> </moderated>
</apply-to> </apply-to>
</message>`); </message>`);
await view.model.queueMessage(retraction); await view.model.handleMessageStanza(retraction);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
expect(view.model.messages.at(0).get('moderated')).toBe('retracted'); expect(view.model.messages.at(0).get('moderated')).toBe('retracted');
expect(view.model.messages.at(0).get('moderation_reason')).toBe(reason); expect(view.model.messages.at(0).get('moderation_reason')).toBe(reason);
...@@ -524,7 +524,7 @@ describe("Message Retractions", function () { ...@@ -524,7 +524,7 @@ describe("Message Retractions", function () {
<stanza-id xmlns='urn:xmpp:sid:0' id='stanza-id-1' by='${muc_jid}'/> <stanza-id xmlns='urn:xmpp:sid:0' id='stanza-id-1' by='${muc_jid}'/>
</message> </message>
`); `);
await view.model.queueMessage(received_stanza); await view.model.handleMessageStanza(received_stanza);
await u.waitUntil(() => view.el.querySelector('.chat-msg__content')); await u.waitUntil(() => view.el.querySelector('.chat-msg__content'));
expect(view.el.querySelector('.chat-msg__content .chat-msg__action-retract')).toBe(null); expect(view.el.querySelector('.chat-msg__content .chat-msg__action-retract')).toBe(null);
const result = await view.model.canModerateMessages(); const result = await view.model.canModerateMessages();
...@@ -551,7 +551,7 @@ describe("Message Retractions", function () { ...@@ -551,7 +551,7 @@ describe("Message Retractions", function () {
<stanza-id xmlns='urn:xmpp:sid:0' id='stanza-id-1' by='${muc_jid}'/> <stanza-id xmlns='urn:xmpp:sid:0' id='stanza-id-1' by='${muc_jid}'/>
</message> </message>
`); `);
await view.model.queueMessage(received_stanza); await view.model.handleMessageStanza(received_stanza);
await u.waitUntil(() => view.model.messages.length === 1); await u.waitUntil(() => view.model.messages.length === 1);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
...@@ -579,7 +579,7 @@ describe("Message Retractions", function () { ...@@ -579,7 +579,7 @@ describe("Message Retractions", function () {
</moderated> </moderated>
</apply-to> </apply-to>
</message>`); </message>`);
await view.model.queueMessage(retraction); await view.model.handleMessageStanza(retraction);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 1);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
...@@ -778,7 +778,7 @@ describe("Message Retractions", function () { ...@@ -778,7 +778,7 @@ describe("Message Retractions", function () {
by="lounge@montague.lit"/> by="lounge@montague.lit"/>
<origin-id xmlns="urn:xmpp:sid:0" id="${msg_obj.get('origin_id')}"/> <origin-id xmlns="urn:xmpp:sid:0" id="${msg_obj.get('origin_id')}"/>
</message>`); </message>`);
await view.model.queueMessage(reflection_stanza); await view.model.handleMessageStanza(reflection_stanza);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length, 500); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length, 500);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
expect(view.model.messages.at(0).get('editable')).toBe(true); expect(view.model.messages.at(0).get('editable')).toBe(true);
...@@ -794,7 +794,7 @@ describe("Message Retractions", function () { ...@@ -794,7 +794,7 @@ describe("Message Retractions", function () {
</moderated> </moderated>
</apply-to> </apply-to>
</message>`); </message>`);
await view.model.queueMessage(retraction); await view.model.handleMessageStanza(retraction);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
expect(view.model.messages.at(0).get('moderated')).toBe('retracted'); expect(view.model.messages.at(0).get('moderated')).toBe('retracted');
expect(view.model.messages.at(0).get('moderation_reason')).toBe(reason); expect(view.model.messages.at(0).get('moderation_reason')).toBe(reason);
...@@ -830,7 +830,7 @@ describe("Message Retractions", function () { ...@@ -830,7 +830,7 @@ describe("Message Retractions", function () {
by="lounge@montague.lit"/> by="lounge@montague.lit"/>
<origin-id xmlns="urn:xmpp:sid:0" id="${msg_obj.get('origin_id')}"/> <origin-id xmlns="urn:xmpp:sid:0" id="${msg_obj.get('origin_id')}"/>
</message>`); </message>`);
await view.model.queueMessage(reflection_stanza); await view.model.handleMessageStanza(reflection_stanza);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length, 500); await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__body.chat-msg__body--received').length, 500);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
expect(view.model.messages.at(0).get('editable')).toBe(true); expect(view.model.messages.at(0).get('editable')).toBe(true);
...@@ -879,7 +879,7 @@ describe("Message Retractions", function () { ...@@ -879,7 +879,7 @@ describe("Message Retractions", function () {
</moderated> </moderated>
</apply-to> </apply-to>
</message>`); </message>`);
await view.model.queueMessage(retraction); await view.model.handleMessageStanza(retraction);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
expect(view.model.messages.at(0).get('moderated')).toBe('retracted'); expect(view.model.messages.at(0).get('moderated')).toBe('retracted');
expect(view.model.messages.at(0).get('moderation_reason')).toBe(undefined); expect(view.model.messages.at(0).get('moderation_reason')).toBe(undefined);
......
...@@ -289,7 +289,7 @@ describe("A groupchat shown in the groupchats list", function () { ...@@ -289,7 +289,7 @@ describe("A groupchat shown in the groupchats list", function () {
const view = _converse.chatboxviews.get(room_jid); const view = _converse.chatboxviews.get(room_jid);
view.model.set({'minimized': true}); view.model.set({'minimized': true});
const nick = mock.chatroom_names[0]; const nick = mock.chatroom_names[0];
await view.model.queueMessage( await view.model.handleMessageStanza(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: u.getUniqueId(), id: u.getUniqueId(),
...@@ -303,7 +303,7 @@ describe("A groupchat shown in the groupchats list", function () { ...@@ -303,7 +303,7 @@ describe("A groupchat shown in the groupchats list", function () {
expect(Array.from(room_el.classList).includes('unread-msgs')).toBeTruthy(); expect(Array.from(room_el.classList).includes('unread-msgs')).toBeTruthy();
// If the user is mentioned, the counter also gets updated // If the user is mentioned, the counter also gets updated
await view.model.queueMessage( await view.model.handleMessageStanza(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: u.getUniqueId(), id: u.getUniqueId(),
...@@ -316,7 +316,7 @@ describe("A groupchat shown in the groupchats list", function () { ...@@ -316,7 +316,7 @@ describe("A groupchat shown in the groupchats list", function () {
expect(indicator_el.textContent).toBe('1'); expect(indicator_el.textContent).toBe('1');
spyOn(view.model, 'incrementUnreadMsgCounter').and.callThrough(); spyOn(view.model, 'incrementUnreadMsgCounter').and.callThrough();
await view.model.queueMessage( await view.model.handleMessageStanza(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: u.getUniqueId(), id: u.getUniqueId(),
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
import { converse } from "@converse/headless/converse-core"; import { converse } from "@converse/headless/converse-core";
import log from "@converse/headless/log"; import log from "@converse/headless/log";
import st from "@converse/headless/utils/stanza";
const { Strophe, sizzle } = converse.env; const { Strophe, sizzle } = converse.env;
const u = converse.env.utils; const u = converse.env.utils;
...@@ -79,7 +80,7 @@ converse.plugins.add('converse-notification', { ...@@ -79,7 +80,7 @@ converse.plugins.add('converse-notification', {
return false; return false;
} else if (message.getAttribute('type') === 'groupchat') { } else if (message.getAttribute('type') === 'groupchat') {
return _converse.shouldNotifyOfGroupMessage(message); return _converse.shouldNotifyOfGroupMessage(message);
} else if (u.isHeadlineMessage(_converse, message)) { } else if (st.isHeadline(message)) {
// We want to show notifications for headline messages. // We want to show notifications for headline messages.
return _converse.isMessageToHiddenChat(message); return _converse.isMessageToHiddenChat(message);
} }
......
This diff is collapsed.
...@@ -7,8 +7,6 @@ import { isString } from "lodash"; ...@@ -7,8 +7,6 @@ import { isString } from "lodash";
import { converse } from "@converse/headless/converse-core"; import { converse } from "@converse/headless/converse-core";
import st from "./utils/stanza"; import st from "./utils/stanza";
const u = converse.env.utils;
converse.plugins.add('converse-headlines', { converse.plugins.add('converse-headlines', {
/* Plugin dependencies are other plugins which might be /* Plugin dependencies are other plugins which might be
...@@ -83,7 +81,7 @@ converse.plugins.add('converse-headlines', { ...@@ -83,7 +81,7 @@ converse.plugins.add('converse-headlines', {
async function onHeadlineMessage (stanza) { async function onHeadlineMessage (stanza) {
// Handler method for all incoming messages of type "headline". // Handler method for all incoming messages of type "headline".
if (u.isHeadlineMessage(_converse, stanza)) { if (st.isHeadline(stanza) || st.isServerMessage(stanza)) {
const from_jid = stanza.getAttribute('from'); const from_jid = stanza.getAttribute('from');
if (from_jid.includes('@') && if (from_jid.includes('@') &&
!_converse.roster.get(from_jid) && !_converse.roster.get(from_jid) &&
...@@ -100,7 +98,7 @@ converse.plugins.add('converse-headlines', { ...@@ -100,7 +98,7 @@ converse.plugins.add('converse-headlines', {
'type': _converse.HEADLINES_TYPE, 'type': _converse.HEADLINES_TYPE,
'from': from_jid 'from': from_jid
}); });
const attrs = await st.parseMessage(stanza, stanza, chatbox, _converse); const attrs = await st.parseMessage(stanza, _converse);
await chatbox.createMessage(attrs); await chatbox.createMessage(attrs);
api.trigger('message', {'chatbox': chatbox, 'stanza': stanza}); api.trigger('message', {'chatbox': chatbox, 'stanza': stanza});
} }
...@@ -109,10 +107,7 @@ converse.plugins.add('converse-headlines', { ...@@ -109,10 +107,7 @@ converse.plugins.add('converse-headlines', {
/************************ BEGIN Event Handlers ************************/ /************************ BEGIN Event Handlers ************************/
function registerHeadlineHandler () { function registerHeadlineHandler () {
_converse.connection.addHandler(message => { _converse.connection.addHandler(message => (onHeadlineMessage(message) || true), null, 'message');
onHeadlineMessage(message);
return true
}, null, 'message');
} }
api.listen.on('connected', registerHeadlineHandler); api.listen.on('connected', registerHeadlineHandler);
api.listen.on('reconnected', registerHeadlineHandler); api.listen.on('reconnected', registerHeadlineHandler);
......
This diff is collapsed.
...@@ -608,6 +608,26 @@ converse.plugins.add('converse-muc', { ...@@ -608,6 +608,26 @@ converse.plugins.add('converse-muc', {
} }
}, },
async handleErrormessageStanza (stanza) {
if (await this.shouldShowErrorMessage(stanza)) {
this.createMessage(await st.parseMUCMessage(stanza, this, _converse));
}
},
async handleMessageStanza (stanza) {
if (st.isArchived(stanza)) {
// MAM messages are handled in converse-mam.
// We shouldn't get MAM messages here because
// they shouldn't have a `type` attribute.
return log.warn(`Received a MAM message with type "groupchat"`);
}
api.trigger('message', {'stanza': stanza});
this.createInfoMessages(stanza);
this.fetchFeaturesIfConfigurationChanged(stanza);
const attrs = await st.parseMUCMessage(stanza, this, _converse);
return attrs && this.queueMessage(attrs);
},
registerHandlers () { registerHandlers () {
// Register presence and message handlers for this groupchat // Register presence and message handlers for this groupchat
const room_jid = this.get('jid'); const room_jid = this.get('jid');
...@@ -618,17 +638,9 @@ converse.plugins.add('converse-muc', { ...@@ -618,17 +638,9 @@ converse.plugins.add('converse-muc', {
{'ignoreNamespaceFragment': true, 'matchBareFromJid': true} {'ignoreNamespaceFragment': true, 'matchBareFromJid': true}
); );
this.message_handler = _converse.connection.addHandler(stanza => { this.message_handler = _converse.connection.addHandler(
if (sizzle(`message > result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop()) { stanza => (!!this.handleMessageStanza(stanza) || true),
// MAM messages are handled in converse-mam. null, 'message', 'groupchat', null, room_jid,
// We shouldn't get MAM messages here because
// they shouldn't have a `type` attribute.
log.warn(`received a mam message with type "chat".`);
return true;
}
this.queueMessage(stanza);
return true;
}, null, 'message', 'groupchat', null, room_jid,
{'matchBareFromJid': true} {'matchBareFromJid': true}
); );
...@@ -1650,7 +1662,7 @@ converse.plugins.add('converse-muc', { ...@@ -1650,7 +1662,7 @@ converse.plugins.add('converse-muc', {
* @private * @private
* @method _converse.ChatRoom#handleSubjectChange * @method _converse.ChatRoom#handleSubjectChange
* @param { object } attrs - Attributes representing a received * @param { object } attrs - Attributes representing a received
* message, as returned by {@link st.parseMessage} * message, as returned by {@link st.parseMUCMessage}
*/ */
async handleSubjectChange (attrs) { async handleSubjectChange (attrs) {
if (isString(attrs.subject) && !attrs.thread && !attrs.message) { if (isString(attrs.subject) && !attrs.thread && !attrs.message) {
...@@ -1703,8 +1715,7 @@ converse.plugins.add('converse-muc', { ...@@ -1703,8 +1715,7 @@ converse.plugins.add('converse-muc', {
* @param { Object } attrs - The message attributes * @param { Object } attrs - The message attributes
*/ */
ignorableCSN (attrs) { ignorableCSN (attrs) {
const is_csn = u.isOnlyChatStateNotification(attrs); return attrs.chat_state && !attrs.body && (attrs.is_delayed || this.isOwnMessage(attrs));
return is_csn && (attrs.is_delayed || this.isOwnMessage(attrs));
}, },
...@@ -1729,21 +1740,17 @@ converse.plugins.add('converse-muc', { ...@@ -1729,21 +1740,17 @@ converse.plugins.add('converse-muc', {
}, },
getUpdatedMessageAttributes (message, stanza) { getUpdatedMessageAttributes (message, attrs) {
// Overridden in converse-muc and converse-mam // Overridden in converse-muc and converse-mam
const attrs = _converse.ChatBox.prototype.getUpdatedMessageAttributes.call(this, message, stanza); const new_attrs = _converse.ChatBox.prototype.getUpdatedMessageAttributes.call(this, message, attrs);
if (this.isOwnMessage(message)) { if (this.isOwnMessage(attrs)) {
const stanza_id = sizzle(`stanza-id[xmlns="${Strophe.NS.SID}"]`, stanza).pop(); const stanza_id_keys = Object.keys(attrs).filter(k => k.startsWith('stanza_id'));
const by_jid = stanza_id ? stanza_id.getAttribute('by') : undefined; Object.assign(new_attrs, pick(attrs, stanza_id_keys));
if (by_jid) {
const key = `stanza_id ${by_jid}`;
attrs[key] = stanza_id.getAttribute('id');
}
if (!message.get('received')) { if (!message.get('received')) {
attrs.received = (new Date()).toISOString(); new_attrs.received = (new Date()).toISOString();
} }
} }
return attrs; return new_attrs;
}, },
/** /**
...@@ -1808,7 +1815,7 @@ converse.plugins.add('converse-muc', { ...@@ -1808,7 +1815,7 @@ converse.plugins.add('converse-muc', {
* @private * @private
* @method _converse.ChatRoom#findDanglingModeration * @method _converse.ChatRoom#findDanglingModeration
* @param { object } attrs - Attributes representing a received * @param { object } attrs - Attributes representing a received
* message, as returned by {@link st.parseMessage} * message, as returned by {@link st.parseMUCMessage}
* @returns { _converse.ChatRoomMessage } * @returns { _converse.ChatRoomMessage }
*/ */
findDanglingModeration (attrs) { findDanglingModeration (attrs) {
...@@ -1839,7 +1846,7 @@ converse.plugins.add('converse-muc', { ...@@ -1839,7 +1846,7 @@ converse.plugins.add('converse-muc', {
* @private * @private
* @method _converse.ChatRoom#handleModeration * @method _converse.ChatRoom#handleModeration
* @param { object } attrs - Attributes representing a received * @param { object } attrs - Attributes representing a received
* message, as returned by {@link st.parseMessage} * message, as returned by {@link st.parseMUCMessage}
* @returns { Boolean } Returns `true` or `false` depending on * @returns { Boolean } Returns `true` or `false` depending on
* whether a message was moderated or not. * whether a message was moderated or not.
*/ */
...@@ -1954,47 +1961,25 @@ converse.plugins.add('converse-muc', { ...@@ -1954,47 +1961,25 @@ converse.plugins.add('converse-muc', {
* should be called. * should be called.
* @private * @private
* @method _converse.ChatRoom#onMessage * @method _converse.ChatRoom#onMessage
* @param { XMLElement } stanza - The message stanza. * @param { MessageAttributes } attrs - The message attributes
*/ */
async onMessage (stanza) { async onMessage (attrs) {
if (sizzle(`message > forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).length) { if (u.isErrorObject(attrs)) {
return log.warn('onMessage: Ignoring unencapsulated forwarded groupchat message'); attrs.stanza && log.error(attrs.stanza);
} return log.error(attrs.message);
if (u.isCarbonMessage(stanza)) {
return log.warn(
'onMessage: Ignoring XEP-0280 "groupchat" message carbon, '+
'according to the XEP groupchat messages SHOULD NOT be carbon copied'
);
}
const original_stanza = stanza;
if (u.isMAMMessage(stanza)) {
if (original_stanza.getAttribute('from') === this.get('jid')) {
const selector = `[xmlns="${Strophe.NS.MAM}"] > forwarded[xmlns="${Strophe.NS.FORWARD}"] > message`;
stanza = sizzle(selector, stanza).pop();
} else {
return log.warn(`onMessage: Ignoring alleged MAM groupchat message from ${stanza.getAttribute('from')}`);
}
} }
await this.createInfoMessages(stanza); // TODO: move to OMEMO
this.fetchFeaturesIfConfigurationChanged(stanza); attrs = attrs.encrypted ? await this.decrypt(attrs) : attrs;
const attrs = await st.parseMessage(stanza, original_stanza, this, _converse);
const message = this.getDuplicateMessage(attrs); const message = this.getDuplicateMessage(attrs);
if (message) { if (message) {
this.updateMessage(message, original_stanza); return this.updateMessage(message, attrs);
} } else if (attrs.is_receipt_request || attrs.is_marker || this.ignorableCSN(attrs)) {
if (message || return;
st.isReceipt(stanza) ||
st.isChatMarker(stanza) ||
this.ignorableCSN(attrs)) {
return api.trigger('message', {'stanza': original_stanza});
} }
if (await this.handleRetraction(attrs) || if (await this.handleRetraction(attrs) ||
await this.handleModeration(attrs) || await this.handleModeration(attrs) ||
await this.handleSubjectChange(attrs)) { await this.handleSubjectChange(attrs)) {
this.removeNotification(attrs.nick, ['composing', 'paused']); return this.removeNotification(attrs.nick, ['composing', 'paused']);
return api.trigger('message', {'stanza': original_stanza});
} }
this.setEditable(attrs, attrs.time); this.setEditable(attrs, attrs.time);
...@@ -2006,7 +1991,6 @@ converse.plugins.add('converse-muc', { ...@@ -2006,7 +1991,6 @@ converse.plugins.add('converse-muc', {
this.removeNotification(attrs.nick, ['composing', 'paused']); this.removeNotification(attrs.nick, ['composing', 'paused']);
this.incrementUnreadMsgCounter(msg); this.incrementUnreadMsgCounter(msg);
} }
api.trigger('message', {'stanza': original_stanza, 'chatbox': this});
}, },
handleModifyError(pres) { handleModifyError(pres) {
......
...@@ -42,16 +42,6 @@ u.toStanza = function (string) { ...@@ -42,16 +42,6 @@ u.toStanza = function (string) {
return node.firstElementChild; return node.firstElementChild;
} }
u.isMAMMessage = function (stanza) {
return sizzle(`message > result[xmlns="${Strophe.NS.MAM}"]`, stanza).length > 0;
}
u.isCarbonMessage = function (stanza) {
const xmlns = Strophe.NS.CARBONS;
return sizzle(`message > received[xmlns="${xmlns}"]`, stanza).length > 0 ||
sizzle(`message > sent[xmlns="${xmlns}"]`, stanza).length > 0;
}
u.getLongestSubstring = function (string, candidates) { u.getLongestSubstring = function (string, candidates) {
function reducer (accumulator, current_value) { function reducer (accumulator, current_value) {
if (string.startsWith(current_value)) { if (string.startsWith(current_value)) {
...@@ -142,6 +132,7 @@ u.isEmptyMessage = function (attrs) { ...@@ -142,6 +132,7 @@ u.isEmptyMessage = function (attrs) {
!attrs['message']; !attrs['message'];
}; };
//TODO: Remove
u.isOnlyChatStateNotification = function (msg) { u.isOnlyChatStateNotification = function (msg) {
if (msg instanceof Element) { if (msg instanceof Element) {
// See XEP-0085 Chat State Notification // See XEP-0085 Chat State Notification
...@@ -174,25 +165,6 @@ u.isChatRoom = function (model) { ...@@ -174,25 +165,6 @@ u.isChatRoom = function (model) {
return model && (model.get('type') === 'chatroom'); return model && (model.get('type') === 'chatroom');
} }
u.isHeadlineMessage = function (_converse, message) {
const from_jid = message.getAttribute('from');
if (message.getAttribute('type') === 'headline') {
return true;
}
const chatbox = _converse.chatboxes.get(Strophe.getBareJidFromJid(from_jid));
if (u.isChatRoom(chatbox)) {
return false;
}
if (message.getAttribute('type') !== 'error' && from_jid && !from_jid.includes('@')) {
// Some servers (I'm looking at you Prosody) don't set the message
// type to "headline" when sending server messages. For now we
// check if an @ signal is included, and if not, we assume it's
// a headline message.
return true;
}
return false;
};
u.isErrorObject = function (o) { u.isErrorObject = function (o) {
return o instanceof Error; return o instanceof Error;
} }
......
This diff is collapsed.
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
websocket_url: 'ws://chat.example.org:5380/xmpp-websocket', websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
// bosh_service_url: 'http://chat.example.org:5280/http-bind', // bosh_service_url: 'http://chat.example.org:5280/http-bind',
muc_show_logs_before_join: true, muc_show_logs_before_join: true,
whitelisted_plugins: ['converse-debug'], whitelisted_plugins: ['converse-debug', 'converse-batched-probe'],
}); });
</script> </script>
</html> </html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment