Bug 1388183 - Queue and play the last download notification instead of truncating/aborting it. r=paolo draft
authorSam Foster <sfoster@mozilla.com>
Fri, 11 Aug 2017 16:06:02 -0700
changeset 648545 4ae681eebc72f83483b09a41ed7716701d13ecc3
parent 646672 5ab5511100233277a760550ac509283278a0e3d9
child 726853 cbe2d68580d4bd899b229f097ca07be4156fa58e
push id74788
push userbmo:sfoster@mozilla.com
push dateThu, 17 Aug 2017 23:02:17 +0000
reviewerspaolo
bugs1388183
milestone57.0a1
Bug 1388183 - Queue and play the last download notification instead of truncating/aborting it. r=paolo * Maintain a _nextNotificationType LIFO "queue" of one, to play overlapping events in series * _showNotification assumes everything could have changed since it was last called * Signature of showEventNotification is unchanged, but most of its work is now done in _showNotification MozReview-Commit-ID: 8sv4DZoZU65
browser/components/downloads/content/indicator.js
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -273,48 +273,72 @@ const DownloadsIndicatorView = {
           aCallback();
         }
       });
   },
 
   // Direct control functions
 
   /**
-   * Set while we are waiting for a notification to fade out.
+   * Set to the type ("start" or "finish") when display of a notification is in-progress
    */
-  _notificationTimeout: null,
+  _currentNotificationType: null,
+
+  /**
+   * Set to the type ("start" or "finish") when a notification arrives while we
+   * are waiting for the timeout of the previous notification
+   */
+  _nextNotificationType: null,
 
   /**
    * Check if the panel containing aNode is open.
    * @param aNode
    *        the node whose panel we're interested in.
    */
   _isAncestorPanelOpen(aNode) {
     while (aNode && aNode.localName != "panel") {
       aNode = aNode.parentNode;
     }
     return aNode && aNode.state == "open";
   },
 
   /**
-   * If the status indicator is visible in its assigned position, shows for a
-   * brief time a visual notification of a relevant event, like a new download.
+   * Display or enqueue a visual notification of a relevant event, like a new download.
    *
    * @param aType
    *        Set to "start" for new downloads, "finish" for completed downloads.
    */
   showEventNotification(aType) {
     if (!this._initialized) {
       return;
     }
 
     if (!DownloadsCommon.animateNotifications) {
       return;
     }
 
+    // enqueue this notification while the current one is being displayed
+    if (this._currentNotificationType) {
+      // only queue up the notification if it is different to the current one
+      if (this._currentNotificationType != aType) {
+        this._nextNotificationType = aType;
+      }
+    } else {
+      this._showNotification(aType);
+    }
+  },
+
+  /**
+   * If the status indicator is visible in its assigned position, shows for a
+   * brief time a visual notification of a relevant event, like a new download.
+   *
+   * @param aType
+   *        Set to "start" for new downloads, "finish" for completed downloads.
+   */
+  _showNotification(aType) {
     // No need to show visual notification if the panel is visible.
     if (DownloadsPanel.isPanelShowing) {
       return;
     }
 
     let anchor = DownloadsButton._placeholder;
     let widgetGroup = CustomizableUI.getWidget("downloads-button");
     let widget = widgetGroup.forWindow(window);
@@ -329,20 +353,16 @@ const DownloadsIndicatorView = {
       // Otherwise, try to use the anchor of the panel:
       anchor = widget.anchor;
     }
     if (!anchor || !isElementVisible(anchor.parentNode)) {
       // Our container isn't visible, so can't show the animation:
       return;
     }
 
-    if (this._notificationTimeout) {
-      clearTimeout(this._notificationTimeout);
-    }
-
     // The notification element is positioned to show in the same location as
     // the downloads button. It's not in the downloads button itself in order to
     // be able to anchor the notification elsewhere if required, and to ensure
     // the notification isn't clipped by overflow properties of the anchor's
     // container.
     // Note: no notifier animation for download finished in Photon
     let notifier = this.notifier;
 
@@ -366,21 +386,34 @@ const DownloadsIndicatorView = {
       notifier.setAttribute("notification", aType);
     }
     anchor.setAttribute("notification", aType);
 
     let animationDuration;
     // This value is determined by the overall duration of animation in CSS.
     animationDuration = aType == "start" ? 760 : 850;
 
-    this._notificationTimeout = setTimeout(() => {
-      notifier.setAttribute("hidden", "true");
-      notifier.removeAttribute("notification");
-      notifier.style.transform = "";
-      anchor.removeAttribute("notification");
+    this._currentNotificationType = aType;
+
+    setTimeout(() => {
+      requestAnimationFrame(() => {
+        notifier.setAttribute("hidden", "true");
+        notifier.removeAttribute("notification");
+        notifier.style.transform = "";
+        anchor.removeAttribute("notification");
+
+        requestAnimationFrame(() => {
+          let nextType = this._nextNotificationType;
+          this._currentNotificationType = null;
+          this._nextNotificationType = null;
+          if (nextType) {
+            this._showNotification(nextType);
+          }
+        });
+      });
     }, animationDuration);
   },
 
   // Callback functions from DownloadsIndicatorData
 
   /**
    * Indicates whether the indicator should be shown because there are some
    * downloads to be displayed.