|
@@ -1,337 +0,0 @@
|
|
|
-/**
|
|
|
- * @module converse-notification
|
|
|
- * @copyright 2020, the Converse.js contributors
|
|
|
- * @license Mozilla Public License (MPLv2)
|
|
|
- */
|
|
|
-import Favico from 'favico.js-slevomat';
|
|
|
-import log from "@converse/headless/log";
|
|
|
-import { __ } from '../i18n';
|
|
|
-import { _converse, api, converse } from "@converse/headless/core";
|
|
|
-
|
|
|
-const { Strophe } = converse.env;
|
|
|
-const u = converse.env.utils;
|
|
|
-
|
|
|
-const supports_html5_notification = "Notification" in window;
|
|
|
-
|
|
|
-converse.env.Favico = Favico;
|
|
|
-let favicon;
|
|
|
-
|
|
|
-function updateUnreadFavicon () {
|
|
|
- if (api.settings.get('show_tab_notifications')) {
|
|
|
- favicon = favicon ?? new converse.env.Favico({type: 'circle', animation: 'pop'});
|
|
|
- const chats = _converse.chatboxes.models;
|
|
|
- const num_unread = chats.reduce((acc, chat) => (acc + (chat.get('num_unread') || 0)), 0);
|
|
|
- favicon.badge(num_unread);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-converse.plugins.add('converse-notification', {
|
|
|
-
|
|
|
- dependencies: ["converse-chatboxes"],
|
|
|
-
|
|
|
- initialize () {
|
|
|
- /* The initialize function gets called as soon as the plugin is
|
|
|
- * loaded by converse.js's plugin machinery.
|
|
|
- */
|
|
|
-
|
|
|
- api.settings.extend({
|
|
|
- // ^ a list of JIDs to ignore concerning chat state notifications
|
|
|
- chatstate_notification_blacklist: [],
|
|
|
- notification_delay: 5000,
|
|
|
- notification_icon: 'logo/conversejs-filled.svg',
|
|
|
- notify_all_room_messages: false,
|
|
|
- notify_nicknames_without_references: false,
|
|
|
- play_sounds: true,
|
|
|
- show_chat_state_notifications: false,
|
|
|
- show_desktop_notifications: true,
|
|
|
- show_tab_notifications: true,
|
|
|
- sounds_path: api.settings.get("assets_path")+'/sounds/',
|
|
|
- });
|
|
|
-
|
|
|
- /**
|
|
|
- * Is this a group message for which we should notify the user?
|
|
|
- * @private
|
|
|
- * @method _converse#shouldNotifyOfGroupMessage
|
|
|
- * @param { MUCMessageAttributes } attrs
|
|
|
- */
|
|
|
- _converse.shouldNotifyOfGroupMessage = function (attrs) {
|
|
|
- if (!attrs?.body) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- const jid = attrs.from;
|
|
|
- const muc_jid = attrs.from_muc;
|
|
|
- const notify_all = api.settings.get('notify_all_room_messages');
|
|
|
- const room = _converse.chatboxes.get(muc_jid);
|
|
|
- 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')) {
|
|
|
- is_mentioned = (new RegExp(`\\b${nick}\\b`)).test(attrs.body);
|
|
|
- }
|
|
|
-
|
|
|
- const references_me = (r) => {
|
|
|
- const jid = r.uri.replace(/^xmpp:/, '');
|
|
|
- return jid == _converse.bare_jid || jid === `${muc_jid}/${nick}`;
|
|
|
- }
|
|
|
- const is_referenced = attrs.references.reduce((acc, r) => acc || references_me(r), false);
|
|
|
- const is_not_mine = sender !== nick;
|
|
|
- const should_notify_user = notify_all === true
|
|
|
- || (Array.isArray(notify_all) && notify_all.includes(muc_jid))
|
|
|
- || is_referenced
|
|
|
- || is_mentioned;
|
|
|
- return is_not_mine && !!should_notify_user;
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * Given parsed attributes for a message stanza, get the related
|
|
|
- * chatbox and check whether it's hidden.
|
|
|
- * @private
|
|
|
- * @method _converse#isMessageToHiddenChat
|
|
|
- * @param { MUCMessageAttributes } attrs
|
|
|
- */
|
|
|
- _converse.isMessageToHiddenChat = function (attrs) {
|
|
|
- return _converse.chatboxes.get(attrs.from)?.isHidden() ?? false;
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * @private
|
|
|
- * @method _converse#shouldNotifyOfMessage
|
|
|
- * @param { MessageData|MUCMessageData } data
|
|
|
- */
|
|
|
- _converse.shouldNotifyOfMessage = function (data) {
|
|
|
- const { attrs, stanza } = data;
|
|
|
- if (!attrs || stanza.querySelector('forwarded') !== null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (attrs['type'] === 'groupchat') {
|
|
|
- return _converse.shouldNotifyOfGroupMessage(attrs);
|
|
|
- } else if (attrs.is_headline) {
|
|
|
- // We want to show notifications for headline messages.
|
|
|
- return _converse.isMessageToHiddenChat(attrs);
|
|
|
- }
|
|
|
- const is_me = Strophe.getBareJidFromJid(attrs.from) === _converse.bare_jid;
|
|
|
- return !u.isOnlyChatStateNotification(stanza) &&
|
|
|
- !u.isOnlyMessageDeliveryReceipt(stanza) &&
|
|
|
- !is_me &&
|
|
|
- (api.settings.get('show_desktop_notifications') === 'all' || _converse.isMessageToHiddenChat(attrs));
|
|
|
- };
|
|
|
-
|
|
|
-
|
|
|
- /**
|
|
|
- * Plays a notification sound
|
|
|
- * @private
|
|
|
- * @method _converse#playSoundNotification
|
|
|
- */
|
|
|
- _converse.playSoundNotification = function () {
|
|
|
- if (api.settings.get('play_sounds') && window.Audio !== undefined) {
|
|
|
- const audioOgg = new Audio(api.settings.get('sounds_path')+"msg_received.ogg");
|
|
|
- const canPlayOgg = audioOgg.canPlayType('audio/ogg');
|
|
|
- if (canPlayOgg === 'probably') {
|
|
|
- return audioOgg.play();
|
|
|
- }
|
|
|
- const audioMp3 = new Audio(api.settings.get('sounds_path')+"msg_received.mp3");
|
|
|
- const canPlayMp3 = audioMp3.canPlayType('audio/mp3');
|
|
|
- if (canPlayMp3 === 'probably') {
|
|
|
- audioMp3.play();
|
|
|
- } else if (canPlayOgg === 'maybe') {
|
|
|
- audioOgg.play();
|
|
|
- } else if (canPlayMp3 === 'maybe') {
|
|
|
- audioMp3.play();
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- _converse.areDesktopNotificationsEnabled = function () {
|
|
|
- return supports_html5_notification &&
|
|
|
- api.settings.get('show_desktop_notifications') &&
|
|
|
- Notification.permission === "granted";
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * Shows an HTML5 Notification with the passed in message
|
|
|
- * @private
|
|
|
- * @method _converse#showMessageNotification
|
|
|
- * @param { MessageData|MUCMessageData } data
|
|
|
- */
|
|
|
- _converse.showMessageNotification = function (data) {
|
|
|
- const { attrs } = data;
|
|
|
- if (attrs.is_error) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!_converse.areDesktopNotificationsEnabled()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- let title, roster_item;
|
|
|
- const full_from_jid = attrs.from,
|
|
|
- from_jid = Strophe.getBareJidFromJid(full_from_jid);
|
|
|
- if (attrs.type === 'headline') {
|
|
|
- if (!from_jid.includes('@') || api.settings.get("allow_non_roster_messaging")) {
|
|
|
- title = __("Notification from %1$s", from_jid);
|
|
|
- } else {
|
|
|
- return;
|
|
|
- }
|
|
|
- } else if (!from_jid.includes('@')) {
|
|
|
- // workaround for Prosody which doesn't give type "headline"
|
|
|
- title = __("Notification from %1$s", from_jid);
|
|
|
- } else if (attrs.type === 'groupchat') {
|
|
|
- title = __("%1$s says", Strophe.getResourceFromJid(full_from_jid));
|
|
|
- } else {
|
|
|
- if (_converse.roster === undefined) {
|
|
|
- log.error("Could not send notification, because roster is undefined");
|
|
|
- return;
|
|
|
- }
|
|
|
- roster_item = _converse.roster.get(from_jid);
|
|
|
- if (roster_item !== undefined) {
|
|
|
- title = __("%1$s says", roster_item.getDisplayName());
|
|
|
- } else {
|
|
|
- if (api.settings.get("allow_non_roster_messaging")) {
|
|
|
- title = __("%1$s says", from_jid);
|
|
|
- } else {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const body = attrs.is_encrypted ? __('Encrypted message received') : attrs.body;
|
|
|
- if (!body) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const n = new Notification(title, {
|
|
|
- 'body': body,
|
|
|
- 'lang': _converse.locale,
|
|
|
- 'icon': api.settings.get('notification_icon'),
|
|
|
- 'requireInteraction': !_converse.notification_delay
|
|
|
- });
|
|
|
- if (api.settings.get('notification_delay')) {
|
|
|
- setTimeout(n.close.bind(n), api.settings.get('notification_delay'));
|
|
|
- }
|
|
|
- n.onclick = function (event) {
|
|
|
- event.preventDefault();
|
|
|
- window.focus();
|
|
|
- const chat = _converse.chatboxes.get(from_jid);
|
|
|
- chat.maybeShow(true);
|
|
|
- }
|
|
|
- n.onclick.bind(_converse);
|
|
|
- };
|
|
|
-
|
|
|
- _converse.showChatStateNotification = function (contact) {
|
|
|
- /* Creates an HTML5 Notification to inform of a change in a
|
|
|
- * contact's chat state.
|
|
|
- */
|
|
|
- if (_converse.chatstate_notification_blacklist.includes(contact.jid)) {
|
|
|
- // Don't notify if the user is being ignored.
|
|
|
- return;
|
|
|
- }
|
|
|
- const chat_state = contact.chat_status;
|
|
|
- let message = null;
|
|
|
- if (chat_state === 'offline') {
|
|
|
- message = __('has gone offline');
|
|
|
- } else if (chat_state === 'away') {
|
|
|
- message = __('has gone away');
|
|
|
- } else if ((chat_state === 'dnd')) {
|
|
|
- message = __('is busy');
|
|
|
- } else if (chat_state === 'online') {
|
|
|
- message = __('has come online');
|
|
|
- }
|
|
|
- if (message === null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const n = new Notification(contact.getDisplayName(), {
|
|
|
- body: message,
|
|
|
- lang: _converse.locale,
|
|
|
- icon: _converse.notification_icon
|
|
|
- });
|
|
|
- setTimeout(n.close.bind(n), 5000);
|
|
|
- };
|
|
|
-
|
|
|
- _converse.showContactRequestNotification = function (contact) {
|
|
|
- const n = new Notification(contact.getDisplayName(), {
|
|
|
- body: __('wants to be your contact'),
|
|
|
- lang: _converse.locale,
|
|
|
- icon: _converse.notification_icon
|
|
|
- });
|
|
|
- setTimeout(n.close.bind(n), 5000);
|
|
|
- };
|
|
|
-
|
|
|
- _converse.showFeedbackNotification = function (data) {
|
|
|
- if (data.klass === 'error' || data.klass === 'warn') {
|
|
|
- const n = new Notification(data.subject, {
|
|
|
- body: data.message,
|
|
|
- lang: _converse.locale,
|
|
|
- icon: _converse.notification_icon
|
|
|
- });
|
|
|
- setTimeout(n.close.bind(n), 5000);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- _converse.handleChatStateNotification = function (contact) {
|
|
|
- /* Event handler for on('contactPresenceChanged').
|
|
|
- * Will show an HTML5 notification to indicate that the chat
|
|
|
- * status has changed.
|
|
|
- */
|
|
|
- if (_converse.areDesktopNotificationsEnabled() && api.settings.get('show_chat_state_notifications')) {
|
|
|
- _converse.showChatStateNotification(contact);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- _converse.handleMessageNotification = function (data) {
|
|
|
- /* Event handler for the on('message') event. Will call methods
|
|
|
- * to play sounds and show HTML5 notifications.
|
|
|
- */
|
|
|
- if (!_converse.shouldNotifyOfMessage(data)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- /**
|
|
|
- * Triggered when a notification (sound or HTML5 notification) for a new
|
|
|
- * message has will be made.
|
|
|
- * @event _converse#messageNotification
|
|
|
- * @type { MessageData|MUCMessageData}
|
|
|
- * @example _converse.api.listen.on('messageNotification', stanza => { ... });
|
|
|
- */
|
|
|
- api.trigger('messageNotification', data);
|
|
|
- _converse.playSoundNotification();
|
|
|
- _converse.showMessageNotification(data);
|
|
|
- };
|
|
|
-
|
|
|
- _converse.handleContactRequestNotification = function (contact) {
|
|
|
- if (_converse.areDesktopNotificationsEnabled(true)) {
|
|
|
- _converse.showContactRequestNotification(contact);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- _converse.handleFeedback = function (data) {
|
|
|
- if (_converse.areDesktopNotificationsEnabled(true)) {
|
|
|
- _converse.showFeedbackNotification(data);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- _converse.requestPermission = function () {
|
|
|
- if (supports_html5_notification && !['denied', 'granted'].includes(Notification.permission)) {
|
|
|
- // Ask user to enable HTML5 notifications
|
|
|
- Notification.requestPermission();
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- /************************ BEGIN Event Handlers ************************/
|
|
|
-
|
|
|
- api.listen.on('clearSession', () => (favicon = null)); // Needed for tests
|
|
|
-
|
|
|
- api.waitUntil('chatBoxesInitialized').then(
|
|
|
- () => _converse.chatboxes.on('change:num_unread', updateUnreadFavicon));
|
|
|
-
|
|
|
- api.listen.on('pluginsInitialized', function () {
|
|
|
- // We only register event handlers after all plugins are
|
|
|
- // registered, because other plugins might override some of our
|
|
|
- // handlers.
|
|
|
- api.listen.on('contactRequest', _converse.handleContactRequestNotification);
|
|
|
- api.listen.on('contactPresenceChanged', _converse.handleChatStateNotification);
|
|
|
- api.listen.on('message', _converse.handleMessageNotification);
|
|
|
- api.listen.on('feedback', _converse.handleFeedback);
|
|
|
- api.listen.on('connected', _converse.requestPermission);
|
|
|
- });
|
|
|
- }
|
|
|
-});
|