Bug 1715713 - Prevent showing multiple newmailalert.xhtml notification windows. r=mkmelin
This patch makes two changes: 1. If a newmailalert.xhtml window is already shown, save the folder and show a new notification only after the current notification is closed. 2. Pass new message keys to newmailalert.js. Previously, newmailalert.js receives a root folder and scans all subfolders for NEW messages, which is unnecessary and may incorrectly include old messages. Differential Revision: https://phabricator.services.mozilla.com/D117617 --HG-- extra : amend_source : fa1b6897ed980895dc24d55421cea03dc6fa3c83
This commit is contained in:
Родитель
b972d9ae1b
Коммит
fa1111d359
|
@ -47,6 +47,35 @@
|
|||
return this.lastElementChild;
|
||||
}
|
||||
|
||||
static createFolderSummaryMessage() {
|
||||
let vbox = document.createXULElement("vbox");
|
||||
vbox.setAttribute("class", "folderSummaryMessage");
|
||||
|
||||
let hbox = document.createXULElement("hbox");
|
||||
hbox.setAttribute("class", "folderSummary-message-row");
|
||||
|
||||
let subject = document.createXULElement("label");
|
||||
subject.setAttribute("class", "folderSummary-subject");
|
||||
|
||||
let sender = document.createXULElement("label");
|
||||
sender.setAttribute("class", "folderSummary-sender");
|
||||
sender.setAttribute("crop", "right");
|
||||
|
||||
hbox.appendChild(subject);
|
||||
hbox.appendChild(sender);
|
||||
|
||||
let preview = document.createXULElement("description");
|
||||
preview.setAttribute(
|
||||
"class",
|
||||
"folderSummary-message-row folderSummary-previewText"
|
||||
);
|
||||
preview.setAttribute("crop", "right");
|
||||
|
||||
vbox.appendChild(hbox);
|
||||
vbox.appendChild(preview);
|
||||
return vbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given folder for NEW messages.
|
||||
* @param {nsIMsgFolder} folder - The folder to examine.
|
||||
|
@ -57,35 +86,6 @@
|
|||
* @returns true if the folder knows about messages that should be shown.
|
||||
*/
|
||||
parseFolder(folder, urlListener, outAsync) {
|
||||
function createFolderSummaryMessage() {
|
||||
let vbox = document.createXULElement("vbox");
|
||||
vbox.setAttribute("class", "folderSummaryMessage");
|
||||
|
||||
let hbox = document.createXULElement("hbox");
|
||||
hbox.setAttribute("class", "folderSummary-message-row");
|
||||
|
||||
let subject = document.createXULElement("label");
|
||||
subject.setAttribute("class", "folderSummary-subject");
|
||||
|
||||
let sender = document.createXULElement("label");
|
||||
sender.setAttribute("class", "folderSummary-sender");
|
||||
sender.setAttribute("crop", "right");
|
||||
|
||||
hbox.appendChild(subject);
|
||||
hbox.appendChild(sender);
|
||||
|
||||
let preview = document.createXULElement("description");
|
||||
preview.setAttribute(
|
||||
"class",
|
||||
"folderSummary-message-row folderSummary-previewText"
|
||||
);
|
||||
preview.setAttribute("crop", "right");
|
||||
|
||||
vbox.appendChild(hbox);
|
||||
vbox.appendChild(preview);
|
||||
return vbox;
|
||||
}
|
||||
|
||||
// Skip servers, Trash, Junk folders and newsgroups.
|
||||
if (
|
||||
!folder ||
|
||||
|
@ -190,7 +190,7 @@
|
|||
i + curHdrsInPopup < this.maxMsgHdrsInPopup && i < msgKeys.length;
|
||||
i++
|
||||
) {
|
||||
let msgBox = createFolderSummaryMessage();
|
||||
let msgBox = MozFolderSummary.createFolderSummaryMessage();
|
||||
let msgHdr = msgDatabase.GetMsgHdrForKey(msgKeys[i]);
|
||||
msgBox.addEventListener("click", event => {
|
||||
if (event.button !== 0) {
|
||||
|
@ -244,6 +244,66 @@
|
|||
}
|
||||
return haveMsgsToShow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render NEW messages in a folder.
|
||||
* @param {nsIMsgFolder} folder - A real folder containing new messages.
|
||||
* @param {number[]} msgKeys - The keys of new messages.
|
||||
*/
|
||||
render(folder, msgKeys) {
|
||||
let msgDatabase = folder.msgDatabase;
|
||||
for (let msgKey of msgKeys) {
|
||||
let msgBox = MozFolderSummary.createFolderSummaryMessage();
|
||||
let msgHdr = msgDatabase.GetMsgHdrForKey(msgKey);
|
||||
msgBox.addEventListener("click", event => {
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
MailUtils.displayMessageInFolderTab(msgHdr);
|
||||
});
|
||||
|
||||
if (this.showSubject) {
|
||||
let msgSubject = msgHdr.mime2DecodedSubject;
|
||||
const kMsgFlagHasRe = 0x0010; // MSG_FLAG_HAS_RE
|
||||
if (msgHdr.flags & kMsgFlagHasRe) {
|
||||
msgSubject = msgSubject ? "Re: " + msgSubject : "Re: ";
|
||||
}
|
||||
msgBox.querySelector(
|
||||
".folderSummary-subject"
|
||||
).textContent = msgSubject;
|
||||
}
|
||||
|
||||
if (this.showSender) {
|
||||
let addrs = MailServices.headerParser.parseEncodedHeader(
|
||||
msgHdr.author,
|
||||
msgHdr.effectiveCharset,
|
||||
false
|
||||
);
|
||||
let folderSummarySender = msgBox.querySelector(
|
||||
".folderSummary-sender"
|
||||
);
|
||||
// Set the label value instead of textContent to avoid wrapping.
|
||||
folderSummarySender.value =
|
||||
addrs.length > 0 ? addrs[0].name || addrs[0].email : "";
|
||||
if (addrs.length > 1) {
|
||||
let andOthersStr = this.messengerBundle.GetStringFromName(
|
||||
"andOthers"
|
||||
);
|
||||
folderSummarySender.value += " " + andOthersStr;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.showPreview) {
|
||||
// Get the preview text as a UTF-8 encoded string.
|
||||
msgBox.querySelector(
|
||||
".folderSummary-previewText"
|
||||
).textContent = decodeURIComponent(
|
||||
escape(msgHdr.getStringProperty("preview") || "")
|
||||
);
|
||||
}
|
||||
this.appendChild(msgBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("folder-summary", MozFolderSummary);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ var NS_ALERT_TOP = 4;
|
|||
var gNumNewMsgsToShowInAlert = 6;
|
||||
var gOpenTime = 4000; // total time the alert should stay up once we are done animating.
|
||||
|
||||
var gPendingPreviewFetchRequests = 0;
|
||||
var gAlertListener = null;
|
||||
var gOrigin = 0; // Default value: alert from bottom right.
|
||||
var gDragService = Cc["@mozilla.org/widget/dragservice;1"].getService(
|
||||
Ci.nsIDragService
|
||||
|
@ -22,70 +22,29 @@ var gDragService = Cc["@mozilla.org/widget/dragservice;1"].getService(
|
|||
|
||||
function prefillAlertInfo() {
|
||||
// unwrap all the args....
|
||||
// arguments[0] --> The nsIMsgFolder with new mail
|
||||
var rootFolder = window.arguments[0];
|
||||
// arguments[0] --> The real nsIMsgFolder with new mail.
|
||||
// arguments[1] --> The keys of new messages.
|
||||
// arguments[2] --> The nsIObserver to receive window closed event.
|
||||
let [folder, newMsgKeys, listener] = window.arguments;
|
||||
newMsgKeys = newMsgKeys.wrappedJSObject;
|
||||
gAlertListener = listener.QueryInterface(Ci.nsIObserver);
|
||||
|
||||
// Generate an account label string based on the root folder.
|
||||
var label = document.getElementById("alertTitle");
|
||||
var totalNumNewMessages = rootFolder.getNumNewMessages(true);
|
||||
var totalNumNewMessages = newMsgKeys.length;
|
||||
let message = document
|
||||
.getElementById("bundle_messenger")
|
||||
.getString("newMailAlert_message");
|
||||
label.value = PluralForm.get(totalNumNewMessages, message)
|
||||
.replace("#1", rootFolder.prettyName)
|
||||
.replace("#1", folder.server.rootFolder.prettyName)
|
||||
.replace("#2", totalNumNewMessages);
|
||||
|
||||
// This is really the root folder and we have to walk through the list to
|
||||
// find the real folder that has new mail in it...:(
|
||||
// <folder-summary> handles rendering of new messages.
|
||||
var folderSummaryInfoEl = document.getElementById("folderSummaryInfo");
|
||||
folderSummaryInfoEl.maxMsgHdrsInPopup = gNumNewMsgsToShowInAlert;
|
||||
for (let folder of rootFolder.descendants) {
|
||||
if (folder.hasNewMessages) {
|
||||
let notify =
|
||||
// Any folder which is an inbox or ...
|
||||
folder.getFlag(Ci.nsMsgFolderFlags.Inbox) ||
|
||||
// any non-special or non-virtual folder. In other words, we don't
|
||||
// notify for Drafts|Trash|SentMail|Templates|Junk|Archive|Queue or virtual.
|
||||
!(
|
||||
folder.flags &
|
||||
(Ci.nsMsgFolderFlags.SpecialUse | Ci.nsMsgFolderFlags.Virtual)
|
||||
);
|
||||
|
||||
if (notify) {
|
||||
var asyncFetch = {};
|
||||
folderSummaryInfoEl.parseFolder(
|
||||
folder,
|
||||
new urlListener(folder),
|
||||
asyncFetch
|
||||
);
|
||||
if (asyncFetch.value) {
|
||||
gPendingPreviewFetchRequests++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
folderSummaryInfoEl.render(folder, newMsgKeys);
|
||||
}
|
||||
|
||||
function urlListener(aFolder) {
|
||||
this.mFolder = aFolder;
|
||||
}
|
||||
|
||||
urlListener.prototype = {
|
||||
OnStartRunningUrl(aUrl) {},
|
||||
|
||||
OnStopRunningUrl(aUrl, aExitCode) {
|
||||
let folderSummaryInfoEl = document.getElementById("folderSummaryInfo");
|
||||
folderSummaryInfoEl.parseFolder(this.mFolder, null, {});
|
||||
gPendingPreviewFetchRequests--;
|
||||
|
||||
// when we are done running all of our urls for fetching the preview text,
|
||||
// start the alert.
|
||||
if (!gPendingPreviewFetchRequests) {
|
||||
showAlert();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function onAlertLoad() {
|
||||
let dragSession = gDragService.getCurrentSession();
|
||||
if (dragSession && dragSession.sourceNode) {
|
||||
|
@ -106,13 +65,9 @@ function doOnAlertLoad() {
|
|||
// bogus call to make sure the window is moved offscreen until we are ready for it.
|
||||
resizeAlert(true);
|
||||
|
||||
// if we aren't waiting to fetch preview text, then go ahead and
|
||||
// start showing the alert.
|
||||
if (!gPendingPreviewFetchRequests) {
|
||||
// Let the JS thread unwind, to give layout
|
||||
// a chance to recompute the styles and widths for our alert text.
|
||||
setTimeout(showAlert, 0);
|
||||
}
|
||||
// Let the JS thread unwind, to give layout
|
||||
// a chance to recompute the styles and widths for our alert text.
|
||||
setTimeout(showAlert, 0);
|
||||
}
|
||||
|
||||
// If the user initiated the alert, show it right away, otherwise start opening the alert with
|
||||
|
@ -186,4 +141,5 @@ function fadeOutAlert() {
|
|||
|
||||
function closeAlert() {
|
||||
window.close();
|
||||
gAlertListener.observe(null, "newmailalert-closed", "");
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@ class MailNotificationManager {
|
|||
this._systemAlertAvailable = true;
|
||||
this._unreadChatCount = 0;
|
||||
this._unreadMailCount = 0;
|
||||
// @type {Map<nsIMsgFolder, number>} - A map of folder and its last biff time.
|
||||
this._folderBiffTime = new Map();
|
||||
// @type {Set<nsIMsgFolder>} - A set of folders to show alert for.
|
||||
this._pendingFolders = new Set();
|
||||
|
||||
this._logger = console.createInstance({
|
||||
prefix: "mail.notification",
|
||||
|
@ -76,22 +80,34 @@ class MailNotificationManager {
|
|||
}
|
||||
|
||||
observe(subject, topic, data) {
|
||||
if (topic == "alertclickcallback") {
|
||||
// Display the associated message when an alert is clicked.
|
||||
let msgHdr = Cc["@mozilla.org/messenger;1"]
|
||||
.getService(Ci.nsIMessenger)
|
||||
.msgHdrFromURI(data);
|
||||
MailUtils.displayMessageInFolderTab(msgHdr);
|
||||
} else if (topic == "unread-im-count-changed") {
|
||||
this._logger.log(`Unread chat count changed to ${this._unreadChatCount}`);
|
||||
this._unreadChatCount = parseInt(data, 10) || 0;
|
||||
this._updateUnreadCount();
|
||||
} else if (topic == "new-directed-incoming-messenger") {
|
||||
this._animateDockIcon();
|
||||
} else if (topic == "window-restored-from-tray") {
|
||||
this._updateUnreadCount();
|
||||
} else if (topic == "profile-before-change") {
|
||||
this._osIntegration?.onExit();
|
||||
switch (topic) {
|
||||
case "alertclickcallback":
|
||||
// Display the associated message when an alert is clicked.
|
||||
let msgHdr = Cc["@mozilla.org/messenger;1"]
|
||||
.getService(Ci.nsIMessenger)
|
||||
.msgHdrFromURI(data);
|
||||
MailUtils.displayMessageInFolderTab(msgHdr);
|
||||
return;
|
||||
case "unread-im-count-changed":
|
||||
this._logger.log(
|
||||
`Unread chat count changed to ${this._unreadChatCount}`
|
||||
);
|
||||
this._unreadChatCount = parseInt(data, 10) || 0;
|
||||
this._updateUnreadCount();
|
||||
return;
|
||||
case "new-directed-incoming-messenger":
|
||||
this._animateDockIcon();
|
||||
return;
|
||||
case "window-restored-from-tray":
|
||||
this._updateUnreadCount();
|
||||
return;
|
||||
case "profile-before-change":
|
||||
this._osIntegration?.onExit();
|
||||
return;
|
||||
case "newmailalert-closed":
|
||||
// newmailalert.xhtml is closed, try to show the next queued folder.
|
||||
this._customizedAlertShown = false;
|
||||
this._showCustomizedAlert();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,15 +141,17 @@ class MailNotificationManager {
|
|||
`OnItemIntPropertyChanged; property=${property}: ${oldValue} => ${newValue}, folder.URI=${folder.URI}`
|
||||
);
|
||||
|
||||
if (
|
||||
property == "BiffState" &&
|
||||
newValue == Ci.nsIMsgFolder.nsMsgBiffState_NewMail
|
||||
) {
|
||||
// The folder argument is a root folder.
|
||||
this._fillAlertInfo(folder);
|
||||
} else if (property == "NewMailReceived") {
|
||||
// The folder argument is a real folder.
|
||||
this._fillAlertInfo(folder);
|
||||
switch (property) {
|
||||
case "BiffState":
|
||||
if (newValue == Ci.nsIMsgFolder.nsMsgBiffState_NewMail) {
|
||||
// The folder argument is a root folder.
|
||||
this._fillAlertInfo(folder);
|
||||
}
|
||||
break;
|
||||
case "NewMailReceived":
|
||||
// The folder argument is a real folder.
|
||||
this._fillAlertInfo(folder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,13 +343,67 @@ class MailNotificationManager {
|
|||
|
||||
// The use_system_alert pref is false or showAlert somehow failed, use the
|
||||
// customized alert window.
|
||||
this._showCustomizedAlert(folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a customized alert window (newmailalert.xhtml), if there is already
|
||||
* one showing, do not show another one, because the newer one will block the
|
||||
* older one. Instead, save the folder and newMsgKeys to this._pendingFolders.
|
||||
* @param {nsIMsgFolder} [folder] - The folder containing new messages.
|
||||
*/
|
||||
_showCustomizedAlert(folder) {
|
||||
let newMsgKeys;
|
||||
if (folder) {
|
||||
// Show this folder or queue it.
|
||||
newMsgKeys = folder.msgDatabase
|
||||
.getNewList()
|
||||
.slice(-folder.getNumNewMessages(false));
|
||||
if (this._customizedAlertShown) {
|
||||
this._pendingFolders.add(folder);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Get the next folder from the queue.
|
||||
folder = this._pendingFolders.keys().next().value;
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
let msgDb = folder.msgDatabase;
|
||||
let lastBiffTime = this._folderBiffTime.get(folder) || 0;
|
||||
newMsgKeys = msgDb.getNewList().filter(key => {
|
||||
// It's possible that after we queued the folder, user has opened the
|
||||
// folder and read some new messages. We compare the timestamp of each
|
||||
// NEW message with the last biff time to make sure only real NEW
|
||||
// messages are alerted.
|
||||
let msgHdr = msgDb.GetMsgHdrForKey(key);
|
||||
return msgHdr.dateInSeconds * 1000 > lastBiffTime;
|
||||
});
|
||||
|
||||
this._pendingFolders.delete(folder);
|
||||
}
|
||||
if (newMsgKeys.length == 0) {
|
||||
// No NEW message in the current folder, try the next queued folder.
|
||||
this._showCustomizedAlert();
|
||||
return;
|
||||
}
|
||||
|
||||
this._folderBiffTime.set(folder, Date.now());
|
||||
|
||||
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
args.appendElement(folder);
|
||||
args.appendElement({
|
||||
wrappedJSObject: newMsgKeys,
|
||||
});
|
||||
args.appendElement(this);
|
||||
Services.ww.openWindow(
|
||||
null,
|
||||
"chrome://messenger/content/newmailalert.xhtml",
|
||||
"_blank",
|
||||
"chrome,dialog=yes,titlebar=no,popup=yes",
|
||||
folder.server.rootFolder
|
||||
args
|
||||
);
|
||||
this._customizedAlertShown = true;
|
||||
}
|
||||
|
||||
async _updateUnreadCount() {
|
||||
|
|
Загрузка…
Ссылка в новой задаче