Add support for the XEP-0333 displayed
chat marker
Credit for this work goes to @deleolajide
This commit is contained in:
parent
ac36adddfe
commit
5a57ded243
|
@ -24,6 +24,7 @@ Soon we'll deprecate the latter, so prepare now.
|
|||
- #1999: Demarcate first unread message
|
||||
- #2002: fix rendering of `muc_roomid_policy_hint`
|
||||
- #2006: fix rendering of emojis in case `use_system_emojis == false`
|
||||
- #2028: Implement XEP-0333 `displayed` chat marker
|
||||
- Filter roster contacts via all available information (JID, nickname and VCard full name).
|
||||
- Allow ignoring of bootstrap modules at build using environment variable. For xample: `export BOOTSTRAP_IGNORE_MODULES="Modal,Dropdown" && make dist`
|
||||
- Bugfix. Handle stanza that clears the MUC subject
|
||||
|
|
21
package-lock.json
generated
21
package-lock.json
generated
|
@ -2244,7 +2244,8 @@
|
|||
"dependencies": {
|
||||
"filesize": {
|
||||
"version": "6.1.0",
|
||||
"resolved": false
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
|
||||
"integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "8.1.0",
|
||||
|
@ -2278,7 +2279,8 @@
|
|||
},
|
||||
"jed": {
|
||||
"version": "1.1.1",
|
||||
"resolved": false
|
||||
"resolved": "https://registry.npmjs.org/jed/-/jed-1.1.1.tgz",
|
||||
"integrity": "sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "5.0.0",
|
||||
|
@ -2299,18 +2301,21 @@
|
|||
},
|
||||
"localforage": {
|
||||
"version": "1.7.3",
|
||||
"resolved": false,
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz",
|
||||
"integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==",
|
||||
"requires": {
|
||||
"lie": "3.1.1"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": false
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
},
|
||||
"pluggable.js": {
|
||||
"version": "2.0.1",
|
||||
"resolved": false,
|
||||
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-2.0.1.tgz",
|
||||
"integrity": "sha512-SBt6v6Tbp20Jf8hU0cpcc/+HBHGMY8/Q+yA6Ih0tBQE8tfdZ6U4PRG0iNvUUjLx/hVyOP53n0UfGBymlfaaXCg==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.11"
|
||||
}
|
||||
|
@ -2324,11 +2329,13 @@
|
|||
},
|
||||
"strophe.js": {
|
||||
"version": "1.3.4",
|
||||
"resolved": false
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.3.4.tgz",
|
||||
"integrity": "sha512-jSLDG8jolhAwGOSgiJ7DTMSYK3wVoEJHKtpVRyEacQZ6CWA6z2WRPJpcFMjsIweq5aP9/XIvKUQqHBu/ZhvESA=="
|
||||
},
|
||||
"twemoji": {
|
||||
"version": "12.1.5",
|
||||
"resolved": false,
|
||||
"resolved": "https://registry.npmjs.org/twemoji/-/twemoji-12.1.5.tgz",
|
||||
"integrity": "sha512-B0PBVy5xomwb1M/WZxf/IqPZfnoIYy1skXnlHjMwLwTNfZ9ljh8VgWQktAPcJXu8080WoEh6YwQGPVhDVqvrVQ==",
|
||||
"requires": {
|
||||
"fs-extra": "^8.0.1",
|
||||
"jsonfile": "^5.0.0",
|
||||
|
|
|
@ -1326,13 +1326,19 @@ describe("Chatboxes", function () {
|
|||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit',
|
||||
msg = mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
|
||||
const view = await mock.openChatBoxFor(_converse, sender_jid)
|
||||
spyOn(view.model, 'sendMarker').and.callThrough();
|
||||
view.model.save('scrolled', true);
|
||||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.model.messages.length);
|
||||
expect(view.model.get('num_unread')).toBe(1);
|
||||
const msgid = view.model.messages.last().get('id');
|
||||
expect(view.model.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => view.model.sendMarker.calls.count() === 1);
|
||||
expect(sent_stanzas[0].nodeTree.querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1345,11 +1351,15 @@ describe("Chatboxes", function () {
|
|||
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit',
|
||||
msg = mock.createChatMessage(_converse, sender_jid, 'This message will be read');
|
||||
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
spyOn(chatbox, 'sendMarker').and.callThrough();
|
||||
await _converse.handleMessageStanza(msg);
|
||||
expect(chatbox.get('num_unread')).toBe(0);
|
||||
await u.waitUntil(() => chatbox.sendMarker.calls.count() === 2);
|
||||
expect(sent_stanzas[1].nodeTree.querySelector('displayed')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1363,8 +1373,12 @@ describe("Chatboxes", function () {
|
|||
const msgFactory = function () {
|
||||
return mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
};
|
||||
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
spyOn(chatbox, 'sendMarker').and.callThrough();
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
|
@ -1372,6 +1386,8 @@ describe("Chatboxes", function () {
|
|||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => chatbox.sendMarker.calls.count() === 1);
|
||||
expect(sent_stanzas[0].nodeTree.querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1383,8 +1399,11 @@ describe("Chatboxes", function () {
|
|||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
spyOn(chatbox, 'sendMarker').and.callThrough();
|
||||
chatbox.save('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
|
@ -1393,6 +1412,8 @@ describe("Chatboxes", function () {
|
|||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => chatbox.sendMarker.calls.count() === 1);
|
||||
expect(sent_stanzas[0].nodeTree.querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1404,8 +1425,11 @@ describe("Chatboxes", function () {
|
|||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
spyOn(chatbox, 'sendMarker').and.callThrough();
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
|
@ -1413,8 +1437,12 @@ describe("Chatboxes", function () {
|
|||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => chatbox.sendMarker.calls.count() === 1);
|
||||
expect(sent_stanzas[0].nodeTree.querySelector('received')).toBeDefined();
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
expect(chatbox.get('num_unread')).toBe(0);
|
||||
await u.waitUntil(() => chatbox.sendMarker.calls.count() === 2);
|
||||
expect(sent_stanzas[1].nodeTree.querySelector('displayed')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -1426,8 +1454,11 @@ describe("Chatboxes", function () {
|
|||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
spyOn(chatbox, 'sendMarker').and.callThrough();
|
||||
chatbox.save('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
|
@ -1436,9 +1467,13 @@ describe("Chatboxes", function () {
|
|||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => chatbox.sendMarker.calls.count() === 1);
|
||||
expect(sent_stanzas[0].nodeTree.querySelector('received')).toBeDefined();
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => chatbox.sendMarker.calls.count() === 1);
|
||||
expect(sent_stanzas[0].nodeTree.querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -2010,7 +2010,7 @@ describe("A XEP-0333 Chat Marker", function () {
|
|||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s));
|
||||
spyOn(view.model, 'sendMarker').and.callThrough();
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await u.waitUntil(() => view.model.sendMarker.calls.count() === 1);
|
||||
await u.waitUntil(() => view.model.sendMarker.calls.count() === 2);
|
||||
expect(Strophe.serialize(sent_stanzas[0])).toBe(
|
||||
`<message from="romeo@montague.lit/orchard" `+
|
||||
`id="${sent_stanzas[0].nodeTree.getAttribute('id')}" `+
|
||||
|
@ -2044,7 +2044,7 @@ describe("A XEP-0333 Chat Marker", function () {
|
|||
.map(s => _.isElement(s) ? s : s.nodeTree)
|
||||
.filter(e => e.nodeName === 'message');
|
||||
|
||||
expect(sent_messages.length).toBe(1);
|
||||
expect(sent_messages.length).toBe(2);
|
||||
expect(Strophe.serialize(sent_messages[0])).toBe(
|
||||
`<message id="${sent_messages[0].getAttribute('id')}" to="${contact_jid}" type="chat" xmlns="jabber:client">`+
|
||||
`<active xmlns="http://jabber.org/protocol/chatstates"/>`+
|
||||
|
|
|
@ -441,6 +441,7 @@ window.addEventListener('converse-loaded', () => {
|
|||
id: (new Date()).getTime()
|
||||
})
|
||||
.c('body').t(message).up()
|
||||
.c('markable', {'xmlns': Strophe.NS.MARKERS}).up()
|
||||
.c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
}
|
||||
|
||||
|
|
|
@ -831,12 +831,19 @@ converse.plugins.add('converse-chat', {
|
|||
return _converse.connection.send(msg);
|
||||
},
|
||||
|
||||
sendMarker(to_jid, id, type) {
|
||||
sendMarkerForMessage (msg) {
|
||||
if (msg?.get('is_markable')) {
|
||||
const from_jid = Strophe.getBareJidFromJid(msg.get('from'));
|
||||
this.sendMarker(from_jid, msg.get('msgid'), 'displayed', msg.get('type'));
|
||||
}
|
||||
},
|
||||
|
||||
sendMarker (to_jid, id, type, msg_type) {
|
||||
const stanza = $msg({
|
||||
'from': _converse.connection.jid,
|
||||
'id': u.getUniqueId(),
|
||||
'to': to_jid,
|
||||
'type': 'chat',
|
||||
'type': msg_type ? msg_type : 'chat'
|
||||
}).c(type, {'xmlns': Strophe.NS.MARKERS, 'id': id});
|
||||
api.send(stanza);
|
||||
},
|
||||
|
@ -1141,22 +1148,29 @@ converse.plugins.add('converse-chat', {
|
|||
* @param {_converse.Message} message
|
||||
*/
|
||||
incrementUnreadMsgCounter (message) {
|
||||
if (!message || !message.get('message')) {
|
||||
return;
|
||||
if (!message?.get('body')) {
|
||||
return
|
||||
}
|
||||
if (utils.isNewMessage(message) && this.isHidden()) {
|
||||
const settings = {
|
||||
'num_unread': this.get('num_unread') + 1
|
||||
};
|
||||
if (this.get('num_unread') === 0) {
|
||||
settings['first_unread_id'] = message.get('id');
|
||||
if (utils.isNewMessage(message)) {
|
||||
if (this.isHidden()) {
|
||||
const settings = {
|
||||
'num_unread': this.get('num_unread') + 1
|
||||
};
|
||||
if (this.get('num_unread') === 0) {
|
||||
settings['first_unread_id'] = message.get('id');
|
||||
}
|
||||
this.save(settings);
|
||||
_converse.incrementMsgCounter();
|
||||
} else {
|
||||
this.sendMarkerForMessage(message);
|
||||
}
|
||||
this.save(settings);
|
||||
_converse.incrementMsgCounter();
|
||||
}
|
||||
},
|
||||
|
||||
clearUnreadMsgCounter () {
|
||||
clearUnreadMsgCounter() {
|
||||
if (this.get('num_unread') > 0) {
|
||||
this.sendMarkerForMessage(this.messages.last());
|
||||
}
|
||||
u.safeSave(this, {'num_unread': 0});
|
||||
},
|
||||
|
||||
|
|
|
@ -2404,25 +2404,32 @@ converse.plugins.add('converse-muc', {
|
|||
* @param { XMLElement } - The <messsage> stanza
|
||||
*/
|
||||
incrementUnreadMsgCounter (message) {
|
||||
if (!message) { return; }
|
||||
const body = message.get('message');
|
||||
if (!body) { return; }
|
||||
if (u.isNewMessage(message) && this.isHidden()) {
|
||||
const settings = {
|
||||
'num_unread_general': this.get('num_unread_general') + 1
|
||||
};
|
||||
if (this.get('num_unread') === 0) {
|
||||
settings['first_unread_id'] = message.get('id');
|
||||
if (!message?.get('body')) {
|
||||
return
|
||||
}
|
||||
if (u.isNewMessage(message)) {
|
||||
if (this.isHidden()) {
|
||||
const settings = {
|
||||
'num_unread_general': this.get('num_unread_general') + 1
|
||||
};
|
||||
if (this.get('num_unread_general') === 0) {
|
||||
settings['first_unread_id'] = message.get('id');
|
||||
}
|
||||
if (this.isUserMentioned(message)) {
|
||||
settings.num_unread = this.get('num_unread') + 1;
|
||||
_converse.incrementMsgCounter();
|
||||
}
|
||||
this.save(settings);
|
||||
} else {
|
||||
this.sendMarkerForMessage(message);
|
||||
}
|
||||
if (this.isUserMentioned(message)) {
|
||||
settings.num_unread = this.get('num_unread') + 1;
|
||||
_converse.incrementMsgCounter();
|
||||
}
|
||||
this.save(settings);
|
||||
}
|
||||
},
|
||||
|
||||
clearUnreadMsgCounter() {
|
||||
if (this.get('num_unread_general') > 0 || this.get('num_unread') > 0) {
|
||||
this.sendMarkerForMessage(this.messages.last());
|
||||
}
|
||||
u.safeSave(this, {
|
||||
'num_unread': 0,
|
||||
'num_unread_general': 0
|
||||
|
|
Loading…
Reference in New Issue
Block a user