forked from mirrors/gecko-dev
Bug 1828673 - Add support for overview page in synced tabs. r=desktop-theme-reviewers,fxview-reviewers,jsudiaman,dao,sfoster,fluent-reviewers,flod
Differential Revision: https://phabricator.services.mozilla.com/D184414
This commit is contained in:
parent
b4e65f0c36
commit
ae0fa36393
6 changed files with 201 additions and 95 deletions
|
|
@ -98,6 +98,9 @@
|
||||||
<div>
|
<div>
|
||||||
<view-recentlyclosed slot="recentlyclosed"></view-recentlyclosed>
|
<view-recentlyclosed slot="recentlyclosed"></view-recentlyclosed>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<view-syncedtabs slot="syncedtabs"></view-syncedtabs>
|
||||||
|
</div>
|
||||||
</view-overview>
|
</view-overview>
|
||||||
<view-history name="history"></view-history>
|
<view-history name="history"></view-history>
|
||||||
<view-opentabs name="opentabs"></view-opentabs>
|
<view-opentabs name="opentabs"></view-opentabs>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
html,
|
html,
|
||||||
ifDefined,
|
ifDefined,
|
||||||
styleMap,
|
styleMap,
|
||||||
|
classMap,
|
||||||
when,
|
when,
|
||||||
} from "chrome://global/content/vendor/lit.all.mjs";
|
} from "chrome://global/content/vendor/lit.all.mjs";
|
||||||
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
||||||
|
|
@ -213,9 +214,19 @@ export default class FxviewTabList extends MozLitElement {
|
||||||
role="list"
|
role="list"
|
||||||
@keydown=${this.handleFocusElementInRow}
|
@keydown=${this.handleFocusElementInRow}
|
||||||
>
|
>
|
||||||
${tabItems.map(
|
${tabItems.map((tabItem, i) => {
|
||||||
(tabItem, i) =>
|
let time;
|
||||||
html`
|
if (tabItem.time || tabItem.closedAt) {
|
||||||
|
let stringTime = (tabItem.time || tabItem.closedAt).toString();
|
||||||
|
// Different APIs return time in different units, so we use
|
||||||
|
// the length to decide if it's milliseconds or nanoseconds.
|
||||||
|
if (stringTime.length === 16) {
|
||||||
|
time = (tabItem.time || tabItem.closedAt) / 1000;
|
||||||
|
} else {
|
||||||
|
time = tabItem.time || tabItem.closedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return html`
|
||||||
<fxview-tab-row
|
<fxview-tab-row
|
||||||
exportparts="secondary-button"
|
exportparts="secondary-button"
|
||||||
?active=${i == activeIndex}
|
?active=${i == activeIndex}
|
||||||
|
|
@ -224,24 +235,21 @@ export default class FxviewTabList extends MozLitElement {
|
||||||
.currentActiveElementId=${currentActiveElementId}
|
.currentActiveElementId=${currentActiveElementId}
|
||||||
.dateTimeFormat=${dateTimeFormat}
|
.dateTimeFormat=${dateTimeFormat}
|
||||||
.favicon=${tabItem.icon}
|
.favicon=${tabItem.icon}
|
||||||
.primaryL10nId=${tabItem.primaryL10nId}
|
.primaryL10nId=${ifDefined(tabItem.primaryL10nId)}
|
||||||
.primaryL10nArgs=${ifDefined(tabItem.primaryL10nArgs)}
|
.primaryL10nArgs=${ifDefined(tabItem.primaryL10nArgs)}
|
||||||
role="listitem"
|
role="listitem"
|
||||||
.secondaryL10nId=${tabItem.secondaryL10nId}
|
.secondaryL10nId=${ifDefined(tabItem.secondaryL10nId)}
|
||||||
.secondaryL10nArgs=${ifDefined(tabItem.secondaryL10nArgs)}
|
.secondaryL10nArgs=${ifDefined(tabItem.secondaryL10nArgs)}
|
||||||
.closedId=${ifDefined(tabItem.closedId || tabItem.closedId)}
|
.closedId=${ifDefined(tabItem.closedId || tabItem.closedId)}
|
||||||
.tabElement=${ifDefined(tabItem.tabElement)}
|
.tabElement=${ifDefined(tabItem.tabElement)}
|
||||||
.time=${(tabItem.time || tabItem.closedAt).toString().length ===
|
.time=${ifDefined(time)}
|
||||||
16
|
|
||||||
? (tabItem.time || tabItem.closedAt) / 1000
|
|
||||||
: tabItem.time || tabItem.closedAt}
|
|
||||||
.timeMsPref=${ifDefined(this.timeMsPref)}
|
.timeMsPref=${ifDefined(this.timeMsPref)}
|
||||||
.title=${tabItem.title}
|
.title=${tabItem.title}
|
||||||
.url=${tabItem.url}
|
.url=${ifDefined(tabItem.url)}
|
||||||
>
|
>
|
||||||
</fxview-tab-row>
|
</fxview-tab-row>
|
||||||
`
|
`;
|
||||||
)}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<slot name="menu"></slot>
|
<slot name="menu"></slot>
|
||||||
`;
|
`;
|
||||||
|
|
@ -336,7 +344,7 @@ export class FxviewTabRow extends MozLitElement {
|
||||||
|
|
||||||
dateFluentId(timestamp, dateTimeFormat, _nowThresholdMs = NOW_THRESHOLD_MS) {
|
dateFluentId(timestamp, dateTimeFormat, _nowThresholdMs = NOW_THRESHOLD_MS) {
|
||||||
if (!timestamp) {
|
if (!timestamp) {
|
||||||
return "";
|
return null;
|
||||||
}
|
}
|
||||||
if (dateTimeFormat === "relative") {
|
if (dateTimeFormat === "relative") {
|
||||||
const elapsed = Date.now() - timestamp;
|
const elapsed = Date.now() - timestamp;
|
||||||
|
|
@ -447,14 +455,17 @@ export class FxviewTabRow extends MozLitElement {
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href=${this.constructor.stylesheetUrl} />
|
<link rel="stylesheet" href=${this.constructor.stylesheetUrl} />
|
||||||
<a
|
<a
|
||||||
href=${this.url}
|
.href=${ifDefined(this.url)}
|
||||||
class="fxview-tab-row-main"
|
class=${classMap({
|
||||||
|
"fxview-tab-row-main": true,
|
||||||
|
"fxview-tab-row-header": !this.url,
|
||||||
|
})}
|
||||||
id="fxview-tab-row-main"
|
id="fxview-tab-row-main"
|
||||||
tabindex=${this.active &&
|
tabindex=${this.active &&
|
||||||
this.currentActiveElementId === "fxview-tab-row-main"
|
this.currentActiveElementId === "fxview-tab-row-main"
|
||||||
? "0"
|
? "0"
|
||||||
: "-1"}
|
: "-1"}
|
||||||
data-l10n-id=${this.primaryL10nId}
|
data-l10n-id=${ifDefined(this.primaryL10nId)}
|
||||||
data-l10n-args=${ifDefined(this.primaryL10nArgs)}
|
data-l10n-args=${ifDefined(this.primaryL10nArgs)}
|
||||||
@click=${this.primaryActionHandler}
|
@click=${this.primaryActionHandler}
|
||||||
@keydown=${this.primaryActionHandler}
|
@keydown=${this.primaryActionHandler}
|
||||||
|
|
@ -492,9 +503,9 @@ export class FxviewTabRow extends MozLitElement {
|
||||||
class="fxview-tab-row-time"
|
class="fxview-tab-row-time"
|
||||||
id="fxview-tab-row-time"
|
id="fxview-tab-row-time"
|
||||||
?hidden=${this.compact || !timeString}
|
?hidden=${this.compact || !timeString}
|
||||||
data-timestamp=${this.time}
|
data-timestamp=${ifDefined(this.time)}
|
||||||
data-l10n-id=${ifDefined(timeString)}
|
data-l10n-id=${ifDefined(timeString)}
|
||||||
data-l10n-args=${timeArgs}
|
data-l10n-args=${ifDefined(timeArgs)}
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,18 @@
|
||||||
background-color: var(--fxviewtabrow-element-background-active);
|
background-color: var(--fxviewtabrow-element-background-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fxview-tab-row-header {
|
||||||
|
margin-top: 8px;
|
||||||
|
cursor: inherit;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fxview-tab-row-header:hover,
|
||||||
|
.fxview-tab-row-header:hover:active {
|
||||||
|
color: inherit;
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-contrast) {
|
@media (prefers-contrast) {
|
||||||
.fxview-tab-row-main,
|
.fxview-tab-row-main,
|
||||||
.fxview-tab-row-main:hover,
|
.fxview-tab-row-main:hover,
|
||||||
|
|
@ -81,7 +93,7 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fxview-tab-row-main:hover .fxview-tab-row-title {
|
.fxview-tab-row-main:hover:not(.fxview-tab-row-header) .fxview-tab-row-title {
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,11 @@ const { TabsSetupFlowManager } = ChromeUtils.importESModule(
|
||||||
"resource:///modules/firefox-view-tabs-setup-manager.sys.mjs"
|
"resource:///modules/firefox-view-tabs-setup-manager.sys.mjs"
|
||||||
);
|
);
|
||||||
|
|
||||||
import { html, ifDefined } from "chrome://global/content/vendor/lit.all.mjs";
|
import {
|
||||||
|
html,
|
||||||
|
ifDefined,
|
||||||
|
when,
|
||||||
|
} from "chrome://global/content/vendor/lit.all.mjs";
|
||||||
import { ViewPage } from "./viewpage.mjs";
|
import { ViewPage } from "./viewpage.mjs";
|
||||||
|
|
||||||
const SYNCED_TABS_CHANGED = "services.sync.tabs.changed";
|
const SYNCED_TABS_CHANGED = "services.sync.tabs.changed";
|
||||||
|
|
@ -31,7 +35,7 @@ class SyncedTabsInView extends ViewPage {
|
||||||
this._id = Math.floor(Math.random() * 10e6);
|
this._id = Math.floor(Math.random() * 10e6);
|
||||||
this.currentSyncedTabs = [];
|
this.currentSyncedTabs = [];
|
||||||
if (this.overview) {
|
if (this.overview) {
|
||||||
this.maxTabsLength = 5;
|
this.maxTabsLength = 6; // 5 tabs plus the device row
|
||||||
} else {
|
} else {
|
||||||
// Setting maxTabsLength to -1 for no max
|
// Setting maxTabsLength to -1 for no max
|
||||||
this.maxTabsLength = -1;
|
this.maxTabsLength = -1;
|
||||||
|
|
@ -218,6 +222,7 @@ class SyncedTabsInView extends ViewPage {
|
||||||
.descriptionLink=${ifDefined(descriptionLink)}
|
.descriptionLink=${ifDefined(descriptionLink)}
|
||||||
class="empty-state synced-tabs"
|
class="empty-state synced-tabs"
|
||||||
?isSelectedTab=${this.selectedTab}
|
?isSelectedTab=${this.selectedTab}
|
||||||
|
?isInnerCard=${this.overview}
|
||||||
mainImageUrl="${ifDefined(mainImageUrl)}"
|
mainImageUrl="${ifDefined(mainImageUrl)}"
|
||||||
headerIconUrl="${ifDefined(headerIconUrl)}"
|
headerIconUrl="${ifDefined(headerIconUrl)}"
|
||||||
>
|
>
|
||||||
|
|
@ -284,15 +289,74 @@ class SyncedTabsInView extends ViewPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
noDeviceTabsTemplate(deviceName, deviceType) {
|
noDeviceTabsTemplate(deviceName, deviceType) {
|
||||||
|
if (this.overview) {
|
||||||
|
return html` ${this.deviceTemplate(deviceName, deviceType, [])}
|
||||||
|
<div
|
||||||
|
class="blackbox notabs"
|
||||||
|
data-l10n-id="firefoxview-syncedtabs-device-notabs"
|
||||||
|
></div>`;
|
||||||
|
}
|
||||||
return html`<card-container>
|
return html`<card-container>
|
||||||
<h2 slot="header">
|
<h2 slot="header">
|
||||||
<div class="icon ${deviceType}" role="presentation"></div>
|
<span class="icon ${deviceType}" role="presentation"></span>
|
||||||
${deviceName}
|
${deviceName}
|
||||||
</h2>
|
</h2>
|
||||||
<div slot="main" class="blackbox notabs">No tabs open on this device</div>
|
<div
|
||||||
|
slot="main"
|
||||||
|
class="blackbox notabs"
|
||||||
|
data-l10n-id="firefoxview-syncedtabs-device-notabs"
|
||||||
|
></div>
|
||||||
</card-container>`;
|
</card-container>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deviceTemplate(deviceName, deviceType, tabs) {
|
||||||
|
let tabItems = this.getTabItems(tabs);
|
||||||
|
if (this.overview) {
|
||||||
|
/* Insert device at the beginning of the tabs array */
|
||||||
|
let icon;
|
||||||
|
switch (deviceType) {
|
||||||
|
case "phone":
|
||||||
|
icon = "chrome://browser/skin/device-phone.svg";
|
||||||
|
break;
|
||||||
|
case "desktop":
|
||||||
|
icon = "chrome://browser/skin/device-desktop.svg";
|
||||||
|
break;
|
||||||
|
case "tablet":
|
||||||
|
icon = "chrome://browser/skin/device-tablet.svg";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tabItems.unshift({
|
||||||
|
icon,
|
||||||
|
title: deviceName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return html`${when(
|
||||||
|
!this.overview,
|
||||||
|
() => html`<h2 slot="header">
|
||||||
|
<span class="icon ${deviceType}" role="presentation"></span>
|
||||||
|
${deviceName}
|
||||||
|
</h2>`
|
||||||
|
)}
|
||||||
|
<fxview-tab-list
|
||||||
|
slot="main"
|
||||||
|
class="syncedtabs"
|
||||||
|
hasPopup="menu"
|
||||||
|
.tabItems=${ifDefined(tabItems)}
|
||||||
|
maxTabsLength=${this.maxTabsLength}
|
||||||
|
@fxview-tab-list-primary-action=${this.onOpenLink}
|
||||||
|
@fxview-tab-list-secondary-action=${this.onContextMenu}
|
||||||
|
>
|
||||||
|
${when(
|
||||||
|
this.overview,
|
||||||
|
() => html`<h2 slot="header">
|
||||||
|
<span class="icon ${deviceType}" role="presentation"></span>
|
||||||
|
${deviceName}
|
||||||
|
</h2>`
|
||||||
|
)}
|
||||||
|
${this.panelListTemplate()}
|
||||||
|
</fxview-tab-list>`;
|
||||||
|
}
|
||||||
|
|
||||||
generateTabList() {
|
generateTabList() {
|
||||||
let renderArray = [];
|
let renderArray = [];
|
||||||
let renderInfo = {};
|
let renderInfo = {};
|
||||||
|
|
@ -320,26 +384,25 @@ class SyncedTabsInView extends ViewPage {
|
||||||
|
|
||||||
for (let id in renderInfo) {
|
for (let id in renderInfo) {
|
||||||
if (renderInfo[id].tabs.length) {
|
if (renderInfo[id].tabs.length) {
|
||||||
renderArray.push(html`<card-container>
|
if (this.overview) {
|
||||||
<h2 slot="header">
|
renderArray.push(
|
||||||
<div
|
this.deviceTemplate(
|
||||||
class="icon ${renderInfo[id].deviceType}"
|
renderInfo[id].name,
|
||||||
role="presentation"
|
renderInfo[id].deviceType,
|
||||||
></div>
|
renderInfo[id].tabs
|
||||||
${renderInfo[id].name}
|
)
|
||||||
</h2>
|
);
|
||||||
<fxview-tab-list
|
} else {
|
||||||
slot="main"
|
renderArray.push(
|
||||||
class="syncedtabs"
|
html`<card-container
|
||||||
hasPopup="menu"
|
>${this.deviceTemplate(
|
||||||
.tabItems=${ifDefined(this.getTabItems(renderInfo[id].tabs))}
|
renderInfo[id].name,
|
||||||
maxTabsLength=${this.maxTabsLength}
|
renderInfo[id].deviceType,
|
||||||
@fxview-tab-list-primary-action=${this.onOpenLink}
|
renderInfo[id].tabs
|
||||||
@fxview-tab-list-secondary-action=${this.onContextMenu}
|
)}</card-container
|
||||||
>
|
>`
|
||||||
${this.panelListTemplate()}
|
);
|
||||||
</fxview-tab-list>
|
}
|
||||||
</card-container>`);
|
|
||||||
} else {
|
} else {
|
||||||
renderArray.push(
|
renderArray.push(
|
||||||
this.noDeviceTabsTemplate(
|
this.noDeviceTabsTemplate(
|
||||||
|
|
@ -351,9 +414,35 @@ class SyncedTabsInView extends ViewPage {
|
||||||
}
|
}
|
||||||
return renderArray;
|
return renderArray;
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
const stateIndex = this._currentSetupStateIndex;
|
|
||||||
|
|
||||||
|
generateCardContent() {
|
||||||
|
switch (this._currentSetupStateIndex) {
|
||||||
|
case 0 /* error-state */:
|
||||||
|
if (this.errorState) {
|
||||||
|
return this.generateMessageCard({ error: true });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
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*/:
|
||||||
|
return this.generateTabList();
|
||||||
|
}
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
this.open =
|
this.open =
|
||||||
!TabsSetupFlowManager.isTabSyncSetupComplete ||
|
!TabsSetupFlowManager.isTabSyncSetupComplete ||
|
||||||
Services.prefs.getBoolPref(UI_OPEN_STATE, true);
|
Services.prefs.getBoolPref(UI_OPEN_STATE, true);
|
||||||
|
|
@ -376,34 +465,23 @@ class SyncedTabsInView extends ViewPage {
|
||||||
</div>`);
|
</div>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (stateIndex) {
|
if (this.overview) {
|
||||||
case 0 /* error-state */:
|
|
||||||
if (this.errorState) {
|
|
||||||
renderArray.push(this.generateMessageCard({ error: true }));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
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
|
|
||||||
renderArray.push(
|
renderArray.push(
|
||||||
this.generateMessageCard({ error: true, errorState: "signed-out" })
|
html`<card-container
|
||||||
|
preserveCollapseState
|
||||||
|
viewAllPage=${this._currentSetupStateIndex == 4 ? "syncedtabs" : null}
|
||||||
|
>
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
slot="header"
|
||||||
|
data-l10n-id="firefoxview-synced-tabs-header"
|
||||||
|
class="overview-header"
|
||||||
|
></h2>
|
||||||
|
<div slot="main">${this.generateCardContent()}</div>
|
||||||
|
</card-container>`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
renderArray.push(this.generateMessageCard({ action: "sign-in" }));
|
renderArray.push(this.generateCardContent());
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2 /* connect-secondary-device*/:
|
|
||||||
renderArray.push(this.generateMessageCard({ action: "add-device" }));
|
|
||||||
break;
|
|
||||||
case 3 /* disabled-tab-sync */:
|
|
||||||
renderArray.push(
|
|
||||||
this.generateMessageCard({ action: "sync-tabs-disabled" })
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 4 /* synced-tabs-loaded*/:
|
|
||||||
renderArray = renderArray.concat(this.generateTabList());
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return renderArray;
|
return renderArray;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,17 +74,17 @@ You'll need to pass along some of the following properties:
|
||||||
* `hasPopup` (**Optional**): The optional aria-haspopup attribute for the secondary action, if required
|
* `hasPopup` (**Optional**): The optional aria-haspopup attribute for the secondary action, if required
|
||||||
* `maxTabsLength` (**Optional**): The max number of tabs you want to display in the tabs list. The default value will be `25` if no max value is given. You may use any negative number such as `-1` to indicate no max.
|
* `maxTabsLength` (**Optional**): The max number of tabs you want to display in the tabs list. The default value will be `25` if no max value is given. You may use any negative number such as `-1` to indicate no max.
|
||||||
* `tabItems` (**Required**): An array of tab data such as History nodes, Bookmark nodes, Synced Tabs, etc.
|
* `tabItems` (**Required**): An array of tab data such as History nodes, Bookmark nodes, Synced Tabs, etc.
|
||||||
* The component is expecting to receive the following properties within each `tabItems` object (you may need to do some normalizing for this):
|
* The component is expecting to receive the following properties within each `tabItems` object (you may need to do some normalizing for this). If you just pass a title and an icon, it creates a header row that is not clickable.
|
||||||
* `icon` (**Required**) - The location string for the favicon. Will fallback to default favicon if none is found.
|
* `icon` (**Required**) - The location string for the favicon. Will fallback to default favicon if none is found.
|
||||||
* `primaryL10nId` (**Required**) - The l10n id to be used for the primary action element. This fluent string should ONLY define a `.title` attribute to describe the link element in each row.
|
* `primaryL10nId` (**Optional**) - The l10n id to be used for the primary action element. This fluent string should ONLY define a `.title` attribute to describe the link element in each row.
|
||||||
* `primaryL10nArgs` (**Optional**) - The l10n args you can optionally pass for the primary action element
|
* `primaryL10nArgs` (**Optional**) - The l10n args you can optionally pass for the primary action element
|
||||||
* `secondaryL10nId` (**Optional**) - The l10n id to be used for the secondary action button. This fluent string should ONLY define a `.title` attribute to describe the secondary button in each row.
|
* `secondaryL10nId` (**Optional**) - The l10n id to be used for the secondary action button. This fluent string should ONLY define a `.title` attribute to describe the secondary button in each row.
|
||||||
* `secondaryL10nArgs` (**Optional**) - The l10n args you can optionally pass for the secondary action button
|
* `secondaryL10nArgs` (**Optional**) - The l10n args you can optionally pass for the secondary action button
|
||||||
* `tabElement` (**Optional**) - The MozTabbrowserTab element for the tab item.
|
* `tabElement` (**Optional**) - The MozTabbrowserTab element for the tab item.
|
||||||
* `tabid` (**Optional**) - Optional property expected for Recently Closed tab data
|
* `tabid` (**Optional**) - Optional property expected for Recently Closed tab data
|
||||||
* `time` (**Required**) - The time in milliseconds for expected last interaction with the tab (Ex: `lastUsed` for SyncedTabs tabs, `closedAt` for RecentlyClosed tabs, etc.)
|
* `time` (**Optional**) - The time in milliseconds for expected last interaction with the tab (Ex: `lastUsed` for SyncedTabs tabs, `closedAt` for RecentlyClosed tabs, etc.)
|
||||||
* `title` (**Required**) - The title for the tab
|
* `title` (**Required**) - The title for the tab
|
||||||
* `url` (**Required**) - The full URL for the tab
|
* `url` (**Optional**) - The full URL for the tab
|
||||||
|
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
|
||||||
|
|
@ -230,4 +230,6 @@ firefoxview-recentlyclosed-empty-header = Closed a tab too soon?
|
||||||
firefoxview-recentlyclosed-empty-description = Here you’ll find the tabs you recently closed, so you can reopen any of them quickly.
|
firefoxview-recentlyclosed-empty-description = Here you’ll find the tabs you recently closed, so you can reopen any of them quickly.
|
||||||
firefoxview-recentlyclosed-empty-description-two = To find tabs from longer ago, view your <a data-l10n-name="history-url">browsing history</a>.
|
firefoxview-recentlyclosed-empty-description-two = To find tabs from longer ago, view your <a data-l10n-name="history-url">browsing history</a>.
|
||||||
|
|
||||||
##
|
## This message is displayed below the name of another connected device when it doesn't have any open tabs.
|
||||||
|
|
||||||
|
firefoxview-syncedtabs-device-notabs = No tabs open on this device
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue