Bug 763522 - Show presence information in email headers, r+ui-r=bwinton.
authorFlorian Quèze <florian@queze.net>
Thu, 28 Jun 2012 17:54:06 +0200
changeset 10536 98534b8f5aa5
parent 10535 07edf83b2991
child 10537 538abfda8a26
push id7959
push userflorian@queze.net
push dateThu, 28 Jun 2012 15:58:28 +0000
treeherdercomm-central@538abfda8a26 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs763522
Bug 763522 - Show presence information in email headers, r+ui-r=bwinton.
mail/base/content/mailWidgets.xml
mail/base/content/msgHdrViewOverlay.js
mail/components/im/content/chat-messenger-overlay.js
mail/themes/gnomestripe/mail/messageHeader.css
mail/themes/pinstripe/mail/messageHeader.css
mail/themes/qute/mail/messageHeader-aero.css
mail/themes/qute/mail/messageHeader.css
--- a/mail/base/content/mailWidgets.xml
+++ b/mail/base/content/mailWidgets.xml
@@ -970,17 +970,21 @@
                        context="emailAddressPopup" popup="emailAddressPopup"
                        flex="1" role="textbox" aria-readonly="true">
         <xul:label class="emaillabel" anonid="emaillabel"
                    xbl:inherits="value=label,crop"/>
         <xul:image class="emailStar" anonid="emailStar"
                    context="emailAddressPopup"
                    onmousedown="event.preventDefault();"
                    onclick="onClickEmailStar(event, this.parentNode.parentNode);"
-                   xbl:inherits="hascard,tooltiptext=tooltipstar"/>
+                   xbl:inherits="hascard,tooltiptext=tooltipstar,chatStatus"/>
+        <xul:image class="emailPresence" anonid="emailPresence"
+                   onmousedown="event.preventDefault();"
+                   onclick="onClickEmailPresence(event, this.parentNode.parentNode);"
+                   xbl:inherits="chatStatus,tooltiptext=presenceTooltip"/>
       </xul:description>
     </content>
 
     <implementation>
       <property name="label"      onset="this.getPart('emailValue').setAttribute('label',val); return val;"
                                   onget="return this.getPart('emailValue').getAttribute('label');"/>
       <property name="crop"       onset="this.getPart('emailValue').setAttribute('crop',val); return val;"
                                   onget="return this.getPart('emailValue').getAttribute('crop');"/>
--- a/mail/base/content/msgHdrViewOverlay.js
+++ b/mail/base/content/msgHdrViewOverlay.js
@@ -6,16 +6,18 @@
  * Functions related to displaying the headers for a selected message in the
  * message pane.
  */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource:///modules/mailServices.js");
 Components.utils.import("resource:///modules/gloda/utils.js");
