forked from mirrors/gecko-dev
Bug 1880539 - Refactor firefoxview/syncedtabs.mjs to make business logic more reusable r=fxview-reviewers,nsharpley,sclements
Decoupling complex business logic from UI components. https://treeherder.mozilla.org/jobs?repo=try&revision=a65ebee15c1ef5f47411326b0c9ba2394e17d80f Differential Revision: https://phabricator.services.mozilla.com/D204081
This commit is contained in:
parent
805b20a4fa
commit
86b13540d5
3 changed files with 342 additions and 294 deletions
297
browser/components/firefoxview/SyncedTabsController.sys.mjs
Normal file
297
browser/components/firefoxview/SyncedTabsController.sys.mjs
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/* 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/. */
|
||||
|
||||
const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
ObjectUtils: "resource://gre/modules/ObjectUtils.sys.mjs",
|
||||
SyncedTabs: "resource://services-sync/SyncedTabs.sys.mjs",
|
||||
});
|
||||
|
||||
import { SyncedTabsErrorHandler } from "resource:///modules/firefox-view-synced-tabs-error-handler.sys.mjs";
|
||||
import { TabsSetupFlowManager } from "resource:///modules/firefox-view-tabs-setup-manager.sys.mjs";
|
||||
import { searchTabList } from "chrome://browser/content/firefoxview/helpers.mjs";
|
||||
|
||||
const SYNCED_TABS_CHANGED = "services.sync.tabs.changed";
|
||||
const TOPIC_SETUPSTATE_CHANGED = "firefox-view.setupstate.changed";
|
||||
|
||||
/**
|
||||
* The controller for synced tabs components.
|
||||
*
|
||||
* @implements {ReactiveController}
|
||||
*/
|
||||
export class SyncedTabsController {
|
||||
currentSetupStateIndex = -1;
|
||||
currentSyncedTabs = [];
|
||||
devices = [];
|
||||
|
||||
/**
|
||||
* The current error state as determined by `SyncedTabsErrorHandler`.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
errorState = null;
|
||||
|
||||
/**
|
||||
* Component associated with this controller.
|
||||
*
|
||||
* @type {ReactiveControllerHost}
|
||||
*/
|
||||
host;
|
||||
|
||||
searchQuery = "";
|
||||
|
||||
/**
|
||||
* Construct a new SyncedTabsController.
|
||||
*
|
||||
* @param {ReactiveControllerHost} host
|
||||
*/
|
||||
constructor(host) {
|
||||
this.observe = this.observe.bind(this);
|
||||
this.host = host;
|
||||
this.host.addController(this);
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
this.host.addEventListener("click", this);
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
this.host.removeEventListener("click", this);
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type == "click" && event.target.dataset.action) {
|
||||
const { ErrorType } = SyncedTabsErrorHandler;
|
||||
switch (event.target.dataset.action) {
|
||||
case `${ErrorType.SYNC_ERROR}`:
|
||||
case `${ErrorType.NETWORK_OFFLINE}`:
|
||||
case `${ErrorType.PASSWORD_LOCKED}`: {
|
||||
TabsSetupFlowManager.tryToClearError();
|
||||
break;
|
||||
}
|
||||
case `${ErrorType.SIGNED_OUT}`:
|
||||
case "sign-in": {
|
||||
TabsSetupFlowManager.openFxASignup(event.target.ownerGlobal);
|
||||
break;
|
||||
}
|
||||
case "add-device": {
|
||||
TabsSetupFlowManager.openFxAPairDevice(event.target.ownerGlobal);
|
||||
break;
|
||||
}
|
||||
case "sync-tabs-disabled": {
|
||||
TabsSetupFlowManager.syncOpenTabs(event.target);
|
||||
break;
|
||||
}
|
||||
case `${ErrorType.SYNC_DISCONNECTED}`: {
|
||||
const win = event.target.ownerGlobal;
|
||||
const { switchToTabHavingURI } =
|
||||
win.docShell.chromeEventHandler.ownerGlobal;
|
||||
switchToTabHavingURI(
|
||||
"about:preferences?action=choose-what-to-sync#sync",
|
||||
true,
|
||||
{}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async observe(_, topic, errorState) {
|
||||
if (topic == TOPIC_SETUPSTATE_CHANGED) {
|
||||
await this.updateStates(errorState);
|
||||
}
|
||||
if (topic == SYNCED_TABS_CHANGED) {
|
||||
await this.getSyncedTabData();
|
||||
}
|
||||
this.host.requestUpdate();
|
||||
}
|
||||
|
||||
async updateStates(errorState) {
|
||||
let stateIndex = TabsSetupFlowManager.uiStateIndex;
|
||||
errorState = errorState || SyncedTabsErrorHandler.getErrorType();
|
||||
|
||||
if (stateIndex == 4 && this.currentSetupStateIndex !== stateIndex) {
|
||||
// trigger an initial request for the synced tabs list
|
||||
await this.getSyncedTabData();
|
||||
}
|
||||
|
||||
this.currentSetupStateIndex = stateIndex;
|
||||
this.errorState = errorState;
|
||||
}
|
||||
|
||||
actionMappings = {
|
||||
"sign-in": {
|
||||
header: "firefoxview-syncedtabs-signin-header",
|
||||
description: "firefoxview-syncedtabs-signin-description",
|
||||
buttonLabel: "firefoxview-syncedtabs-signin-primarybutton",
|
||||
},
|
||||
"add-device": {
|
||||
header: "firefoxview-syncedtabs-adddevice-header",
|
||||
description: "firefoxview-syncedtabs-adddevice-description",
|
||||
buttonLabel: "firefoxview-syncedtabs-adddevice-primarybutton",
|
||||
descriptionLink: {
|
||||
name: "url",
|
||||
url: "https://support.mozilla.org/kb/how-do-i-set-sync-my-computer#w_connect-additional-devices-to-sync",
|
||||
},
|
||||
},
|
||||
"sync-tabs-disabled": {
|
||||
header: "firefoxview-syncedtabs-synctabs-header",
|
||||
description: "firefoxview-syncedtabs-synctabs-description",
|
||||
buttonLabel: "firefoxview-tabpickup-synctabs-primarybutton",
|
||||
},
|
||||
loading: {
|
||||
header: "firefoxview-syncedtabs-loading-header",
|
||||
description: "firefoxview-syncedtabs-loading-description",
|
||||
},
|
||||
};
|
||||
|
||||
#getMessageCardForState({ error = false, action, errorState }) {
|
||||
errorState = errorState || this.errorState;
|
||||
let header,
|
||||
description,
|
||||
descriptionLink,
|
||||
buttonLabel,
|
||||
headerIconUrl,
|
||||
mainImageUrl;
|
||||
let descriptionArray;
|
||||
if (error) {
|
||||
let link;
|
||||
({ header, description, link, buttonLabel } =
|
||||
SyncedTabsErrorHandler.getFluentStringsForErrorType(errorState));
|
||||
action = `${errorState}`;
|
||||
headerIconUrl = "chrome://global/skin/icons/info-filled.svg";
|
||||
mainImageUrl =
|
||||
"chrome://browser/content/firefoxview/synced-tabs-error.svg";
|
||||
descriptionArray = [description];
|
||||
if (errorState == "password-locked") {
|
||||
descriptionLink = {};
|
||||
// This is ugly, but we need to special case this link so we can
|
||||
// coexist with the old view.
|
||||
descriptionArray.push("firefoxview-syncedtab-password-locked-link");
|
||||
descriptionLink.name = "syncedtab-password-locked-link";
|
||||
descriptionLink.url = link.href;
|
||||
}
|
||||
} else {
|
||||
header = this.actionMappings[action].header;
|
||||
description = this.actionMappings[action].description;
|
||||
buttonLabel = this.actionMappings[action].buttonLabel;
|
||||
descriptionLink = this.actionMappings[action].descriptionLink;
|
||||
mainImageUrl =
|
||||
"chrome://browser/content/firefoxview/synced-tabs-error.svg";
|
||||
descriptionArray = [description];
|
||||
}
|
||||
return {
|
||||
action,
|
||||
buttonLabel,
|
||||
descriptionArray,
|
||||
descriptionLink,
|
||||
error,
|
||||
header,
|
||||
headerIconUrl,
|
||||
mainImageUrl,
|
||||
};
|
||||
}
|
||||
|
||||
getRenderInfo() {
|
||||
let renderInfo = {};
|
||||
for (let tab of this.currentSyncedTabs) {
|
||||
if (!(tab.client in renderInfo)) {
|
||||
renderInfo[tab.client] = {
|
||||
name: tab.device,
|
||||
deviceType: tab.deviceType,
|
||||
tabs: [],
|
||||
};
|
||||
}
|
||||
renderInfo[tab.client].tabs.push(tab);
|
||||
}
|
||||
|
||||
// Add devices without tabs
|
||||
for (let device of this.devices) {
|
||||
if (!(device.id in renderInfo)) {
|
||||
renderInfo[device.id] = {
|
||||
name: device.name,
|
||||
deviceType: device.clientType,
|
||||
tabs: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (let id in renderInfo) {
|
||||
renderInfo[id].tabItems = this.searchQuery
|
||||
? searchTabList(this.searchQuery, this.getTabItems(renderInfo[id].tabs))
|
||||
: this.getTabItems(renderInfo[id].tabs);
|
||||
}
|
||||
return renderInfo;
|
||||
}
|
||||
|
||||
getMessageCard() {
|
||||
switch (this.currentSetupStateIndex) {
|
||||
case 0 /* error-state */:
|
||||
if (this.errorState) {
|
||||
return this.#getMessageCardForState({ error: true });
|
||||
}
|
||||
return this.#getMessageCardForState({ action: "loading" });
|
||||
case 1 /* not-signed-in */:
|
||||
if (Services.prefs.prefHasUserValue("services.sync.lastversion")) {
|
||||
// If this pref is set, the user has signed out of sync.
|
||||
// This path is also taken if we are disconnected from sync. See bug 1784055
|
||||
return this.#getMessageCardForState({
|
||||
error: true,
|
||||
errorState: "signed-out",
|
||||
});
|
||||
}
|
||||
return this.#getMessageCardForState({ action: "sign-in" });
|
||||
case 2 /* connect-secondary-device*/:
|
||||
return this.#getMessageCardForState({ action: "add-device" });
|
||||
case 3 /* disabled-tab-sync */:
|
||||
return this.#getMessageCardForState({ action: "sync-tabs-disabled" });
|
||||
case 4 /* synced-tabs-loaded*/:
|
||||
// There seems to be an edge case where sync says everything worked
|
||||
// fine but we have no devices.
|
||||
if (!this.devices.length) {
|
||||
return this.#getMessageCardForState({ action: "add-device" });
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getTabItems(tabs) {
|
||||
return tabs?.map(tab => ({
|
||||
icon: tab.icon,
|
||||
title: tab.title,
|
||||
time: tab.lastUsed * 1000,
|
||||
url: tab.url,
|
||||
primaryL10nId: "firefoxview-tabs-list-tab-button",
|
||||
primaryL10nArgs: JSON.stringify({ targetURI: tab.url }),
|
||||
secondaryL10nId: "fxviewtabrow-options-menu-button",
|
||||
secondaryL10nArgs: JSON.stringify({ tabTitle: tab.title }),
|
||||
}));
|
||||
}
|
||||
|
||||
updateTabsList(syncedTabs) {
|
||||
if (!syncedTabs.length) {
|
||||
this.currentSyncedTabs = syncedTabs;
|
||||
}
|
||||
|
||||
const tabsToRender = syncedTabs;
|
||||
|
||||
// Return early if new tabs are the same as previous ones
|
||||
if (lazy.ObjectUtils.deepEqual(tabsToRender, this.currentSyncedTabs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentSyncedTabs = tabsToRender;
|
||||
}
|
||||
|
||||
async getSyncedTabData() {
|
||||
this.devices = await lazy.SyncedTabs.getTabClients();
|
||||
let tabs = await lazy.SyncedTabs.createRecentTabsList(this.devices, 50, {
|
||||
removeAllDupes: false,
|
||||
removeDeviceDupes: true,
|
||||
});
|
||||
|
||||
this.updateTabsList(tabs);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,9 @@
|
|||
|
||||
const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
SyncedTabs: "resource://services-sync/SyncedTabs.sys.mjs",
|
||||
SyncedTabsController: "resource:///modules/SyncedTabsController.sys.mjs",
|
||||
});
|
||||
|
||||
const { SyncedTabsErrorHandler } = ChromeUtils.importESModule(
|
||||
"resource:///modules/firefox-view-synced-tabs-error-handler.sys.mjs"
|
||||
);
|
||||
const { TabsSetupFlowManager } = ChromeUtils.importESModule(
|
||||
"resource:///modules/firefox-view-tabs-setup-manager.sys.mjs"
|
||||
);
|
||||
|
|
@ -23,7 +20,6 @@ import { ViewPage } from "./viewpage.mjs";
|
|||
import {
|
||||
escapeHtmlEntities,
|
||||
isSearchEnabled,
|
||||
searchTabList,
|
||||
MAX_TABS_FOR_RECENT_BROWSING,
|
||||
navigateToLink,
|
||||
} from "./helpers.mjs";
|
||||
|
|
@ -33,34 +29,26 @@ const TOPIC_SETUPSTATE_CHANGED = "firefox-view.setupstate.changed";
|
|||
const UI_OPEN_STATE = "browser.tabs.firefox-view.ui-state.tab-pickup.open";
|
||||
|
||||
class SyncedTabsInView extends ViewPage {
|
||||
controller = new lazy.SyncedTabsController(this);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._started = false;
|
||||
this.boundObserve = (...args) => this.observe(...args);
|
||||
this._currentSetupStateIndex = -1;
|
||||
this.errorState = null;
|
||||
this._id = Math.floor(Math.random() * 10e6);
|
||||
this.currentSyncedTabs = [];
|
||||
if (this.recentBrowsing) {
|
||||
this.maxTabsLength = MAX_TABS_FOR_RECENT_BROWSING;
|
||||
} else {
|
||||
// Setting maxTabsLength to -1 for no max
|
||||
this.maxTabsLength = -1;
|
||||
}
|
||||
this.devices = [];
|
||||
this.fullyUpdated = false;
|
||||
this.searchQuery = "";
|
||||
this.showAll = false;
|
||||
this.cumulativeSearches = 0;
|
||||
this.onSearchQuery = this.onSearchQuery.bind(this);
|
||||
}
|
||||
|
||||
static properties = {
|
||||
...ViewPage.properties,
|
||||
errorState: { type: Number },
|
||||
currentSyncedTabs: { type: Array },
|
||||
_currentSetupStateIndex: { type: Number },
|
||||
devices: { type: Array },
|
||||
searchQuery: { type: String },
|
||||
showAll: { type: Boolean },
|
||||
cumulativeSearches: { type: Number },
|
||||
};
|
||||
|
|
@ -72,26 +60,21 @@ class SyncedTabsInView extends ViewPage {
|
|||
tabLists: { all: "fxview-tab-list" },
|
||||
};
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.addEventListener("click", this);
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = true;
|
||||
Services.obs.addObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
|
||||
Services.obs.addObserver(this.boundObserve, SYNCED_TABS_CHANGED);
|
||||
Services.obs.addObserver(this.controller.observe, TOPIC_SETUPSTATE_CHANGED);
|
||||
Services.obs.addObserver(this.controller.observe, SYNCED_TABS_CHANGED);
|
||||
|
||||
this.updateStates();
|
||||
this.controller.updateStates();
|
||||
this.onVisibilityChange();
|
||||
|
||||
if (this.recentBrowsing) {
|
||||
this.recentBrowsingElement.addEventListener(
|
||||
"fxview-search-textbox-query",
|
||||
this
|
||||
this.onSearchQuery
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -104,74 +87,25 @@ class SyncedTabsInView extends ViewPage {
|
|||
TabsSetupFlowManager.updateViewVisibility(this._id, "unloaded");
|
||||
this.onVisibilityChange();
|
||||
|
||||
Services.obs.removeObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
|
||||
Services.obs.removeObserver(this.boundObserve, SYNCED_TABS_CHANGED);
|
||||
Services.obs.removeObserver(
|
||||
this.controller.observe,
|
||||
TOPIC_SETUPSTATE_CHANGED
|
||||
);
|
||||
Services.obs.removeObserver(this.controller.observe, SYNCED_TABS_CHANGED);
|
||||
|
||||
if (this.recentBrowsing) {
|
||||
this.recentBrowsingElement.removeEventListener(
|
||||
"fxview-search-textbox-query",
|
||||
this
|
||||
this.onSearchQuery
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
willUpdate(changedProperties) {
|
||||
if (changedProperties.has("searchQuery")) {
|
||||
this.cumulativeSearches = this.searchQuery
|
||||
? this.cumulativeSearches + 1
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.stop();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type == "click" && event.target.dataset.action) {
|
||||
const { ErrorType } = SyncedTabsErrorHandler;
|
||||
switch (event.target.dataset.action) {
|
||||
case `${ErrorType.SYNC_ERROR}`:
|
||||
case `${ErrorType.NETWORK_OFFLINE}`:
|
||||
case `${ErrorType.PASSWORD_LOCKED}`: {
|
||||
TabsSetupFlowManager.tryToClearError();
|
||||
break;
|
||||
}
|
||||
case `${ErrorType.SIGNED_OUT}`:
|
||||
case "sign-in": {
|
||||
TabsSetupFlowManager.openFxASignup(event.target.ownerGlobal);
|
||||
break;
|
||||
}
|
||||
case "add-device": {
|
||||
TabsSetupFlowManager.openFxAPairDevice(event.target.ownerGlobal);
|
||||
break;
|
||||
}
|
||||
case "sync-tabs-disabled": {
|
||||
TabsSetupFlowManager.syncOpenTabs(event.target);
|
||||
break;
|
||||
}
|
||||
case `${ErrorType.SYNC_DISCONNECTED}`: {
|
||||
const win = event.target.ownerGlobal;
|
||||
const { switchToTabHavingURI } =
|
||||
win.docShell.chromeEventHandler.ownerGlobal;
|
||||
switchToTabHavingURI(
|
||||
"about:preferences?action=choose-what-to-sync#sync",
|
||||
true,
|
||||
{}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.type == "change") {
|
||||
TabsSetupFlowManager.syncOpenTabs(event.target);
|
||||
}
|
||||
if (this.recentBrowsing && event.type === "fxview-search-textbox-query") {
|
||||
this.onSearchQuery(event);
|
||||
}
|
||||
}
|
||||
|
||||
viewVisibleCallback() {
|
||||
this.start();
|
||||
}
|
||||
|
|
@ -196,90 +130,16 @@ class SyncedTabsInView extends ViewPage {
|
|||
this.toggleVisibilityInCardContainer();
|
||||
}
|
||||
|
||||
async observe(subject, topic, errorState) {
|
||||
if (topic == TOPIC_SETUPSTATE_CHANGED) {
|
||||
this.updateStates(errorState);
|
||||
}
|
||||
if (topic == SYNCED_TABS_CHANGED) {
|
||||
this.getSyncedTabData();
|
||||
}
|
||||
}
|
||||
|
||||
updateStates(errorState) {
|
||||
let stateIndex = TabsSetupFlowManager.uiStateIndex;
|
||||
errorState = errorState || SyncedTabsErrorHandler.getErrorType();
|
||||
|
||||
if (stateIndex == 4 && this._currentSetupStateIndex !== stateIndex) {
|
||||
// trigger an initial request for the synced tabs list
|
||||
this.getSyncedTabData();
|
||||
}
|
||||
|
||||
this._currentSetupStateIndex = stateIndex;
|
||||
this.errorState = errorState;
|
||||
}
|
||||
|
||||
actionMappings = {
|
||||
"sign-in": {
|
||||
header: "firefoxview-syncedtabs-signin-header",
|
||||
description: "firefoxview-syncedtabs-signin-description",
|
||||
buttonLabel: "firefoxview-syncedtabs-signin-primarybutton",
|
||||
},
|
||||
"add-device": {
|
||||
header: "firefoxview-syncedtabs-adddevice-header",
|
||||
description: "firefoxview-syncedtabs-adddevice-description",
|
||||
buttonLabel: "firefoxview-syncedtabs-adddevice-primarybutton",
|
||||
descriptionLink: {
|
||||
name: "url",
|
||||
url: "https://support.mozilla.org/kb/how-do-i-set-sync-my-computer#w_connect-additional-devices-to-sync",
|
||||
},
|
||||
},
|
||||
"sync-tabs-disabled": {
|
||||
header: "firefoxview-syncedtabs-synctabs-header",
|
||||
description: "firefoxview-syncedtabs-synctabs-description",
|
||||
buttonLabel: "firefoxview-tabpickup-synctabs-primarybutton",
|
||||
},
|
||||
loading: {
|
||||
header: "firefoxview-syncedtabs-loading-header",
|
||||
description: "firefoxview-syncedtabs-loading-description",
|
||||
},
|
||||
};
|
||||
|
||||
generateMessageCard({ error = false, action, errorState }) {
|
||||
errorState = errorState || this.errorState;
|
||||
let header,
|
||||
description,
|
||||
descriptionLink,
|
||||
buttonLabel,
|
||||
headerIconUrl,
|
||||
mainImageUrl;
|
||||
let descriptionArray;
|
||||
if (error) {
|
||||
let link;
|
||||
({ header, description, link, buttonLabel } =
|
||||
SyncedTabsErrorHandler.getFluentStringsForErrorType(errorState));
|
||||
action = `${errorState}`;
|
||||
headerIconUrl = "chrome://global/skin/icons/info-filled.svg";
|
||||
mainImageUrl =
|
||||
"chrome://browser/content/firefoxview/synced-tabs-error.svg";
|
||||
descriptionArray = [description];
|
||||
if (errorState == "password-locked") {
|
||||
descriptionLink = {};
|
||||
// This is ugly, but we need to special case this link so we can
|
||||
// coexist with the old view.
|
||||
descriptionArray.push("firefoxview-syncedtab-password-locked-link");
|
||||
descriptionLink.name = "syncedtab-password-locked-link";
|
||||
descriptionLink.url = link.href;
|
||||
}
|
||||
} else {
|
||||
header = this.actionMappings[action].header;
|
||||
description = this.actionMappings[action].description;
|
||||
buttonLabel = this.actionMappings[action].buttonLabel;
|
||||
descriptionLink = this.actionMappings[action].descriptionLink;
|
||||
mainImageUrl =
|
||||
"chrome://browser/content/firefoxview/synced-tabs-error.svg";
|
||||
descriptionArray = [description];
|
||||
}
|
||||
|
||||
generateMessageCard({
|
||||
action,
|
||||
buttonLabel,
|
||||
descriptionArray,
|
||||
descriptionLink,
|
||||
error,
|
||||
header,
|
||||
headerIconUrl,
|
||||
mainImageUrl,
|
||||
}) {
|
||||
return html`
|
||||
<fxview-empty-state
|
||||
headerLabel=${header}
|
||||
|
|
@ -299,7 +159,7 @@ class SyncedTabsInView extends ViewPage {
|
|||
?hidden=${!buttonLabel}
|
||||
data-l10n-id="${ifDefined(buttonLabel)}"
|
||||
data-action="${action}"
|
||||
@click=${this.handleEvent}
|
||||
@click=${this.controller.handleEvent}
|
||||
aria-details="empty-container"
|
||||
></button>
|
||||
</fxview-empty-state>
|
||||
|
|
@ -319,7 +179,7 @@ class SyncedTabsInView extends ViewPage {
|
|||
}
|
||||
);
|
||||
|
||||
if (this.searchQuery) {
|
||||
if (this.controller.searchQuery) {
|
||||
const searchesHistogram = Services.telemetry.getKeyedHistogramById(
|
||||
"FIREFOX_VIEW_CUMULATIVE_SEARCHES"
|
||||
);
|
||||
|
|
@ -375,7 +235,7 @@ class SyncedTabsInView extends ViewPage {
|
|||
class="blackbox notabs search-results-empty"
|
||||
data-l10n-id="firefoxview-search-results-empty"
|
||||
data-l10n-args=${JSON.stringify({
|
||||
query: escapeHtmlEntities(this.searchQuery),
|
||||
query: escapeHtmlEntities(this.controller.searchQuery),
|
||||
})}
|
||||
></div>
|
||||
`,
|
||||
|
|
@ -396,7 +256,8 @@ class SyncedTabsInView extends ViewPage {
|
|||
}
|
||||
|
||||
onSearchQuery(e) {
|
||||
this.searchQuery = e.detail.query;
|
||||
this.controller.searchQuery = e.detail.query;
|
||||
this.cumulativeSearches = e.detail.query ? this.cumulativeSearches + 1 : 0;
|
||||
this.showAll = false;
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +274,7 @@ class SyncedTabsInView extends ViewPage {
|
|||
secondaryActionClass="options-button"
|
||||
hasPopup="menu"
|
||||
.tabItems=${ifDefined(tabItems)}
|
||||
.searchQuery=${this.searchQuery}
|
||||
.searchQuery=${this.controller.searchQuery}
|
||||
maxTabsLength=${this.showAll ? -1 : this.maxTabsLength}
|
||||
@fxview-tab-list-primary-action=${this.onOpenLink}
|
||||
@fxview-tab-list-secondary-action=${this.onContextMenu}
|
||||
|
|
@ -425,33 +286,9 @@ class SyncedTabsInView extends ViewPage {
|
|||
|
||||
generateTabList() {
|
||||
let renderArray = [];
|
||||
let renderInfo = {};
|
||||
for (let tab of this.currentSyncedTabs) {
|
||||
if (!(tab.client in renderInfo)) {
|
||||
renderInfo[tab.client] = {
|
||||
name: tab.device,
|
||||
deviceType: tab.deviceType,
|
||||
tabs: [],
|
||||
};
|
||||
}
|
||||
renderInfo[tab.client].tabs.push(tab);
|
||||
}
|
||||
|
||||
// Add devices without tabs
|
||||
for (let device of this.devices) {
|
||||
if (!(device.id in renderInfo)) {
|
||||
renderInfo[device.id] = {
|
||||
name: device.name,
|
||||
deviceType: device.clientType,
|
||||
tabs: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let renderInfo = this.controller.getRenderInfo();
|
||||
for (let id in renderInfo) {
|
||||
let tabItems = this.searchQuery
|
||||
? searchTabList(this.searchQuery, this.getTabItems(renderInfo[id].tabs))
|
||||
: this.getTabItems(renderInfo[id].tabs);
|
||||
let tabItems = renderInfo[id].tabItems;
|
||||
if (tabItems.length) {
|
||||
const template = this.recentBrowsing
|
||||
? this.deviceTemplate(
|
||||
|
|
@ -500,7 +337,7 @@ class SyncedTabsInView extends ViewPage {
|
|||
isShowAllLinkVisible(tabItems) {
|
||||
return (
|
||||
this.recentBrowsing &&
|
||||
this.searchQuery &&
|
||||
this.controller.searchQuery &&
|
||||
tabItems.length > this.maxTabsLength &&
|
||||
!this.showAll
|
||||
);
|
||||
|
|
@ -527,35 +364,10 @@ class SyncedTabsInView extends ViewPage {
|
|||
}
|
||||
|
||||
generateCardContent() {
|
||||
switch (this._currentSetupStateIndex) {
|
||||
case 0 /* error-state */:
|
||||
if (this.errorState) {
|
||||
return this.generateMessageCard({ error: true });
|
||||
}
|
||||
return this.generateMessageCard({ action: "loading" });
|
||||
case 1 /* not-signed-in */:
|
||||
if (Services.prefs.prefHasUserValue("services.sync.lastversion")) {
|
||||
// If this pref is set, the user has signed out of sync.
|
||||
// This path is also taken if we are disconnected from sync. See bug 1784055
|
||||
return this.generateMessageCard({
|
||||
error: true,
|
||||
errorState: "signed-out",
|
||||
});
|
||||
}
|
||||
return this.generateMessageCard({ action: "sign-in" });
|
||||
case 2 /* connect-secondary-device*/:
|
||||
return this.generateMessageCard({ action: "add-device" });
|
||||
case 3 /* disabled-tab-sync */:
|
||||
return this.generateMessageCard({ action: "sync-tabs-disabled" });
|
||||
case 4 /* synced-tabs-loaded*/:
|
||||
// There seems to be an edge case where sync says everything worked
|
||||
// fine but we have no devices.
|
||||
if (!this.devices.length) {
|
||||
return this.generateMessageCard({ action: "add-device" });
|
||||
}
|
||||
return this.generateTabList();
|
||||
}
|
||||
return html``;
|
||||
const cardProperties = this.controller.getMessageCard();
|
||||
return cardProperties
|
||||
? this.generateMessageCard(cardProperties)
|
||||
: this.generateTabList();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -580,7 +392,7 @@ class SyncedTabsInView extends ViewPage {
|
|||
data-l10n-id="firefoxview-synced-tabs-header"
|
||||
></h2>
|
||||
${when(
|
||||
isSearchEnabled() || this._currentSetupStateIndex === 4,
|
||||
isSearchEnabled() || this.controller.currentSetupStateIndex === 4,
|
||||
() => html`<div class="syncedtabs-header">
|
||||
${when(
|
||||
isSearchEnabled(),
|
||||
|
|
@ -597,12 +409,12 @@ class SyncedTabsInView extends ViewPage {
|
|||
</div>`
|
||||
)}
|
||||
${when(
|
||||
this._currentSetupStateIndex === 4,
|
||||
this.controller.currentSetupStateIndex === 4,
|
||||
() => html`
|
||||
<button
|
||||
class="small-button"
|
||||
data-action="add-device"
|
||||
@click=${this.handleEvent}
|
||||
@click=${this.controller.handleEvent}
|
||||
>
|
||||
<img
|
||||
class="icon"
|
||||
|
|
@ -626,9 +438,9 @@ class SyncedTabsInView extends ViewPage {
|
|||
html`<card-container
|
||||
preserveCollapseState
|
||||
shortPageName="syncedtabs"
|
||||
?showViewAll=${this._currentSetupStateIndex == 4 &&
|
||||
this.currentSyncedTabs.length}
|
||||
?isEmptyState=${!this.currentSyncedTabs.length}
|
||||
?showViewAll=${this.controller.currentSetupStateIndex == 4 &&
|
||||
this.controller.currentSyncedTabs.length}
|
||||
?isEmptyState=${!this.controller.currentSyncedTabs.length}
|
||||
>
|
||||
>
|
||||
<h3
|
||||
|
|
@ -647,71 +459,9 @@ class SyncedTabsInView extends ViewPage {
|
|||
return renderArray;
|
||||
}
|
||||
|
||||
async onReload() {
|
||||
await TabsSetupFlowManager.syncOnPageReload();
|
||||
}
|
||||
|
||||
getTabItems(tabs) {
|
||||
tabs = tabs || this.tabs;
|
||||
return tabs?.map(tab => ({
|
||||
icon: tab.icon,
|
||||
title: tab.title,
|
||||
time: tab.lastUsed * 1000,
|
||||
url: tab.url,
|
||||
primaryL10nId: "firefoxview-tabs-list-tab-button",
|
||||
primaryL10nArgs: JSON.stringify({ targetURI: tab.url }),
|
||||
secondaryL10nId: "fxviewtabrow-options-menu-button",
|
||||
secondaryL10nArgs: JSON.stringify({ tabTitle: tab.title }),
|
||||
}));
|
||||
}
|
||||
|
||||
updateTabsList(syncedTabs) {
|
||||
if (!syncedTabs.length) {
|
||||
this.currentSyncedTabs = syncedTabs;
|
||||
this.sendTabTelemetry(0);
|
||||
}
|
||||
|
||||
const tabsToRender = syncedTabs;
|
||||
|
||||
// Return early if new tabs are the same as previous ones
|
||||
if (
|
||||
JSON.stringify(tabsToRender) == JSON.stringify(this.currentSyncedTabs)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentSyncedTabs = tabsToRender;
|
||||
// Record the full tab count
|
||||
this.sendTabTelemetry(syncedTabs.length);
|
||||
}
|
||||
|
||||
async getSyncedTabData() {
|
||||
this.devices = await lazy.SyncedTabs.getTabClients();
|
||||
let tabs = await lazy.SyncedTabs.createRecentTabsList(this.devices, 50, {
|
||||
removeAllDupes: false,
|
||||
removeDeviceDupes: true,
|
||||
});
|
||||
|
||||
this.updateTabsList(tabs);
|
||||
}
|
||||
|
||||
updated() {
|
||||
this.fullyUpdated = true;
|
||||
this.toggleVisibilityInCardContainer();
|
||||
}
|
||||
|
||||
sendTabTelemetry() {
|
||||
/*
|
||||
Services.telemetry.recordEvent(
|
||||
"firefoxview_next",
|
||||
"synced_tabs",
|
||||
"tabs",
|
||||
null,
|
||||
{
|
||||
count: numTabs.toString(),
|
||||
}
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
customElements.define("view-syncedtabs", SyncedTabsInView);
|
||||
|
|
|
|||
|
|
@ -605,7 +605,8 @@ add_task(async function test_cumulative_searches_syncedtabs_telemetry() {
|
|||
);
|
||||
await TestUtils.waitForCondition(
|
||||
() =>
|
||||
syncedTabs.tabLists[0].rowEls.length === 1 && syncedTabs?.searchQuery,
|
||||
syncedTabs.tabLists[0].rowEls.length === 1 &&
|
||||
syncedTabs.controller.searchQuery,
|
||||
"Expected search results are not shown yet."
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue