Updates #1896: Properly identify archived one-on-one messages

Also, rename attribute from `is_receipt_request` to `is_valid_receipt_request` to avoid confusion.
This commit is contained in:
JC Brand 2020-08-13 11:05:23 +02:00
parent 78bf07ecff
commit 09371712b0
9 changed files with 236 additions and 196 deletions

View File

@ -46,6 +46,7 @@ module.exports = function(config) {
{ pattern: "spec/chatbox.js", type: 'module' },
{ pattern: "spec/user-details-modal.js", type: 'module' },
{ pattern: "spec/messages.js", type: 'module' },
{ pattern: "spec/receipts.js", type: 'module' },
{ pattern: "spec/muc_messages.js", type: 'module' },
{ pattern: "spec/mentions.js", type: 'module' },
{ pattern: "spec/retractions.js", type: 'module' },

136
package-lock.json generated
View File

@ -5069,9 +5069,9 @@
}
},
"@octokit/types": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.1.2.tgz",
"integrity": "sha512-+zuMnja97vuZmWa+HdUY+0KB9MLwcEHueSSyKu0G/HqZaFYCVdLpBkavb0xyDlH7eoBdvAvSX/+Y8+4FOEZkrQ==",
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.4.0.tgz",
"integrity": "sha512-D/uotqF69M50OIlwMqgyIg9PuLT2daOiBAYF0P40I2ekFA2ESwwBY5dxZe/UhXdPvIbNKDzuZmQrO7rMpuFbcg==",
"dev": true,
"requires": {
"@types/node": ">= 8"
@ -5532,9 +5532,9 @@
}
},
"abab": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
"integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg=="
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz",
"integrity": "sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ=="
},
"abbrev": {
"version": "1.1.1",
@ -7297,23 +7297,29 @@
"dev": true
},
"compare-func": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.4.tgz",
"integrity": "sha512-sq2sWtrqKPkEXAC8tEJA1+BqAH9GbFkGBtUOqrUX57VSfwp8xyktctk+uLoRy5eccTdxzDcVIztlYDpKs3Jv1Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
"integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
"dev": true,
"requires": {
"array-ify": "^1.0.0",
"dot-prop": "^3.0.0"
"dot-prop": "^5.1.0"
},
"dependencies": {
"dot-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz",
"integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
"integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
"dev": true,
"requires": {
"is-obj": "^1.0.0"
"is-obj": "^2.0.0"
}
},
"is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"dev": true
}
}
},
@ -7483,12 +7489,12 @@
"dev": true
},
"conventional-changelog-angular": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.10.tgz",
"integrity": "sha512-k7RPPRs0vp8+BtPsM9uDxRl6KcgqtCJmzRD1wRtgqmhQ96g8ifBGo9O/TZBG23jqlXS/rg8BKRDELxfnQQGiaA==",
"version": "5.0.11",
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz",
"integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==",
"dev": true,
"requires": {
"compare-func": "^1.3.1",
"compare-func": "^2.0.0",
"q": "^1.5.1"
}
},
@ -7538,12 +7544,12 @@
"dev": true
},
"conventional-changelog-writer": {
"version": "4.0.16",
"resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.16.tgz",
"integrity": "sha512-jmU1sDJDZpm/dkuFxBeRXvyNcJQeKhGtVcFFkwTphUAzyYWcwz2j36Wcv+Mv2hU3tpvLMkysOPXJTLO55AUrYQ==",
"version": "4.0.17",
"resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz",
"integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==",
"dev": true,
"requires": {
"compare-func": "^1.3.1",
"compare-func": "^2.0.0",
"conventional-commits-filter": "^2.0.6",
"dateformat": "^3.0.0",
"handlebars": "^4.7.6",
@ -8883,9 +8889,9 @@
}
},
"duplexer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
"dev": true
},
"duplexify": {
@ -11635,9 +11641,9 @@
}
},
"git-up": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.1.tgz",
"integrity": "sha512-LFTZZrBlrCrGCG07/dm1aCjjpL1z9L3+5aEeI9SBhAqSc+kiA9Or1bgZhQFNppJX6h/f5McrvJt1mQXTFm6Qrw==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.2.tgz",
"integrity": "sha512-kbuvus1dWQB2sSW4cbfTeGpCMd8ge9jx9RKnhXhuJ7tnvT+NIrTVfYZxjtflZddQYcmdOTlkAcjmx7bor+15AQ==",
"dev": true,
"requires": {
"is-ssh": "^1.3.0",
@ -11645,9 +11651,9 @@
}
},
"git-url-parse": {
"version": "11.1.2",
"resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.2.tgz",
"integrity": "sha512-gZeLVGY8QVKMIkckncX+iCq2/L8PlwncvDFKiWkBn9EtCfYDbliRTTp6qzyQ1VMdITUfq7293zDzfpjdiGASSQ==",
"version": "11.1.3",
"resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.3.tgz",
"integrity": "sha512-GPsfwticcu52WQ+eHp0IYkAyaOASgYdtsQDIt4rUp6GbiNt1P9ddrh3O0kQB0eD4UJZszVqNT3+9Zwcg40fywA==",
"dev": true,
"requires": {
"git-up": "^4.0.0"
@ -12873,9 +12879,9 @@
}
},
"is-ssh": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz",
"integrity": "sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.2.tgz",
"integrity": "sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ==",
"dev": true,
"requires": {
"protocols": "^1.1.0"
@ -13735,9 +13741,9 @@
}
},
"localforage": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.8.1.tgz",
"integrity": "sha512-azSSJJfc7h4bVpi0PGi+SmLQKJl2/8NErI+LhJsrORNikMZnhaQ7rv9fHj+ofwgSHrKRlsDCL/639a6nECIKuQ==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz",
"integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==",
"requires": {
"lie": "3.1.1"
}
@ -14112,18 +14118,16 @@
}
},
"meow": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/meow/-/meow-7.0.1.tgz",
"integrity": "sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz",
"integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==",
"dev": true,
"requires": {
"@types/minimist": "^1.2.0",
"arrify": "^2.0.1",
"camelcase": "^6.0.0",
"camelcase-keys": "^6.2.2",
"decamelize-keys": "^1.1.0",
"hard-rejection": "^2.1.0",
"minimist-options": "^4.0.2",
"minimist-options": "4.1.0",
"normalize-package-data": "^2.5.0",
"read-pkg-up": "^7.0.1",
"redent": "^3.0.0",
@ -14132,18 +14136,6 @@
"yargs-parser": "^18.1.3"
},
"dependencies": {
"arrify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
"dev": true
},
"camelcase": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz",
"integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==",
"dev": true
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@ -19385,9 +19377,9 @@
"dev": true
},
"parse-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.1.tgz",
"integrity": "sha512-d7yhga0Oc+PwNXDvQ0Jv1BuWkLVPXcAoQ/WREgd6vNNoKYaW52KI+RdOFjI63wjkmps9yUE8VS4veP+AgpQ/hA==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.2.tgz",
"integrity": "sha512-HSqVz6iuXSiL8C1ku5Gl1Z5cwDd9Wo0q8CoffdAghP6bz8pJa1tcMC+m4N+z6VAS8QdksnIGq1TB6EgR4vPR6w==",
"dev": true,
"requires": {
"is-ssh": "^1.3.0",
@ -19401,9 +19393,9 @@
"dev": true
},
"parse-url": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.1.tgz",
"integrity": "sha512-flNUPP27r3vJpROi0/R3/2efgKkyXqnXwyP1KQ2U0SfFRgdizOdWfvrrvJg1LuOoxs7GQhmxJlq23IpQ/BkByg==",
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.2.tgz",
"integrity": "sha512-Czj+GIit4cdWtxo3ISZCvLiUjErSo0iI3wJ+q9Oi3QuMYTI6OZu+7cewMWZ+C1YAnKhYTk6/TLuhIgCypLthPA==",
"dev": true,
"requires": {
"is-ssh": "^1.3.0",
@ -20435,9 +20427,9 @@
"dev": true
},
"protocols": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz",
"integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==",
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz",
"integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==",
"dev": true
},
"protoduck": {
@ -21281,9 +21273,9 @@
}
},
"rxjs": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz",
"integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==",
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
"integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
@ -22852,8 +22844,8 @@
}
},
"strophe.js": {
"version": "github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f",
"from": "github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f",
"version": "github:strophe/strophejs#ceb5640786dc60de4a13b18c9a263f2ef112874c",
"from": "github:strophe/strophejs#ceb5640786dc60de4a13b18c9a263f2ef112874c",
"requires": {
"abab": "^2.0.3",
"ws": "^7.0.0",
@ -24351,9 +24343,9 @@
"dev": true
},
"windows-release": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.1.tgz",
"integrity": "sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.3.tgz",
"integrity": "sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg==",
"dev": true,
"requires": {
"execa": "^1.0.0"

View File

@ -1188,117 +1188,9 @@ describe("A Chat Message", function () {
done();
}));
it("received may emit a message delivery receipt",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current');
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const msg_id = u.getUniqueId();
const sent_stanzas = [];
spyOn(_converse.connection, 'send').and.callFake(stanza => sent_stanzas.push(stanza));
const msg = $msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': msg_id,
}).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
await _converse.handleMessageStanza(msg);
const sent_messages = sent_stanzas.map(s => _.isElement(s) ? s : s.nodeTree).filter(s => s.nodeName === 'message');
// A chat state message is also included
expect(sent_messages.length).toBe(2);
const receipt = sizzle(`received[xmlns="${Strophe.NS.RECEIPTS}"]`, sent_messages[1]).pop();
expect(Strophe.serialize(receipt)).toBe(`<received id="${msg_id}" xmlns="${Strophe.NS.RECEIPTS}"/>`);
done();
}));
it("carbon received does not emit a message delivery receipt",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current', 1);
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const msg_id = u.getUniqueId();
const view = await mock.openChatBoxFor(_converse, sender_jid);
spyOn(view.model, 'sendReceiptStanza').and.callThrough();
const msg = $msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': u.getUniqueId(),
}).c('received', {'xmlns': 'urn:xmpp:carbons:2'})
.c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
.c('message', {
'xmlns': 'jabber:client',
'from': sender_jid,
'to': _converse.bare_jid+'/another-resource',
'type': 'chat',
'id': msg_id
}).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
await _converse.handleMessageStanza(msg);
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
done();
}));
describe("when sent", function () {
it("can have its delivery acknowledged by a receipt",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current', 1);
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await mock.openChatBoxFor(_converse, contact_jid);
const view = _converse.chatboxviews.get(contact_jid);
const textarea = view.el.querySelector('textarea.chat-textarea');
textarea.value = 'But soft, what light through yonder airlock breaks?';
view.onKeyDown({
target: textarea,
preventDefault: function preventDefault () {},
keyCode: 13 // Enter
});
const chatbox = _converse.chatboxes.get(contact_jid);
expect(chatbox).toBeDefined();
await new Promise(resolve => view.model.messages.once('rendered', resolve));
let msg_obj = chatbox.messages.models[0];
let msg_id = msg_obj.get('msgid');
let msg = $msg({
'from': contact_jid,
'to': _converse.connection.jid,
'id': u.getUniqueId(),
}).c('received', {'id': msg_id, xmlns: Strophe.NS.RECEIPTS}).up().tree();
_converse.connection._dataRecv(mock.createRequest(msg));
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__receipt').length === 1);
// Also handle receipts with type 'chat'. See #1353
spyOn(_converse, 'handleMessageStanza').and.callThrough();
textarea.value = 'Another message';
view.onKeyDown({
target: textarea,
preventDefault: function preventDefault () {},
keyCode: 13 // Enter
});
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_obj = chatbox.messages.models[1];
msg_id = msg_obj.get('msgid');
msg = $msg({
'from': contact_jid,
'type': 'chat',
'to': _converse.connection.jid,
'id': u.getUniqueId(),
}).c('received', {'id': msg_id, xmlns: Strophe.NS.RECEIPTS}).up().tree();
_converse.connection._dataRecv(mock.createRequest(msg));
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__receipt').length === 2);
expect(_converse.handleMessageStanza.calls.count()).toBe(1);
done();
}));
it("will appear inside the chatbox it was sent from",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},

154
spec/receipts.js Normal file
View File

@ -0,0 +1,154 @@
/*global mock, converse */
const { Promise, Strophe, $msg, sizzle, _ } = converse.env;
const u = converse.env.utils;
describe("A delivery receipt", function () {
it("is emitted for a received message which requests it",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current');
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const msg_id = u.getUniqueId();
const sent_stanzas = [];
spyOn(_converse.connection, 'send').and.callFake(stanza => sent_stanzas.push(stanza));
const msg = $msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': msg_id,
}).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
await _converse.handleMessageStanza(msg);
const sent_messages = sent_stanzas.map(s => _.isElement(s) ? s : s.nodeTree).filter(s => s.nodeName === 'message');
// A chat state message is also included
expect(sent_messages.length).toBe(2);
const receipt = sizzle(`received[xmlns="${Strophe.NS.RECEIPTS}"]`, sent_messages[1]).pop();
expect(Strophe.serialize(receipt)).toBe(`<received id="${msg_id}" xmlns="${Strophe.NS.RECEIPTS}"/>`);
done();
}));
it("is not emitted for a carbon message",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current', 1);
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const msg_id = u.getUniqueId();
const view = await mock.openChatBoxFor(_converse, sender_jid);
spyOn(view.model, 'sendReceiptStanza').and.callThrough();
const msg = $msg({
'from': sender_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': u.getUniqueId(),
}).c('received', {'xmlns': 'urn:xmpp:carbons:2'})
.c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
.c('message', {
'xmlns': 'jabber:client',
'from': sender_jid,
'to': _converse.bare_jid+'/another-resource',
'type': 'chat',
'id': msg_id
}).c('body').t('Message!').up()
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
await _converse.handleMessageStanza(msg);
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
done();
}));
it("is not emitted for an archived message",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current', 1);
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const view = await mock.openChatBoxFor(_converse, sender_jid);
spyOn(view.model, 'sendReceiptStanza').and.callThrough();
const stanza = u.toStanza(
`<message xmlns="jabber:client" to="${_converse.jid}">
<result xmlns="urn:xmpp:mam:2" id="9ZWxmXMR8SVor-tC" queryid="f543c5f9-55e7-400e-860a-56baac121e6a">
<forwarded xmlns="urn:xmpp:forward:0">
<delay xmlns="urn:xmpp:delay" stamp="2020-01-10T22:19:30Z"/>
<message xmlns="jabber:client" type="chat" to="${_converse.jid}" from="${sender_jid}" id="id8b6426b4-40fe-4151-941e-4c64e380acb9">
<body>Please confirm receipt</body>
<request xmlns="urn:xmpp:receipts"/>
<origin-id xmlns="urn:xmpp:sid:0" id="id8b6426b4-40fe-4151-941e-4c64e380acb9"/>
</message>
</forwarded>
</result>
</message>`);
spyOn(view.model, 'getDuplicateMessage').and.callThrough();
view.model.handleMAMResult({ 'messages': [stanza] });
let message_attrs;
_converse.api.listen.on('MAMResult', async data => {
message_attrs = await data.messages[0];
});
await u.waitUntil(() => view.model.getDuplicateMessage.calls.count());
expect(message_attrs.is_archived).toBe(true);
expect(message_attrs.is_valid_receipt_request).toBe(false);
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
done();
}));
it("can be received for a sent message",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
async function (done, _converse) {
await mock.waitForRoster(_converse, 'current', 1);
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await mock.openChatBoxFor(_converse, contact_jid);
const view = _converse.chatboxviews.get(contact_jid);
const textarea = view.el.querySelector('textarea.chat-textarea');
textarea.value = 'But soft, what light through yonder airlock breaks?';
view.onKeyDown({
target: textarea,
preventDefault: function preventDefault () {},
keyCode: 13 // Enter
});
const chatbox = _converse.chatboxes.get(contact_jid);
expect(chatbox).toBeDefined();
await new Promise(resolve => view.model.messages.once('rendered', resolve));
let msg_obj = chatbox.messages.models[0];
let msg_id = msg_obj.get('msgid');
let msg = $msg({
'from': contact_jid,
'to': _converse.connection.jid,
'id': u.getUniqueId(),
}).c('received', {'id': msg_id, xmlns: Strophe.NS.RECEIPTS}).up().tree();
_converse.connection._dataRecv(mock.createRequest(msg));
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__receipt').length === 1);
// Also handle receipts with type 'chat'. See #1353
spyOn(_converse, 'handleMessageStanza').and.callThrough();
textarea.value = 'Another message';
view.onKeyDown({
target: textarea,
preventDefault: function preventDefault () {},
keyCode: 13 // Enter
});
await new Promise(resolve => view.model.messages.once('rendered', resolve));
msg_obj = chatbox.messages.models[1];
msg_id = msg_obj.get('msgid');
msg = $msg({
'from': contact_jid,
'type': 'chat',
'to': _converse.connection.jid,
'id': u.getUniqueId(),
}).c('received', {'id': msg_id, xmlns: Strophe.NS.RECEIPTS}).up().tree();
_converse.connection._dataRecv(mock.createRequest(msg));
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg__receipt').length === 2);
expect(_converse.handleMessageStanza.calls.count()).toBe(1);
done();
}));
});

View File

@ -889,7 +889,7 @@ converse.plugins.add('converse-chat', {
handleReceipt (attrs) {
if (attrs.sender === 'them') {
if (attrs.is_receipt_request) {
if (attrs.is_valid_receipt_request) {
this.sendReceiptStanza(attrs.from, attrs.msgid);
} else if (attrs.receipt_id) {
const message = this.messages.findWhere({'msgid': attrs.receipt_id});

View File

@ -1998,7 +1998,7 @@ converse.plugins.add('converse-muc', {
const message = this.getDuplicateMessage(attrs);
if (message) {
return this.updateMessage(message, attrs);
} else if (attrs.is_receipt_request || attrs.is_marker || this.ignorableCSN(attrs)) {
} else if (attrs.is_valid_receipt_request || attrs.is_marker || this.ignorableCSN(attrs)) {
return;
}
if (await this.handleRetraction(attrs) ||

View File

@ -14,9 +14,9 @@
}
},
"abab": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
"integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz",
"integrity": "sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ==",
"dev": true
},
"filesize": {
@ -83,8 +83,8 @@
}
},
"strophe.js": {
"version": "github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f",
"from": "github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f",
"version": "github:strophe/strophejs#ceb5640786dc60de4a13b18c9a263f2ef112874c",
"from": "github:strophe/strophejs#ceb5640786dc60de4a13b18c9a263f2ef112874c",
"dev": true,
"requires": {
"abab": "^2.0.3",

View File

@ -42,6 +42,6 @@
"localforage": "^1.7.3",
"lodash-es": "^4.17.15",
"pluggable.js": "2.0.1",
"strophe.js": "strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f"
"strophe.js": "strophe/strophejs#ceb5640786dc60de4a13b18c9a263f2ef112874c"
}
}

View File

@ -72,7 +72,7 @@ function getEncryptionAttributes (stanza, _converse) {
}
function isReceiptRequest (stanza, attrs) {
function isValidReceiptRequest (stanza, attrs) {
return (
attrs.sender !== 'me' &&
!attrs.is_carbon &&
@ -382,6 +382,7 @@ const st = {
return new StanzaParseError(`Ignoring incoming message intended for a different resource: ${to_jid}`, stanza);
}
const original_stanza = stanza;
let from_jid = stanza.getAttribute('from') || _converse.bare_jid;
if (isCarbon(stanza)) {
if (from_jid === _converse.bare_jid) {
@ -396,7 +397,8 @@ const st = {
}
}
if (st.isArchived(stanza)) {
const is_archived = st.isArchived(stanza);
if (is_archived) {
if (from_jid === _converse.bare_jid) {
const selector = `[xmlns="${Strophe.NS.MAM}"] > forwarded[xmlns="${Strophe.NS.FORWARD}"] > message`;
stanza = sizzle(selector, stanza).pop();
@ -446,7 +448,7 @@ const st = {
* @property { Boolean } is_markable - Can this message be marked with a XEP-0333 chat marker?
* @property { Boolean } is_marker - Is this message a XEP-0333 Chat Marker?
* @property { Boolean } is_only_emojis - Does the message body contain only emojis?
* @property { Boolean } is_receipt_request - Does this message request a XEP-0184 receipt?
* @property { Boolean } is_valid_receipt_request - Does this message request a XEP-0184 receipt (and is not from us or a carbon or archived message)
* @property { Boolean } is_spoiler - Is this a XEP-0382 spoiler message?
* @property { Boolean } is_tombstone - Is this a XEP-0424 tombstone?
* @property { Object } encrypted - XEP-0384 encryption payload attributes
@ -479,18 +481,17 @@ const st = {
* @property { String } to - The recipient JID
* @property { String } type - The type of message
*/
const original_stanza = stanza;
const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop();
const marker = st.getChatMarker(stanza);
const now = (new Date()).toISOString();
let attrs = Object.assign({
contact_jid,
is_archived,
is_headline,
is_server_message,
'body': stanza.querySelector('body')?.textContent?.trim(),
'chat_state': getChatState(stanza),
'from': Strophe.getBareJidFromJid(stanza.getAttribute('from')),
'is_archived': st.isArchived(original_stanza),
'is_carbon': isCarbon(original_stanza),
'is_delayed': !!delay,
'is_markable': !!sizzle(`markable[xmlns="${Strophe.NS.MARKERS}"]`, stanza).length,
@ -527,7 +528,7 @@ const st = {
attrs = Object.assign({
'message': attrs.body || attrs.error, // TODO: Remove and use body and error attributes instead
'is_only_emojis': attrs.body ? u.isOnlyEmojis(attrs.body) : false,
'is_receipt_request': isReceiptRequest(stanza, attrs)
'is_valid_receipt_request': isValidReceiptRequest(stanza, attrs)
}, attrs);
// We prefer to use one of the XEP-0359 unique and stable stanza IDs
@ -581,7 +582,7 @@ const st = {
* @property { Boolean } is_markable - Can this message be marked with a XEP-0333 chat marker?
* @property { Boolean } is_marker - Is this message a XEP-0333 Chat Marker?
* @property { Boolean } is_only_emojis - Does the message body contain only emojis?
* @property { Boolean } is_receipt_request - Does this message request a XEP-0184 receipt?
* @property { Boolean } is_valid_receipt_request - Does this message request a XEP-0184 receipt (and is not from us or a carbon or archived message)
* @property { Boolean } is_spoiler - Is this a XEP-0382 spoiler message?
* @property { Boolean } is_tombstone - Is this a XEP-0424 tombstone?
* @property { Object } encrypted - XEP-0384 encryption payload attributes
@ -654,7 +655,7 @@ const st = {
await api.emojis.initialize();
attrs = Object.assign({
'is_only_emojis': attrs.body ? u.isOnlyEmojis(attrs.body) : false,
'is_receipt_request': isReceiptRequest(stanza, attrs),
'is_valid_receipt_request': isValidReceiptRequest(stanza, attrs),
'message': attrs.body || attrs.error, // TODO: Remove and use body and error attributes instead
'sender': attrs.nick === chatbox.get('nick') ? 'me': 'them',
}, attrs);