+let {Status: statusUtils} =
+  Components.utils.import("resource:///modules/imStatusUtils.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////////
 // Warning: It's critical that the code in here for displaying the message
 // headers for a selected message remain as fast as possible. In particular,
 // right now, we only introduce one reflow per message. i.e. if you click on
 // a message in the thread pane, we batch up all the changes for displaying
 // the header pane (to, cc, attachements button, etc.) and we make a single
 // pass to display them. It's critical that we maintain this one reflow per
@@ -1268,16 +1270,57 @@ function UpdateEmailNodeDetails(aEmailAd
     aDocumentNode.setAttribute("tooltipstar",
       document.getElementById("addToAddressBookItem").label);
   } else {
     aDocumentNode.setAttribute("hascard", "true");
     aDocumentNode.setAttribute("tooltipstar",
       document.getElementById("editContactItem").label);
   }
 
+  let chatAddresses = [aEmailAddress];
+  let card = cardDetails.card;
+  if (card) {
+    let gTalk = card.getProperty("_GoogleTalk", null);
+    if (gTalk)
+      chatAddresses.push(gTalk);
+    let jid = card.getProperty("_JabberId", null);
+    if (jid)
+      chatAddresses.push(jid);
+  }
+  let chatContact;
+  let onlineContacts = "chatHandler" in window ? chatHandler.onlineContacts : {};
+  for each (let chatAddress in chatAddresses) {
+    if (Object.prototype.hasOwnProperty.call(onlineContacts, chatAddresses)) {
+      chatContact = onlineContacts[chatAddress];
+      break;
+    }
+  }
+  if (aDocumentNode.chatContact) {
+    aDocumentNode.chatContact.removeObserver(aDocumentNode.chatContactObserver);
+    delete aDocumentNode.chatContact;
+    delete aDocumentNode.chatContactObserver;
+  }
+  if (chatContact) {
+    aDocumentNode.chatContact = chatContact;
+    aDocumentNode.chatContactObserver = function(aSubject, aTopic, aData) {
+      if (aTopic == "contact-removed") {
+        this.chatContact.removeObserver(this.chatContactObserver);
+        delete this.chatContact;
+        delete this.chatContactObserver;
+        this.removeAttribute("chatStatus");
+        this.removeAttribute("presenceTooltip");
+      }
+      else if (aTopic == "contact-status-changed") {
+        UpdateEmailPresenceDetails(this, this.chatContact);
+      }
+    }.bind(aDocumentNode);
+    chatContact.addObserver(aDocumentNode.chatContactObserver);
+  }
+  UpdateEmailPresenceDetails(aDocumentNode, chatContact);
+
   // When we are adding cards, we don't want to move the display around if the
   // user has clicked on the star, therefore if it is locked, just exit and
   // leave the display updates until later.
   if (aDocumentNode.hasAttribute("updatingUI"))
     return;
 
   var displayName = FormatDisplayName(aEmailAddress,
                                       aDocumentNode.getAttribute("displayName"),
@@ -1289,16 +1332,39 @@ function UpdateEmailNodeDetails(aEmailAd
     aDocumentNode.setAttribute("tooltiptext", aEmailAddress);
   } else {
     aDocumentNode.setAttribute("label",
       aDocumentNode.getAttribute("fullAddress") ||
       aDocumentNode.getAttribute("displayName"));
   }
 }
 
+function UpdateEmailPresenceDetails(aDocumentNode, aChatContact) {
+  if (!aChatContact) {
+    aDocumentNode.removeAttribute("chatStatus");
+    aDocumentNode.removeAttribute("presenceTooltip");
+    return;
+  }
+
+  let statusType = aChatContact.statusType;
+  if (statusType < Ci.imIStatusInfo.STATUS_IDLE)
+    aDocumentNode.removeAttribute("chatStatus");
+  else if (statusType == Ci.imIStatusInfo.STATUS_AVAILABLE)
+    aDocumentNode.setAttribute("chatStatus", "available");
+  else
+    aDocumentNode.setAttribute("chatStatus", "away");
+
+  let tooltipText = aChatContact.preferredBuddy.protocol.name + "\n" +
+                    statusUtils.toLabel(aChatContact.statusType);
+  let statusText = aChatContact.statusText;
+  if (statusText)
+    tooltipText += " - " + statusText;
+  aDocumentNode.setAttribute("presenceTooltip", tooltipText);
+}
+
 function UpdateExtraAddressProcessing(aAddressData, aDocumentNode, aAction,
                                       aParentDir, aItem)
 {
   switch (aAction) {
   case nsIAbListener.itemChanged:
     if (aAddressData &&
         aDocumentNode.cardDetails.card &&
         aItem.hasEmailAddress(aAddressData.emailAddress)) {
@@ -1447,16 +1513,28 @@ function onClickEmailStar(event, emailAd
   if (emailAddressNode && emailAddressNode.cardDetails &&
       emailAddressNode.cardDetails.card) {
     EditContact(emailAddressNode);
   } else {
     AddContact(emailAddressNode);
   }
 }
 
+function onClickEmailPresence(event, emailAddressNode)
+{
+  // Only care about left-click events
+  if (event.button != 0)
+    return;
+
+  showChatTab();
+  let prplConv = emailAddressNode.chatContact.createConversation();
+  let uiConv = Services.conversations.getUIConversation(prplConv);
+  chatHandler.focusConversation(uiConv);
+}
+
 /**
  * Takes the email address node, adds a new contact from the node's
  * displayName and emailAddress attributes to the personal address book.
  *
  * @param emailAddressNode  a node with displayName and emailAddress attributes
  */
 function AddContact(emailAddressNode)
 {
--- a/mail/components/im/content/chat-messenger-overlay.js
+++ b/mail/components/im/content/chat-messenger-overlay.js
@@ -632,16 +632,17 @@ var chatHandler = {
   },
   addBuddy: function() {
      this._openDialog("addbuddy");
   },
   joinChat: function() {
     this._openDialog("joinchat");
   },
 
+  onlineContacts: {},
   _colorCache: {},
   // Duplicated code from imconversation.xml :-(
   _computeColor: function(aName) {
     if (Object.prototype.hasOwnProperty.call(this._colorCache, aName))
       return this._colorCache[aName];
 
     // Compute the color based on the nick
     var nick = aName.match(/[a-zA-Z0-9]+/);
@@ -810,21 +811,23 @@ var chatHandler = {
         aTopic == "account-added" || aTopic == "account-removed") {
       this._updateNoConvPlaceHolder();
       return;
     }
 
     if (aTopic == "contact-signed-on") {
       document.getElementById("onlinecontactsGroup").addContact(aSubject);
       document.getElementById("offlinecontactsGroup").removeContact(aSubject);
+      this.onlineContacts[aSubject.preferredBuddy.normalizedName] = aSubject;
       return;
     }
     if (aTopic == "contact-signed-off") {
       document.getElementById("offlinecontactsGroup").addContact(aSubject);
       document.getElementById("onlinecontactsGroup").removeContact(aSubject);
+      delete this.onlineContacts[aSubject.preferredBuddy.normalizedName];
       return;
     }
     if (aTopic == "contact-added") {
       let groupName = (aSubject.online ? "on" : "off") + "linecontactsGroup";
       document.getElementById(groupName).addContact(aSubject);
       return;
     }
     if (aTopic == "contact-removed") {
--- a/mail/themes/gnomestripe/mail/messageHeader.css
+++ b/mail/themes/gnomestripe/mail/messageHeader.css
@@ -532,16 +532,35 @@ mail-emailaddress[selected="true"] .emai
 .emailStar:hover:active {
   -moz-image-region:rect(0px 48px 16px 32px);
 }
 
 .emailStar[hascard="true"] {
   list-style-image: url("chrome://messenger/skin/contactStarred.png");
 }
 
+.emailPresence {
+  padding: 0px;
+  -moz-margin-start: -2px !important;
+  -moz-image-region: rect(6px, 16px, 16px, 6px);
+}
+
+.emailStar[chatStatus],
+.emailPresence:not([chatStatus]) {
+  display: none;
+}
+
+.emailPresence[chatStatus="available"] {
+  list-style-image: url("chrome://chat/skin/available.png");
+}
+
+.emailPresence[chatStatus="away"] {
+  list-style-image: url("chrome://chat/skin/away.png");
+}
+
 .emailPopup {
   font-size: inherit;
   max-height: 7px; /* the height of the image */
   margin: 0.2em 2px 0px; /* 0.2em just to move it off the top of the text */
   list-style-image: url("chrome://messenger/skin/icons/arrow-dn-grey.png");
 }
 
 .emailPopup:hover {
--- a/mail/themes/pinstripe/mail/messageHeader.css
+++ b/mail/themes/pinstripe/mail/messageHeader.css
@@ -633,16 +633,35 @@ mail-emailaddress[selected="true"] .emai
 .emailStar:hover[hascard="true"] {
   -moz-image-region: rect(16px, 32px, 32px, 16px);
 }
 
 .emailStar:hover:active[hascard="true"] {
   -moz-image-region: rect(16px, 48px, 32px, 32px);
 }
 
+.emailPresence {
+  padding: 0px;
+  margin: 0px 2px;
+  -moz-image-region: rect(6px, 16px, 16px, 6px);
+}
+
+.emailStar[chatStatus],
+.emailPresence:not([chatStatus]) {
+  display: none;
+}
+
+.emailPresence[chatStatus="available"] {
+  list-style-image: url("chrome://chat/skin/available.png");
+}
+
+.emailPresence[chatStatus="away"] {
+  list-style-image: url("chrome://chat/skin/away.png");
+}
+
 .emailPopup {
   font-size: inherit;
   max-height: 7px; /* the height of the image */
   margin: 0.2em 2px 0px; /* 0.2em just to move it off the top of the text */
   list-style-image: url("chrome://messenger/skin/icons/arrow-dn-7.png");
 }
 
 .emailPopup:hover {
--- a/mail/themes/qute/mail/messageHeader-aero.css
+++ b/mail/themes/qute/mail/messageHeader-aero.css
@@ -651,16 +651,35 @@ mail-emailaddress[selected="true"] .emai
 .emailStar:hover:active {
   -moz-image-region:rect(0px 48px 16px 32px);
 }
 
 .emailStar[hascard="true"] {
   list-style-image: url("chrome://messenger/skin/contactStarred.png");
 }
 
+.emailPresence {
+  padding: 0px;
+  -moz-margin-start: -2px !important;
+  -moz-image-region: rect(6px, 16px, 16px, 6px);
+}
+
+.emailStar[chatStatus],
+.emailPresence:not([chatStatus]) {
+  display: none;
+}
+
+.emailPresence[chatStatus="available"] {
+  list-style-image: url("chrome://chat/skin/available.png");
+}
+
+.emailPresence[chatStatus="away"] {
+  list-style-image: url("chrome://chat/skin/away.png");
+}
+
 .emailPopup {
   font-size: inherit;
   max-height: 7px; /* the height of the image */
   margin: 0.2em 2px 0px; /* 0.2em just to move it off the top of the text */
   list-style-image: url("chrome://messenger/skin/icons/arrow-dn-grey.png");
 }
 
 .emailPopup:hover {
--- a/mail/themes/qute/mail/messageHeader.css
+++ b/mail/themes/qute/mail/messageHeader.css
@@ -579,16 +579,35 @@ mail-emailaddress[selected="true"] .emai
 .emailStar:hover:active {
   -moz-image-region:rect(0px 48px 16px 32px);
 }
 
 .emailStar[hascard="true"] {
   list-style-image: url("chrome://messenger/skin/contactStarred.png");
 }
 
+.emailPresence {
+  padding: 0px;
+  -moz-margin-start: -2px !important;
+  -moz-image-region: rect(6px, 16px, 16px, 6px);
+}
+
+.emailStar[chatStatus],
+.emailPresence:not([chatStatus]) {
+  display: none;
+}
+
+.emailPresence[chatStatus="available"] {
+  list-style-image: url("chrome://chat/skin/available.png");
+}
+
+.emailPresence[chatStatus="away"] {
+  list-style-image: url("chrome://chat/skin/away.png");
+}
+
 .emailPopup {
   font-size: inherit;
   max-height: 7px; /* the height of the image */
   margin: 0.2em 2px 0px; /* 0.2em just to move it off the top of the text */
   list-style-image: url("chrome://messenger/skin/icons/arrow-dn-grey.png");
 }
 
 .emailPopup:hover {