diff options
Diffstat (limited to '')
-rw-r--r-- | comm/mail/components/im/modules/chatNotifications.sys.mjs | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/comm/mail/components/im/modules/chatNotifications.sys.mjs b/comm/mail/components/im/modules/chatNotifications.sys.mjs new file mode 100644 index 0000000000..664fe4e5ca --- /dev/null +++ b/comm/mail/components/im/modules/chatNotifications.sys.mjs @@ -0,0 +1,262 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { IMServices } from "resource:///modules/IMServices.sys.mjs"; + +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; +import { PluralForm } from "resource://gre/modules/PluralForm.sys.mjs"; + +import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs"; +import { ChatIcons } from "resource:///modules/chatIcons.sys.mjs"; + +// Time in seconds: it is the minimum time of inactivity +// needed to show the bundled notification. +var kTimeToWaitForMoreMsgs = 3; + +export var Notifications = { + get ellipsis() { + let ellipsis = "[\u2026]"; + + try { + ellipsis = Services.prefs.getComplexValue( + "intl.ellipsis", + Ci.nsIPrefLocalizedString + ).data; + } catch (e) {} + return ellipsis; + }, + + // Holds the first direct message of a bundle while we wait for further + // messages from the same sender to arrive. + _heldMessage: null, + // Number of messages to be bundled in the notification (excluding + // _heldMessage). + _msgCounter: 0, + // Time the last message was received. + _lastMessageTime: 0, + // Sender of the last message. + _lastMessageSender: null, + // timeout Id for the set timeout for showing notification. + _timeoutId: null, + + _showMessageNotification(aMessage, aCounter = 0) { + // We are about to show the notification, so let's play the notification sound. + // We play the sound if the user is away from TB window or even away from chat tab. + let win = Services.wm.getMostRecentWindow("mail:3pane"); + if ( + !Services.focus.activeWindow || + win.document.getElementById("tabmail").currentTabInfo.mode.name != "chat" + ) { + Services.obs.notifyObservers(aMessage, "play-chat-notification-sound"); + } + + // If TB window has focus, there's no need to show the notification.. + if (win && win.document.hasFocus()) { + this._heldMessage = null; + this._msgCounter = 0; + return; + } + + let bundle = Services.strings.createBundle( + "chrome://messenger/locale/chat.properties" + ); + let messageText, icon, name; + let notificationContent = Services.prefs.getIntPref( + "mail.chat.notification_info" + ); + // 0 - show all the info, + // 1 - show only the sender not the message, + // 2 - show no details about the message being notified. + switch (notificationContent) { + case 0: + let parser = new DOMParser(); + let doc = parser.parseFromString(aMessage.displayMessage, "text/html"); + let body = doc.querySelector("body"); + let encoder = Cu.createDocumentEncoder("text/plain"); + encoder.init(doc, "text/plain", 0); + encoder.setNode(body); + messageText = encoder.encodeToString().replace(/\s+/g, " "); + + // Crop the end of the text if needed. + if (messageText.length > 50) { + messageText = messageText.substr(0, 50); + if (aCounter == 0) { + messageText = messageText + this.ellipsis; + } + } + + // If there are more messages being bundled, add the count string. + // ellipsis is a part of bundledMessagePreview so we don't include it here. + if (aCounter > 0) { + let bundledMessage = bundle.formatStringFromName( + "bundledMessagePreview", + [messageText] + ); + messageText = PluralForm.get(aCounter, bundledMessage).replace( + "#1", + aCounter + ); + } + // Falls through + case 1: + // Use the buddy icon if available for the icon of the notification. + let conv = aMessage.conversation; + icon = conv.convIconFilename; + if (!icon && !conv.isChat) { + icon = conv.buddy?.buddyIconFilename; + } + + // Handle third person messages + name = aMessage.alias || aMessage.who; + if (messageText && aMessage.action) { + messageText = name + " " + messageText; + } + // Falls through + case 2: + if (!icon) { + icon = ChatIcons.fallbackUserIconURI; + } + + if (!messageText) { + let bundle = Services.strings.createBundle( + "chrome://messenger/locale/chat.properties" + ); + messageText = bundle.GetStringFromName("messagePreview"); + } + } + + let alert = Cc["@mozilla.org/alert-notification;1"].createInstance( + Ci.nsIAlertNotification + ); + alert.init( + "", // name + icon, + name, // title + messageText, + true // clickable + ); + // Show the notification! + Cc["@mozilla.org/alerts-service;1"] + .getService(Ci.nsIAlertsService) + .showAlert(alert, (subject, topic, data) => { + if (topic != "alertclickcallback") { + return; + } + + // If there is a timeout set, clear it. + clearTimeout(this._timeoutId); + this._heldMessage = null; + this._msgCounter = 0; + this._lastMessageTime = 0; + this._lastMessageSender = null; + // Focus the conversation if the notification is clicked. + let uiConv = IMServices.conversations.getUIConversation( + aMessage.conversation + ); + let mainWindow = Services.wm.getMostRecentWindow("mail:3pane"); + if (mainWindow) { + mainWindow.focus(); + mainWindow.showChatTab(); + mainWindow.chatHandler.focusConversation(uiConv); + } else { + Services.appShell.hiddenDOMWindow.openDialog( + "chrome://messenger/content/messenger.xhtml", + "_blank", + "chrome,dialog=no,all", + null, + { + tabType: "chat", + tabParams: { convType: "focus", conv: uiConv }, + } + ); + } + if (AppConstants.platform == "macosx") { + Cc["@mozilla.org/widget/macdocksupport;1"] + .getService(Ci.nsIMacDockSupport) + .activateApplication(true); + } + }); + + this._heldMessage = null; + this._msgCounter = 0; + }, + + init() { + Services.obs.addObserver(Notifications, "new-otr-verification-request"); + Services.obs.addObserver(Notifications, "new-directed-incoming-message"); + Services.obs.addObserver(Notifications, "alertclickcallback"); + }, + + _notificationPrefName: "mail.chat.show_desktop_notifications", + observe(aSubject, aTopic, aData) { + if (!Services.prefs.getBoolPref(this._notificationPrefName)) { + return; + } + + switch (aTopic) { + case "new-directed-incoming-message": + // If this is the first message, we show the notification and + // store the sender's name. + let sender = aSubject.who || aSubject.alias; + if (this._lastMessageSender == null) { + this._lastMessageSender = sender; + this._lastMessageTime = aSubject.time; + this._showMessageNotification(aSubject); + } else if ( + this._lastMessageSender != sender || + aSubject.time > this._lastMessageTime + kTimeToWaitForMoreMsgs + ) { + // If the sender is not the same as the previous sender or the + // time elapsed since the last message is greater than kTimeToWaitForMoreMsgs, + // we show the held notification and set timeout for the message just arrived. + if (this._heldMessage) { + // if the time for the current message is greater than _lastMessageTime by + // more than kTimeToWaitForMoreMsgs, this will not happen since the notification will + // have already been dispatched. + clearTimeout(this._timeoutId); + this._showMessageNotification(this._heldMessage, this._msgCounter); + } + this._lastMessageSender = sender; + this._lastMessageTime = aSubject.time; + this._showMessageNotification(aSubject); + } else if ( + this._lastMessageSender == sender && + this._lastMessageTime + kTimeToWaitForMoreMsgs >= aSubject.time + ) { + // If the sender is same as the previous sender and the time elapsed since the + // last held message is less than kTimeToWaitForMoreMsgs, we increase the held messages + // counter and update the last message's arrival time. + this._lastMessageTime = aSubject.time; + if (!this._heldMessage) { + this._heldMessage = aSubject; + } else { + this._msgCounter++; + } + + clearTimeout(this._timeoutId); + this._timeoutId = setTimeout(() => { + this._showMessageNotification(this._heldMessage, this._msgCounter); + }, kTimeToWaitForMoreMsgs * 1000); + } + break; + + case "new-otr-verification-request": + // If the Chat tab is not focused, play the sounds and update the icon + // counter, and show the counter in the buddy richlistitem. + let win = Services.wm.getMostRecentWindow("mail:3pane"); + if ( + !Services.focus.activeWindow || + win.document.getElementById("tabmail").currentTabInfo.mode.name != + "chat" + ) { + Services.obs.notifyObservers( + aSubject, + "play-chat-notification-sound" + ); + } + + break; + } + }, +}; |