338 lines
11 KiB
JavaScript
338 lines
11 KiB
JavaScript
/* 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/. */
|
|
|
|
/* eslint-env mozilla/remote-page */
|
|
|
|
import { onToggleContainer } from "./helpers.mjs";
|
|
|
|
const { TabsSetupFlowManager } = ChromeUtils.importESModule(
|
|
"resource:///modules/firefox-view-tabs-setup-manager.sys.mjs"
|
|
);
|
|
|
|
const TOPIC_SETUPSTATE_CHANGED = "firefox-view.setupstate.changed";
|
|
const UI_OPEN_STATE = "browser.tabs.firefox-view.ui-state.tab-pickup.open";
|
|
|
|
class TabPickupContainer extends HTMLDetailsElement {
|
|
constructor() {
|
|
super();
|
|
this.boundObserve = (...args) => this.observe(...args);
|
|
this._currentSetupStateIndex = -1;
|
|
this.errorState = null;
|
|
this.tabListAdded = null;
|
|
this._id = Math.floor(Math.random() * 10e6);
|
|
}
|
|
get setupContainerElem() {
|
|
return this.querySelector(".sync-setup-container");
|
|
}
|
|
|
|
get tabsContainerElem() {
|
|
return this.querySelector(".synced-tabs-container");
|
|
}
|
|
|
|
get tabPickupListElem() {
|
|
return this.querySelector(".synced-tabs-container tab-pickup-list");
|
|
}
|
|
|
|
getWindow() {
|
|
return this.ownerGlobal.browsingContext.embedderWindowGlobal.browsingContext
|
|
.window;
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.addEventListener("click", this);
|
|
this.addEventListener("toggle", this);
|
|
this.ownerDocument.addEventListener("visibilitychange", this);
|
|
Services.obs.addObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
|
|
|
|
for (let elem of this.querySelectorAll("a[data-support-url]")) {
|
|
elem.href =
|
|
Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
|
elem.dataset.supportUrl;
|
|
}
|
|
|
|
// we wait until the list shows up before trying to populate it,
|
|
// when its safe to assume the custom-element's methods will be available
|
|
this.tabListAdded = this.promiseChildAdded();
|
|
this.update();
|
|
this.onVisibilityChange();
|
|
}
|
|
|
|
promiseChildAdded() {
|
|
return new Promise(resolve => {
|
|
if (typeof this.tabPickupListElem?.getSyncedTabData == "function") {
|
|
resolve();
|
|
return;
|
|
}
|
|
this.addEventListener(
|
|
"list-ready",
|
|
event => {
|
|
resolve();
|
|
},
|
|
{ once: true }
|
|
);
|
|
});
|
|
}
|
|
|
|
cleanup() {
|
|
TabsSetupFlowManager.updateViewVisibility(this._id, "unloaded");
|
|
this.ownerDocument?.removeEventListener("visibilitychange", this);
|
|
Services.obs.removeObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this.cleanup();
|
|
}
|
|
|
|
handleEvent(event) {
|
|
if (event.type == "toggle") {
|
|
onToggleContainer(this);
|
|
this.onVisibilityChange();
|
|
return;
|
|
}
|
|
if (event.type == "click" && event.target.dataset.action) {
|
|
switch (event.target.dataset.action) {
|
|
case "view0-sync-error-action":
|
|
case "view0-network-offline-action":
|
|
case "view0-password-locked-action": {
|
|
TabsSetupFlowManager.tryToClearError();
|
|
break;
|
|
}
|
|
case "view0-signed-out-action":
|
|
case "view1-primary-action": {
|
|
TabsSetupFlowManager.openFxASignup(event.target.ownerGlobal);
|
|
break;
|
|
}
|
|
case "view2-primary-action":
|
|
case "mobile-promo-primary-action": {
|
|
TabsSetupFlowManager.openFxAPairDevice(event.target.ownerGlobal);
|
|
break;
|
|
}
|
|
case "view3-primary-action": {
|
|
TabsSetupFlowManager.syncOpenTabs(event.target);
|
|
break;
|
|
}
|
|
case "mobile-promo-dismiss": {
|
|
TabsSetupFlowManager.dismissMobilePromo(event.target);
|
|
break;
|
|
}
|
|
case "mobile-confirmation-dismiss": {
|
|
TabsSetupFlowManager.dismissMobileConfirmation(event.target);
|
|
break;
|
|
}
|
|
case "view0-sync-disconnected-action": {
|
|
const win = event.target.ownerGlobal;
|
|
const {
|
|
switchToTabHavingURI,
|
|
} = win.docShell.chromeEventHandler.ownerGlobal;
|
|
switchToTabHavingURI(
|
|
"about:preferences?action=choose-what-to-sync#sync",
|
|
true,
|
|
{}
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Returning to fxview seems like a likely time for a device check
|
|
if (event.type == "visibilitychange") {
|
|
this.onVisibilityChange();
|
|
}
|
|
}
|
|
onVisibilityChange() {
|
|
const isVisible = document.visibilityState == "visible";
|
|
const isOpen = this.open;
|
|
if (isVisible && isOpen) {
|
|
this.update();
|
|
TabsSetupFlowManager.updateViewVisibility(this._id, "visible");
|
|
} else {
|
|
TabsSetupFlowManager.updateViewVisibility(
|
|
this._id,
|
|
isVisible ? "closed" : "hidden"
|
|
);
|
|
}
|
|
}
|
|
|
|
async observe(subject, topic, errorState) {
|
|
if (topic == TOPIC_SETUPSTATE_CHANGED) {
|
|
this.update({ errorState });
|
|
}
|
|
}
|
|
|
|
get mobilePromoElem() {
|
|
return this.querySelector(".promo-box");
|
|
}
|
|
get mobileSuccessElem() {
|
|
return this.querySelector(".confirmation-message-box");
|
|
}
|
|
|
|
update({
|
|
stateIndex = TabsSetupFlowManager.uiStateIndex,
|
|
showMobilePromo = TabsSetupFlowManager.shouldShowMobilePromo,
|
|
showMobilePairSuccess = TabsSetupFlowManager.shouldShowMobileConnectedSuccess,
|
|
errorState = TabsSetupFlowManager.getErrorType(),
|
|
waitingForTabs = TabsSetupFlowManager.waitingForTabs,
|
|
} = {}) {
|
|
let needsRender = false;
|
|
if (waitingForTabs !== this._waitingForTabs) {
|
|
this._waitingForTabs = waitingForTabs;
|
|
needsRender = true;
|
|
}
|
|
|
|
if (showMobilePromo !== this._showMobilePromo) {
|
|
this._showMobilePromo = showMobilePromo;
|
|
needsRender = true;
|
|
}
|
|
if (showMobilePairSuccess !== this._showMobilePairSuccess) {
|
|
this._showMobilePairSuccess = showMobilePairSuccess;
|
|
needsRender = true;
|
|
}
|
|
if (stateIndex == 4 && this._currentSetupStateIndex !== stateIndex) {
|
|
// trigger an initial request for the synced tabs list
|
|
this.tabListAdded.then(() => {
|
|
this.tabPickupListElem.getSyncedTabData();
|
|
});
|
|
}
|
|
if (stateIndex !== this._currentSetupStateIndex || stateIndex == 0) {
|
|
this._currentSetupStateIndex = stateIndex;
|
|
needsRender = true;
|
|
this.errorState = errorState;
|
|
}
|
|
needsRender && this.render();
|
|
}
|
|
|
|
generateErrorMessage() {
|
|
// We map the error state strings to Fluent string IDs so that it's easier
|
|
// to change strings in the future without having to update all of the
|
|
// error state strings.
|
|
const errorStateStringMappings = {
|
|
"sync-error": {
|
|
header: "firefoxview-tabpickup-sync-error-header",
|
|
description: "firefoxview-tabpickup-generic-sync-error-description",
|
|
buttonLabel: "firefoxview-tabpickup-sync-error-primarybutton",
|
|
},
|
|
|
|
"fxa-admin-disabled": {
|
|
header: "firefoxview-tabpickup-fxa-admin-disabled-header",
|
|
description: "firefoxview-tabpickup-fxa-admin-disabled-description",
|
|
// The button is hidden for this errorState, so we don't include the
|
|
// buttonLabel property.
|
|
},
|
|
|
|
"network-offline": {
|
|
header: "firefoxview-tabpickup-network-offline-header",
|
|
description: "firefoxview-tabpickup-network-offline-description",
|
|
buttonLabel: "firefoxview-tabpickup-network-offline-primarybutton",
|
|
},
|
|
|
|
"sync-disconnected": {
|
|
header: "firefoxview-tabpickup-sync-disconnected-header",
|
|
description: "firefoxview-tabpickup-sync-disconnected-description",
|
|
buttonLabel: "firefoxview-tabpickup-sync-disconnected-primarybutton",
|
|
},
|
|
|
|
"password-locked": {
|
|
header: "firefoxview-tabpickup-password-locked-header",
|
|
description: "firefoxview-tabpickup-password-locked-description",
|
|
buttonLabel: "firefoxview-tabpickup-password-locked-primarybutton",
|
|
link: {
|
|
label: "firefoxview-tabpickup-password-locked-link",
|
|
href:
|
|
Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
|
"primary-password-stored-logins",
|
|
},
|
|
},
|
|
"signed-out": {
|
|
header: "firefoxview-tabpickup-signed-out-header",
|
|
description: "firefoxview-tabpickup-signed-out-description",
|
|
buttonLabel: "firefoxview-tabpickup-signed-out-primarybutton",
|
|
},
|
|
};
|
|
|
|
const errorStateHeader = this.querySelector(
|
|
"#tabpickup-steps-view0-header"
|
|
);
|
|
const errorStateDescription = this.querySelector(
|
|
"#error-state-description"
|
|
);
|
|
const errorStateButton = this.querySelector("#error-state-button");
|
|
const errorStateLink = this.querySelector("#error-state-link");
|
|
const errorStateProperties = errorStateStringMappings[this.errorState];
|
|
|
|
document.l10n.setAttributes(errorStateHeader, errorStateProperties.header);
|
|
document.l10n.setAttributes(
|
|
errorStateDescription,
|
|
errorStateProperties.description
|
|
);
|
|
|
|
errorStateButton.hidden = this.errorState == "fxa-admin-disabled";
|
|
|
|
if (this.errorState != "fxa-admin-disabled") {
|
|
document.l10n.setAttributes(
|
|
errorStateButton,
|
|
errorStateProperties.buttonLabel
|
|
);
|
|
errorStateButton.setAttribute(
|
|
"data-action",
|
|
`view0-${this.errorState}-action`
|
|
);
|
|
}
|
|
|
|
if (errorStateProperties.link) {
|
|
document.l10n.setAttributes(
|
|
errorStateLink,
|
|
errorStateProperties.link.label
|
|
);
|
|
errorStateLink.href = errorStateProperties.link.href;
|
|
errorStateLink.hidden = false;
|
|
} else {
|
|
errorStateLink.hidden = true;
|
|
}
|
|
}
|
|
|
|
render() {
|
|
if (!this.isConnected) {
|
|
return;
|
|
}
|
|
|
|
let setupElem = this.setupContainerElem;
|
|
let tabsElem = this.tabsContainerElem;
|
|
let mobilePromoElem = this.mobilePromoElem;
|
|
let mobileSuccessElem = this.mobileSuccessElem;
|
|
|
|
const stateIndex = this._currentSetupStateIndex;
|
|
const isLoading = this._waitingForTabs;
|
|
|
|
mobilePromoElem.hidden = !this._showMobilePromo;
|
|
mobileSuccessElem.hidden = !this._showMobilePairSuccess;
|
|
|
|
this.open =
|
|
!TabsSetupFlowManager.isTabSyncSetupComplete ||
|
|
Services.prefs.getBoolPref(UI_OPEN_STATE, true);
|
|
|
|
// show/hide either the setup or tab list containers, creating each as necessary
|
|
if (stateIndex < 4) {
|
|
tabsElem.hidden = true;
|
|
setupElem.hidden = false;
|
|
setupElem.selectedViewName = `sync-setup-view${stateIndex}`;
|
|
|
|
if (stateIndex == 0 && this.errorState) {
|
|
this.generateErrorMessage();
|
|
}
|
|
return;
|
|
}
|
|
|
|
setupElem.hidden = true;
|
|
tabsElem.hidden = false;
|
|
tabsElem.classList.toggle("loading", isLoading);
|
|
}
|
|
|
|
async onReload() {
|
|
await TabsSetupFlowManager.syncOnPageReload();
|
|
}
|
|
}
|
|
customElements.define("tab-pickup-container", TabPickupContainer, {
|
|
extends: "details",
|
|
});
|
|
|
|
export { TabPickupContainer };
|