зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1768695 - Add error states handling to FirefoxView r=sfoster,fluent-reviewers,desktop-theme-reviewers
* Add new card and styling for network offline, sync error and sync disabled by admin errors * Change loading spinner to rotating sync svg * Add tests Differential Revision: https://phabricator.services.mozilla.com/D153069
This commit is contained in:
Родитель
dc4af7919e
Коммит
f0e1d0730e
|
@ -19,12 +19,20 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
|
|||
UIState: "resource://services-sync/UIState.jsm",
|
||||
SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(lazy, "fxAccounts", () => {
|
||||
return ChromeUtils.import(
|
||||
"resource://gre/modules/FxAccounts.jsm"
|
||||
).getFxAccountsSingleton();
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
lazy,
|
||||
"gNetworkLinkService",
|
||||
"@mozilla.org/network/network-link-service;1",
|
||||
"nsINetworkLinkService"
|
||||
);
|
||||
|
||||
const SYNC_TABS_PREF = "services.sync.engine.tabs";
|
||||
const RECENT_TABS_SYNC = "services.sync.lastTabFetch";
|
||||
const MOBILE_PROMO_DISMISSED_PREF =
|
||||
|
@ -32,6 +40,10 @@ const MOBILE_PROMO_DISMISSED_PREF =
|
|||
const LOGGING_PREF = "browser.tabs.firefox-view.logLevel";
|
||||
const TOPIC_SETUPSTATE_CHANGED = "firefox-view.setupstate.changed";
|
||||
const TOPIC_DEVICELIST_UPDATED = "fxaccounts:devicelist_updated";
|
||||
const NETWORK_STATUS_CHANGED = "network:offline-status-changed";
|
||||
const SYNC_SERVICE_ERROR = "weave:service:sync:error";
|
||||
const FXA_ENABLED = "identity.fxaccounts.enabled";
|
||||
const SYNC_SERVICE_FINISHED = "weave:service:sync:finished";
|
||||
|
||||
function openTabInWindow(window, url) {
|
||||
const {
|
||||
|
@ -46,31 +58,46 @@ export const TabsSetupFlowManager = new (class {
|
|||
|
||||
this.setupState = new Map();
|
||||
this.resetInternalState();
|
||||
this._currentSetupStateName = "";
|
||||
this.networkIsOnline =
|
||||
lazy.gNetworkLinkService.linkStatusKnown &&
|
||||
lazy.gNetworkLinkService.isLinkUp;
|
||||
this.syncIsWorking = true;
|
||||
|
||||
this.registerSetupState({
|
||||
uiStateIndex: 0,
|
||||
name: "error-state",
|
||||
exitConditions: () => {
|
||||
return (
|
||||
this.networkIsOnline &&
|
||||
this.syncIsWorking &&
|
||||
!Services.prefs.prefIsLocked(FXA_ENABLED)
|
||||
);
|
||||
},
|
||||
});
|
||||
this.registerSetupState({
|
||||
uiStateIndex: 1,
|
||||
name: "not-signed-in",
|
||||
exitConditions: () => {
|
||||
return this.fxaSignedIn;
|
||||
},
|
||||
});
|
||||
// TODO: handle offline, sync service not ready or available
|
||||
this.registerSetupState({
|
||||
uiStateIndex: 1,
|
||||
uiStateIndex: 2,
|
||||
name: "connect-secondary-device",
|
||||
exitConditions: () => {
|
||||
return this.secondaryDeviceConnected;
|
||||
},
|
||||
});
|
||||
this.registerSetupState({
|
||||
uiStateIndex: 2,
|
||||
uiStateIndex: 3,
|
||||
name: "disabled-tab-sync",
|
||||
exitConditions: () => {
|
||||
return this.syncTabsPrefEnabled;
|
||||
},
|
||||
});
|
||||
this.registerSetupState({
|
||||
uiStateIndex: 3,
|
||||
uiStateIndex: 4,
|
||||
name: "synced-tabs-not-ready",
|
||||
enter: () => {
|
||||
if (!this.didRecentTabSync) {
|
||||
|
@ -82,7 +109,7 @@ export const TabsSetupFlowManager = new (class {
|
|||
},
|
||||
});
|
||||
this.registerSetupState({
|
||||
uiStateIndex: 4,
|
||||
uiStateIndex: 5,
|
||||
name: "synced-tabs-loaded",
|
||||
exitConditions: () => {
|
||||
// This is the end state
|
||||
|
@ -92,6 +119,9 @@ export const TabsSetupFlowManager = new (class {
|
|||
|
||||
Services.obs.addObserver(this, lazy.UIState.ON_UPDATE);
|
||||
Services.obs.addObserver(this, TOPIC_DEVICELIST_UPDATED);
|
||||
Services.obs.addObserver(this, NETWORK_STATUS_CHANGED);
|
||||
Services.obs.addObserver(this, SYNC_SERVICE_ERROR);
|
||||
Services.obs.addObserver(this, SYNC_SERVICE_FINISHED);
|
||||
|
||||
// this.syncTabsPrefEnabled will track the value of the tabs pref
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
|
@ -145,9 +175,29 @@ export const TabsSetupFlowManager = new (class {
|
|||
secondaryDeviceConnected: this.secondaryDeviceConnected,
|
||||
};
|
||||
}
|
||||
|
||||
getErrorType() {
|
||||
// this ordering is important for dealing with multiple errors at once
|
||||
const errorStates = {
|
||||
"network-offline": !this.networkIsOnline,
|
||||
"sync-error": !this.syncIsWorking,
|
||||
"fxa-admin-disabled": Services.prefs.prefIsLocked(FXA_ENABLED),
|
||||
};
|
||||
|
||||
for (let [type, value] of Object.entries(errorStates)) {
|
||||
if (value) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
uninit() {
|
||||
Services.obs.removeObserver(this, lazy.UIState.ON_UPDATE);
|
||||
Services.obs.removeObserver(this, TOPIC_DEVICELIST_UPDATED);
|
||||
Services.obs.removeObserver(this, NETWORK_STATUS_CHANGED);
|
||||
Services.obs.removeObserver(this, SYNC_SERVICE_ERROR);
|
||||
Services.obs.removeObserver(this, SYNC_SERVICE_FINISHED);
|
||||
}
|
||||
get currentSetupState() {
|
||||
return this.setupState.get(this._currentSetupStateName);
|
||||
|
@ -225,6 +275,24 @@ export const TabsSetupFlowManager = new (class {
|
|||
this.refreshDevices();
|
||||
this.maybeUpdateUI();
|
||||
break;
|
||||
case "fxaccounts:device_connected":
|
||||
case "fxaccounts:device_disconnected":
|
||||
await lazy.fxAccounts.device.refreshDeviceList();
|
||||
this.maybeUpdateUI();
|
||||
break;
|
||||
case SYNC_SERVICE_ERROR:
|
||||
this.syncIsWorking = false;
|
||||
this.maybeUpdateUI();
|
||||
break;
|
||||
case NETWORK_STATUS_CHANGED:
|
||||
this.networkIsOnline = data == "online";
|
||||
this.maybeUpdateUI();
|
||||
break;
|
||||
case SYNC_SERVICE_FINISHED:
|
||||
if (!this.syncIsWorking) {
|
||||
this.syncIsWorking = true;
|
||||
this.maybeUpdateUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,6 +341,7 @@ export const TabsSetupFlowManager = new (class {
|
|||
|
||||
maybeUpdateUI() {
|
||||
let nextSetupStateName = this._currentSetupStateName;
|
||||
let errorState = null;
|
||||
|
||||
// state transition conditions
|
||||
for (let state of this.setupState.values()) {
|
||||
|
@ -283,8 +352,14 @@ export const TabsSetupFlowManager = new (class {
|
|||
}
|
||||
|
||||
let setupState = this.currentSetupState;
|
||||
if (nextSetupStateName != this._currentSetupStateName) {
|
||||
setupState = this.setupState.get(nextSetupStateName);
|
||||
const state = this.setupState.get(nextSetupStateName);
|
||||
const uiStateIndex = state.uiStateIndex;
|
||||
|
||||
if (
|
||||
uiStateIndex == 0 ||
|
||||
nextSetupStateName != this._currentSetupStateName
|
||||
) {
|
||||
setupState = state;
|
||||
this._currentSetupStateName = nextSetupStateName;
|
||||
this._uiUpdateNeeded = true;
|
||||
}
|
||||
|
@ -294,7 +369,10 @@ export const TabsSetupFlowManager = new (class {
|
|||
if (this.shouldShowMobilePromo) {
|
||||
this._didShowMobilePromo = true;
|
||||
}
|
||||
Services.obs.notifyObservers(null, TOPIC_SETUPSTATE_CHANGED);
|
||||
if (uiStateIndex == 0) {
|
||||
errorState = this.getErrorType();
|
||||
}
|
||||
Services.obs.notifyObservers(null, TOPIC_SETUPSTATE_CHANGED, errorState);
|
||||
}
|
||||
if ("function" == typeof setupState.enter) {
|
||||
setupState.enter();
|
||||
|
|
|
@ -45,6 +45,17 @@ firefoxview-tabpickup-synctabs-description = Allow { -brand-short-name } to shar
|
|||
firefoxview-tabpickup-synctabs-learn-how = Learn how
|
||||
firefoxview-tabpickup-synctabs-primarybutton = Sync open tabs
|
||||
|
||||
firefoxview-tabpickup-fxa-admin-disabled-header = Your organization has disabled sync
|
||||
firefoxview-tabpickup-fxa-admin-disabled-description = { -brand-short-name } is not able to sync tabs between devices because your administrator has disabled syncing.
|
||||
|
||||
firefoxview-tabpickup-network-offline-header = Check your internet connection
|
||||
firefoxview-tabpickup-network-offline-description = If you’re using a firewall or proxy, check that { -brand-short-name } has permission to access the web.
|
||||
firefoxview-tabpickup-network-offline-primarybutton = Try again
|
||||
|
||||
firefoxview-tabpickup-sync-error-header = We’re having trouble syncing
|
||||
firefoxview-tabpickup-sync-error-description = { -brand-short-name } can’t reach the service right now. Try again in a few moments.
|
||||
firefoxview-tabpickup-sync-error-primarybutton = Try again
|
||||
|
||||
firefoxview-tabpickup-syncing = Sit tight while your tabs sync. It’ll be just a moment.
|
||||
|
||||
firefoxview-mobile-promo-header = Grab tabs from your phone or tablet
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
--colorways-grid-template-rows: auto auto auto;
|
||||
--colorways-figure-display: flex;
|
||||
--colorways-header-flex-direction: column;
|
||||
|
||||
--info-icon-background-color: #0090ED;
|
||||
--card-border-zap-gradient: linear-gradient(90deg, #9059FF 0%, #FF4AA2 52.08%, #FFBD4F 100%);
|
||||
}
|
||||
|
||||
|
@ -282,6 +282,19 @@ button.close {
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.error-state {
|
||||
text-align: center;
|
||||
padding: 0 0 20px;
|
||||
border: 1px solid var(--fxview-contrast-border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.error-state > h3 {
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
padding: 24px 16px;
|
||||
|
@ -423,12 +436,9 @@ button.close {
|
|||
|
||||
.synced-tabs-container > .loading-content {
|
||||
text-align: center;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
background: url(chrome://global/skin/icons/loading-dial.svg) no-repeat center top;
|
||||
background-size: 32px;
|
||||
margin-top: 32px;
|
||||
padding: 48px 16px 16px;
|
||||
color: var(--fxview-text-secondary-color);
|
||||
margin-top: 40px;
|
||||
padding: 20px 16px 16px;
|
||||
}
|
||||
|
||||
.closed-tabs-list {
|
||||
|
@ -694,6 +704,17 @@ button.close {
|
|||
background-image: url('chrome://browser/skin/synced-tabs.svg');
|
||||
}
|
||||
|
||||
.info {
|
||||
background-image: url('chrome://global/skin/icons/info-filled.svg');
|
||||
}
|
||||
|
||||
.error-state > .info {
|
||||
vertical-align: text-top;
|
||||
margin-inline-end: 7px;
|
||||
margin-top: 1px;
|
||||
color: var(--info-icon-background-color);
|
||||
}
|
||||
|
||||
.favicon {
|
||||
background-size: cover;
|
||||
-moz-context-properties: fill;
|
||||
|
@ -705,6 +726,25 @@ button.close {
|
|||
height: 16px;
|
||||
}
|
||||
|
||||
.sync {
|
||||
background-image: url(chrome://browser/skin/sync.svg);
|
||||
background-size: cover;
|
||||
height: 19px;
|
||||
width: 19px;
|
||||
color: var(--fxview-text-secondary-color);
|
||||
}
|
||||
|
||||
@keyframes syncRotate {
|
||||
from { transform: rotate(0); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.sync {
|
||||
animation: syncRotate 0.8s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.last-active-badge {
|
||||
height: 1.25em;
|
||||
width: 6em;
|
||||
|
|
|
@ -33,52 +33,61 @@
|
|||
<main>
|
||||
<template id="sync-setup-template">
|
||||
<named-deck class="sync-setup-container" data-prefix="id:">
|
||||
<div name="sync-setup-view0" data-prefix="id:-view0" class="card zap-card setup-step" data-prefix="aria-labelledby:-view0-header">
|
||||
<h2 data-prefix="id:-view0-header" data-l10n-id="firefoxview-tabpickup-step-signin-header" class="card-header"></h2>
|
||||
<div name="sync-setup-view0" data-prefix="id:-view0" class="card error-state" data-prefix="aria-labelledby:-view0-header">
|
||||
<icon class="icon info primary"></icon><h3 data-prefix="id:-view0-header" class="card-header"></h3>
|
||||
<section>
|
||||
<p>
|
||||
<span id="error-state-description"></span>
|
||||
</p>
|
||||
<button id="error-state-button" class="primary"></button>
|
||||
</section>
|
||||
</div>
|
||||
<div name="sync-setup-view1" data-prefix="id:-view1" class="card zap-card setup-step" data-prefix="aria-labelledby:-view1-header">
|
||||
<h2 data-prefix="id:-view1-header" data-l10n-id="firefoxview-tabpickup-step-signin-header" class="card-header"></h2>
|
||||
<section class="step-body">
|
||||
<p class="step-content" data-l10n-id="firefoxview-tabpickup-step-signin-description"></p>
|
||||
<button class="primary" data-action="view0-primary-action" data-l10n-id="firefoxview-tabpickup-step-signin-primarybutton"></button>
|
||||
<button class="primary" data-action="view1-primary-action" data-l10n-id="firefoxview-tabpickup-step-signin-primarybutton"></button>
|
||||
</section>
|
||||
<footer>
|
||||
<progress data-prefix="id:-view0-progress" class="step-progress" max="100" value="11"></progress>
|
||||
<progress data-prefix="id:-view1-progress" class="step-progress" max="100" value="11"></progress>
|
||||
<label
|
||||
data-prefix="for:-view0-progress"
|
||||
data-prefix="for:-view1-progress"
|
||||
data-l10n-id="firefoxview-tabpickup-progress-label"
|
||||
data-l10n-args='{"percentValue":"11"}'></label>
|
||||
</footer>
|
||||
</div>
|
||||
<div name="sync-setup-view1" data-prefix="id:-view1" class="card zap-card setup-step" data-prefix="aria-labelledby:-view1-header">
|
||||
<h2 data-prefix="id:-view1-header" data-l10n-id="firefoxview-tabpickup-adddevice-header" class="card-header"></h2>
|
||||
<div name="sync-setup-view2" data-prefix="id:-view2" class="card zap-card setup-step" data-prefix="aria-labelledby:-view2-header">
|
||||
<h2 data-prefix="id:-view2-header" data-l10n-id="firefoxview-tabpickup-adddevice-header" class="card-header"></h2>
|
||||
<section class="step-body">
|
||||
<p class="step-content">
|
||||
<span data-l10n-id="firefoxview-tabpickup-adddevice-description"></span>
|
||||
<br/>
|
||||
<a target="_blank" data-support-url="tab-pickup-firefox-view" data-l10n-id="firefoxview-tabpickup-adddevice-learn-how"></a>
|
||||
</p>
|
||||
<button class="primary" data-action="view1-primary-action" data-l10n-id="firefoxview-tabpickup-adddevice-primarybutton"></button>
|
||||
<button class="primary" data-action="view2-primary-action" data-l10n-id="firefoxview-tabpickup-adddevice-primarybutton"></button>
|
||||
</section>
|
||||
<footer>
|
||||
<progress data-prefix="id:-view1-progress" class="step-progress" max="100" value="33"></progress>
|
||||
<progress data-prefix="id:-view2-progress" class="step-progress" max="100" value="33"></progress>
|
||||
<label
|
||||
data-prefix="for:-view1-progress"
|
||||
data-prefix="for:-view2-progress"
|
||||
data-l10n-id="firefoxview-tabpickup-progress-label"
|
||||
data-l10n-args='{"percentValue":"33"}'></label>
|
||||
</footer>
|
||||
</div>
|
||||
<div name="sync-setup-view2" data-prefix="id:-view2" class="card zap-card setup-step" data-prefix="aria-labelledby:-view2-header">
|
||||
<h2 data-prefix="id:-view2-header" data-l10n-id="firefoxview-tabpickup-synctabs-header" class="card-header"></h2>
|
||||
<div name="sync-setup-view3" data-prefix="id:-view3" class="card zap-card setup-step" data-prefix="aria-labelledby:-view3-header">
|
||||
<h2 data-prefix="id:-view3-header" data-l10n-id="firefoxview-tabpickup-synctabs-header" class="card-header"></h2>
|
||||
<section class="step-body">
|
||||
<p class="step-content">
|
||||
<span data-l10n-id="firefoxview-tabpickup-synctabs-description"></span>
|
||||
<br/>
|
||||
<a target="_blank" data-support-url="tab-pickup-firefox-view" data-l10n-id="firefoxview-tabpickup-synctabs-learn-how"></a>
|
||||
</p>
|
||||
<button class="primary" data-action="view2-primary-action" data-l10n-id="firefoxview-tabpickup-synctabs-primarybutton"></button>
|
||||
<button class="primary" data-action="view3-primary-action" data-l10n-id="firefoxview-tabpickup-synctabs-primarybutton"></button>
|
||||
</section>
|
||||
<footer>
|
||||
<progress data-prefix="id:-view2-progress" class="step-progress" max="100" value="66"></progress>
|
||||
<progress data-prefix="id:-view3-progress" class="step-progress" max="100" value="66"></progress>
|
||||
<label
|
||||
data-prefix="for:-view2-progress"
|
||||
data-prefix="for:-view3-progress"
|
||||
data-l10n-id="firefoxview-tabpickup-progress-label"
|
||||
data-l10n-args='{"percentValue":"66"}'></label>
|
||||
</footer>
|
||||
|
@ -94,7 +103,10 @@
|
|||
<icon class="icon synced-tabs"></icon>
|
||||
<p data-l10n-id="firefoxview-synced-tabs-placeholder" class="placeholder-text"></p>
|
||||
</div>
|
||||
<p class="loading-content" data-l10n-id="firefoxview-tabpickup-syncing"></p>
|
||||
<div class="loading-content">
|
||||
<icon class="icon sync"></icon>
|
||||
<p data-l10n-id="firefoxview-tabpickup-syncing"></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ class TabPickupContainer extends HTMLElement {
|
|||
super();
|
||||
this.boundObserve = (...args) => this.observe(...args);
|
||||
this._currentSetupStateIndex = -1;
|
||||
this.errorState = null;
|
||||
}
|
||||
get setupContainerElem() {
|
||||
return this.querySelector(".sync-setup-container");
|
||||
|
@ -29,6 +30,11 @@ class TabPickupContainer extends HTMLElement {
|
|||
return this.querySelector("#collapsible-synced-tabs-button");
|
||||
}
|
||||
|
||||
getWindow() {
|
||||
return this.ownerGlobal.browsingContext.embedderWindowGlobal.browsingContext
|
||||
.window;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.addEventListener("click", this);
|
||||
this.addEventListener("visibilitychange", this);
|
||||
|
@ -51,15 +57,20 @@ class TabPickupContainer extends HTMLElement {
|
|||
}
|
||||
if (event.type == "click" && event.target.dataset.action) {
|
||||
switch (event.target.dataset.action) {
|
||||
case "view0-primary-action": {
|
||||
TabsSetupFlowManager.openFxASignup(event.target.ownerGlobal);
|
||||
case "view0-sync-error-action":
|
||||
case "view0-network-offline-action": {
|
||||
this.getWindow().gBrowser.reload();
|
||||
break;
|
||||
}
|
||||
case "view1-primary-action": {
|
||||
TabsSetupFlowManager.openSyncPreferences(event.target.ownerGlobal);
|
||||
TabsSetupFlowManager.openFxASignup(event.target.ownerGlobal);
|
||||
break;
|
||||
}
|
||||
case "view2-primary-action": {
|
||||
TabsSetupFlowManager.openSyncPreferences(event.target.ownerGlobal);
|
||||
break;
|
||||
}
|
||||
case "view3-primary-action": {
|
||||
TabsSetupFlowManager.syncOpenTabs(event.target);
|
||||
break;
|
||||
}
|
||||
|
@ -86,9 +97,9 @@ class TabPickupContainer extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
async observe(subject, topic, data) {
|
||||
async observe(subject, topic, errorState) {
|
||||
if (topic == TOPIC_SETUPSTATE_CHANGED) {
|
||||
this.update();
|
||||
this.update({ errorState });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,6 +143,7 @@ class TabPickupContainer extends HTMLElement {
|
|||
stateIndex = TabsSetupFlowManager.uiStateIndex,
|
||||
showMobilePromo = TabsSetupFlowManager.shouldShowMobilePromo,
|
||||
showMobilePairSuccess = TabsSetupFlowManager.shouldShowMobileConnectedSuccess,
|
||||
errorState = TabsSetupFlowManager.getErrorType(),
|
||||
} = {}) {
|
||||
let needsRender = false;
|
||||
if (showMobilePromo !== this._showMobilePromo) {
|
||||
|
@ -142,12 +154,46 @@ class TabPickupContainer extends HTMLElement {
|
|||
this._showMobilePairSuccess = showMobilePairSuccess;
|
||||
needsRender = true;
|
||||
}
|
||||
if (stateIndex !== this._currentSetupStateIndex) {
|
||||
if (stateIndex !== this._currentSetupStateIndex || stateIndex == 0) {
|
||||
this._currentSetupStateIndex = stateIndex;
|
||||
needsRender = true;
|
||||
this.errorState = errorState;
|
||||
}
|
||||
needsRender && this.render();
|
||||
}
|
||||
|
||||
generateErrorMessage() {
|
||||
const errorStateHeader = this.querySelector(
|
||||
"#tabpickup-steps-view0-header"
|
||||
);
|
||||
const errorStateDescription = this.querySelector(
|
||||
"#error-state-description"
|
||||
);
|
||||
const errorStateButton = this.querySelector("#error-state-button");
|
||||
|
||||
document.l10n.setAttributes(
|
||||
errorStateHeader,
|
||||
`firefoxview-tabpickup-${this.errorState}-header`
|
||||
);
|
||||
document.l10n.setAttributes(
|
||||
errorStateDescription,
|
||||
`firefoxview-tabpickup-${this.errorState}-description`
|
||||
);
|
||||
|
||||
errorStateButton.hidden = this.errorState == "fxa-admin-disabled";
|
||||
|
||||
if (this.errorState != "fxa-admin-disabled") {
|
||||
document.l10n.setAttributes(
|
||||
errorStateButton,
|
||||
`firefoxview-tabpickup-${this.errorState}-primarybutton`
|
||||
);
|
||||
errorStateButton.setAttribute(
|
||||
"data-action",
|
||||
`view0-${this.errorState}-action`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.isConnected) {
|
||||
return;
|
||||
|
@ -159,10 +205,10 @@ class TabPickupContainer extends HTMLElement {
|
|||
let mobileSuccessElem = this.mobileSuccessElem;
|
||||
|
||||
const stateIndex = this._currentSetupStateIndex;
|
||||
const isLoading = stateIndex == 3;
|
||||
const isLoading = stateIndex == 4;
|
||||
|
||||
// show/hide either the setup or tab list containers, creating each as necessary
|
||||
if (stateIndex < 3) {
|
||||
if (stateIndex < 4) {
|
||||
if (!setupElem) {
|
||||
this.insertTemplatedElement(
|
||||
"sync-setup-template",
|
||||
|
@ -176,6 +222,10 @@ class TabPickupContainer extends HTMLElement {
|
|||
}
|
||||
setupElem.hidden = false;
|
||||
setupElem.selectedViewName = `sync-setup-view${stateIndex}`;
|
||||
|
||||
if (stateIndex == 0 && this.errorState) {
|
||||
this.generateErrorMessage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -193,7 +243,7 @@ class TabPickupContainer extends HTMLElement {
|
|||
tabsElem.hidden = false;
|
||||
tabsElem.classList.toggle("loading", isLoading);
|
||||
|
||||
if (stateIndex == 4) {
|
||||
if (stateIndex == 5) {
|
||||
this.collapsibleButton.hidden = false;
|
||||
}
|
||||
mobilePromoElem.hidden = !this._showMobilePromo;
|
||||
|
|
|
@ -62,7 +62,7 @@ async function setupWithDesktopDevices() {
|
|||
},
|
||||
],
|
||||
});
|
||||
// ensure tab sync is false so we don't skip onto next step
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["services.sync.engine.tabs", true]],
|
||||
});
|
||||
|
@ -139,6 +139,9 @@ async function tearDown(sandbox) {
|
|||
}
|
||||
|
||||
add_setup(async function() {
|
||||
// we only use this for the first test, then we reset it
|
||||
Services.prefs.lockPref("identity.fxaccounts.enabled");
|
||||
|
||||
if (!Services.prefs.getBoolPref("browser.tabs.firefox-view")) {
|
||||
info(
|
||||
"firefox-view pref was off, toggling it on and adding the tabstrip widget"
|
||||
|
@ -170,12 +173,55 @@ add_setup(async function() {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(async function test_sync_admin_disabled() {
|
||||
const sandbox = setupMocks({ state: UIState.STATUS_NOT_CONFIGURED });
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
is(
|
||||
Services.prefs.getBoolPref("identity.fxaccounts.enabled"),
|
||||
true,
|
||||
"Expected identity.fxaccounts.enabled pref to be false"
|
||||
);
|
||||
|
||||
is(
|
||||
Services.prefs.prefIsLocked("identity.fxaccounts.enabled"),
|
||||
true,
|
||||
"Expected identity.fxaccounts.enabled pref to be locked"
|
||||
);
|
||||
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view0",
|
||||
});
|
||||
|
||||
const errorStateHeader = document.querySelector(
|
||||
"#tabpickup-steps-view0-header"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.waitForMutationCondition(
|
||||
errorStateHeader,
|
||||
{ childList: true },
|
||||
() => errorStateHeader.textContent.includes("disabled")
|
||||
);
|
||||
|
||||
ok(
|
||||
errorStateHeader
|
||||
.getAttribute("data-l10n-id")
|
||||
.includes("fxa-admin-disabled"),
|
||||
"Correct message should show when fxa is disabled by an admin"
|
||||
);
|
||||
});
|
||||
Services.prefs.unlockPref("identity.fxaccounts.enabled");
|
||||
await tearDown(sandbox);
|
||||
});
|
||||
|
||||
add_task(async function test_unconfigured_initial_state() {
|
||||
const sandbox = setupMocks({ state: UIState.STATUS_NOT_CONFIGURED });
|
||||
await withFirefoxView({}, async browser => {
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view0",
|
||||
expectedVisible: "#tabpickup-steps-view1",
|
||||
});
|
||||
checkMobilePromo(browser, {
|
||||
mobilePromo: false,
|
||||
|
@ -197,10 +243,11 @@ add_task(async function test_signed_in() {
|
|||
},
|
||||
],
|
||||
});
|
||||
|
||||
await withFirefoxView({}, async browser => {
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view1",
|
||||
expectedVisible: "#tabpickup-steps-view2",
|
||||
});
|
||||
|
||||
is(
|
||||
|
@ -242,7 +289,7 @@ add_task(async function test_2nd_desktop_connected() {
|
|||
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view2",
|
||||
expectedVisible: "#tabpickup-steps-view3",
|
||||
});
|
||||
|
||||
is(fxAccounts.device.recentDeviceList?.length, 2, "2 devices connected");
|
||||
|
@ -286,7 +333,7 @@ add_task(async function test_mobile_connected() {
|
|||
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view2",
|
||||
expectedVisible: "#tabpickup-steps-view3",
|
||||
});
|
||||
|
||||
is(fxAccounts.device.recentDeviceList?.length, 2, "2 devices connected");
|
||||
|
@ -326,7 +373,7 @@ add_task(async function test_tab_sync_enabled() {
|
|||
|
||||
// test initial state, with the pref not enabled
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view2",
|
||||
expectedVisible: "#tabpickup-steps-view3",
|
||||
});
|
||||
checkMobilePromo(browser, {
|
||||
mobilePromo: false,
|
||||
|
@ -346,7 +393,7 @@ add_task(async function test_tab_sync_enabled() {
|
|||
// reset and test clicking the action button
|
||||
await SpecialPowers.popPrefEnv();
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view2",
|
||||
expectedVisible: "#tabpickup-steps-view3",
|
||||
});
|
||||
checkMobilePromo(browser, {
|
||||
mobilePromo: false,
|
||||
|
@ -354,7 +401,7 @@ add_task(async function test_tab_sync_enabled() {
|
|||
});
|
||||
|
||||
const actionButton = browser.contentWindow.document.querySelector(
|
||||
"#tabpickup-steps-view2 button.primary"
|
||||
"#tabpickup-steps-view3 button.primary"
|
||||
);
|
||||
actionButton.click();
|
||||
|
||||
|
@ -673,3 +720,76 @@ add_task(async function test_mobile_promo_windows() {
|
|||
});
|
||||
await tearDown(sandbox);
|
||||
});
|
||||
|
||||
add_task(async function test_network_offline() {
|
||||
const sandbox = await setupWithDesktopDevices();
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"network:offline-status-changed",
|
||||
"offline"
|
||||
);
|
||||
await waitForElementVisible(browser, "#tabpickup-steps", true);
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view0",
|
||||
});
|
||||
|
||||
const errorStateHeader = document.querySelector(
|
||||
"#tabpickup-steps-view0-header"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.waitForMutationCondition(
|
||||
errorStateHeader,
|
||||
{ childList: true },
|
||||
() => errorStateHeader.textContent.includes("connection")
|
||||
);
|
||||
|
||||
ok(
|
||||
errorStateHeader.getAttribute("data-l10n-id").includes("network-offline"),
|
||||
"Correct message should show when network connection is lost"
|
||||
);
|
||||
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"network:offline-status-changed",
|
||||
"online"
|
||||
);
|
||||
|
||||
await waitForElementVisible(browser, "#tabpickup-tabs-container", true);
|
||||
});
|
||||
await tearDown(sandbox);
|
||||
});
|
||||
|
||||
add_task(async function test_sync_error() {
|
||||
const sandbox = await setupWithDesktopDevices();
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:error");
|
||||
|
||||
await waitForElementVisible(browser, "#tabpickup-steps", true);
|
||||
await waitForVisibleStep(browser, {
|
||||
expectedVisible: "#tabpickup-steps-view0",
|
||||
});
|
||||
|
||||
const errorStateHeader = document.querySelector(
|
||||
"#tabpickup-steps-view0-header"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.waitForMutationCondition(
|
||||
errorStateHeader,
|
||||
{ childList: true },
|
||||
() => errorStateHeader.textContent.includes("trouble syncing")
|
||||
);
|
||||
|
||||
ok(
|
||||
errorStateHeader.getAttribute("data-l10n-id").includes("sync-error"),
|
||||
"Correct message should show when there's a sync service error"
|
||||
);
|
||||
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:finished");
|
||||
});
|
||||
await tearDown(sandbox);
|
||||
});
|
||||
|
|
|
@ -77,9 +77,6 @@
|
|||
skin/classic/global/icons/link.svg (../../shared/icons/link.svg)
|
||||
skin/classic/global/icons/loading.png (../../shared/icons/loading.png)
|
||||
skin/classic/global/icons/loading@2x.png (../../shared/icons/loading@2x.png)
|
||||
#ifdef NIGHTLY_BUILD
|
||||
skin/classic/global/icons/loading-dial.svg (../../shared/icons/loading-dial.svg)
|
||||
#endif
|
||||
skin/classic/global/icons/more.svg (../../shared/icons/more.svg)
|
||||
skin/classic/global/icons/open-in-new.svg (../../shared/icons/open-in-new.svg)
|
||||
skin/classic/global/icons/page-portrait.svg (../../shared/icons/page-portrait.svg)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<!-- 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/.-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity" style="animation:spinIcon 1.2s steps(12,end) infinite">
|
||||
<style>@keyframes spinIcon{to{transform:rotate(360deg)}}</style>
|
||||
<path d="m7 3 0-2s0-1 1-1 1 1 1 1l0 2s0 1-1 1-1-1-1-1z"/>
|
||||
<path d="m4.634 4.17-1-1.732s-.5-.866.366-1.366 1.366.366 1.366.366l1 1.732s.5.866-.366 1.366-1.366-.366-1.366-.366z" opacity=".93"/>
|
||||
<path d="m3.17 6.366-1.732-1S.572 4.866 1.072 4s1.366-.366 1.366-.366l1.732 1s.866.5.366 1.366-1.366.366-1.366.366z" opacity=".86"/>
|
||||
<path d="M3 9 1 9S0 9 0 8s1-1 1-1l2 0s1 0 1 1-1 1-1 1z" opacity=".79"/>
|
||||
<path d="m4.17 11.366-1.732 1s-.866.5-1.366-.366.366-1.366.366-1.366l1.732-1s.866-.5 1.366.366-.366 1.366-.366 1.366z" opacity=".72"/>
|
||||
<path d="m6.366 12.83-1 1.732s-.5.866-1.366.366-.366-1.366-.366-1.366l1-1.732s.5-.866 1.366-.366.366 1.366.366 1.366z" opacity=".65"/>
|
||||
<path d="m9 13 0 2s0 1-1 1-1-1-1-1l0-2s0-1 1-1 1 1 1 1z" opacity=".58"/>
|
||||
<path d="m11.366 11.83 1 1.732s.5.866-.366 1.366-1.366-.366-1.366-.366l-1-1.732s-.5-.866.366-1.366 1.366.366 1.366.366z" opacity=".51"/>
|
||||
<path d="m12.83 9.634 1.732 1s.866.5.366 1.366-1.366.366-1.366.366l-1.732-1s-.866-.5-.366-1.366 1.366-.366 1.366-.366z" opacity=".44"/>
|
||||
<path d="m13 7 2 0s1 0 1 1-1 1-1 1l-2 0s-1 0-1-1 1-1 1-1z" opacity=".37"/>
|
||||
<path d="m11.83 4.634 1.732-1s.866-.5 1.366.366-.366 1.366-.366 1.366l-1.732 1s-.866.5-1.366-.366.366-1.366.366-1.366z" opacity=".5"/>
|
||||
<path d="m9.634 3.17 1-1.732s.5-.866 1.366-.366.366 1.366.366 1.366l-1 1.732s-.5.866-1.366.366-.366-1.366-.366-1.366z" opacity=".75"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.8 KiB |
Загрузка…
Ссылка в новой задаче