Bug 1551590 - OTR: improve notifications when a chat starts with a verification request. r=kaie

This commit is contained in:
Alessandro Castellani 2020-03-10 22:53:51 +02:00
Родитель 87e443fdb0
Коммит d45e4b377e
11 изменённых файлов: 204 добавлений и 80 удалений

Просмотреть файл

@ -18,6 +18,11 @@ interface imIConversation: prplIConversation {
// Note: this will not be logged. // Note: this will not be logged.
void systemMessage(in AUTF8String aMessage, [optional] in boolean aIsError); void systemMessage(in AUTF8String aMessage, [optional] in boolean aIsError);
// Write a system message into the conversation and trigger the udpate of the
// notification counter during an off-the-record authentication request.
// Note: this will not be logged.
void notificationOTR(in AUTF8String aMessage);
attribute prplIConversation target; attribute prplIConversation target;
// Number of unread messages (all messages, including system // Number of unread messages (all messages, including system
@ -29,6 +34,8 @@ interface imIConversation: prplIConversation {
// Number of unread incoming messages (both targeted and untargeted // Number of unread incoming messages (both targeted and untargeted
// messages are counted). // messages are counted).
readonly attribute unsigned long unreadIncomingMessageCount; readonly attribute unsigned long unreadIncomingMessageCount;
// Number of unread off-the-record authentication requests.
readonly attribute unsigned long unreadOTRNotificationCount;
// Reset all unread message counts. // Reset all unread message counts.
void markAsRead(); void markAsRead();

Просмотреть файл

@ -249,10 +249,15 @@ UIConversation.prototype = {
get unreadIncomingMessageCount() { get unreadIncomingMessageCount() {
return this._unreadIncomingMessageCount; return this._unreadIncomingMessageCount;
}, },
_unreadOTRNotificationCount: 0,
get unreadOTRNotificationCount() {
return this._unreadOTRNotificationCount;
},
markAsRead() { markAsRead() {
delete this._unreadMessageCount; delete this._unreadMessageCount;
delete this._unreadTargetedMessageCount; delete this._unreadTargetedMessageCount;
delete this._unreadIncomingMessageCount; delete this._unreadIncomingMessageCount;
delete this._unreadOTRNotificationCount;
this._notifyUnreadCountChanged(); this._notifyUnreadCountChanged();
}, },
_lastNotifiedUnreadCount: 0, _lastNotifiedUnreadCount: 0,
@ -445,6 +450,18 @@ UIConversation.prototype = {
new Message("system", aText, flags).conversation = this; new Message("system", aText, flags).conversation = this;
}, },
notificationOTR(aText) {
this._unreadOTRNotificationCount++;
this.systemMessage(aText);
for (let observer of this._observers) {
observer.observe(
this,
"unread-message-count-changed",
this._unreadOTRNotificationCount.toString()
);
}
},
// prplIConversation // prplIConversation
get isChat() { get isChat() {
return this.target.isChat; return this.target.isChat;

Просмотреть файл

@ -169,6 +169,13 @@ var otrAuth = {
if (mode === "ask") { if (mode === "ask") {
let context = OTR.getContext(uiConv.target); let context = OTR.getContext(uiConv.target);
OTR.abortSMP(context); OTR.abortSMP(context);
// Close the ask-auth notification if it was previously triggered.
OTR.notifyObservers(
{
context,
},
"otr:cancel-ask-auth"
);
} }
}, },

Просмотреть файл

@ -61,6 +61,10 @@ state-unverified-label = Unverified
state-private-label = Private state-private-label = Private
state-finished-label = Finished state-finished-label = Finished
# Variables:
# $name (String) - the screen name of a chat contact person
verify-request = { $name } requested the verification of your identity.
# Variables: # Variables:
# $name (String) - the screen name of a chat contact person # $name (String) - the screen name of a chat contact person
afterauth-private = You have verified the identity of { $name }. afterauth-private = You have verified the identity of { $name }.

Просмотреть файл

@ -257,6 +257,7 @@ var OTRUI = {
} }
this.enabled = true; this.enabled = true;
this.notificationbox = null;
OTR.addObserver(OTRUI); OTR.addObserver(OTRUI);
OTR.loadFiles() OTR.loadFiles()
@ -479,9 +480,52 @@ var OTRUI = {
return; return;
} }
let window = this.globalDoc.defaultView;
let name = uiConv.target.normalizedName; let name = uiConv.target.normalizedName;
OTRUI.openAuth(window, name, "ask", uiConv, aObject); let msg = _strArgs("verify-request", { name });
// Trigger the udpate of the unread message counter.
uiConv.notificationOTR(msg);
Services.obs.notifyObservers(uiConv, "new-otr-verification-request");
// Trigger the inline notification.
let window = this.globalDoc.defaultView;
let buttons = [
{
label: _str("finger-verify"),
accessKey: _str("finger-verify-accessKey"),
callback() {
OTRUI.openAuth(window, name, "ask", uiConv, aObject);
// prevent closing of notification bar when the button is hit
return true;
},
},
];
let mainWindow = Services.wm.getMostRecentWindow("mail:3pane");
this.notificationbox = mainWindow.chatHandler.msgNotificationBar;
let priority = this.globalBox.PRIORITY_WARNING_MEDIUM;
this.notificationbox.appendNotification(
msg,
name,
null,
priority,
buttons,
null
);
},
closeAskAuthNotification(aObject) {
if (!this.notificationbox) {
return;
}
let name = aObject.context.username;
let notification = this.notificationbox.getNotificationWithValue(name);
if (!notification) {
return;
}
this.notificationbox.removeNotification(notification);
}, },
closeUnverified(context) { closeUnverified(context) {
@ -630,7 +674,7 @@ var OTRUI = {
} }
}, },
notifyVerification(context, key, cancelable) { notifyVerification(context, key, cancelable, verifiable) {
let uiConv = OTR.getUIConvFromContext(context); let uiConv = OTR.getUIConvFromContext(context);
if (!uiConv) { if (!uiConv) {
return; return;
@ -656,6 +700,23 @@ var OTRUI = {
]; ];
} }
if (verifiable) {
let window = this.globalDoc.defaultView;
buttons = [
{
label: _str("finger-verify"),
accessKey: _str("finger-verify-accessKey"),
callback() {
let name = uiConv.target.normalizedName;
OTRUI.openAuth(window, name, "start", uiConv);
// prevent closing of notification bar when the button is hit
return true;
},
},
];
}
// higher priority to overlay the current notifyUnverified // higher priority to overlay the current notifyUnverified
let priority = this.globalBox.PRIORITY_WARNING_HIGH; let priority = this.globalBox.PRIORITY_WARNING_HIGH;
OTRUI.closeUnverified(context); OTRUI.closeUnverified(context);
@ -675,15 +736,17 @@ var OTRUI = {
// let uiConv = OTR.getUIConvFromContext(aObj.context); // let uiConv = OTR.getUIConvFromContext(aObj.context);
if (!aObj.progress) { if (!aObj.progress) {
OTRUI.closeAuth(aObj.context); OTRUI.closeAuth(aObj.context);
OTRUI.notifyVerification(aObj.context, "otr:auth-error", false); OTRUI.notifyVerification(aObj.context, "otr:auth-error", false, false);
} else if (aObj.progress === 100) { } else if (aObj.progress === 100) {
let key; let key;
let verifiable = false;
if (aObj.success) { if (aObj.success) {
if (aObj.context.trust) { if (aObj.context.trust) {
key = "otr:auth-success"; key = "otr:auth-success";
OTR.notifyTrust(aObj.context); OTR.notifyTrust(aObj.context);
} else { } else {
key = "otr:auth-successThem"; key = "otr:auth-successThem";
verifiable = true;
} }
} else { } else {
key = "otr:auth-fail"; key = "otr:auth-fail";
@ -691,12 +754,13 @@ var OTRUI = {
OTR.notifyTrust(aObj.context); OTR.notifyTrust(aObj.context);
} }
} }
OTRUI.notifyVerification(aObj.context, key, false); OTRUI.notifyVerification(aObj.context, key, false, verifiable);
} else { } else {
// TODO: show the aObj.progress to the user with a // TODO: show the aObj.progress to the user with a
// <progressmeter mode="determined" value="10" /> // <progressmeter mode="determined" value="10" />
OTRUI.notifyVerification(aObj.context, "otr:auth-waiting", true); OTRUI.notifyVerification(aObj.context, "otr:auth-waiting", true, false);
} }
OTRUI.closeAskAuthNotification(aObj);
}, },
onAccountCreated(acc) { onAccountCreated(acc) {
@ -829,6 +893,9 @@ var OTRUI = {
case "otr:auth-update": case "otr:auth-update":
OTRUI.updateAuth(aObject); OTRUI.updateAuth(aObject);
break; break;
case "otr:cancel-ask-auth":
OTRUI.closeAskAuthNotification(aObject);
break;
} }
}, },

