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();
})); }));
}); });
})); }));
This diff is collapsed.
This diff is collapsed.
...@@ -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