From fce337e352a576a86fa4294a6dc914da69e636bc Mon Sep 17 00:00:00 2001 From: Ariel Fuggini Date: Tue, 25 Aug 2020 17:02:53 -0500 Subject: [PATCH] New configuration setting: notify_nicknames_without_references --- CHANGES.md | 1 + docs/source/configuration.rst | 10 +++++++ spec/notification.js | 38 +++++++++++++++++------ src/converse-notification.js | 48 ++++++++++++++++-------------- src/headless/converse-chat.js | 2 +- src/headless/converse-headlines.js | 2 +- src/headless/converse-muc.js | 2 +- 7 files changed, 68 insertions(+), 35 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c1871864d..bd6284a77 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -50,6 +50,7 @@ Soon we'll deprecate the latter, so prepare now. - New config option [modtools_disable_query](https://conversejs.org/docs/html/configuration.html#modtools-disable-query) - New config option [muc_hats_from_vcard](https://conversejs.org/docs/html/configuration.html#muc-hats-from-vcard). - New config option [muc_send_probes](https://conversejs.org/docs/html/configuration.html#muc-send-probes). +- New config option [notify_nicknames_without_references](https://conversejs.org/docs/html/configuration.html#notify-nicknames-without-references). - New config option [show_message_avatar](https://conversejs.org/docs/html/configuration.html#show-message-avatar). - New public API [converse.insertInto](https://conversejs.org/docs/html/api/converse.html#.insertInto) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index a2b68deb6..ae1baf66d 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -1366,6 +1366,16 @@ notification_icon This option specifies which icon is shown in HTML5 notifications, as provided by the ``src/converse-notification.js`` plugin. +notify_nicknames_without_references +----------------------------------- + +* Default: ``false`` + +Enables notifications for nicknames in messages that don't have associated +XEP-0372 references linking them to the JID of the person being mentioned. + +In Converse, these would be nicknames that weren't mentioned via the ``@`` sign. + oauth_providers --------------- diff --git a/spec/notification.js b/spec/notification.js index 7e1a18afd..fc6c1ac41 100644 --- a/spec/notification.js +++ b/spec/notification.js @@ -56,19 +56,38 @@ describe("Notifications", function () { spyOn(_converse, 'showMessageNotification').and.callThrough(); spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true); - const message = 'romeo: This message will show a desktop notification'; - const nick = mock.chatroom_names[0], - msg = $msg({ - from: 'lounge@montague.lit/'+nick, - id: u.getUniqueId(), - to: 'romeo@montague.lit', - type: 'groupchat' - }).c('body').t(message).tree(); - _converse.connection._dataRecv(mock.createRequest(msg)); + // Test mention with setting false + const nick = mock.chatroom_names[0]; + const makeMsg = text => $msg({ + from: 'lounge@montague.lit/'+nick, + id: u.getUniqueId(), + to: 'romeo@montague.lit', + type: 'groupchat' + }).c('body').t(text).tree(); + _converse.connection._dataRecv(mock.createRequest(makeMsg('romeo: this will NOT show a notification'))); await new Promise(resolve => view.model.messages.once('rendered', resolve)); + await u.waitUntil(() => _converse.areDesktopNotificationsEnabled.calls.count() === 0); + expect(_converse.showMessageNotification).not.toHaveBeenCalled(); + // Test reference + const message_with_ref = $msg({ + from: 'lounge@montague.lit/'+nick, + id: u.getUniqueId(), + to: 'romeo@montague.lit', + type: 'groupchat' + }).c('body').t('romeo: this will show a notification').up() + .c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'0', 'end':'5', 'type':'mention', 'uri':'xmpp:romeo@montague.lit'}).tree(); + _converse.connection._dataRecv(mock.createRequest(message_with_ref)); + await new Promise(resolve => view.model.messages.once('rendered', resolve)); await u.waitUntil(() => _converse.areDesktopNotificationsEnabled.calls.count() === 1); expect(_converse.showMessageNotification).toHaveBeenCalled(); + + // Test mention with setting true + _converse.api.settings.set('notify_all_room_messages', true); + _converse.connection._dataRecv(mock.createRequest(makeMsg('romeo: this will show a notification'))); + await new Promise(resolve => view.model.messages.once('rendered', resolve)); + await u.waitUntil(() => _converse.areDesktopNotificationsEnabled.calls.count() === 2); + expect(_converse.showMessageNotification).toHaveBeenCalled(); if (no_notification) { delete window.Notification; } @@ -172,6 +191,7 @@ describe("Notifications", function () { to: 'romeo@montague.lit', type: 'groupchat' }).c('body').t(text); + _converse.api.settings.set('notify_all_room_messages', true); await view.model.handleMessageStanza(message.nodeTree); await u.waitUntil(() => _converse.playSoundNotification.calls.count()); expect(_converse.playSoundNotification).toHaveBeenCalled(); diff --git a/src/converse-notification.js b/src/converse-notification.js index 5787f076a..a190b4051 100644 --- a/src/converse-notification.js +++ b/src/converse-notification.js @@ -32,32 +32,34 @@ converse.plugins.add('converse-notification', { play_sounds: true, sounds_path: api.settings.get("assets_path")+'/sounds/', notification_icon: 'logo/conversejs-filled.svg', - notification_delay: 5000 + notification_delay: 5000, + notify_nicknames_without_references: false }); - _converse.shouldNotifyOfGroupMessage = function (message) { + _converse.shouldNotifyOfGroupMessage = function (message, data) { /* Is this a group message worthy of notification? */ - let notify_all = api.settings.get('notify_all_room_messages'); - const jid = message.getAttribute('from'), - resource = Strophe.getResourceFromJid(jid), - room_jid = Strophe.getBareJidFromJid(jid), - sender = resource && Strophe.unescapeNode(resource) || ''; - if (sender === '' || message.querySelectorAll('delay').length > 0) { - return false; - } + const jid = message.getAttribute('from'); + const room_jid = Strophe.getBareJidFromJid(jid); + const notify_all = api.settings.get('notify_all_room_messages'); const room = _converse.chatboxes.get(room_jid); - const body = message.querySelector('body'); - if (body === null) { - return false; + const resource = Strophe.getResourceFromJid(jid); + const sender = resource && Strophe.unescapeNode(resource) || ''; + let is_mentioned = false; + const nick = room.get('nick'); + + if (api.settings.get('notify_nicknames_without_references')) { + const body = message.querySelector('body'); + is_mentioned = (new RegExp(`\\b${nick}\\b`)).test(body.textContent); } - const mentioned = (new RegExp(`\\b${room.get('nick')}\\b`)).test(body.textContent); - notify_all = notify_all === true || - (Array.isArray(notify_all) && notify_all.includes(room_jid)); - if (sender === room.get('nick') || (!notify_all && !mentioned)) { - return false; - } - return true; + + const is_referenced = data.attrs.references.map(r => r.value).includes(nick); + const is_not_mine = sender !== nick; + const should_notify_user = notify_all === true + || (Array.isArray(notify_all) && notify_all.includes(room_jid)) + || is_referenced + || is_mentioned; + return is_not_mine && !!should_notify_user; }; _converse.isMessageToHiddenChat = function (message) { @@ -72,12 +74,12 @@ converse.plugins.add('converse-notification', { return _converse.windowState === 'hidden'; } - _converse.shouldNotifyOfMessage = function (message) { + _converse.shouldNotifyOfMessage = function (message, data) { const forwarded = message.querySelector('forwarded'); if (forwarded !== null) { return false; } else if (message.getAttribute('type') === 'groupchat') { - return _converse.shouldNotifyOfGroupMessage(message); + return _converse.shouldNotifyOfGroupMessage(message, data); } else if (st.isHeadline(message)) { // We want to show notifications for headline messages. return _converse.isMessageToHiddenChat(message); @@ -251,7 +253,7 @@ converse.plugins.add('converse-notification', { * to play sounds and show HTML5 notifications. */ const message = data.stanza; - if (!_converse.shouldNotifyOfMessage(message)) { + if (!_converse.shouldNotifyOfMessage(message, data)) { return false; } /** diff --git a/src/headless/converse-chat.js b/src/headless/converse-chat.js index 95716cc67..6089b0a2b 100644 --- a/src/headless/converse-chat.js +++ b/src/headless/converse-chat.js @@ -1223,7 +1223,7 @@ converse.plugins.add('converse-chat', { * @property { XMLElement } stanza * @example _converse.api.listen.on('message', obj => { ... }); */ - api.trigger('message', {'stanza': stanza}); + api.trigger('message', {stanza, attrs}); } diff --git a/src/headless/converse-headlines.js b/src/headless/converse-headlines.js index 6aa4180bf..306f24fc0 100644 --- a/src/headless/converse-headlines.js +++ b/src/headless/converse-headlines.js @@ -98,7 +98,7 @@ converse.plugins.add('converse-headlines', { }); const attrs = await st.parseMessage(stanza, _converse); await chatbox.createMessage(attrs); - api.trigger('message', {'chatbox': chatbox, 'stanza': stanza}); + api.trigger('message', {chatbox, stanza, attrs}); } } diff --git a/src/headless/converse-muc.js b/src/headless/converse-muc.js index 8dc782051..e31cbbbf0 100644 --- a/src/headless/converse-muc.js +++ b/src/headless/converse-muc.js @@ -669,10 +669,10 @@ converse.plugins.add('converse-muc', { // they shouldn't have a `type` attribute. return log.warn(`Received a MAM message with type "groupchat"`); } - api.trigger('message', {'stanza': stanza}); this.createInfoMessages(stanza); this.fetchFeaturesIfConfigurationChanged(stanza); const attrs = await st.parseMUCMessage(stanza, this, _converse); + api.trigger('message', {stanza, attrs}); return attrs && this.queueMessage(attrs); },