Просмотреть файл

@ -82,8 +82,7 @@
</stack> </stack>
</hbox> </hbox>
<hbox class="otr-container" <hbox class="otr-container"
align="start" align="middle"
valign="middle"
hidden="true"> hidden="true">
<label class="otr-label" <label class="otr-label"
crop="end" crop="end"

Просмотреть файл

@ -173,15 +173,11 @@
this.setAttribute("flex", "1"); this.setAttribute("flex", "1");
this.classList.add("convBox"); this.classList.add("convBox");
this.convTop = document.createXULElement("hbox"); this.convTop = document.createXULElement("vbox");
this.convTop.setAttribute("flex", "1"); this.convTop.setAttribute("flex", "1");
this.convTop.classList.add("conv-top"); this.convTop.classList.add("conv-top");
this.notification = document.createXULElement("hbox"); this.notification = document.createXULElement("vbox");
this.notification.setAttribute("flex", "1");
let nbox = document.createXULElement("vbox");
nbox.setAttribute("flex", "1");
this.convBrowser = document.createXULElement("browser", { this.convBrowser = document.createXULElement("browser", {
is: "conversation-browser", is: "conversation-browser",
@ -198,11 +194,10 @@
this.findbar = document.createXULElement("findbar"); this.findbar = document.createXULElement("findbar");
this.findbar.setAttribute("reversed", "true"); this.findbar.setAttribute("reversed", "true");
nbox.appendChild(this.convBrowser);
nbox.appendChild(this.progressBar);
nbox.appendChild(this.findbar);
this.notification.appendChild(nbox);
this.convTop.appendChild(this.notification); this.convTop.appendChild(this.notification);
this.convTop.appendChild(this.convBrowser);
this.convTop.appendChild(this.progressBar);
this.convTop.appendChild(this.findbar);
this.splitter = document.createXULElement("splitter"); this.splitter = document.createXULElement("splitter");
this.splitter.setAttribute("orient", "vertical"); this.splitter.setAttribute("orient", "vertical");
@ -295,15 +290,15 @@
} }
get msgNotificationBar() { get msgNotificationBar() {
delete this.msgNotificationBar; delete this._msgNotificationBar;
let newNotificationBox = new MozElements.NotificationBox(element => { let newNotificationBox = new MozElements.NotificationBox(element => {
element.setAttribute("flex", "1"); element.setAttribute("flex", "1");
element.setAttribute("notificationside", "top"); element.setAttribute("notificationside", "top");
this.notification.append(element); this.notification.prepend(element);
}); });
return (this.msgNotificationBar = newNotificationBox); return (this._msgNotificationBar = newNotificationBox);
} }
destroy() { destroy() {

Просмотреть файл

@ -170,7 +170,9 @@
this.removeAttribute("unread"); this.removeAttribute("unread");
this.removeAttribute("attention"); this.removeAttribute("attention");
} else { } else {
let unreadCount = this.conv.unreadIncomingMessageCount; let unreadCount =
this.conv.unreadIncomingMessageCount +
this.conv.unreadOTRNotificationCount;
let directedMessages = unreadCount; let directedMessages = unreadCount;
if (unreadCount) { if (unreadCount) {
this.setAttribute("unread", "true"); this.setAttribute("unread", "true");

Просмотреть файл

@ -407,14 +407,14 @@ var chatTabType = {
var chatHandler = { var chatHandler = {
get msgNotificationBar() { get msgNotificationBar() {
delete this.msgNotificationBar; delete this._msgNotificationBar;
let newNotificationBox = new MozElements.NotificationBox(element => { let newNotificationBox = new MozElements.NotificationBox(element => {
element.setAttribute("notificationside", "top"); element.setAttribute("notificationside", "top");
document.getElementById("chat-notification-top").prepend(element); document.getElementById("chat-notification-top").prepend(element);
}); });
return (this.msgNotificationBar = newNotificationBox); return (this._msgNotificationBar = newNotificationBox);
}, },
_addConversation(aConv) { _addConversation(aConv) {
@ -476,26 +476,32 @@ var chatHandler = {
return; return;
} }
let [unreadTargettedCount, unreadTotalCount] = this.countUnreadMessages(); let [
chatButton.badgeCount = unreadTargettedCount; unreadTargettedCount,
unreadTotalCount,
unreadOTRNotificationCount,
] = this.countUnreadMessages();
let unreadMsgAndNotificationCount =
unreadTargettedCount + unreadOTRNotificationCount;
chatButton.badgeCount = unreadMsgAndNotificationCount;
if (unreadTotalCount) { if (unreadTotalCount || unreadOTRNotificationCount) {
chatButton.setAttribute("unreadMessages", "true"); chatButton.setAttribute("unreadMessages", "true");
} else { } else {
chatButton.removeAttribute("unreadMessages"); chatButton.removeAttribute("unreadMessages");
} }
if (unreadTargettedCount != this._notifiedUnreadCount) { if (unreadMsgAndNotificationCount != this._notifiedUnreadCount) {
let unreadInt = Cc["@mozilla.org/supports-PRInt32;1"].createInstance( let unreadInt = Cc["@mozilla.org/supports-PRInt32;1"].createInstance(
Ci.nsISupportsPRInt32 Ci.nsISupportsPRInt32
); );
unreadInt.data = unreadTargettedCount; unreadInt.data = unreadMsgAndNotificationCount;
Services.obs.notifyObservers( Services.obs.notifyObservers(
unreadInt, unreadInt,
"unread-im-count-changed", "unread-im-count-changed",
unreadTargettedCount unreadMsgAndNotificationCount
); );
this._notifiedUnreadCount = unreadTargettedCount; this._notifiedUnreadCount = unreadMsgAndNotificationCount;
} }
}, },
@ -503,11 +509,13 @@ var chatHandler = {
let convs = imServices.conversations.getUIConversations(); let convs = imServices.conversations.getUIConversations();
let unreadTargettedCount = 0; let unreadTargettedCount = 0;
let unreadTotalCount = 0; let unreadTotalCount = 0;
let unreadOTRNotificationCount = 0;
for (let conv of convs) { for (let conv of convs) {
unreadTargettedCount += conv.unreadTargetedMessageCount; unreadTargettedCount += conv.unreadTargetedMessageCount;
unreadTotalCount += conv.unreadIncomingMessageCount; unreadTotalCount += conv.unreadIncomingMessageCount;
unreadOTRNotificationCount += conv.unreadOTRNotificationCount;
} }
return [unreadTargettedCount, unreadTotalCount]; return [unreadTargettedCount, unreadTotalCount, unreadOTRNotificationCount];
}, },
updateTitle() { updateTitle() {

Просмотреть файл

@ -45,13 +45,13 @@ var gAccountManager = {
_connectedLabelInterval: 0, _connectedLabelInterval: 0,
get msgNotificationBar() { get msgNotificationBar() {
delete this.msgNotificationBar; delete this._msgNotificationBar;
let newNotificationBox = new MozElements.NotificationBox(element => { let newNotificationBox = new MozElements.NotificationBox(element => {
document.getElementById("accounts-notification-box").prepend(element); document.getElementById("accounts-notification-box").prepend(element);
}); });
return (this.msgNotificationBar = newNotificationBox); return (this._msgNotificationBar = newNotificationBox);
}, },
load() { load() {

Просмотреть файл

@ -189,62 +189,80 @@ var Notifications = {
}, },
init() { init() {
Services.obs.addObserver(Notifications, "new-otr-verification-request");
Services.obs.addObserver(Notifications, "new-directed-incoming-message"); Services.obs.addObserver(Notifications, "new-directed-incoming-message");
Services.obs.addObserver(Notifications, "alertclickcallback"); Services.obs.addObserver(Notifications, "alertclickcallback");
}, },
_notificationPrefName: "mail.chat.show_desktop_notifications", _notificationPrefName: "mail.chat.show_desktop_notifications",
observe(aSubject, aTopic, aData) { observe(aSubject, aTopic, aData) {
if ( if (!Services.prefs.getBoolPref(this._notificationPrefName)) {
aTopic == "new-directed-incoming-message" && return;
Services.prefs.getBoolPref(this._notificationPrefName) }
) {
// If this is the first message, we show the notification and switch (aTopic) {
// store the sender's name. case "new-directed-incoming-message":
let sender = aSubject.who || aSubject.alias; // If this is the first message, we show the notification and
if (this._lastMessageSender == null) { // store the sender's name.
this._lastMessageSender = sender; let sender = aSubject.who || aSubject.alias;
this._lastMessageTime = aSubject.time; if (this._lastMessageSender == null) {
this._showMessageNotification(aSubject); this._lastMessageSender = sender;
} else if ( this._lastMessageTime = aSubject.time;
this._lastMessageSender != sender || this._showMessageNotification(aSubject);
aSubject.time > this._lastMessageTime + kTimeToWaitForMoreMsgs } else if (
) { this._lastMessageSender != sender ||
// If the sender is not the same as the previous sender or the aSubject.time > this._lastMessageTime + kTimeToWaitForMoreMsgs
// time elapsed since the last message is greater than kTimeToWaitForMoreMsgs, ) {
// we show the held notification and set timeout for the message just arrived. // If the sender is not the same as the previous sender or the
if (this._heldMessage) { // time elapsed since the last message is greater than kTimeToWaitForMoreMsgs,
// if the time for the current message is greater than _lastMessageTime by // we show the held notification and set timeout for the message just arrived.
// more than kTimeToWaitForMoreMsgs, this will not happen since the notification will if (this._heldMessage) {
// have already been dispatched. // 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); clearTimeout(this._timeoutId);
this._showMessageNotification(this._heldMessage, this._msgCounter); this._timeoutId = setTimeout(() => {
this._showMessageNotification(this._heldMessage, this._msgCounter);
}, kTimeToWaitForMoreMsgs * 1000);
} }
this._lastMessageSender = sender; break;
this._lastMessageTime = aSubject.time;
this._showMessageNotification(aSubject); case "new-otr-verification-request":
} else if ( // If the Chat tab is not focused, play the sounds and update the icon
this._lastMessageSender == sender && // counter, and show the counter in the buddy richlistitem.
this._lastMessageTime + kTimeToWaitForMoreMsgs >= aSubject.time let win = Services.wm.getMostRecentWindow("mail:3pane");
) { if (
// If the sender is same as the previous sender and the time elapsed since the !Services.focus.activeWindow ||
// last held message is less than kTimeToWaitForMoreMsgs, we increase the held messages win.document.getElementById("tabmail").currentTabInfo.mode.name !=
// counter and update the last message's arrival time. "chat"
this._lastMessageTime = aSubject.time; ) {
if (!this._heldMessage) { Services.obs.notifyObservers(
this._heldMessage = aSubject; aSubject,
} else { "play-chat-notification-sound"
this._msgCounter++; );
} }
clearTimeout(this._timeoutId); break;
this._timeoutId = setTimeout(function() {
Notifications._showMessageNotification(
Notifications._heldMessage,
Notifications._msgCounter
);
}, kTimeToWaitForMoreMsgs * 1000);
}
} }
}, },
}; };