Bug 955653 - Show a notification bar in the awesometab while log sweeping, r=florian.
authoraleth <aleth@instantbird.org>
Wed, 27 Nov 2013 23:02:28 +0100
changeset 19244 72787b02f400e7263ab620a1ebadb204019f7449
parent 19243 f64f0684d462a47d4b0827104875395316b3d3ee
child 19245 696da7e27739c30704866ab4b4fa54746a87051e
push id1103
push usermbanner@mozilla.com
push dateTue, 18 Mar 2014 07:44:06 +0000
treeherdercomm-beta@50c6279a0af0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersflorian
bugs955653
Bug 955653 - Show a notification bar in the awesometab while log sweeping, r=florian.
im/components/ibConvStatsService.js
im/content/blist.css
im/content/newtab.xml
im/locales/en-US/chrome/instantbird/newtab.properties
--- a/im/components/ibConvStatsService.js
+++ b/im/components/ibConvStatsService.js
@@ -38,22 +38,25 @@ var gStatsByConvId = {};
 // or it was loaded from the JSON cache file).
 var gStatsByContactId;
 
 // Recursively sweeps log folders and parses log files for conversation statistics.
 var gLogParser = {
   _statsService: null,
   _accounts: [],
   _logFolders: [],
+  inProgress: false,
 
   // The general path of a log is logs/prpl/account/conv/date.json.
   // First, sweep the logs folder for prpl folders.
   sweep: function(aStatsService) {
+    initLogModule("stats-service-log-sweeper", this);
+    this.inProgress = true;
     this._statsService = aStatsService;
-    initLogModule("stats-service-log-sweeper", this);
+    this._statsService._notifyObservers("log-sweeping", "ongoing");
     let logsPath = OS.Path.join(OS.Constants.Path.profileDir, "logs");
     let iterator = new OS.File.DirectoryIterator(logsPath);
     iterator.nextBatch().then((function(aEntries) {
       // Filter out any stray files (e.g. system files).
       aEntries = aEntries.filter(function(e) e.isDir);
       this._sweepPrpls(aEntries);
     }).bind(this), function(aError) {
       if (aError instanceof OS.File.Error && !aError.becauseNoSuchFile)
@@ -110,19 +113,21 @@ var gLogParser = {
   _sweepLogFolder: function(aLogs) {
     aLogs = aLogs.filter(function(l) l.name.endsWith(".json"));
     let decoder = new TextDecoder();
     let __sweepLogFolder = function() {
       if (!aLogs.length) {
         if (this._logFolders.length)
           this._sweepLogFolders();
         else { // We're done.
+          delete this.inProgress;
           let statsService = this._statsService;
           statsService._cacheAllStats(); // Flush stats to JSON cache.
           statsService._convs.sort(statsService._sortComparator);
+          statsService._notifyObservers("log-sweeping", "done");
           gStatsByContactId = {}; // Initialize stats cache for contacts.
         }
         return;
       }
       let log = aLogs.shift().path;
       OS.File.read(log).then(function(aArray) {
         // Try to parse the log file. If anything goes wrong here, the log file
         // has likely been tampered with so we ignore it and move on.
@@ -369,16 +374,21 @@ ConvStatsService.prototype = {
           });
         });
       });
     }
     return new nsSimpleEnumerator(filteredConvs);
   },
 
   _cacheAllStats: function() {
+    // Don't save anything to the JSON file until log sweeping is done. This is to
+    // ensure that a re-sweep is triggered on next startup if log sweeping could
+    // not complete.
+    if (gLogParser.inProgress)
+      return;
     let encoder = new TextEncoder();
     let objToWrite = {version: gStatsCacheVersion, stats: gStatsByConvId};
     OS.File.writeAtomic(this._statsCacheFilePath,
                         encoder.encode(JSON.stringify(objToWrite)),
                         {tmpPath: this._statsCacheFilePath + ".tmp"});
     if (this._statsCacheUpdateTimer) {
       clearTimeout(this._statsCacheUpdateTimer);
       delete this._statsCacheUpdateTimer;
@@ -414,30 +424,33 @@ ConvStatsService.prototype = {
     }
   },
 
   addObserver: function(aObserver) {
     if (this._observers.indexOf(aObserver) != -1)
       return;
     this._observers.push(aObserver);
 
+    if (gLogParser.inProgress)
+      aObserver.observe(this, "stats-service-log-sweeping", "ongoing");
+
     this._repositionConvsWithUpdatedStats();
 
     // We request chat lists from accounts when adding new observers.
     this._requestRoomInfo();
   },
 
   removeObserver: function(aObserver) {
     this._observers = this._observers.filter(function(o) o !== aObserver);
   },
 
-  _notifyObservers: function(aTopic) {
+  _notifyObservers: function(aTopic, aData) {
     for each (let observer in this._observers) {
       if ("observe" in observer) // Avoid failing on destructed XBL bindings.
-        observer.observe(this, "stats-service-" + aTopic);
+        observer.observe(this, "stats-service-" + aTopic, aData);
     }
   },
 
   // Maps prplConversation ids to their ConversationStats objects.
   _statsByPrplConvId: new Map(),
   // Maps prplConversation ids to the corresponding PossibleConversations.
   _convsByPrplConvId: new Map(),
   // These will be repositioned to reflect their new scores when a newtab is opened.
@@ -497,19 +510,17 @@ ConvStatsService.prototype = {
         let chatList = this._chatsByAccountIdAndName.get(conv.account.id);
         if (chatList && chatList.has(conv.normalizedName))
           possibleConv = chatList.get(conv.name);
       }
       this._convsByPrplConvId.set(conv.id, possibleConv);
     }
     else if (aTopic == "conversation-closed")
       this._statsByPrplConvId.delete(aSubject.id);
-    if (kNotificationsToObserve.indexOf(aTopic) == -1)
-      return;
-    if (aTopic == "contact-no-longer-dummy") {
+    else if (aTopic == "contact-no-longer-dummy") {
       // Contact ID changed. aData is the old ID.
       let id = aSubject.id;
       let oldId = parseInt(aData, 10);
       this._contactsById.set(id, this._contactsById.get(oldId));
       this._contactsById.delete(oldId);
       this._contactsById.get(id)._contactId = id;
       return;
     }
--- a/im/content/blist.css
+++ b/im/content/blist.css
@@ -149,10 +149,10 @@ conv {
 }
 
 /* Make the notification bar work with narrow windows. */
 .notification-inner > hbox {
   display: inline-block;
 }
 
 .notification-inner > hbox > .messageImage {
-  display: none;
+  max-width: 0;
 }
--- a/im/content/newtab.xml
+++ b/im/content/newtab.xml
@@ -19,17 +19,19 @@
     </resources>
 
     <content>
       <xul:vbox flex="1">
         <xul:toolbar class="newtab-toolbar">
           <xul:textbox class="filterbox" anonid="filterbox" type="search"
                        placeholder="&filterbox.placeholder;" flex="1"/>
         </xul:toolbar>
-        <xul:richlistbox anonid="newtab-listbox" class="newtab-listbox" flex="1"/>
+        <xul:notificationbox anonid="newtab-notifications" flex="1">
+          <xul:richlistbox anonid="newtab-listbox" class="newtab-listbox" flex="1"/>
+        </xul:notificationbox>
       </xul:vbox>
     </content>
 
     <implementation implements="nsIObserver">
       <property name="statsService" readonly="true">
         <getter>
         <![CDATA[
           if (!this._statsService) {
@@ -317,16 +319,31 @@
 
       <!-- nsIObserver implementation -->
       <method name="observe">
         <parameter name="aSubject"/>
         <parameter name="aTopic"/>
         <parameter name="aData"/>
         <body>
         <![CDATA[
+          if (aTopic == "stats-service-log-sweeping") {
+            let nb = document.getAnonymousElementByAttribute(this, "anonid",
+                                                             "newtab-notifications");
+            let notification = nb.getNotificationWithValue("log-sweeping");
+            if (aData == "ongoing" && !notification) {
+              let notificationText =
+                Services.strings.createBundle("chrome://instantbird/locale/newtab.properties")
+                        .GetStringFromName("newtab.logSweepingInProgress");
+              nb.appendNotification(notificationText, "log-sweeping", "",
+                                    nb.PRIORITY_WARNING_MEDIUM, []);
+            }
+            else if (aData == "done" && notification)
+              nb.removeNotification(notification);
+            return;
+          }
           this._forceRefresh = true;
           this.refresh();
         ]]>
         </body>
       </method>
     </implementation>
 
     <handlers>
--- a/im/locales/en-US/chrome/instantbird/newtab.properties
+++ b/im/locales/en-US/chrome/instantbird/newtab.properties
@@ -1,6 +1,7 @@
 # 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/.
 
 newtab.label=New Conversation
+newtab.logSweepingInProgress=Optimizing conversation suggestions…
 existingConv.infoText=Switch to conversation