Initial support for chat markers. Updates #324
This commit is contained in:
parent
4c964c56c0
commit
e1f8d53c46
145
dist/converse.js
vendored
145
dist/converse.js
vendored
|
@ -61304,6 +61304,7 @@ const u = _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].env.utils;
|
|||
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
|
||||
Strophe.addNamespace('RECEIPTS', 'urn:xmpp:receipts');
|
||||
Strophe.addNamespace('REFERENCE', 'urn:xmpp:reference:0');
|
||||
Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0');
|
||||
_converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-chatboxes', {
|
||||
dependencies: ["converse-roster", "converse-vcard"],
|
||||
|
||||
|
@ -61634,7 +61635,84 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
|
|||
return false;
|
||||
},
|
||||
|
||||
handleReceipt(stanza) {
|
||||
sendMarker(to_jid, id, type) {
|
||||
const stanza = $msg({
|
||||
'from': _converse.connection.jid,
|
||||
'id': _converse.connection.getUniqueId(),
|
||||
'to': to_jid,
|
||||
'type': 'chat'
|
||||
}).c(type, {
|
||||
'xmlns': Strophe.NS.MARKERS,
|
||||
'id': id
|
||||
});
|
||||
|
||||
_converse.api.send(stanza);
|
||||
},
|
||||
|
||||
handleChatMarker(stanza, from_jid, is_carbon) {
|
||||
const to_bare_jid = Strophe.getBareJidFromJid(stanza.getAttribute('to'));
|
||||
|
||||
if (to_bare_jid !== _converse.bare_jid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const markers = sizzle(`[xmlns="${Strophe.NS.MARKERS}"]`, stanza);
|
||||
|
||||
if (markers.length === 0) {
|
||||
return false;
|
||||
} else if (markers.length > 1) {
|
||||
_converse.log('onMessage: Ignoring incoming stanza with multiple message markers', Strophe.LogLevel.ERROR);
|
||||
|
||||
_converse.log(stanza, Strophe.LogLevel.ERROR);
|
||||
|
||||
return false;
|
||||
} else {
|
||||
const marker = markers.pop();
|
||||
|
||||
if (marker.nodeName === 'markable' && !is_carbon) {
|
||||
this.sendMarker(from_jid, stanza.getAttribute('id'), 'received');
|
||||
return false;
|
||||
} else {
|
||||
const msgid = marker && marker.getAttribute('id'),
|
||||
message = msgid && this.messages.findWhere({
|
||||
msgid
|
||||
}),
|
||||
field_name = `marker_${marker.nodeName}`;
|
||||
|
||||
if (message && !message.get(field_name)) {
|
||||
message.save({
|
||||
field_name: moment().format()
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
sendReceiptStanza(to_jid, id) {
|
||||
const receipt_stanza = $msg({
|
||||
'from': _converse.connection.jid,
|
||||
'id': _converse.connection.getUniqueId(),
|
||||
'to': to_jid,
|
||||
'type': 'chat'
|
||||
}).c('received', {
|
||||
'xmlns': Strophe.NS.RECEIPTS,
|
||||
'id': id
|
||||
}).up().c('store', {
|
||||
'xmlns': Strophe.NS.HINTS
|
||||
}).up();
|
||||
|
||||
_converse.api.send(receipt_stanza);
|
||||
},
|
||||
|
||||
handleReceipt(stanza, from_jid, is_carbon, is_me) {
|
||||
const requests_receipt = !_.isUndefined(sizzle(`request[xmlns="${Strophe.NS.RECEIPTS}"]`, stanza).pop());
|
||||
|
||||
if (requests_receipt && !is_carbon && !is_me) {
|
||||
this.sendReceiptStanza(from_jid, stanza.getAttribute('id'));
|
||||
}
|
||||
|
||||
const to_bare_jid = Strophe.getBareJidFromJid(stanza.getAttribute('to'));
|
||||
|
||||
if (to_bare_jid === _converse.bare_jid) {
|
||||
|
@ -61927,6 +62005,23 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
|
|||
return attrs;
|
||||
},
|
||||
|
||||
async createMessage(stanza, original_stanza) {
|
||||
const msgid = stanza.getAttribute('id'),
|
||||
message = msgid && this.messages.findWhere({
|
||||
msgid
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
// Only create the message when we're sure it's not a duplicate
|
||||
const attrs = await this.getMessageAttributesFromStanza(stanza, original_stanza);
|
||||
|
||||
if (attrs['chat_state'] || !u.isEmptyMessage(attrs)) {
|
||||
const msg = this.messages.create(attrs);
|
||||
this.incrementUnreadMsgCounter(msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isHidden() {
|
||||
/* Returns a boolean to indicate whether a newly received
|
||||
* message will be visible to the user or not.
|
||||
|
@ -62078,22 +62173,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
|
|||
}
|
||||
},
|
||||
|
||||
sendReceiptStanza(to_jid, id) {
|
||||
const receipt_stanza = $msg({
|
||||
'from': _converse.connection.jid,
|
||||
'id': _converse.connection.getUniqueId(),
|
||||
'to': to_jid,
|
||||
'type': 'chat'
|
||||
}).c('received', {
|
||||
'xmlns': Strophe.NS.RECEIPTS,
|
||||
'id': id
|
||||
}).up().c('store', {
|
||||
'xmlns': Strophe.NS.HINTS
|
||||
}).up();
|
||||
|
||||
_converse.api.send(receipt_stanza);
|
||||
},
|
||||
|
||||
async onMessage(stanza) {
|
||||
/* Handler method for all incoming single-user chat "message"
|
||||
* stanzas.
|
||||
|
@ -62141,12 +62220,6 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
|
|||
const from_bare_jid = Strophe.getBareJidFromJid(from_jid),
|
||||
from_resource = Strophe.getResourceFromJid(from_jid),
|
||||
is_me = from_bare_jid === _converse.bare_jid;
|
||||
const requests_receipt = !_.isUndefined(sizzle(`request[xmlns="${Strophe.NS.RECEIPTS}"]`, stanza).pop());
|
||||
|
||||
if (requests_receipt && !is_carbon && !is_me) {
|
||||
this.sendReceiptStanza(from_jid, stanza.getAttribute('id'));
|
||||
}
|
||||
|
||||
let contact_jid;
|
||||
|
||||
if (is_me) {
|
||||
|
@ -62158,27 +62231,17 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
|
|||
contact_jid = Strophe.getBareJidFromJid(to_jid);
|
||||
} else {
|
||||
contact_jid = from_bare_jid;
|
||||
}
|
||||
} // Get chat box, but only create when the message has something to show to the user
|
||||
|
||||
const attrs = {
|
||||
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname') // Get chat box, but only create a new one when the message has a body.
|
||||
|
||||
};
|
||||
const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0;
|
||||
const chatbox = this.getChatBox(contact_jid, attrs, has_body);
|
||||
const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0,
|
||||
chatbox_attrs = {
|
||||
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname')
|
||||
},
|
||||
chatbox = this.getChatBox(contact_jid, chatbox_attrs, has_body);
|
||||
|
||||
if (chatbox && !chatbox.handleMessageCorrection(stanza) && !chatbox.handleReceipt(stanza)) {
|
||||
const msgid = stanza.getAttribute('id'),
|
||||
message = msgid && chatbox.messages.findWhere({
|
||||
msgid
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
// Only create the message when we're sure it's not a duplicate
|
||||
const attrs = await chatbox.getMessageAttributesFromStanza(stanza, original_stanza);
|
||||
const msg = chatbox.messages.create(attrs);
|
||||
chatbox.incrementUnreadMsgCounter(msg);
|
||||
}
|
||||
if (chatbox && !chatbox.handleMessageCorrection(stanza) && !chatbox.handleReceipt(stanza, from_jid, is_carbon, is_me) && !chatbox.handleChatMarker(stanza, from_jid, is_carbon)) {
|
||||
await chatbox.createMessage(stanza, original_stanza);
|
||||
}
|
||||
|
||||
_converse.emit('message', {
|
||||
|
|
|
@ -530,6 +530,38 @@
|
|||
|
||||
describe("A Chat Status Notification", function () {
|
||||
|
||||
it("is ignored when it's a carbon copy of one of my own",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
test_utils.openControlBox();
|
||||
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
await test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
let stanza = Strophe.xmlHtmlNode(
|
||||
`<message from="${sender_jid}"
|
||||
type="chat"
|
||||
to="dummy@localhost/resource">
|
||||
<composing xmlns="http://jabber.org/protocol/chatstates"/>
|
||||
<no-store xmlns="urn:xmpp:hints"/>
|
||||
<no-permanent-store xmlns="urn:xmpp:hints"/>
|
||||
</message>`).firstChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
stanza = Strophe.xmlHtmlNode(
|
||||
`<message from="${sender_jid}"
|
||||
type="chat"
|
||||
to="dummy@localhost/resource">
|
||||
<paused xmlns="http://jabber.org/protocol/chatstates"/>
|
||||
<no-store xmlns="urn:xmpp:hints"/>
|
||||
<no-permanent-store xmlns="urn:xmpp:hints"/>
|
||||
</message>`).firstChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
done();
|
||||
}));
|
||||
|
||||
it("does not open a new chatbox",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
|
@ -656,12 +688,14 @@
|
|||
async function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
_converse.emit('rosterContactsFetched');
|
||||
test_utils.openControlBox();
|
||||
|
||||
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
|
||||
spyOn(_converse, 'emit');
|
||||
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
|
||||
await test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
|
||||
// <composing> state
|
||||
let msg = $msg({
|
||||
|
@ -678,7 +712,6 @@
|
|||
await test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1])
|
||||
// Check that the notification appears inside the chatbox in the DOM
|
||||
let events = view.el.querySelectorAll('.chat-state-notification');
|
||||
expect(events.length).toBe(1);
|
||||
expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing');
|
||||
|
||||
// Check that it doesn't appear twice
|
||||
|
@ -687,7 +720,7 @@
|
|||
to: _converse.connection.jid,
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
await _converse.chatboxes.onMessage(msg);
|
||||
events = view.el.querySelectorAll('.chat-state-notification');
|
||||
expect(events.length).toBe(1);
|
||||
|
@ -802,24 +835,24 @@
|
|||
null, ['rosterGroupsFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
test_utils.createContacts(_converse, 'current', 2);
|
||||
_converse.emit('rosterContactsFetched');
|
||||
test_utils.openControlBox();
|
||||
await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
|
||||
// TODO: only show paused state if the previous state was composing
|
||||
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse, 'emit').and.callThrough();
|
||||
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
const view = await test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
// <paused> state
|
||||
const msg = $msg({
|
||||
from: sender_jid,
|
||||
to: _converse.connection.jid,
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
}).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
await _converse.chatboxes.onMessage(msg);
|
||||
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
await test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1])
|
||||
var event = view.el.querySelector('.chat-info.chat-state-notification');
|
||||
expect(event.textContent).toEqual(mock.cur_names[1] + ' has stopped typing');
|
||||
|
|
100
spec/messages.js
100
spec/messages.js
|
@ -313,8 +313,6 @@
|
|||
await _converse.chatboxes.onMessage(msg);
|
||||
await test_utils.waitUntil(() => _converse.api.chats.get().length);
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
|
||||
msg = $msg({'id': 'aeb214', 'to': _converse.bare_jid})
|
||||
.c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
|
||||
.c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2017-12-31T22:08:25Z'}).up()
|
||||
|
@ -392,7 +390,6 @@
|
|||
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
|
||||
.tree();
|
||||
await _converse.chatboxes.onMessage(msg);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
|
||||
msg = $msg({
|
||||
'id': 'aeb220',
|
||||
|
@ -527,7 +524,6 @@
|
|||
await test_utils.waitUntil(() => _converse.api.chats.get().length)
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
|
||||
expect(chatbox).toBeDefined();
|
||||
expect(view).toBeDefined();
|
||||
|
@ -579,7 +575,6 @@
|
|||
// Check that the chatbox and its view now exist
|
||||
const chatbox = _converse.chatboxes.get(recipient_jid);
|
||||
const view = _converse.chatboxviews.get(recipient_jid);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
expect(chatbox).toBeDefined();
|
||||
expect(view).toBeDefined();
|
||||
// Check that the message was received and check the message parameters
|
||||
|
@ -1225,7 +1220,8 @@
|
|||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
const msg_id = u.getUniqueId();
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.chatboxes, 'sendReceiptStanza').and.callThrough();
|
||||
const view = await test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
spyOn(view.model, 'sendReceiptStanza').and.callThrough();
|
||||
const msg = $msg({
|
||||
'from': sender_jid,
|
||||
'to': _converse.connection.jid,
|
||||
|
@ -1243,7 +1239,7 @@
|
|||
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
|
||||
await _converse.chatboxes.onMessage(msg);
|
||||
await test_utils.waitUntil(() => _converse.api.chats.get().length);
|
||||
expect(_converse.chatboxes.sendReceiptStanza).not.toHaveBeenCalled();
|
||||
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1255,7 +1251,8 @@
|
|||
const recipient_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
const msg_id = u.getUniqueId();
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.chatboxes, 'sendReceiptStanza').and.callThrough();
|
||||
const view = await test_utils.openChatBoxFor(_converse, recipient_jid);
|
||||
spyOn(view.model, 'sendReceiptStanza').and.callThrough();
|
||||
const msg = $msg({
|
||||
'from': converse.bare_jid,
|
||||
'to': _converse.connection.jid,
|
||||
|
@ -1272,7 +1269,7 @@
|
|||
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
|
||||
await _converse.chatboxes.onMessage(msg);
|
||||
await test_utils.waitUntil(() => _converse.api.chats.get().length);
|
||||
expect(_converse.chatboxes.sendReceiptStanza).not.toHaveBeenCalled();
|
||||
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1482,7 +1479,6 @@
|
|||
// Check that the chatbox and its view now exist
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
|
||||
expect(chatbox).toBeDefined();
|
||||
expect(view).toBeDefined();
|
||||
|
@ -1531,7 +1527,6 @@
|
|||
|
||||
await test_utils.waitUntil(() => _converse.api.chats.get().length)
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
|
||||
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
|
||||
// onMessage is a handler for received XMPP messages
|
||||
|
@ -1843,7 +1838,6 @@
|
|||
await _converse.chatboxes.onMessage(msg);
|
||||
await test_utils.waitUntil(() => _converse.chatboxviews.keys().length > 1, 1000);
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
await test_utils.waitUntil(() => view.model.messages.length);
|
||||
expect(_converse.chatboxes.getChatBox).toHaveBeenCalled();
|
||||
var chat_content = $(view.el).find('.chat-content:last')[0];
|
||||
|
@ -2023,6 +2017,88 @@
|
|||
});
|
||||
});
|
||||
|
||||
describe("A XEP-0333 Chat Marker", function () {
|
||||
|
||||
it("is sent when a markable message is received",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
await test_utils.openChatBoxFor(_converse, contact_jid);
|
||||
const view = await _converse.api.chatviews.get(contact_jid);
|
||||
const msgid = u.getUniqueId();
|
||||
const stanza = Strophe.xmlHtmlNode(`
|
||||
<message from='${contact_jid}'
|
||||
id='${msgid}'
|
||||
type="chat"
|
||||
to='${_converse.jid}'>
|
||||
<body>My lord, dispatch; read o'er these articles.</body>
|
||||
<markable xmlns='urn:xmpp:chat-markers:0'/>
|
||||
</message>`).firstElementChild;
|
||||
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
spyOn(view.model, 'sendMarker').and.callThrough();
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
await test_utils.waitUntil(() => view.model.sendMarker.calls.count() === 1);
|
||||
expect(Strophe.serialize(sent_stanzas[0])).toBe(
|
||||
`<message from="dummy@localhost/resource" `+
|
||||
`id="${sent_stanzas[0].nodeTree.getAttribute('id')}" `+
|
||||
`to="${contact_jid}" type="chat" xmlns="jabber:client">`+
|
||||
`<received id="${msgid}" xmlns="urn:xmpp:chat-markers:0"/>`+
|
||||
`</message>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is ignored if it's a carbon copy of one that I sent from a different client",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
async function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
await test_utils.openChatBoxFor(_converse, contact_jid);
|
||||
const view = await _converse.api.chatviews.get(contact_jid);
|
||||
|
||||
let stanza = Strophe.xmlHtmlNode(`
|
||||
<message xmlns="jabber:client"
|
||||
to="${_converse.bare_jid}"
|
||||
type="chat"
|
||||
id="2e972ea0-0050-44b7-a830-f6638a2595b3"
|
||||
from="${contact_jid}">
|
||||
<body>😊</body>
|
||||
<markable xmlns="urn:xmpp:chat-markers:0"/>
|
||||
<origin-id xmlns="urn:xmpp:sid:0" id="2e972ea0-0050-44b7-a830-f6638a2595b3"/>
|
||||
<stanza-id xmlns="urn:xmpp:sid:0" id="IxVDLJ0RYbWcWvqC" by="${_converse.bare_jid}"/>
|
||||
</message>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(view.model.messages.length).toBe(1);
|
||||
|
||||
stanza = Strophe.xmlHtmlNode(
|
||||
`<message xmlns="jabber:client" to="${_converse.bare_jid}" type="chat" from="${contact_jid}">
|
||||
<sent xmlns="urn:xmpp:carbons:2">
|
||||
<forwarded xmlns="urn:xmpp:forward:0">
|
||||
<message xmlns="jabber:client" to="${contact_jid}" type="chat" from="${_converse.bare_jid}/other-resource">
|
||||
<received xmlns="urn:xmpp:chat-markers:0" id="2e972ea0-0050-44b7-a830-f6638a2595b3"/>
|
||||
<store xmlns="urn:xmpp:hints"/>
|
||||
<stanza-id xmlns="urn:xmpp:sid:0" id="F4TC6CvHwzqRbeHb" by="jc@opkode.com"/>
|
||||
</message>
|
||||
</forwarded>
|
||||
</sent>
|
||||
</message>`).firstElementChild;
|
||||
spyOn(_converse, 'emit').and.callThrough();
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
await test_utils.waitUntil(() => _converse.emit.calls.count() === 1);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(view.model.messages.length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe("A Groupchat Message", function () {
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ const u = converse.env.utils;
|
|||
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
|
||||
Strophe.addNamespace('RECEIPTS', 'urn:xmpp:receipts');
|
||||
Strophe.addNamespace('REFERENCE', 'urn:xmpp:reference:0');
|
||||
Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0');
|
||||
|
||||
|
||||
converse.plugins.add('converse-chatboxes', {
|
||||
|
@ -313,7 +314,65 @@ converse.plugins.add('converse-chatboxes', {
|
|||
return false;
|
||||
},
|
||||
|
||||
handleReceipt (stanza) {
|
||||
sendMarker(to_jid, id, type) {
|
||||
const stanza = $msg({
|
||||
'from': _converse.connection.jid,
|
||||
'id': _converse.connection.getUniqueId(),
|
||||
'to': to_jid,
|
||||
'type': 'chat',
|
||||
}).c(type, {'xmlns': Strophe.NS.MARKERS, 'id': id});
|
||||
_converse.api.send(stanza);
|
||||
},
|
||||
|
||||
handleChatMarker (stanza, from_jid, is_carbon) {
|
||||
const to_bare_jid = Strophe.getBareJidFromJid(stanza.getAttribute('to'));
|
||||
if (to_bare_jid !== _converse.bare_jid) {
|
||||
return false;
|
||||
}
|
||||
const markers = sizzle(`[xmlns="${Strophe.NS.MARKERS}"]`, stanza);
|
||||
if (markers.length === 0) {
|
||||
return false;
|
||||
} else if (markers.length > 1) {
|
||||
_converse.log(
|
||||
'onMessage: Ignoring incoming stanza with multiple message markers',
|
||||
Strophe.LogLevel.ERROR
|
||||
);
|
||||
_converse.log(stanza, Strophe.LogLevel.ERROR);
|
||||
return false;
|
||||
} else {
|
||||
const marker = markers.pop();
|
||||
if (marker.nodeName === 'markable' && !is_carbon) {
|
||||
this.sendMarker(from_jid, stanza.getAttribute('id'), 'received');
|
||||
return false;
|
||||
} else {
|
||||
const msgid = marker && marker.getAttribute('id'),
|
||||
message = msgid && this.messages.findWhere({msgid}),
|
||||
field_name = `marker_${marker.nodeName}`;
|
||||
|
||||
if (message && !message.get(field_name)) {
|
||||
message.save({field_name: moment().format()});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
sendReceiptStanza (to_jid, id) {
|
||||
const receipt_stanza = $msg({
|
||||
'from': _converse.connection.jid,
|
||||
'id': _converse.connection.getUniqueId(),
|
||||
'to': to_jid,
|
||||
'type': 'chat',
|
||||
}).c('received', {'xmlns': Strophe.NS.RECEIPTS, 'id': id}).up()
|
||||
.c('store', {'xmlns': Strophe.NS.HINTS}).up();
|
||||
_converse.api.send(receipt_stanza);
|
||||
},
|
||||
|
||||
handleReceipt (stanza, from_jid, is_carbon, is_me) {
|
||||
const requests_receipt = !_.isUndefined(sizzle(`request[xmlns="${Strophe.NS.RECEIPTS}"]`, stanza).pop());
|
||||
if (requests_receipt && !is_carbon && !is_me) {
|
||||
this.sendReceiptStanza(from_jid, stanza.getAttribute('id'));
|
||||
}
|
||||
const to_bare_jid = Strophe.getBareJidFromJid(stanza.getAttribute('to'));
|
||||
if (to_bare_jid === _converse.bare_jid) {
|
||||
const receipt = sizzle(`received[xmlns="${Strophe.NS.RECEIPTS}"]`, stanza).pop();
|
||||
|
@ -321,9 +380,7 @@ converse.plugins.add('converse-chatboxes', {
|
|||
const msgid = receipt && receipt.getAttribute('id'),
|
||||
message = msgid && this.messages.findWhere({msgid});
|
||||
if (message && !message.get('received')) {
|
||||
message.save({
|
||||
'received': moment().format()
|
||||
});
|
||||
message.save({'received': moment().format()});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -575,6 +632,19 @@ converse.plugins.add('converse-chatboxes', {
|
|||
return attrs;
|
||||
},
|
||||
|
||||
async createMessage (stanza, original_stanza) {
|
||||
const msgid = stanza.getAttribute('id'),
|
||||
message = msgid && this.messages.findWhere({msgid});
|
||||
if (!message) {
|
||||
// Only create the message when we're sure it's not a duplicate
|
||||
const attrs = await this.getMessageAttributesFromStanza(stanza, original_stanza);
|
||||
if (attrs['chat_state'] || !u.isEmptyMessage(attrs)) {
|
||||
const msg = this.messages.create(attrs);
|
||||
this.incrementUnreadMsgCounter(msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isHidden () {
|
||||
/* Returns a boolean to indicate whether a newly received
|
||||
* message will be visible to the user or not.
|
||||
|
@ -708,17 +778,6 @@ converse.plugins.add('converse-chatboxes', {
|
|||
}
|
||||
},
|
||||
|
||||
sendReceiptStanza (to_jid, id) {
|
||||
const receipt_stanza = $msg({
|
||||
'from': _converse.connection.jid,
|
||||
'id': _converse.connection.getUniqueId(),
|
||||
'to': to_jid,
|
||||
'type': 'chat',
|
||||
}).c('received', {'xmlns': Strophe.NS.RECEIPTS, 'id': id}).up()
|
||||
.c('store', {'xmlns': Strophe.NS.HINTS}).up();
|
||||
_converse.api.send(receipt_stanza);
|
||||
},
|
||||
|
||||
async onMessage (stanza) {
|
||||
/* Handler method for all incoming single-user chat "message"
|
||||
* stanzas.
|
||||
|
@ -765,16 +824,10 @@ converse.plugins.add('converse-chatboxes', {
|
|||
from_jid = stanza.getAttribute('from');
|
||||
to_jid = stanza.getAttribute('to');
|
||||
}
|
||||
|
||||
const from_bare_jid = Strophe.getBareJidFromJid(from_jid),
|
||||
from_resource = Strophe.getResourceFromJid(from_jid),
|
||||
is_me = from_bare_jid === _converse.bare_jid;
|
||||
|
||||
const requests_receipt = !_.isUndefined(sizzle(`request[xmlns="${Strophe.NS.RECEIPTS}"]`, stanza).pop());
|
||||
if (requests_receipt && !is_carbon && !is_me) {
|
||||
this.sendReceiptStanza(from_jid, stanza.getAttribute('id'));
|
||||
}
|
||||
|
||||
let contact_jid;
|
||||
if (is_me) {
|
||||
// I am the sender, so this must be a forwarded message...
|
||||
|
@ -788,21 +841,15 @@ converse.plugins.add('converse-chatboxes', {
|
|||
} else {
|
||||
contact_jid = from_bare_jid;
|
||||
}
|
||||
const attrs = {
|
||||
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname')
|
||||
}
|
||||
// Get chat box, but only create a new one when the message has a body.
|
||||
const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0;
|
||||
const chatbox = this.getChatBox(contact_jid, attrs, has_body);
|
||||
if (chatbox && !chatbox.handleMessageCorrection(stanza) && !chatbox.handleReceipt(stanza)) {
|
||||
const msgid = stanza.getAttribute('id'),
|
||||
message = msgid && chatbox.messages.findWhere({msgid});
|
||||
if (!message) {
|
||||
// Only create the message when we're sure it's not a duplicate
|
||||
const attrs = await chatbox.getMessageAttributesFromStanza(stanza, original_stanza);
|
||||
const msg = chatbox.messages.create(attrs);
|
||||
chatbox.incrementUnreadMsgCounter(msg);
|
||||
}
|
||||
// Get chat box, but only create when the message has something to show to the user
|
||||
const has_body = sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length > 0,
|
||||
chatbox_attrs = {'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname')},
|
||||
chatbox = this.getChatBox(contact_jid, chatbox_attrs, has_body);
|
||||
if (chatbox &&
|
||||
!chatbox.handleMessageCorrection(stanza) &&
|
||||
!chatbox.handleReceipt (stanza, from_jid, is_carbon, is_me) &&
|
||||
!chatbox.handleChatMarker(stanza, from_jid, is_carbon)) {
|
||||
await chatbox.createMessage(stanza, original_stanza);
|
||||
}
|
||||
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue
Block a user