Bug 1357021 - Part 1: Handle tours completed state, r=mossop

This commit
- turns on the `onboarding-complete` css style for completed tours
- sets individual tour as completed when action button of that tour is clicked
- sets all tours as completed if hide-the-tour checkbox is checked after toggling the overlay

MozReview-Commit-ID: mps3BrdhOz

--HG--
extra : rebase_source : 3023997897dc80f18b59b69e79a82c211338f88c
This commit is contained in:
Fischer.json 2017-06-18 14:46:09 +08:00
parent a6052d02e5
commit 219d62f6f1
4 changed files with 75 additions and 32 deletions

View File

@ -15,6 +15,15 @@ const PREF_WHITELIST = [
"browser.onboarding.notification.lastPrompted"
];
[
"onboarding-tour-private-browsing",
"onboarding-tour-addons",
"onboarding-tour-customize",
"onboarding-tour-search",
"onboarding-tour-default-browser",
"onboarding-tour-sync",
].forEach(tourId => PREF_WHITELIST.push(`browser.onboarding.tour.${tourId}.completed`));
/**
* Set pref. Why no `getPrefs` function is due to the priviledge level.
* We cannot set prefs inside a framescript but can read.

View File

@ -177,7 +177,7 @@
padding: 7px;
}
#onboarding-tour-sync-page form > button {
#onboarding-tour-sync-page form > #onboarding-tour-sync-button {
padding: 10px 20px;
min-width: 40%;
font-size: 15px;
@ -190,6 +190,7 @@
box-shadow: 0 1px 0 rgba(0,0,0,0.23);
cursor: pointer;
margin: 15px 0;
float: none;
}
/* Onboarding tour pages */
@ -242,18 +243,18 @@
grid-column: tour-content-start / tour-page-end;
}
.onboarding-tour-button {
.onboarding-tour-button-container {
grid-row: tour-button-start / tour-page-end;
grid-column: tour-content-start / tour-page-end;
}
.onboarding-tour-page.onboarding-no-button > .onboarding-tour-button {
.onboarding-tour-page.onboarding-no-button > .onboarding-tour-button-container {
display: none;
grid-row: tour-page-end;
grid-column: tour-page-end;
}
.onboarding-tour-button > button {
.onboarding-tour-action-button {
padding: 10px 20px;
font-size: 15px;
font-weight: 600;
@ -269,7 +270,7 @@
margin-top: -32px;
}
.onboarding-tour-button > button:active {
.onboarding-tour-action-button:active {
background: #0881dd;
}
@ -418,6 +419,7 @@
#onboarding-notification-tour-icon {
width: 64px;
height: 64px;
background-size: 64px;
background-repeat: no-repeat;
}

View File

@ -34,9 +34,9 @@ const BRAND_SHORT_NAME = Services.strings
* - button: // The string of tour notification action button title
* // Return a div appended with elements for this tours.
* // Each tour should contain the following 3 sections in the div:
* // .onboarding-tour-description, .onboarding-tour-content, .onboarding-tour-button.
* // Add onboarding-no-button css class in the div if this tour does not need a button.
* // The overlay layout will responsively position and distribute space for these 3 sections based on viewport size
* // .onboarding-tour-description, .onboarding-tour-content, .onboarding-tour-button-container.
* // Add onboarding-no-button css class in the div if this tour does not need a button container.
* // If there was a .onboarding-tour-action-button present and was clicked, tour would be marked as completed.
* getPage() {},
* },
**/
@ -61,8 +61,8 @@ var onboardingTours = [
<section class="onboarding-tour-content">
<img src="resource://onboarding/img/figure_private.svg" />
</section>
<aside class="onboarding-tour-button">
<button id="onboarding-tour-private-browsing-button" data-l10n-id="onboarding.tour-private-browsing.button"></button>
<aside class="onboarding-tour-button-container">
<button id="onboarding-tour-private-browsing-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-private-browsing.button"></button>
</aside>
`;
return div;
@ -88,8 +88,8 @@ var onboardingTours = [
<section class="onboarding-tour-content">
<img src="resource://onboarding/img/figure_addons.svg" />
</section>
<aside class="onboarding-tour-button">
<button id="onboarding-tour-addons-button" data-l10n-id="onboarding.tour-addons.button"></button>
<aside class="onboarding-tour-button-container">
<button id="onboarding-tour-addons-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-addons.button"></button>
</aside>
`;
return div;
@ -115,8 +115,8 @@ var onboardingTours = [
<section class="onboarding-tour-content">
<img src="resource://onboarding/img/figure_customize.svg" />
</section>
<aside class="onboarding-tour-button">
<button id="onboarding-tour-customize-button" data-l10n-id="onboarding.tour-customize.button"></button>
<aside class="onboarding-tour-button-container">
<button id="onboarding-tour-customize-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-customize.button"></button>
</aside>
`;
return div;
@ -142,8 +142,8 @@ var onboardingTours = [
<section class="onboarding-tour-content">
<img src="resource://onboarding/img/figure_search.svg" />
</section>
<aside class="onboarding-tour-button">
<button id="onboarding-tour-search-button" data-l10n-id="onboarding.tour-search.button"></button>
<aside class="onboarding-tour-button-container">
<button id="onboarding-tour-search-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-search.button"></button>
</aside>
`;
return div;
@ -171,8 +171,8 @@ var onboardingTours = [
<section class="onboarding-tour-content">
<img src="resource://onboarding/img/figure_default.svg" />
</section>
<aside class="onboarding-tour-button">
<button id="onboarding-tour-default-browser-button" data-l10n-id="${defaultBrowserButtonId}"></button>
<aside class="onboarding-tour-button-container">
<button id="onboarding-tour-default-browser-button" class="onboarding-tour-action-button" data-l10n-id="${defaultBrowserButtonId}"></button>
</aside>
`;
return div;
@ -201,7 +201,7 @@ var onboardingTours = [
<h3 data-l10n-id="onboarding.tour-sync.form.title"></h3>
<p data-l10n-id="onboarding.tour-sync.form.description"></p>
<input id="onboarding-tour-sync-email-input" type="text"></input><br />
<button id="onboarding-tour-sync-button" data-l10n-id="onboarding.tour-sync.button"></button>
<button id="onboarding-tour-sync-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-sync.button"></button>
</form>
<img src="resource://onboarding/img/figure_sync.svg" />
</section>
@ -278,6 +278,12 @@ class Onboarding {
this.destroy();
}
});
onboardingTours.forEach(tour => {
let tourId = tour.id;
this._prefsObserved.set(`browser.onboarding.tour.${tourId}.completed`, () => {
this.markTourCompletionState(tourId);
});
});
for (let [name, callback] of this._prefsObserved) {
Preferences.observe(name, callback);
}
@ -323,8 +329,12 @@ class Onboarding {
this.gotoPage(tourId);
break;
}
if (evt.target.classList.contains("onboarding-tour-item")) {
let classList = evt.target.classList;
if (classList.contains("onboarding-tour-item")) {
this.gotoPage(evt.target.id);
} else if (classList.contains("onboarding-tour-action-button")) {
let activeItem = this._tourItems.find(item => item.classList.contains("onboarding-active"));
this.setToursCompleted([ activeItem.id ]);
}
}
@ -370,6 +380,29 @@ class Onboarding {
return Preferences.get(`browser.onboarding.tour.${tourId}.completed`, false);
}
setToursCompleted(tourIds) {
let params = [];
tourIds.forEach(id => {
if (!this.isTourCompleted(id)) {
params.push({
name: `browser.onboarding.tour.${id}.completed`,
value: true
});
}
});
if (params.length > 0) {
this.sendMessageToChrome("set-prefs", params);
}
}
markTourCompletionState(tourId) {
// We are doing lazy load so there might be no items.
if (this._tourItems.length > 0 && this.isTourCompleted(tourId)) {
let targetItem = this._tourItems.find(item => item.id == tourId);
targetItem.classList.add("onboarding-complete");
}
}
showNotification() {
if (Preferences.get("browser.onboarding.notification.finished", false)) {
return;
@ -469,6 +502,7 @@ class Onboarding {
}
hide() {
this.setToursCompleted(onboardingTours.map(tour => tour.id));
this.sendMessageToChrome("set-prefs", [
{
name: "browser.onboarding.hidden",
@ -500,7 +534,7 @@ class Onboarding {
`;
div.querySelector("label[for='onboarding-tour-hidden-checkbox']").textContent =
this._bundle.GetStringFromName("onboarding.hidden-checkbox-label");
this._bundle.GetStringFromName("onboarding.hidden-checkbox-label-text");
div.querySelector("#onboarding-header").textContent =
this._bundle.formatStringFromName("onboarding.overlay-title", [BRAND_SHORT_NAME], 1);
return div;
@ -544,6 +578,7 @@ class Onboarding {
this._tourItems.push(li);
this._tourPages.push(div);
}
tours.forEach(tour => this.markTourCompletionState(tour.id));
let dialog = this._window.document.getElementById("onboarding-overlay-dialog");
let ul = this._window.document.getElementById("onboarding-tour-list");

View File

@ -3,6 +3,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# LOCALIZATION NOTE(onboarding.overlay-title): This string will be used in the overlay title. %S is brandShortName
onboarding.overlay-title=Getting started with %S
onboarding.hidden-checkbox-label-text=Mark all as complete, and hide the tour
#LOCALIZATION NOTE(onboarding.button.learnMore): this string is used as a button label, displayed near the message, and shared across all the onboarding notifications.
onboarding.button.learnMore=Learn More
# LOCALIZATION NOTE(onboarding.notification-icon-tool-tip): %S is brandShortName.
onboarding.notification-icon-tool-tip=New to %S?
onboarding.tour-search=One-Click Search
onboarding.tour-search.title=Find the needle or the haystack.
@ -26,13 +31,13 @@ onboarding.notification.onboarding-tour-private-browsing.message=Theres no re
onboarding.tour-addons=Add-ons
onboarding.tour-addons.title=Add more functionality.
# LOCALIZATION NOTE(onboarding.tour-addons.description): This string will be used in the add-on tour description. %1$S is brandShortName
onboarding.tour-addons.description=Add-ons expand %1$Ss built-in features, so %1$S works the way you do. Compare prices, check the weather or express your personality with a custom theme.
onboarding.tour-addons.button=Show Add-ons in Menu
onboarding.notification.onboarding-tour-addons.title=Get more done.
# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-addons.message): %S is brandShortName.
onboarding.notification.onboarding-tour-addons.message=Add-ons are small apps you can add to %S that do lots of things — from managing to-do lists, to downloading videos, to changing the look of your browser.
# LOCALIZATION NOTE(onboarding.tour-addons.description): This string will be used in the add-on tour description. %1$S is brandShortName
onboarding.tour-addons.description=Add-ons expand %1$Ss built-in features, so %1$S works the way you do. Compare prices, check the weather or express your personality with a custom theme.
onboarding.tour-addons.button=Show Add-ons in Menu
onboarding.tour-customize=Customize
onboarding.tour-customize.title=Do things your way.
# LOCALIZATION NOTE(onboarding.tour-customize.description): This string will be used in the customize tour description. %S is brandShortName
@ -55,14 +60,6 @@ onboarding.notification.onboarding-tour-default-browser.title=Make %S your go-to
# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-default-browser.message): %1$S is brandShortName
onboarding.notification.onboarding-tour-default-browser.message=It doesnt take much to get the most from %1$S. Just set %1$S as your default browser and put control, customization, and protection on autopilot.
onboarding.hidden-checkbox-label=Hide the tour
#LOCALIZATION NOTE(onboarding.button.learnMore): this string is used as a button label, displayed near the message, and shared across all the onboarding notifications.
onboarding.button.learnMore=Learn More
# LOCALIZATION NOTE(onboarding.notification-icon-tool-tip): %S is brandShortName.
onboarding.notification-icon-tool-tip=New to %S?
onboarding.tour-sync=Firefox Sync
onboarding.tour-sync.title=Sync brings it all together.
onboarding.tour-sync.description=Access your bookmarks and passwords on any device. You can even send a tab from your laptop to your phone! Better yet, you can choose what you sync and what you dont.