forked from mirrors/gecko-dev
		
	 eaa5d9e321
			
		
	
	
		eaa5d9e321
		
	
	
	
	
		
			
			* Sketch in recently-closed-tabs section and listing * Add some styles and suggested markup for the page-level sections & headers Differential Revision: https://phabricator.services.mozilla.com/D143365
		
			
				
	
	
		
			192 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
	
		
			5.2 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, {
 | |
|   Services: "resource://gre/modules/Services.jsm",
 | |
|   SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
 | |
|   PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
 | |
| });
 | |
| 
 | |
| const relativeTimeFormat = new Services.intl.RelativeTimeFormat(undefined, {});
 | |
| 
 | |
| class RecentlyClosedTabsList extends HTMLElement {
 | |
|   get tabsList() {
 | |
|     return this.querySelector("ol");
 | |
|   }
 | |
| 
 | |
|   get fluentStrings() {
 | |
|     if (!this._fluentStrings) {
 | |
|       this._fluentStrings = new Localization(["preview/myFirefox.ftl"], true);
 | |
|     }
 | |
|     return this._fluentStrings;
 | |
|   }
 | |
| 
 | |
|   connectedCallback() {
 | |
|     this.addEventListener("click", this);
 | |
|   }
 | |
| 
 | |
|   handleEvent(event) {
 | |
|     if (event.type == "click") {
 | |
|       const item = event.target.closest(".closed-tab-li");
 | |
|       event.preventDefault();
 | |
|       this.openTab(item.dataset.targetURI);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   getWindow() {
 | |
|     return window.windowRoot.ownerGlobal;
 | |
|   }
 | |
| 
 | |
|   convertTimestamp(timestamp) {
 | |
|     const elapsed = Date.now() - timestamp;
 | |
|     const nowThresholdMs = 91000;
 | |
|     let formattedTime;
 | |
|     if (elapsed <= nowThresholdMs) {
 | |
|       // Use a different string for very recent timestamps
 | |
|       formattedTime = this.fluentStrings.formatValueSync(
 | |
|         "myfirefox-just-now-timestamp"
 | |
|       );
 | |
|     } else {
 | |
|       formattedTime = relativeTimeFormat.formatBestUnit(new Date(timestamp));
 | |
|     }
 | |
|     return formattedTime;
 | |
|   }
 | |
| 
 | |
|   formatURIForDisplay(uriString) {
 | |
|     // TODO: Bug 1764816: Make sure we handle file:///, jar:, blob, IP4/IP6 etc. addresses
 | |
|     let uri;
 | |
|     try {
 | |
|       uri = Services.io.newURI(uriString);
 | |
|     } catch (ex) {
 | |
|       return uriString;
 | |
|     }
 | |
|     if (!uri.asciiHost) {
 | |
|       return uriString;
 | |
|     }
 | |
|     let displayHost;
 | |
|     try {
 | |
|       // This might fail if it's an IP address or doesn't have more than 1 part
 | |
|       displayHost = Services.eTLD.getBaseDomain(uri);
 | |
|     } catch (ex) {
 | |
|       return uri.displayHostPort;
 | |
|     }
 | |
|     return displayHost.length ? displayHost : uriString;
 | |
|   }
 | |
| 
 | |
|   getTargetURI(tab) {
 | |
|     let targetURI = "";
 | |
|     const tabEntries = tab.state.entries;
 | |
|     const activeIndex = tabEntries.length - 1;
 | |
| 
 | |
|     if (activeIndex >= 0 && tabEntries[activeIndex]) {
 | |
|       targetURI = tabEntries[activeIndex].url;
 | |
|     }
 | |
| 
 | |
|     return targetURI;
 | |
|   }
 | |
| 
 | |
|   openTab(targetURI) {
 | |
|     window.open(targetURI, "_blank");
 | |
|   }
 | |
| 
 | |
|   generateTabs() {
 | |
|     let closedTabs = SessionStore.getClosedTabData(this.getWindow());
 | |
|     closedTabs = closedTabs.slice(0, 25);
 | |
| 
 | |
|     for (const tab of closedTabs) {
 | |
|       let li = document.createElement("li");
 | |
|       li.classList.add("closed-tab-li");
 | |
| 
 | |
|       if (tab.image) {
 | |
|         // TODO - figure out how to render this properly
 | |
|         PlacesUIUtils.setImage(tab, li);
 | |
|       }
 | |
| 
 | |
|       let title = document.createElement("span");
 | |
|       title.textContent = `${tab.title}`;
 | |
|       title.classList.add("closed-tab-li-title");
 | |
| 
 | |
|       const targetURI = this.getTargetURI(tab);
 | |
|       li.dataset.targetURI = targetURI;
 | |
|       document.l10n.setAttributes(li, "myfirefox-closed-tabs-tab-button", {
 | |
|         targetURI,
 | |
|       });
 | |
| 
 | |
|       let url = document.createElement("span");
 | |
| 
 | |
|       if (targetURI) {
 | |
|         url.textContent = this.formatURIForDisplay(targetURI);
 | |
|         url.classList.add("closed-tab-li-url");
 | |
|       }
 | |
| 
 | |
|       let time = document.createElement("span");
 | |
|       time.textContent = this.convertTimestamp(tab.closedAt);
 | |
|       time.classList.add("closed-tab-li-time");
 | |
| 
 | |
|       li.append(title, url, time);
 | |
|       this.tabsList.appendChild(li);
 | |
|     }
 | |
|     this.tabsList.hidden = false;
 | |
|   }
 | |
| }
 | |
| customElements.define("recently-closed-tabs-list", RecentlyClosedTabsList);
 | |
| 
 | |
| class RecentlyClosedTabsContainer extends HTMLElement {
 | |
|   getWindow = () => window.windowRoot.ownerGlobal;
 | |
| 
 | |
|   connectedCallback() {
 | |
|     this.noTabsElement = this.querySelector(
 | |
|       "#recently-closed-tabs-placeholder"
 | |
|     );
 | |
|     this.list = this.querySelector("recently-closed-tabs-list");
 | |
|     this.collapsibleContainer = this.querySelector(
 | |
|       "#collapsible-tabs-container"
 | |
|     );
 | |
|     this.collapsibleButton = this.querySelector("#collapsible-tabs-button");
 | |
| 
 | |
|     this.collapsibleButton.addEventListener("click", this);
 | |
|   }
 | |
| 
 | |
|   onLoad() {
 | |
|     if (this.getClosedTabCount() == 0) {
 | |
|       this.noTabsElement.hidden = false;
 | |
|       this.collapsibleContainer.classList.add("empty-container");
 | |
|     } else {
 | |
|       this.list.generateTabs();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   handleEvent(event) {
 | |
|     if (event.type == "click" && event.target == this.collapsibleButton) {
 | |
|       this.toggleTabs();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   getClosedTabCount = () => {
 | |
|     try {
 | |
|       return SessionStore.getClosedTabCount(this.getWindow());
 | |
|     } catch (ex) {
 | |
|       return 0;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   toggleTabs = () => {
 | |
|     const arrowUp = "arrow-up";
 | |
|     const arrowDown = "arrow-down";
 | |
|     const isOpen = this.collapsibleButton.classList.contains(arrowUp);
 | |
| 
 | |
|     this.collapsibleButton.classList.toggle(arrowUp, !isOpen);
 | |
|     this.collapsibleButton.classList.toggle(arrowDown, isOpen);
 | |
|     this.collapsibleContainer.hidden = isOpen;
 | |
|   };
 | |
| }
 | |
| customElements.define(
 | |
|   "recently-closed-tabs-container",
 | |
|   RecentlyClosedTabsContainer
 | |
| );
 |