198 lines
5.3 KiB
JavaScript
198 lines
5.3 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/. */
|
|
|
|
"use strict";
|
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
);
|
|
XPCOMUtils.defineLazyModuleGetters(globalThis, {
|
|
SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
|
|
Services: "resource://gre/modules/Services.jsm",
|
|
});
|
|
|
|
import {
|
|
formatURIForDisplay,
|
|
convertTimestamp,
|
|
createFaviconElement,
|
|
} from "./helpers.mjs";
|
|
|
|
const SYNCED_TABS_CHANGED = "services.sync.tabs.changed";
|
|
|
|
class TabPickupList extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.maxTabsLength = 3;
|
|
this.boundObserve = (...args) => this.getSyncedTabData(...args);
|
|
}
|
|
|
|
get tabsList() {
|
|
return this.querySelector("ol");
|
|
}
|
|
|
|
get fluentStrings() {
|
|
if (!this._fluentStrings) {
|
|
this._fluentStrings = new Localization(["preview/firefoxView.ftl"], true);
|
|
}
|
|
return this._fluentStrings;
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.addEventListener("click", this);
|
|
|
|
this.getSyncedTabData();
|
|
Services.obs.addObserver(this.boundObserve, SYNCED_TABS_CHANGED);
|
|
}
|
|
|
|
handleEvent(event) {
|
|
if (
|
|
event.type == "click" ||
|
|
(event.type == "keydown" && event.keyCode == KeyEvent.DOM_VK_RETURN)
|
|
) {
|
|
this.openTab(event);
|
|
}
|
|
}
|
|
|
|
cleanup() {
|
|
Services.obs.removeObserver(this.boundObserve, SYNCED_TABS_CHANGED);
|
|
}
|
|
|
|
openTab(event) {
|
|
event.preventDefault();
|
|
const item = event.target.closest(".synced-tab-li");
|
|
window.open(item.dataset.targetURI, "_blank");
|
|
}
|
|
|
|
async getSyncedTabData() {
|
|
let tabs = [];
|
|
let clients = await SyncedTabs.getTabClients();
|
|
|
|
for (let client of clients) {
|
|
for (let tab of client.tabs) {
|
|
tab.device = client.name;
|
|
tab.deviceType = client.clientType;
|
|
}
|
|
tabs = [...tabs, ...client.tabs.reverse()];
|
|
}
|
|
tabs = tabs
|
|
.sort((a, b) => b.lastUsed - a.lastUsed)
|
|
.slice(0, this.maxTabsLength);
|
|
|
|
this.updateTabsList(tabs);
|
|
}
|
|
|
|
updateTabsList(syncedTabs) {
|
|
while (this.tabsList.firstChild) {
|
|
this.tabsList.firstChild.remove();
|
|
}
|
|
|
|
if (!syncedTabs.length) {
|
|
// TODO show empty state placeholder, see bug 1774168
|
|
this.tabsList.hidden = true;
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < this.maxTabsLength; i++) {
|
|
let li = null;
|
|
if (!syncedTabs[i]) {
|
|
li = this.generatePlaceholder();
|
|
} else {
|
|
li = this.generateListItem(syncedTabs[i], i);
|
|
}
|
|
this.tabsList.append(li);
|
|
}
|
|
|
|
if (this.tabsList.hidden) {
|
|
this.tabsList.hidden = false;
|
|
}
|
|
}
|
|
|
|
generatePlaceholder() {
|
|
const li = document.createElement("li");
|
|
li.classList.add("synced-tab-li-placeholder");
|
|
li.setAttribute("role", "presentation");
|
|
|
|
const favicon = document.createElement("span");
|
|
favicon.classList.add("li-placeholder-favicon");
|
|
|
|
const title = document.createElement("span");
|
|
title.classList.add("li-placeholder-title");
|
|
|
|
const domain = document.createElement("span");
|
|
domain.classList.add("li-placeholder-domain");
|
|
|
|
li.append(favicon, title, domain);
|
|
return li;
|
|
}
|
|
|
|
generateListItem(tab, index) {
|
|
const li = document.createElement("li");
|
|
li.classList.add("synced-tab-li");
|
|
li.setAttribute("tabindex", 0);
|
|
li.setAttribute("role", "button");
|
|
|
|
const title = document.createElement("span");
|
|
title.textContent = tab.title;
|
|
title.classList.add("synced-tab-li-title");
|
|
|
|
const favicon = createFaviconElement(tab.icon);
|
|
const targetURI = tab.url;
|
|
|
|
li.dataset.targetURI = targetURI;
|
|
document.l10n.setAttributes(li, "firefoxview-tabs-list-tab-button", {
|
|
targetURI,
|
|
});
|
|
|
|
const time = document.createElement("span");
|
|
time.textContent = convertTimestamp(
|
|
tab.lastUsed * 1000,
|
|
this.fluentStrings
|
|
);
|
|
time.classList.add("synced-tab-li-time");
|
|
|
|
const url = document.createElement("span");
|
|
const device = document.createElement("span");
|
|
const deviceIcon = document.createElement("div");
|
|
deviceIcon.classList.add("icon", tab.deviceType);
|
|
deviceIcon.setAttribute("role", "presentation");
|
|
|
|
const deviceText = tab.device;
|
|
device.textContent = deviceText;
|
|
device.prepend(deviceIcon);
|
|
device.title = deviceText;
|
|
|
|
url.textContent = formatURIForDisplay(tab.url);
|
|
url.classList.add("synced-tab-li-url");
|
|
device.classList.add("synced-tab-li-device");
|
|
|
|
// the first list item is diffent from second and third
|
|
if (index == 0) {
|
|
const badge = this.createBadge();
|
|
li.append(favicon, badge, title, url, device, time);
|
|
} else {
|
|
const urlWithDevice = document.createElement("span");
|
|
urlWithDevice.append(url, " • ", device);
|
|
urlWithDevice.classList.add("synced-tab-li-url-device");
|
|
li.append(favicon, title, urlWithDevice, time);
|
|
}
|
|
|
|
return li;
|
|
}
|
|
|
|
createBadge() {
|
|
const badge = document.createElement("div");
|
|
const dot = document.createElement("span");
|
|
const badgeText = document.createElement("span");
|
|
|
|
badgeText.setAttribute("data-l10n-id", "firefoxview-pickup-tabs-badge");
|
|
badgeText.classList.add("badge-text");
|
|
badge.classList.add("last-active-badge");
|
|
dot.classList.add("dot");
|
|
badge.append(dot, badgeText);
|
|
return badge;
|
|
}
|
|
}
|
|
|
|
customElements.define("tab-pickup-list", TabPickupList);
|