Bug 1392472 - Implement the blue dot on the overlay icon button, r?gasolin
The patch
- fixes
Bug 1392471 together: before the 1st notification mute session show the speech bubble by default; after the 1st notification mute session show the blue dot by default.
- shows the blue dot by default if the window width is smaller then 1150px disregarding the 1st notification mute session
- shows the speech bubble when hovering on the blue dot
MozReview-Commit-ID: 6TXZrwDwfV3
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -45,27 +45,47 @@
}
#onboarding-overlay-button-icon {
width: 32px;
vertical-align: top;
}
#onboarding-overlay-button::after {
+ content: " ";
+ border-radius: 50%;
+ margin-top: -1px;
+ margin-inline-start: -13px;
+ border: 2px solid #f2f2f2;
+ background: #0A84FF;
+ padding: 0;
+ width: 10px;
+ height: 10px;
+ min-width: unset;
+ max-width: unset;
+ display: block;
+ box-sizing: content-box;
+ float: inline-end;
+ position: relative;
+}
+
+#onboarding-overlay-button:hover::after,
+#onboarding-overlay-button.onboarding-speech-bubble::after {
background: #0060df;
font-size: 13px;
text-align: center;
color: #fff;
box-sizing: content-box;
font-weight: 400;
content: attr(aria-label);
- display: inline-block;
border: 1px solid transparent;
border-radius: 2px;
padding: 10px 16px;
+ width: auto;
+ height: auto;
min-width: 100px;
max-width: 140px;
white-space: pre-line;
margin-inline-start: 4px;
margin-top: -10px;
box-shadow: -2px 0 5px 0 rgba(74, 74, 79, 0.25);
}
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -16,16 +16,18 @@ const ABOUT_NEWTAB_URL = "about:newtab";
const BUNDLE_URI = "chrome://onboarding/locale/onboarding.properties";
const UITOUR_JS_URI = "resource://onboarding/lib/UITour-lib.js";
const TOUR_AGENT_JS_URI = "resource://onboarding/onboarding-tour-agent.js";
const BRAND_SHORT_NAME = Services.strings
.createBundle("chrome://branding/locale/brand.properties")
.GetStringFromName("brandShortName");
const PROMPT_COUNT_PREF = "browser.onboarding.notification.prompt-count";
const ONBOARDING_DIALOG_ID = "onboarding-overlay-dialog";
+const ONBOARDING_MIN_WIDTH_PX = 960;
+const SPEECH_BUBBLE_MIN_WIDTH_PX = 1150;
/**
* Add any number of tours, key is the tourId, value should follow the format below
* "tourId": { // The short tour id which could be saved in pref
* // The unique tour id
* id: "onboarding-tour-addons",
* // The string id of tour name which would be displayed on the navigation bar
* tourNameId: "onboarding.tour-addon",
@@ -345,21 +347,28 @@ class Onboarding {
this._window.addEventListener("unload", () => this.destroy());
this.uiInitialized = false;
this._resizeTimerId =
this._window.requestIdleCallback(() => this._resizeUI());
}
_resizeUI() {
- // Don't show the overlay UI before we get to a better, responsive design.
- if (this._window.document.body.getBoundingClientRect().width < 960) {
+ let width = this._window.document.body.getBoundingClientRect().width;
+ if (width < ONBOARDING_MIN_WIDTH_PX) {
+ // Don't show the overlay UI before we get to a better, responsive design.
this.destroy();
+ return;
+ }
+
+ this._initUI();
+ if (this._isFirstSession && width >= SPEECH_BUBBLE_MIN_WIDTH_PX) {
+ this._overlayIcon.classList.add("onboarding-speech-bubble");
} else {
- this._initUI();
+ this._overlayIcon.classList.remove("onboarding-speech-bubble");
}
}
_initUI() {
if (this.uiInitialized) {
return;
}
this.uiInitialized = true;
@@ -746,38 +755,55 @@ class Onboarding {
targetItem.classList.remove("onboarding-complete");
targetItem.removeAttribute("aria-describedby");
if (completedText) {
completedText.remove();
}
}
}
- _muteNotificationOnFirstSession() {
- if (Services.prefs.prefHasUserValue("browser.onboarding.notification.tour-ids-queue")) {
- // There is a queue. We had prompted before, this must not be the 1st session.
+ get _isFirstSession() {
+ // Should only directly return on the "false" case. Consider:
+ // 1. On the 1st session, `_firstSession` is true
+ // 2. During the 1st session, user resizes window so that the UI is destroyed
+ // 3. After the 1st mute session, user resizes window so that the UI is re-init
+ if (this._firstSession === false) {
return false;
}
+ this._firstSession = true;
- let muteDuration = Services.prefs.getIntPref("browser.onboarding.notification.mute-duration-on-first-session-ms");
- if (muteDuration == 0) {
- // Don't mute when this is set to 0 on purpose.
+ // There is a queue, which means we had prompted tour notifications before. Therefore this is not the 1st session.
+ if (Services.prefs.prefHasUserValue("browser.onboarding.notification.tour-ids-queue")) {
+ this._firstSession = false;
+ }
+
+ // When this is set to 0 on purpose, always judge as not the 1st session
+ if (Services.prefs.getIntPref("browser.onboarding.notification.mute-duration-on-first-session-ms") === 0) {
+ this._firstSession = false;
+ }
+
+ return this._firstSession;
+ }
+
+ _muteNotificationOnFirstSession() {
+ if (!this._isFirstSession) {
return false;
}
// Reuse the `last-time-of-changing-tour-sec` to save the time that
// we try to prompt on the 1st session.
let lastTime = 1000 * Services.prefs.getIntPref("browser.onboarding.notification.last-time-of-changing-tour-sec", 0);
if (lastTime <= 0) {
sendMessageToChrome("set-prefs", [{
name: "browser.onboarding.notification.last-time-of-changing-tour-sec",
value: Math.floor(Date.now() / 1000)
}]);
return true;
}
+ let muteDuration = Services.prefs.getIntPref("browser.onboarding.notification.mute-duration-on-first-session-ms");
return Date.now() - lastTime <= muteDuration;
}
_isTimeForNextTourNotification() {
let promptCount = Services.prefs.getIntPref("browser.onboarding.notification.prompt-count", 0);
let maxCount = Services.prefs.getIntPref("browser.onboarding.notification.max-prompt-count-per-tour");
if (promptCount >= maxCount) {
return true;
@@ -835,16 +861,19 @@ class Onboarding {
showNotification() {
if (Services.prefs.getBoolPref("browser.onboarding.notification.finished", false)) {
return;
}
if (this._muteNotificationOnFirstSession()) {
return;
}
+ // After the notification mute on the 1st session,
+ // we don't want to show the speech bubble by default
+ this._overlayIcon.classList.remove("onboarding-speech-bubble");
let queue = this._getNotificationQueue();
let startQueueLength = queue.length;
// See if need to move on to the next tour
if (queue.length > 0 && this._isTimeForNextTourNotification()) {
queue.shift();
}
// We don't want to prompt completed tour.