Commit 3c0e3d3f authored by JC Brand's avatar JC Brand

Refactor out `createMessage`.

Changes:

* Avoids leaky abstraction of MUC code into converse-chatboxes
* Avoid creating unnecessary message objects (e.g. without <body)
* Add fix for #1369.
* Rename spec/chatroom.js to spec/muc.js
parent 3aaff4e9
...@@ -51920,11 +51920,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins ...@@ -51920,11 +51920,11 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins
'afterShown': _.noop 'afterShown': _.noop
}); });
function onHeadlineMessage(message) { async function onHeadlineMessage(message) {
/* Handler method for all incoming messages of type "headline". */ /* Handler method for all incoming messages of type "headline". */
const from_jid = message.getAttribute('from');
if (utils.isHeadlineMessage(_converse, message)) { 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.api.contacts.get(from_jid) && !_converse.allow_non_roster_messaging) {
return; return;
} }
...@@ -51942,19 +51942,21 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins ...@@ -51942,19 +51942,21 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins
'from': from_jid 'from': from_jid
}); });
chatbox.createMessage(message, message); const attrs = await chatbox.getMessageAttributesFromStanza(message, message);
await chatbox.messages.create(attrs);
_converse.emit('message', { _converse.emit('message', {
'chatbox': chatbox, 'chatbox': chatbox,
'stanza': message 'stanza': message
}); });
} }
return true;
} }
function registerHeadlineHandler() { function registerHeadlineHandler() {
_converse.connection.addHandler(onHeadlineMessage, null, 'message'); _converse.connection.addHandler(message => {
onHeadlineMessage(message);
return true;
}, null, 'message');
} }
_converse.on('connected', registerHeadlineHandler); _converse.on('connected', registerHeadlineHandler);
...@@ -61803,8 +61805,10 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha ...@@ -61803,8 +61805,10 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
'is_delayed': !_.isNil(delay), 'is_delayed': !_.isNil(delay),
'is_spoiler': !_.isNil(spoiler), 'is_spoiler': !_.isNil(spoiler),
'message': _converse.chatboxes.getMessageBody(stanza) || undefined, 'message': _converse.chatboxes.getMessageBody(stanza) || undefined,
'references': this.getReferencesFromStanza(stanza),
'msgid': stanza.getAttribute('id'), 'msgid': stanza.getAttribute('id'),
'references': this.getReferencesFromStanza(stanza),
'subject': _.propertyOf(stanza.querySelector('subject'))('textContent'),
'thread': _.propertyOf(stanza.querySelector('thread'))('textContent'),
'time': delay ? delay.getAttribute('stamp') : moment().format(), 'time': delay ? delay.getAttribute('stamp') : moment().format(),
'type': stanza.getAttribute('type') 'type': stanza.getAttribute('type')
}; };
...@@ -61837,25 +61841,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha ...@@ -61837,25 +61841,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
return attrs; return attrs;
}, },
async createMessage(message, original_stanza) {
/* Create a Backbone.Message object inside this chat box
* based on the identified message stanza.
*/
const attrs = await this.getMessageAttributesFromStanza(message, original_stanza),
is_csn = u.isOnlyChatStateNotification(attrs);
if (is_csn && (attrs.is_delayed || attrs.type === 'groupchat' && Strophe.getResourceFromJid(attrs.from) == this.get('nick'))) {
// XXX: MUC leakage
// No need showing delayed or our own CSN messages
return;
} else if (!is_csn && !attrs.file && !attrs.plaintext && !attrs.message && !attrs.oob_url && attrs.type !== 'error') {
// TODO: handle <subject> messages (currently being done by ChatRoom)
return;
} else {
return this.messages.create(attrs);
}
},
isHidden() { isHidden() {
/* Returns a boolean to indicate whether a newly received /* Returns a boolean to indicate whether a newly received
* message will be visible to the user or not. * message will be visible to the user or not.
...@@ -61952,7 +61937,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha ...@@ -61952,7 +61937,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
}); });
}, },
onErrorMessage(message) { async onErrorMessage(message) {
/* Handler method for all incoming error message stanzas /* Handler method for all incoming error message stanzas
*/ */
const from_jid = Strophe.getBareJidFromJid(message.getAttribute('from')); const from_jid = Strophe.getBareJidFromJid(message.getAttribute('from'));
...@@ -61990,8 +61975,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha ...@@ -61990,8 +61975,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
_converse.log(message, Strophe.LogLevel.ERROR); _converse.log(message, Strophe.LogLevel.ERROR);
} }
chatbox.createMessage(message, message); const attrs = await chatbox.getMessageAttributesFromStanza(message, message);
return true; chatbox.messages.create(attrs);
}, },
getMessageBody(stanza) { getMessageBody(stanza) {
...@@ -62023,7 +62008,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha ...@@ -62023,7 +62008,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
_converse.api.send(receipt_stanza); _converse.api.send(receipt_stanza);
}, },
onMessage(stanza) { async onMessage(stanza) {
/* Handler method for all incoming single-user chat "message" /* Handler method for all incoming single-user chat "message"
* stanzas. * stanzas.
* *
...@@ -62104,7 +62089,9 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha ...@@ -62104,7 +62089,9 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
if (!message) { if (!message) {
// Only create the message when we're sure it's not a duplicate // Only create the message when we're sure it's not a duplicate
chatbox.createMessage(stanza, original_stanza).then(msg => chatbox.incrementUnreadMsgCounter(msg)).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); const attrs = await chatbox.getMessageAttributesFromStanza(stanza, original_stanza);
const msg = chatbox.messages.create(attrs);
chatbox.incrementUnreadMsgCounter(msg);
} }
} }
...@@ -62112,8 +62099,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha ...@@ -62112,8 +62099,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
'stanza': original_stanza, 'stanza': original_stanza,
'chatbox': chatbox 'chatbox': chatbox
}); });
return true;
}, },
getChatBox(jid, attrs = {}, create) { getChatBox(jid, attrs = {}, create) {
...@@ -65298,7 +65283,9 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam ...@@ -65298,7 +65283,9 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
this.addSpinner(); this.addSpinner();
_converse.api.archive.query(_.extend({ _converse.api.archive.query( // TODO: only query from the last message we have
// in our history
_.extend({
'groupchat': is_groupchat, 'groupchat': is_groupchat,
'before': '', 'before': '',
// Page backwards from the most recent message // Page backwards from the most recent message
...@@ -66839,28 +66826,37 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc ...@@ -66839,28 +66826,37 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
return; return;
} }
const jid = stanza.getAttribute('from'), const attrs = await this.getMessageAttributesFromStanza(stanza, original_stanza);
resource = Strophe.getResourceFromJid(jid),
sender = resource && Strophe.unescapeNode(resource) || '';
if (!this.handleMessageCorrection(stanza)) {
if (sender === '') {
return;
}
const subject_el = stanza.querySelector('subject'); if (!attrs.nick) {
return;
}
if (subject_el) { if (!this.handleMessageCorrection(stanza)) {
const subject = _.propertyOf(subject_el)('textContent') || ''; if (attrs.subject && !attrs.thread && !attrs.message) {
// https://xmpp.org/extensions/xep-0045.html#subject-mod
// -----------------------------------------------------
// The subject is changed by sending a message of type "groupchat" to the <room@service>,
// where the <message/> MUST contain a <subject/> element that specifies the new subject but
// MUST NOT contain a <body/> element (or a <thread/> element).
_utils_form__WEBPACK_IMPORTED_MODULE_7__["default"].safeSave(this, { _utils_form__WEBPACK_IMPORTED_MODULE_7__["default"].safeSave(this, {
'subject': { 'subject': {
'author': sender, 'author': attrs.nick,
'text': subject 'text': attrs.subject || ''
} }
}); });
return;
}
const is_csn = _utils_form__WEBPACK_IMPORTED_MODULE_7__["default"].isOnlyChatStateNotification(attrs),
own_message = Strophe.getResourceFromJid(attrs.from) == this.get('nick');
if (is_csn && (attrs.is_delayed || own_message)) {
// No need showing delayed or our own CSN messages
return;
} }
const msg = await this.createMessage(stanza, original_stanza); const msg = await this.messages.create(attrs);
if (forwarded && msg && msg.get('sender') === 'me') { if (forwarded && msg && msg.get('sender') === 'me') {
msg.save({ msg.save({
...@@ -66871,7 +66867,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc ...@@ -66871,7 +66867,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
this.incrementUnreadMsgCounter(msg); this.incrementUnreadMsgCounter(msg);
} }
if (sender !== this.get('nick')) { if (attrs.nick !== this.get('nick')) {
// We only emit an event if it's not our own message // We only emit an event if it's not our own message
_converse.emit('message', { _converse.emit('message', {
'stanza': original_stanza, 'stanza': original_stanza,
...@@ -62,114 +62,111 @@ ...@@ -62,114 +62,111 @@
it("autocompletes when the user presses tab", it("autocompletes when the user presses tab",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy') await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
.then(() => { const view = _converse.chatboxviews.get('lounge@localhost');
const view = _converse.chatboxviews.get('lounge@localhost'); expect(view.model.occupants.length).toBe(1);
expect(view.model.occupants.length).toBe(1); let presence = $pres({
let presence = $pres({ 'to': 'dummy@localhost/resource',
'to': 'dummy@localhost/resource', 'from': 'lounge@localhost/some1'
'from': 'lounge@localhost/some1' })
}) .c('x', {xmlns: Strophe.NS.MUC_USER})
.c('x', {xmlns: Strophe.NS.MUC_USER}) .c('item', {
.c('item', { 'affiliation': 'none',
'affiliation': 'none', 'jid': 'some1@localhost/resource',
'jid': 'some1@localhost/resource', 'role': 'participant'
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.model.occupants.length).toBe(2);
const textarea = view.el.querySelector('textarea.chat-textarea');
textarea.value = "hello som";
// Press tab
const tab_event = {
'target': textarea,
'preventDefault': _.noop,
'stopPropagation': _.noop,
'keyCode': 9
}
view.keyPressed(tab_event);
view.keyUp(tab_event);
expect(view.el.querySelector('.suggestion-box__results').hidden).toBeFalsy();
expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(1);
expect(view.el.querySelector('.suggestion-box__results li').textContent).toBe('some1');
const backspace_event = {
'target': textarea,
'preventDefault': _.noop,
'keyCode': 8
}
for (var i=0; i<3; i++) {
// Press backspace 3 times to remove "som"
view.keyPressed(backspace_event);
textarea.value = textarea.value.slice(0, textarea.value.length-1)
view.keyUp(backspace_event);
}
expect(view.el.querySelector('.suggestion-box__results').hidden).toBeTruthy();
presence = $pres({
'to': 'dummy@localhost/resource',
'from': 'lounge@localhost/some2'
})
.c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'some2@localhost/resource',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
textarea.value = "hello s s";
view.keyPressed(tab_event);
view.keyUp(tab_event);
expect(view.el.querySelector('.suggestion-box__results').hidden).toBeFalsy();
expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(2);
const up_arrow_event = {
'target': textarea,
'preventDefault': () => (up_arrow_event.defaultPrevented = true),
'stopPropagation': _.noop,
'keyCode': 38
}
view.keyPressed(up_arrow_event);
view.keyUp(up_arrow_event);
expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(2);
expect(view.el.querySelector('.suggestion-box__results li[aria-selected="false"]').textContent).toBe('some1');
expect(view.el.querySelector('.suggestion-box__results li[aria-selected="true"]').textContent).toBe('some2');
view.keyPressed({
'target': textarea,
'preventDefault': _.noop,
'stopPropagation': _.noop,
'keyCode': 13 // Enter
}); });
expect(textarea.value).toBe('hello s @some2 '); _converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.model.occupants.length).toBe(2);
// Test that pressing tab twice selects
presence = $pres({ const textarea = view.el.querySelector('textarea.chat-textarea');
'to': 'dummy@localhost/resource', textarea.value = "hello som";
'from': 'lounge@localhost/z3r0'
}) // Press tab
.c('x', {xmlns: Strophe.NS.MUC_USER}) const tab_event = {
.c('item', { 'target': textarea,
'affiliation': 'none', 'preventDefault': _.noop,
'jid': 'z3r0@localhost/resource', 'stopPropagation': _.noop,
'role': 'participant' 'keyCode': 9
}); }
_converse.connection._dataRecv(test_utils.createRequest(presence)); view.keyPressed(tab_event);
textarea.value = "hello z"; view.keyUp(tab_event);
view.keyPressed(tab_event); expect(view.el.querySelector('.suggestion-box__results').hidden).toBeFalsy();
view.keyUp(tab_event); expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(1);
expect(view.el.querySelector('.suggestion-box__results li').textContent).toBe('some1');
view.keyPressed(tab_event);
view.keyUp(tab_event); const backspace_event = {
expect(textarea.value).toBe('hello @z3r0 '); 'target': textarea,
'preventDefault': _.noop,
done(); 'keyCode': 8
}).catch(_.partial(console.error, _)); }
for (var i=0; i<3; i++) {
// Press backspace 3 times to remove "som"
view.keyPressed(backspace_event);
textarea.value = textarea.value.slice(0, textarea.value.length-1)
view.keyUp(backspace_event);
}
expect(view.el.querySelector('.suggestion-box__results').hidden).toBeTruthy();
presence = $pres({
'to': 'dummy@localhost/resource',
'from': 'lounge@localhost/some2'
})
.c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'some2@localhost/resource',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
textarea.value = "hello s s";
view.keyPressed(tab_event);
view.keyUp(tab_event);
expect(view.el.querySelector('.suggestion-box__results').hidden).toBeFalsy();
expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(2);
const up_arrow_event = {
'target': textarea,
'preventDefault': () => (up_arrow_event.defaultPrevented = true),
'stopPropagation': _.noop,
'keyCode': 38
}
view.keyPressed(up_arrow_event);
view.keyUp(up_arrow_event);
expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(2);
expect(view.el.querySelector('.suggestion-box__results li[aria-selected="false"]').textContent).toBe('some1');
expect(view.el.querySelector('.suggestion-box__results li[aria-selected="true"]').textContent).toBe('some2');
view.keyPressed({
'target': textarea,
'preventDefault': _.noop,
'stopPropagation': _.noop,
'keyCode': 13 // Enter
});
expect(textarea.value).toBe('hello s @some2 ');
// Test that pressing tab twice selects
presence = $pres({
'to': 'dummy@localhost/resource',
'from': 'lounge@localhost/z3r0'
})
.c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'z3r0@localhost/resource',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
textarea.value = "hello z";
view.keyPressed(tab_event);
view.keyUp(tab_event);
view.keyPressed(tab_event);
view.keyUp(tab_event);
expect(textarea.value).toBe('hello @z3r0 ');
done();
})); }));
}); });
})); }));
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
type: 'chat', type: 'chat',
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').t('hello world').tree(); }).c('body').t('hello world').tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => view.content.querySelectorAll('.chat-msg').length); await test_utils.waitUntil(() => view.content.querySelectorAll('.chat-msg').length);
expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1); expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
done(); done();
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
}).c('body').t(message).up() }).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
const view = _converse.chatboxviews.get(sender_jid); const view = _converse.chatboxviews.get(sender_jid);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(1); expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(1);
...@@ -322,7 +322,7 @@ ...@@ -322,7 +322,7 @@
done(); done();
})); }));
it("can be closed by clicking a DOM element with class 'close-chatbox-button'", it("can be closed by clicking a DOM element with class 'close-chatbox-button'",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {}, null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) { async function (done, _converse) {
...@@ -533,7 +533,7 @@ ...@@ -533,7 +533,7 @@
it("does not open a new chatbox", it("does not open a new chatbox",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');
test_utils.openControlBox(); test_utils.openControlBox();
...@@ -547,7 +547,7 @@ ...@@ -547,7 +547,7 @@
'type': 'chat', 'type': 'chat',
'id': (new Date()).getTime() 'id': (new Date()).getTime()
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
done(); done();
})); }));
...@@ -660,21 +660,20 @@ ...@@ -660,21 +660,20 @@
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
spyOn(_converse, 'emit'); spyOn(_converse, 'emit');
var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost'; const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, sender_jid); test_utils.openChatBoxFor(_converse, sender_jid);
// <composing> state // <composing> state
var msg = $msg({ let msg = $msg({
from: sender_jid, from: sender_jid,
to: _converse.connection.jid, to: _converse.connection.jid,
type: 'chat', type: 'chat',
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
var view = _converse.chatboxviews.get(sender_jid); var view = _converse.chatboxviews.get(sender_jid);
expect(view).toBeDefined(); expect(view).toBeDefined();
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
await test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1]) await test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1])
// Check that the notification appears inside the chatbox in the DOM // Check that the notification appears inside the chatbox in the DOM
...@@ -689,8 +688,7 @@ ...@@ -689,8 +688,7 @@
type: 'chat', type: 'chat',
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
events = view.el.querySelectorAll('.chat-state-notification'); events = view.el.querySelectorAll('.chat-state-notification');
expect(events.length).toBe(1); expect(events.length).toBe(1);
expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing'); expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing');
...@@ -725,7 +723,7 @@ ...@@ -725,7 +723,7 @@
'to': recipient_jid, 'to': recipient_jid,
'type': 'chat' 'type': 'chat'
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => view.model.messages.length); await test_utils.waitUntil(() => view.model.messages.length);
// Check that the chatbox and its view now exist // Check that the chatbox and its view now exist
var chatbox = _converse.chatboxes.get(recipient_jid); var chatbox = _converse.chatboxes.get(recipient_jid);
...@@ -818,7 +816,7 @@ ...@@ -818,7 +816,7 @@
type: 'chat', type: 'chat',
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
const view = _converse.chatboxviews.get(sender_jid); const view = _converse.chatboxviews.get(sender_jid);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
...@@ -856,7 +854,7 @@ ...@@ -856,7 +854,7 @@
'to': recipient_jid, 'to': recipient_jid,
'type': 'chat' 'type': 'chat'
}).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => view.model.messages.length); await test_utils.waitUntil(() => view.model.messages.length);
// Check that the chatbox and its view now exist // Check that the chatbox and its view now exist
var chatbox = _converse.chatboxes.get(recipient_jid); var chatbox = _converse.chatboxes.get(recipient_jid);
...@@ -998,7 +996,7 @@ ...@@ -998,7 +996,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up() .c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => view.model.messages.length); await test_utils.waitUntil(() => view.model.messages.length);
expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1); expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1);
msg = $msg({ msg = $msg({
...@@ -1007,7 +1005,7 @@ ...@@ -1007,7 +1005,7 @@
type: 'chat', type: 'chat',
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => (view.model.messages.length > 1)); await test_utils.waitUntil(() => (view.model.messages.length > 1));
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(0); expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(0);
...@@ -1034,7 +1032,7 @@ ...@@ -1034,7 +1032,7 @@
type: 'chat', type: 'chat',
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
const view = _converse.chatboxviews.get(sender_jid); const view = _converse.chatboxviews.get(sender_jid);
await test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1]); await test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1]);
...@@ -1121,7 +1119,7 @@ ...@@ -1121,7 +1119,7 @@
spyOn(_converse, 'incrementMsgCounter').and.callThrough(); spyOn(_converse, 'incrementMsgCounter').and.callThrough();
spyOn(_converse, 'clearMsgCounter').and.callThrough(); spyOn(_converse, 'clearMsgCounter').and.callThrough();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(_converse.incrementMsgCounter).toHaveBeenCalled(); expect(_converse.incrementMsgCounter).toHaveBeenCalled();
expect(_converse.clearMsgCounter).not.toHaveBeenCalled(); expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
...@@ -1149,7 +1147,7 @@ ...@@ -1149,7 +1147,7 @@
it("is not incremented when the message is received and the window is focused", it("is not incremented when the message is received and the window is focused",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');
test_utils.openControlBox(); test_utils.openControlBox();
...@@ -1157,8 +1155,8 @@ ...@@ -1157,8 +1155,8 @@
expect(_converse.msg_counter).toBe(0); expect(_converse.msg_counter).toBe(0);
spyOn(_converse, 'incrementMsgCounter').and.callThrough(); spyOn(_converse, 'incrementMsgCounter').and.callThrough();
_converse.saveWindowState(null, 'focus'); _converse.saveWindowState(null, 'focus');
var message = 'This message will not increment the message counter'; const message = 'This message will not increment the message counter';
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost', const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
msg = $msg({ msg = $msg({
from: sender_jid, from: sender_jid,
to: _converse.connection.jid, to: _converse.connection.jid,
...@@ -1166,7 +1164,7 @@ ...@@ -1166,7 +1164,7 @@
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').t(message).up() }).c('body').t(message).up()
.c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree(); .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
expect(_converse.incrementMsgCounter).not.toHaveBeenCalled(); expect(_converse.incrementMsgCounter).not.toHaveBeenCalled();
expect(_converse.msg_counter).toBe(0); expect(_converse.msg_counter).toBe(0);
done(); done();
...@@ -1237,7 +1235,7 @@ ...@@ -1237,7 +1235,7 @@
const view = await test_utils.openChatBoxFor(_converse, sender_jid) const view = await test_utils.openChatBoxFor(_converse, sender_jid)
view.model.save('scrolled', true); view.model.save('scrolled', true);
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => view.model.messages.length); await test_utils.waitUntil(() => view.model.messages.length);
expect(view.model.get('num_unread')).toBe(1); expect(view.model.get('num_unread')).toBe(1);
done(); done();
...@@ -1256,7 +1254,7 @@ ...@@ -1256,7 +1254,7 @@
await test_utils.openChatBoxFor(_converse, sender_jid); await test_utils.openChatBoxFor(_converse, sender_jid);
const chatbox = _converse.chatboxes.get(sender_jid); const chatbox = _converse.chatboxes.get(sender_jid);
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
expect(chatbox.get('num_unread')).toBe(0); expect(chatbox.get('num_unread')).toBe(0);
done(); done();
})); }));
...@@ -1359,14 +1357,14 @@ ...@@ -1359,14 +1357,14 @@
const chatbox = _converse.chatboxes.get(sender_jid); const chatbox = _converse.chatboxes.get(sender_jid);
chatbox.save('scrolled', true); chatbox.save('scrolled', true);
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread'); msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => chatbox.messages.length); await test_utils.waitUntil(() => chatbox.messages.length);
const selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator'; const selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
indicator_el = sizzle(selector, _converse.rosterview.el).pop(); indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('1'); expect(indicator_el.textContent).toBe('1');
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too'); msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => chatbox.messages.length > 1); await test_utils.waitUntil(() => chatbox.messages.length > 1);
indicator_el = sizzle(selector, _converse.rosterview.el).pop(); indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('2'); expect(indicator_el.textContent).toBe('2');
...@@ -1390,14 +1388,14 @@ ...@@ -1390,14 +1388,14 @@
chatboxview.minimize(); chatboxview.minimize();
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread'); msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => chatbox.messages.length); await test_utils.waitUntil(() => chatbox.messages.length);
const selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator'; const selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
indicator_el = sizzle(selector, _converse.rosterview.el).pop(); indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('1'); expect(indicator_el.textContent).toBe('1');
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too'); msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => chatbox.messages.length > 1); await test_utils.waitUntil(() => chatbox.messages.length > 1);
indicator_el = sizzle(selector, _converse.rosterview.el).pop(); indicator_el = sizzle(selector, _converse.rosterview.el).pop();
expect(indicator_el.textContent).toBe('2'); expect(indicator_el.textContent).toBe('2');
......
...@@ -310,7 +310,7 @@ ...@@ -310,7 +310,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('body').t("message") .c('body').t("message")
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length); await test_utils.waitUntil(() => _converse.api.chats.get().length);
const view = _converse.chatboxviews.get(sender_jid); const view = _converse.chatboxviews.get(sender_jid);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
...@@ -325,7 +325,7 @@ ...@@ -325,7 +325,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('body').t("Older message") .c('body').t("Older message")
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg = $msg({'id': 'aeb215', 'to': _converse.bare_jid}) msg = $msg({'id': 'aeb215', 'to': _converse.bare_jid})
...@@ -338,7 +338,7 @@ ...@@ -338,7 +338,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('body').t("Inbetween message").up() .c('body').t("Inbetween message").up()
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg = $msg({'id': 'aeb216', 'to': _converse.bare_jid}) msg = $msg({'id': 'aeb216', 'to': _converse.bare_jid})
...@@ -351,7 +351,7 @@ ...@@ -351,7 +351,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('body').t("another inbetween message") .c('body').t("another inbetween message")
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg = $msg({'id': 'aeb217', 'to': _converse.bare_jid}) msg = $msg({'id': 'aeb217', 'to': _converse.bare_jid})
...@@ -364,7 +364,7 @@ ...@@ -364,7 +364,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('body').t("An earlier message on the next day") .c('body').t("An earlier message on the next day")
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg = $msg({'id': 'aeb218', 'to': _converse.bare_jid}) msg = $msg({'id': 'aeb218', 'to': _converse.bare_jid})
...@@ -377,7 +377,7 @@ ...@@ -377,7 +377,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('body').t("newer message from the next day") .c('body').t("newer message from the next day")
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
// Insert <composing> message, to also check that // Insert <composing> message, to also check that
...@@ -391,7 +391,7 @@ ...@@ -391,7 +391,7 @@
'type': 'chat'}) 'type': 'chat'})
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up() .c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
msg = $msg({ msg = $msg({
...@@ -403,7 +403,7 @@ ...@@ -403,7 +403,7 @@
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up() .c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
.c('body').t("latest message") .c('body').t("latest message")
.tree(); .tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
const chat_content = view.el.querySelector('.chat-content'); const chat_content = view.el.querySelector('.chat-content');
...@@ -464,7 +464,7 @@ ...@@ -464,7 +464,7 @@
it("is ignored if it's a malformed headline message", it("is ignored if it's a malformed headline message",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');
test_utils.openControlBox(); test_utils.openControlBox();
...@@ -481,7 +481,7 @@ ...@@ -481,7 +481,7 @@
type: 'chat', type: 'chat',
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').t("This headline message will not be shown").tree(); }).c('body').t("This headline message will not be shown").tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
expect(_converse.log.calledWith( expect(_converse.log.calledWith(
"onMessage: Ignoring incoming headline message from JID: localhost", "onMessage: Ignoring incoming headline message from JID: localhost",
Strophe.LogLevel.INFO Strophe.LogLevel.INFO
...@@ -523,7 +523,7 @@ ...@@ -523,7 +523,7 @@
'to': _converse.bare_jid+'/another-resource', 'to': _converse.bare_jid+'/another-resource',
'type': 'chat' 'type': 'chat'
}).c('body').t(msgtext).tree(); }).c('body').t(msgtext).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length) await test_utils.waitUntil(() => _converse.api.chats.get().length)
const chatbox = _converse.chatboxes.get(sender_jid); const chatbox = _converse.chatboxes.get(sender_jid);
const view = _converse.chatboxviews.get(sender_jid); const view = _converse.chatboxviews.get(sender_jid);
...@@ -574,7 +574,7 @@ ...@@ -574,7 +574,7 @@
'to': recipient_jid, 'to': recipient_jid,
'type': 'chat' 'type': 'chat'
}).c('body').t(msgtext).tree(); }).c('body').t(msgtext).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length); await test_utils.waitUntil(() => _converse.api.chats.get().length);
// Check that the chatbox and its view now exist // Check that the chatbox and its view now exist
const chatbox = _converse.chatboxes.get(recipient_jid); const chatbox = _converse.chatboxes.get(recipient_jid);
...@@ -598,25 +598,25 @@ ...@@ -598,25 +598,25 @@
it("will be discarded if it's a malicious message meant to look like a carbon copy", it("will be discarded if it's a malicious message meant to look like a carbon copy",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');
test_utils.openControlBox(); test_utils.openControlBox();
/* <message from="mallory@evil.example" to="b@xmpp.example"> /* <message from="mallory@evil.example" to="b@xmpp.example">
* <received xmlns='urn:xmpp:carbons:2'> * <received xmlns='urn:xmpp:carbons:2'>
* <forwarded xmlns='urn:xmpp:forward:0'> * <forwarded xmlns='urn:xmpp:forward:0'>
* <message from="alice@xmpp.example" to="bob@xmpp.example/client1"> * <message from="alice@xmpp.example" to="bob@xmpp.example/client1">
* <body>Please come to Creepy Valley tonight, alone!</body> * <body>Please come to Creepy Valley tonight, alone!</body>
* </message> * </message>
* </forwarded> * </forwarded>
* </received> * </received>
* </message> * </message>
*/ */
spyOn(_converse, 'log'); spyOn(_converse, 'log');
var msgtext = 'Please come to Creepy Valley tonight, alone!'; const msgtext = 'Please come to Creepy Valley tonight, alone!';
var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost'; const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
var impersonated_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; const impersonated_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
var msg = $msg({ const msg = $msg({
'from': sender_jid, 'from': sender_jid,
'id': (new Date()).getTime(), 'id': (new Date()).getTime(),
'to': _converse.connection.jid, 'to': _converse.connection.jid,
...@@ -630,10 +630,10 @@ ...@@ -630,10 +630,10 @@
'to': _converse.connection.jid, 'to': _converse.connection.jid,
'type': 'chat' 'type': 'chat'
}).c('body').t(msgtext).tree(); }).c('body').t(msgtext).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
// Check that chatbox for impersonated user is not created. // Check that chatbox for impersonated user is not created.
var chatbox = _converse.chatboxes.get(impersonated_jid); let chatbox = _converse.chatboxes.get(impersonated_jid);
expect(chatbox).not.toBeDefined(); expect(chatbox).not.toBeDefined();
// Check that the chatbox for the malicous user is not created // Check that the chatbox for the malicous user is not created
...@@ -673,7 +673,7 @@ ...@@ -673,7 +673,7 @@
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').t(message).up() }).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => chatview.model.messages.length); await test_utils.waitUntil(() => chatview.model.messages.length);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
...@@ -734,7 +734,7 @@ ...@@ -734,7 +734,7 @@
}).c('body').t(message).up() }).c('body').t(message).up()
.c('delay', { xmlns:'urn:xmpp:delay', from: 'localhost', stamp: one_day_ago.format() }) .c('delay', { xmlns:'urn:xmpp:delay', from: 'localhost', stamp: one_day_ago.format() })
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
...@@ -766,7 +766,7 @@ ...@@ -766,7 +766,7 @@
id: new Date().getTime() id: new Date().getTime()
}).c('body').t(message).up() }).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
...@@ -1196,7 +1196,7 @@ ...@@ -1196,7 +1196,7 @@
it("received may emit a message delivery receipt", it("received may emit a message delivery receipt",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {}, null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.createContacts(_converse, 'current', 1); test_utils.createContacts(_converse, 'current', 1);
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const msg_id = u.getUniqueId(); const msg_id = u.getUniqueId();
...@@ -1211,7 +1211,7 @@ ...@@ -1211,7 +1211,7 @@
'id': msg_id, 'id': msg_id,
}).c('body').t('Message!').up() }).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree(); .c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
const receipt = sizzle(`received[xmlns="${Strophe.NS.RECEIPTS}"]`, sent_stanzas[0].tree()).pop(); const receipt = sizzle(`received[xmlns="${Strophe.NS.RECEIPTS}"]`, sent_stanzas[0].tree()).pop();
expect(Strophe.serialize(receipt)).toBe(`<received id="${msg_id}" xmlns="${Strophe.NS.RECEIPTS}"/>`); expect(Strophe.serialize(receipt)).toBe(`<received id="${msg_id}" xmlns="${Strophe.NS.RECEIPTS}"/>`);
done(); done();
...@@ -1241,7 +1241,7 @@ ...@@ -1241,7 +1241,7 @@
'id': msg_id 'id': msg_id
}).c('body').t('Message!').up() }).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree(); .c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length); await test_utils.waitUntil(() => _converse.api.chats.get().length);
expect(_converse.chatboxes.sendReceiptStanza).not.toHaveBeenCalled(); expect(_converse.chatboxes.sendReceiptStanza).not.toHaveBeenCalled();
done(); done();
...@@ -1270,7 +1270,7 @@ ...@@ -1270,7 +1270,7 @@
'id': msg_id 'id': msg_id
}).c('body').t('Message!').up() }).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree(); .c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length); await test_utils.waitUntil(() => _converse.api.chats.get().length);
expect(_converse.chatboxes.sendReceiptStanza).not.toHaveBeenCalled(); expect(_converse.chatboxes.sendReceiptStanza).not.toHaveBeenCalled();
done(); done();
...@@ -1475,7 +1475,7 @@ ...@@ -1475,7 +1475,7 @@
// We don't already have an open chatbox for this user // We don't already have an open chatbox for this user
expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined(); expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length); await test_utils.waitUntil(() => _converse.api.chats.get().length);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
...@@ -1527,7 +1527,7 @@ ...@@ -1527,7 +1527,7 @@
let chatbox = _converse.chatboxes.get(sender_jid); let chatbox = _converse.chatboxes.get(sender_jid);
expect(chatbox).not.toBeDefined(); expect(chatbox).not.toBeDefined();
// onMessage is a handler for received XMPP messages // onMessage is a handler for received XMPP messages
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length) await test_utils.waitUntil(() => _converse.api.chats.get().length)
const view = _converse.chatboxviews.get(sender_jid); const view = _converse.chatboxviews.get(sender_jid);
...@@ -1536,7 +1536,7 @@ ...@@ -1536,7 +1536,7 @@
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
// onMessage is a handler for received XMPP messages // onMessage is a handler for received XMPP messages
_converse.allow_non_roster_messaging =true; _converse.allow_non_roster_messaging =true;
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => view.model.messages.length); await test_utils.waitUntil(() => view.model.messages.length);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
// Check that the chatbox and its view now exist // Check that the chatbox and its view now exist
...@@ -1824,7 +1824,7 @@ ...@@ -1824,7 +1824,7 @@
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').t("This message will not be shown").up() }).c('body').t("This message will not be shown").up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.api.chats.get().length); await test_utils.waitUntil(() => _converse.api.chats.get().length);
expect(_converse.log).toHaveBeenCalledWith( expect(_converse.log).toHaveBeenCalledWith(
"onMessage: Ignoring incoming message intended for a different resource: dummy@localhost/some-other-resource", "onMessage: Ignoring incoming message intended for a different resource: dummy@localhost/some-other-resource",
...@@ -1840,7 +1840,7 @@ ...@@ -1840,7 +1840,7 @@
id: '134234623462346' id: '134234623462346'
}).c('body').t(message).up() }).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg); await _converse.chatboxes.onMessage(msg);
await test_utils.waitUntil(() => _converse.chatboxviews.keys().length > 1, 1000); await test_utils.waitUntil(() => _converse.chatboxviews.keys().length > 1, 1000);
const view = _converse.chatboxviews.get(sender_jid); const view = _converse.chatboxviews.get(sender_jid);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
...@@ -2043,9 +2043,9 @@ ...@@ -2043,9 +2043,9 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect($(view.el).find('.chat-msg').hasClass('mentioned')).toBeTruthy(); expect(u.hasClass('mentioned', view.el.querySelector('.chat-msg'))).toBeTruthy();
done(); done();
})); }));
...@@ -2064,8 +2064,7 @@ ...@@ -2064,8 +2064,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t('I wrote this message!').tree(); }).c('body').t('I wrote this message!').tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(view.model.messages.last().get('sender')).toBe('me'); expect(view.model.messages.last().get('sender')).toBe('me');
done(); done();
})); }));
...@@ -2091,7 +2090,7 @@ ...@@ -2091,7 +2090,7 @@
}).tree(); }).tree();
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
const msg_id = u.getUniqueId(); const msg_id = u.getUniqueId();
view.model.onMessage($msg({ await view.model.onMessage($msg({
'from': 'lounge@localhost/newguy', 'from': 'lounge@localhost/newguy',
'to': _converse.connection.jid, 'to': _converse.connection.jid,
'type': 'groupchat', 'type': 'groupchat',
...@@ -2102,10 +2101,10 @@ ...@@ -2102,10 +2101,10 @@
expect(view.el.querySelector('.chat-msg__text').textContent) expect(view.el.querySelector('.chat-msg__text').textContent)
.toBe('But soft, what light through yonder airlock breaks?'); .toBe('But soft, what light through yonder airlock breaks?');
view.model.onMessage($msg({ await view.model.onMessage($msg({
'from': 'lounge@localhost/newguy', 'from': 'lounge@localhost/newguy',
'to': _converse.connection.jid, 'to': _converse.connection.jid,
'type': 'chat', 'type': 'groupchat',
'id': u.getUniqueId(), 'id': u.getUniqueId(),
}).c('body').t('But soft, what light through yonder chimney breaks?').up() }).c('body').t('But soft, what light through yonder chimney breaks?').up()
.c('replace', {'id': msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).tree()); .c('replace', {'id': msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).tree());
...@@ -2114,10 +2113,10 @@ ...@@ -2114,10 +2113,10 @@
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1); expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
expect(view.el.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1); expect(view.el.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
view.model.onMessage($msg({ await view.model.onMessage($msg({
'from': 'lounge@localhost/newguy', 'from': 'lounge@localhost/newguy',
'to': _converse.connection.jid, 'to': _converse.connection.jid,
'type': 'chat', 'type': 'groupchat',
'id': u.getUniqueId(), 'id': u.getUniqueId(),
}).c('body').t('But soft, what light through yonder window breaks?').up() }).c('body').t('But soft, what light through yonder window breaks?').up()
.c('replace', {'id': msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).tree()); .c('replace', {'id': msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).tree());
...@@ -2207,7 +2206,7 @@ ...@@ -2207,7 +2206,7 @@
expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false); expect(u.hasClass('correcting', view.el.querySelector('.chat-msg'))).toBe(false);
// Check that messages from other users are skipped // Check that messages from other users are skipped
view.model.onMessage($msg({ await view.model.onMessage($msg({
'from': room_jid+'/someone-else', 'from': room_jid+'/someone-else',
'id': (new Date()).getTime(), 'id': (new Date()).getTime(),
'to': 'dummy@localhost', 'to': 'dummy@localhost',
...@@ -2264,8 +2263,7 @@ ...@@ -2264,8 +2263,7 @@
'to': 'dummy@localhost', 'to': 'dummy@localhost',
'type': 'groupchat', 'type': 'groupchat',
}).c('body').t(body).up().tree(); }).c('body').t(body).up().tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.model.messages.once('rendered', resolve));
expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(1); expect(view.el.querySelectorAll('.chat-msg__receipt').length).toBe(1);
done(); done();
})); }));
...@@ -2302,7 +2300,7 @@ ...@@ -2302,7 +2300,7 @@
.c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'6', 'end':'10', 'type':'mention', 'uri':'xmpp:z3r0@localhost'}).up() .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'6', 'end':'10', 'type':'mention', 'uri':'xmpp:z3r0@localhost'}).up()
.c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'11', 'end':'14', 'type':'mention', 'uri':'xmpp:dummy@localhost'}).up() .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'11', 'end':'14', 'type':'mention', 'uri':'xmpp:dummy@localhost'}).up()
.c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'15', 'end':'23', 'type':'mention', 'uri':'xmpp:mr.robot@localhost'}).nodeTree; .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'15', 'end':'23', 'type':'mention', 'uri':'xmpp:mr.robot@localhost'}).nodeTree;
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(view.el.querySelectorAll('.chat-msg__text').length).toBe(1); expect(view.el.querySelectorAll('.chat-msg__text').length).toBe(1);
expect(view.el.querySelector('.chat-msg__text').outerHTML).toBe( expect(view.el.querySelector('.chat-msg__text').outerHTML).toBe(
......
...@@ -411,7 +411,7 @@ ...@@ -411,7 +411,7 @@
'type': 'groupchat' 'type': 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
view.el.querySelector('.chat-msg__text a').click(); view.el.querySelector('.chat-msg__text a').click();
await test_utils.waitUntil(() => _converse.chatboxes.length === 3) await test_utils.waitUntil(() => _converse.chatboxes.length === 3)
...@@ -933,7 +933,7 @@ ...@@ -933,7 +933,7 @@
'type': 'groupchat' 'type': 'groupchat'
}).c('body').t('Some message').tree(); }).c('body').t('Some message').tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
let stanza = Strophe.xmlHtmlNode( let stanza = Strophe.xmlHtmlNode(
...@@ -1151,7 +1151,7 @@ ...@@ -1151,7 +1151,7 @@
'to': 'dummy@localhost', 'to': 'dummy@localhost',
'type': 'groupchat' 'type': 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
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).toBe('is tired'); expect(view.el.querySelector('.chat-msg__text').textContent).toBe('is tired');
...@@ -1163,7 +1163,7 @@ ...@@ -1163,7 +1163,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(message).tree(); }).c('body').t(message).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect(_.includes(sizzle('.chat-msg__author:last', view.el).pop().textContent, '**Max Mustermann')).toBeTruthy(); expect(_.includes(sizzle('.chat-msg__author:last', view.el).pop().textContent, '**Max Mustermann')).toBeTruthy();
expect(sizzle('.chat-msg__text:last', view.el).pop().textContent).toBe('is as well'); expect(sizzle('.chat-msg__text:last', view.el).pop().textContent).toBe('is as well');
...@@ -1836,7 +1836,7 @@ ...@@ -1836,7 +1836,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
view.model.onMessage(message.nodeTree); await view.model.onMessage(message.nodeTree);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
const chat_content = view.el.querySelector('.chat-content'); const chat_content = view.el.querySelector('.chat-content');
expect(chat_content.querySelectorAll('.chat-msg').length).toBe(1); expect(chat_content.querySelectorAll('.chat-msg').length).toBe(1);
...@@ -1878,7 +1878,7 @@ ...@@ -1878,7 +1878,7 @@
type: 'groupchat', type: 'groupchat',
id: view.model.messages.at(0).get('msgid') id: view.model.messages.at(0).get('msgid')
}).c('body').t(text); }).c('body').t(text);
view.model.onMessage(message.nodeTree); await view.model.onMessage(message.nodeTree);
expect(chat_content.querySelectorAll('.chat-msg').length).toBe(1); expect(chat_content.querySelectorAll('.chat-msg').length).toBe(1);
expect(sizzle('.chat-msg__text:last').pop().textContent).toBe(text); expect(sizzle('.chat-msg__text:last').pop().textContent).toBe(text);
expect(view.model.messages.length).toBe(1); expect(view.model.messages.length).toBe(1);
...@@ -1912,7 +1912,7 @@ ...@@ -1912,7 +1912,7 @@
// 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;
view.model.onMessage( await view.model.onMessage(
$msg({ $msg({
from: 'lounge@localhost/someone', from: 'lounge@localhost/someone',
to: 'dummy@localhost.com', to: 'dummy@localhost.com',
...@@ -1945,6 +1945,7 @@ ...@@ -1945,6 +1945,7 @@
'</message>').firstChild; '</message>').firstChild;
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
const view = _converse.chatboxviews.get('jdev@conference.jabber.org'); const view = _converse.chatboxviews.get('jdev@conference.jabber.org');
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
const chat_content = view.el.querySelector('.chat-content'); const chat_content = view.el.querySelector('.chat-content');
expect(sizzle('.chat-event:last').pop().textContent).toBe('Topic set by ralphm'); expect(sizzle('.chat-event:last').pop().textContent).toBe('Topic set by ralphm');
expect(sizzle('.chat-topic:last').pop().textContent).toBe(text); expect(sizzle('.chat-topic:last').pop().textContent).toBe(text);
...@@ -3999,7 +4000,7 @@ ...@@ -3999,7 +4000,7 @@
var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
const nick = mock.chatroom_names[0]; const nick = mock.chatroom_names[0];
view.model.onMessage($msg({ await view.model.onMessage($msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: (new Date()).getTime(), id: (new Date()).getTime(),
to: 'dummy@localhost', to: 'dummy@localhost',
...@@ -4010,7 +4011,7 @@ ...@@ -4010,7 +4011,7 @@
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).toBe('1'); expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('1');
view.model.onMessage($msg({ await view.model.onMessage($msg({
'from': room_jid+'/'+nick, 'from': room_jid+'/'+nick,
'id': (new Date()).getTime(), 'id': (new Date()).getTime(),
'to': 'dummy@localhost', 'to': 'dummy@localhost',
...@@ -4076,14 +4077,14 @@ ...@@ -4076,14 +4077,14 @@
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
// <composing> state // <composing> state
var msg = $msg({ let msg = $msg({
from: room_jid+'/newguy', from: room_jid+'/newguy',
id: (new Date()).getTime(), id: (new Date()).getTime(),
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-state-notification').length); await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-state-notification').length);
// Check that the notification appears inside the chatbox in the DOM // Check that the notification appears inside the chatbox in the DOM
...@@ -4109,8 +4110,7 @@ ...@@ -4109,8 +4110,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
events = view.el.querySelectorAll('.chat-event'); events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3); expect(events.length).toBe(3);
...@@ -4131,8 +4131,7 @@ ...@@ -4131,8 +4131,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
events = view.el.querySelectorAll('.chat-event'); events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3); expect(events.length).toBe(3);
expect(events[0].textContent).toEqual('some1 has entered the groupchat'); expect(events[0].textContent).toEqual('some1 has entered the groupchat');
...@@ -4153,7 +4152,7 @@ ...@@ -4153,7 +4152,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t('hello world').tree(); }).c('body').t('hello world').tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve)); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
const messages = view.el.querySelectorAll('.message'); const messages = view.el.querySelectorAll('.message');
...@@ -4259,8 +4258,7 @@ ...@@ -4259,8 +4258,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
// Check that the notification appears inside the chatbox in the DOM // Check that the notification appears inside the chatbox in the DOM
var events = view.el.querySelectorAll('.chat-event'); var events = view.el.querySelectorAll('.chat-event');
...@@ -4280,8 +4278,7 @@ ...@@ -4280,8 +4278,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
events = view.el.querySelectorAll('.chat-event'); events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3); expect(events.length).toBe(3);
...@@ -4300,8 +4297,7 @@ ...@@ -4300,8 +4297,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
events = view.el.querySelectorAll('.chat-event'); events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3); expect(events.length).toBe(3);
expect(events[0].textContent).toEqual('some1 has entered the groupchat'); expect(events[0].textContent).toEqual('some1 has entered the groupchat');
...@@ -4320,8 +4316,7 @@ ...@@ -4320,8 +4316,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree(); }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
view.model.onMessage(msg); await view.model.onMessage(msg);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
events = view.el.querySelectorAll('.chat-event'); events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3); expect(events.length).toBe(3);
expect(events[0].textContent).toEqual('some1 has entered the groupchat'); expect(events[0].textContent).toEqual('some1 has entered the groupchat');
......
...@@ -16,16 +16,17 @@ ...@@ -16,16 +16,17 @@
it("is shown when a new private message is received", it("is shown when a new private message is received",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
// TODO: not yet testing show_desktop_notifications setting // TODO: not yet testing show_desktop_notifications setting
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');
await test_utils.createContacts(_converse, 'current');
spyOn(_converse, 'showMessageNotification').and.callThrough(); spyOn(_converse, 'showMessageNotification').and.callThrough();
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true); spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
spyOn(_converse, 'isMessageToHiddenChat').and.returnValue(true); spyOn(_converse, 'isMessageToHiddenChat').and.returnValue(true);
var message = 'This message will show a desktop notification'; const message = 'This message will show a desktop notification';
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost', const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
msg = $msg({ msg = $msg({
from: sender_jid, from: sender_jid,
to: _converse.connection.jid, to: _converse.connection.jid,
...@@ -33,60 +34,63 @@ ...@@ -33,60 +34,63 @@
id: (new Date()).getTime() id: (new Date()).getTime()
}).c('body').t(message).up() }).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg); // This will emit 'message' await _converse.chatboxes.onMessage(msg); // This will emit 'message'
await test_utils.waitUntil(() => _converse.api.chatviews.get(sender_jid));
expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled(); expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
expect(_converse.showMessageNotification).toHaveBeenCalled(); expect(_converse.showMessageNotification).toHaveBeenCalled();
done(); done();
})); }));
it("is shown when you are mentioned in a chat room", it("is shown when you are mentioned in a groupchat",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.createContacts(_converse, 'current'); await test_utils.createContacts(_converse, 'current');
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () { await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
var view = _converse.chatboxviews.get('lounge@localhost'); const view = _converse.api.chatviews.get('lounge@localhost');
if (!$(view.el).find('.chat-area').length) { view.renderChatArea(); } if (!view.el.querySelectorAll('.chat-area').length) {
var no_notification = false; view.renderChatArea();
if (typeof window.Notification === 'undefined') { }
no_notification = true; let no_notification = false;
window.Notification = function () { if (typeof window.Notification === 'undefined') {
return { no_notification = true;
'close': function () {} window.Notification = function () {
}; return {
'close': function () {}
}; };
} };
spyOn(_converse, 'showMessageNotification').and.callThrough(); }
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true); spyOn(_converse, 'showMessageNotification').and.callThrough();
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
var message = 'dummy: This message will show a desktop notification';
var nick = mock.chatroom_names[0], const message = 'dummy: This message will show a desktop notification';
msg = $msg({ const nick = mock.chatroom_names[0],
from: 'lounge@localhost/'+nick, msg = $msg({
id: (new Date()).getTime(), from: 'lounge@localhost/'+nick,
to: 'dummy@localhost', id: (new Date()).getTime(),
type: 'groupchat' to: 'dummy@localhost',
}).c('body').t(message).tree(); type: 'groupchat'
_converse.chatboxes.onMessage(msg); // This will emit 'message' }).c('body').t(message).tree();
expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled(); await _converse.chatboxes.onMessage(msg); // This will emit 'message'
expect(_converse.showMessageNotification).toHaveBeenCalled(); await new Promise((resolve, reject) => view.once('messageInserted', resolve));
if (no_notification) { expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
delete window.Notification; expect(_converse.showMessageNotification).toHaveBeenCalled();
} if (no_notification) {
done(); delete window.Notification;
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }
done();
})); }));
it("is shown for headline messages", it("is shown for headline messages",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
spyOn(_converse, 'showMessageNotification').and.callThrough(); spyOn(_converse, 'showMessageNotification').and.callThrough();
spyOn(_converse, 'isMessageToHiddenChat').and.returnValue(true); spyOn(_converse, 'isMessageToHiddenChat').and.returnValue(true);
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true); spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
var stanza = $msg({ const stanza = $msg({
'type': 'headline', 'type': 'headline',
'from': 'notify.example.com', 'from': 'notify.example.com',
'to': 'dummy@localhost', 'to': 'dummy@localhost',
...@@ -97,6 +101,9 @@ ...@@ -97,6 +101,9 @@
.c('x', {'xmlns': 'jabber:x:oob'}) .c('x', {'xmlns': 'jabber:x:oob'})
.c('url').t('imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18'); .c('url').t('imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18');
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
await test_utils.waitUntil(() => _converse.chatboxviews.keys().length);
const view = _converse.chatboxviews.get('notify.example.com');
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
expect( expect(
_.includes(_converse.chatboxviews.keys(), _.includes(_converse.chatboxviews.keys(),
'notify.example.com') 'notify.example.com')
...@@ -156,7 +163,7 @@ ...@@ -156,7 +163,7 @@
describe("When play_sounds is set to true", function () { describe("When play_sounds is set to true", function () {
describe("A notification sound", function () { describe("A notification sound", function () {
it("is played when the current user is mentioned in a chat room", it("is played when the current user is mentioned in a groupchat",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
async function (done, _converse) { async function (done, _converse) {
...@@ -176,8 +183,7 @@ ...@@ -176,8 +183,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
view.model.onMessage(message.nodeTree); await view.model.onMessage(message.nodeTree);
await test_utils.waitUntil(() => _converse.playSoundNotification.calls.count()); await test_utils.waitUntil(() => _converse.playSoundNotification.calls.count());
expect(_converse.playSoundNotification).toHaveBeenCalled(); expect(_converse.playSoundNotification).toHaveBeenCalled();
...@@ -188,7 +194,7 @@ ...@@ -188,7 +194,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
view.model.onMessage(message.nodeTree); await view.model.onMessage(message.nodeTree);
expect(_converse.playSoundNotification, 1); expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false; _converse.play_sounds = false;
...@@ -199,7 +205,7 @@ ...@@ -199,7 +205,7 @@
to: 'dummy@localhost', to: 'dummy@localhost',
type: 'groupchat' type: 'groupchat'
}).c('body').t(text); }).c('body').t(text);
view.model.onMessage(message.nodeTree); await view.model.onMessage(message.nodeTree);
expect(_converse.playSoundNotification, 1); expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false; _converse.play_sounds = false;
done(); done();
......
...@@ -285,7 +285,7 @@ ...@@ -285,7 +285,7 @@
view.model.set({'minimized': true}); view.model.set({'minimized': true});
const contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; const contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
const nick = mock.chatroom_names[0]; const nick = mock.chatroom_names[0];
view.model.onMessage( await view.model.onMessage(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: (new Date()).getTime(), id: (new Date()).getTime(),
...@@ -293,13 +293,12 @@ ...@@ -293,13 +293,12 @@
type: 'groupchat' type: 'groupchat'
}).c('body').t('foo').tree()); }).c('body').t('foo').tree());
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
// If the user isn't mentioned, the counter doesn't get incremented, but the text of the groupchat is bold // If the user isn't mentioned, the counter doesn't get incremented, but the text of the groupchat is bold
let room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom"); let room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
expect(_.includes(room_el.classList, 'unread-msgs')).toBeTruthy(); expect(_.includes(room_el.classList, 'unread-msgs')).toBeTruthy();
// If the user is mentioned, the counter also gets updated // If the user is mentioned, the counter also gets updated
view.model.onMessage( await view.model.onMessage(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: (new Date()).getTime(), id: (new Date()).getTime(),
...@@ -311,7 +310,7 @@ ...@@ -311,7 +310,7 @@
spyOn(view.model, 'incrementUnreadMsgCounter').and.callThrough(); spyOn(view.model, 'incrementUnreadMsgCounter').and.callThrough();
let indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator"); let indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
expect(indicator_el.textContent).toBe('1'); expect(indicator_el.textContent).toBe('1');
view.model.onMessage( await view.model.onMessage(
$msg({ $msg({
from: room_jid+'/'+nick, from: room_jid+'/'+nick,
id: (new Date()).getTime(), id: (new Date()).getTime(),
......
...@@ -105,10 +105,10 @@ converse.plugins.add('converse-headline', { ...@@ -105,10 +105,10 @@ converse.plugins.add('converse-headline', {
'afterShown': _.noop 'afterShown': _.noop
}); });
function onHeadlineMessage (message) { async function onHeadlineMessage (message) {
/* Handler method for all incoming messages of type "headline". */ /* Handler method for all incoming messages of type "headline". */
const from_jid = message.getAttribute('from');
if (utils.isHeadlineMessage(_converse, message)) { if (utils.isHeadlineMessage(_converse, message)) {
const from_jid = message.getAttribute('from');
if (_.includes(from_jid, '@') && if (_.includes(from_jid, '@') &&
!_converse.api.contacts.get(from_jid) && !_converse.api.contacts.get(from_jid) &&
!_converse.allow_non_roster_messaging) { !_converse.allow_non_roster_messaging) {
...@@ -125,14 +125,17 @@ converse.plugins.add('converse-headline', { ...@@ -125,14 +125,17 @@ converse.plugins.add('converse-headline', {
'type': _converse.HEADLINES_TYPE, 'type': _converse.HEADLINES_TYPE,
'from': from_jid 'from': from_jid
}); });
chatbox.createMessage(message, message); const attrs = await chatbox.getMessageAttributesFromStanza(message, message);
await chatbox.messages.create(attrs);
_converse.emit('message', {'chatbox': chatbox, 'stanza': message}); _converse.emit('message', {'chatbox': chatbox, 'stanza': message});
} }
return true;
} }
function registerHeadlineHandler () { function registerHeadlineHandler () {
_converse.connection.addHandler(onHeadlineMessage, null, 'message'); _converse.connection.addHandler(message => {
onHeadlineMessage(message);
return true
}, null, 'message');
} }
_converse.on('connected', registerHeadlineHandler); _converse.on('connected', registerHeadlineHandler);
_converse.on('reconnected', registerHeadlineHandler); _converse.on('reconnected', registerHeadlineHandler);
......
...@@ -544,8 +544,10 @@ converse.plugins.add('converse-chatboxes', { ...@@ -544,8 +544,10 @@ converse.plugins.add('converse-chatboxes', {
'is_delayed': !_.isNil(delay), 'is_delayed': !_.isNil(delay),
'is_spoiler': !_.isNil(spoiler), 'is_spoiler': !_.isNil(spoiler),
'message': _converse.chatboxes.getMessageBody(stanza) || undefined, 'message': _converse.chatboxes.getMessageBody(stanza) || undefined,
'references': this.getReferencesFromStanza(stanza),
'msgid': stanza.getAttribute('id'), 'msgid': stanza.getAttribute('id'),
'references': this.getReferencesFromStanza(stanza),
'subject': _.propertyOf(stanza.querySelector('subject'))('textContent'),
'thread': _.propertyOf(stanza.querySelector('thread'))('textContent'),
'time': delay ? delay.getAttribute('stamp') : moment().format(), 'time': delay ? delay.getAttribute('stamp') : moment().format(),
'type': stanza.getAttribute('type') 'type': stanza.getAttribute('type')
}; };
...@@ -573,25 +575,6 @@ converse.plugins.add('converse-chatboxes', { ...@@ -573,25 +575,6 @@ converse.plugins.add('converse-chatboxes', {
return attrs; return attrs;
}, },
async createMessage (message, original_stanza) {
/* Create a Backbone.Message object inside this chat box
* based on the identified message stanza.
*/
const attrs = await this.getMessageAttributesFromStanza(message, original_stanza),
is_csn = u.isOnlyChatStateNotification(attrs);
if (is_csn && (attrs.is_delayed || (attrs.type === 'groupchat' && Strophe.getResourceFromJid(attrs.from) == this.get('nick')))) {
// XXX: MUC leakage
// No need showing delayed or our own CSN messages
return;
} else if (!is_csn && !attrs.file && !attrs.plaintext && !attrs.message && !attrs.oob_url && attrs.type !== 'error') {
// TODO: handle <subject> messages (currently being done by ChatRoom)
return;
} else {
return this.messages.create(attrs);
}
},
isHidden () { isHidden () {
/* Returns a boolean to indicate whether a newly received /* Returns a boolean to indicate whether a newly received
* message will be visible to the user or not. * message will be visible to the user or not.
...@@ -680,7 +663,7 @@ converse.plugins.add('converse-chatboxes', { ...@@ -680,7 +663,7 @@ converse.plugins.add('converse-chatboxes', {
}); });
}, },
onErrorMessage (message) { async onErrorMessage (message) {
/* Handler method for all incoming error message stanzas /* Handler method for all incoming error message stanzas
*/ */
const from_jid = Strophe.getBareJidFromJid(message.getAttribute('from')); const from_jid = Strophe.getBareJidFromJid(message.getAttribute('from'));
...@@ -708,8 +691,8 @@ converse.plugins.add('converse-chatboxes', { ...@@ -708,8 +691,8 @@ converse.plugins.add('converse-chatboxes', {
_converse.log('Received an error message without id attribute!', Strophe.LogLevel.ERROR); _converse.log('Received an error message without id attribute!', Strophe.LogLevel.ERROR);
_converse.log(message, Strophe.LogLevel.ERROR); _converse.log(message, Strophe.LogLevel.ERROR);
} }
chatbox.createMessage(message, message); const attrs = await chatbox.getMessageAttributesFromStanza(message, message);
return true; chatbox.messages.create(attrs);
}, },
getMessageBody (stanza) { getMessageBody (stanza) {
...@@ -736,7 +719,7 @@ converse.plugins.add('converse-chatboxes', { ...@@ -736,7 +719,7 @@ converse.plugins.add('converse-chatboxes', {
_converse.api.send(receipt_stanza); _converse.api.send(receipt_stanza);
}, },
onMessage (stanza) { async onMessage (stanza) {
/* Handler method for all incoming single-user chat "message" /* Handler method for all incoming single-user chat "message"
* stanzas. * stanzas.
* *
...@@ -816,13 +799,12 @@ converse.plugins.add('converse-chatboxes', { ...@@ -816,13 +799,12 @@ converse.plugins.add('converse-chatboxes', {
message = msgid && chatbox.messages.findWhere({msgid}); message = msgid && chatbox.messages.findWhere({msgid});
if (!message) { if (!message) {
// Only create the message when we're sure it's not a duplicate // Only create the message when we're sure it's not a duplicate
chatbox.createMessage(stanza, original_stanza) const attrs = await chatbox.getMessageAttributesFromStanza(stanza, original_stanza);
.then(msg => chatbox.incrementUnreadMsgCounter(msg)) const msg = chatbox.messages.create(attrs);
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); chatbox.incrementUnreadMsgCounter(msg);
} }
} }
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox}); _converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
return true;
}, },
getChatBox (jid, attrs={}, create) { getChatBox (jid, attrs={}, create) {
......
...@@ -218,6 +218,8 @@ converse.plugins.add('converse-mam', { ...@@ -218,6 +218,8 @@ converse.plugins.add('converse-mam', {
if (!results.length) { return; } if (!results.length) { return; }
this.addSpinner(); this.addSpinner();
_converse.api.archive.query( _converse.api.archive.query(
// TODO: only query from the last message we have
// in our history
_.extend({ _.extend({
'groupchat': is_groupchat, 'groupchat': is_groupchat,
'before': '', // Page backwards from the most recent message 'before': '', // Page backwards from the most recent message
......
...@@ -987,33 +987,40 @@ converse.plugins.add('converse-muc', { ...@@ -987,33 +987,40 @@ converse.plugins.add('converse-muc', {
const original_stanza = stanza, const original_stanza = stanza,
forwarded = sizzle(`forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).pop(); forwarded = sizzle(`forwarded[xmlns="${Strophe.NS.FORWARD}"]`, stanza).pop();
if (forwarded) { if (forwarded) {
stanza = forwarded.querySelector('message'); stanza = forwarded.querySelector('message');
} }
if (this.isDuplicate(stanza, original_stanza)) { if (this.isDuplicate(stanza, original_stanza)) {
return; return;
} }
const jid = stanza.getAttribute('from'), const attrs = await this.getMessageAttributesFromStanza(stanza, original_stanza);
resource = Strophe.getResourceFromJid(jid), if (!attrs.nick) {
sender = resource && Strophe.unescapeNode(resource) || ''; return;
}
if (!this.handleMessageCorrection(stanza)) { if (!this.handleMessageCorrection(stanza)) {
if (sender === '') { if (attrs.subject && !attrs.thread && !attrs.message) {
// https://xmpp.org/extensions/xep-0045.html#subject-mod
// -----------------------------------------------------
// The subject is changed by sending a message of type "groupchat" to the <room@service>,
// where the <message/> MUST contain a <subject/> element that specifies the new subject but
// MUST NOT contain a <body/> element (or a <thread/> element).
u.safeSave(this, {'subject': {'author': attrs.nick, 'text': attrs.subject || ''}});
return; return;
} }
const subject_el = stanza.querySelector('subject');
if (subject_el) { const is_csn = u.isOnlyChatStateNotification(attrs),
const subject = _.propertyOf(subject_el)('textContent') || ''; own_message = Strophe.getResourceFromJid(attrs.from) == this.get('nick');
u.safeSave(this, {'subject': {'author': sender, 'text': subject}}); if (is_csn && (attrs.is_delayed || own_message)) {
// No need showing delayed or our own CSN messages
return;
} }
const msg = await this.createMessage(stanza, original_stanza); const msg = await this.messages.create(attrs);
if (forwarded && msg && msg.get('sender') === 'me') { if (forwarded && msg && msg.get('sender') === 'me') {
msg.save({'received': moment().format()}); msg.save({'received': moment().format()});
} }
this.incrementUnreadMsgCounter(msg); this.incrementUnreadMsgCounter(msg);
} }
if (sender !== this.get('nick')) { if (attrs.nick !== this.get('nick')) {
// We only emit an event if it's not our own message // We only emit an event if it's not our own message
_converse.emit('message', {'stanza': original_stanza, 'chatbox': this}); _converse.emit('message', {'stanza': original_stanza, 'chatbox': this});
} }
......
...@@ -112,7 +112,7 @@ var specs = [ ...@@ -112,7 +112,7 @@ var specs = [
"spec/chatbox", "spec/chatbox",
"spec/user-details-modal", "spec/user-details-modal",
"spec/messages", "spec/messages",
"spec/chatroom", "spec/muc",
"spec/room_registration", "spec/room_registration",
"spec/autocomplete", "spec/autocomplete",
"spec/minchats", "spec/minchats",
......
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