From 22604abe11be241dda943fe71f69ea575261917c Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Wed, 23 Sep 2026 23:28:40 +0200 Subject: [PATCH] gh-15533: Toggle 'open when done' for library downloads (gh-15534) --- locales/en-US/browser/browser/zen-library.ftl | 1 + src/zen/library/ZenLibraryWidget.sys.mjs | 51 +++++++++++++++--- .../sections/ZenLibraryDownloadsSection.mjs | 53 +++++++++++++++---- src/zen/library/zen-library.css | 5 ++ 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/locales/en-US/browser/browser/zen-library.ftl b/locales/en-US/browser/browser/zen-library.ftl index 906055768..8dca1d8ce 100644 --- a/locales/en-US/browser/browser/zen-library.ftl +++ b/locales/en-US/browser/browser/zen-library.ftl @@ -78,6 +78,7 @@ library-downloads-opening-in = { PLATFORM() -> [windows] Opening in File Explorer… *[other] Opening in file manager… } +library-downloads-open-when-done = Opens when finished library-downloads-cancel-button = .title = Cancel download library-downloads-filter-title = Filter Downloads… diff --git a/src/zen/library/ZenLibraryWidget.sys.mjs b/src/zen/library/ZenLibraryWidget.sys.mjs index 90a08ba53..d5406c7c0 100644 --- a/src/zen/library/ZenLibraryWidget.sys.mjs +++ b/src/zen/library/ZenLibraryWidget.sys.mjs @@ -235,7 +235,31 @@ class ZenLibraryDownloadStack { .finally(() => download.target.refresh()); } + /** + * @param {object} download + * @returns {boolean} Whether it will be opened as soon as it is here + */ + #opensWhenDone(download) { + return !download.stopped && !!download.launchWhenSucceeded; + } + + /** + * Clicking a download that is still coming in asks for it to be opened as + * soon as it is here, the way the downloads panel does. + * + * @param {object} download + */ + #toggleOpenWhenDone(download) { + download.launchWhenSucceeded = !download.launchWhenSucceeded; + download._launchedFromPanel = download.launchWhenSucceeded; + this.#updateList(); + } + #openDownload(download) { + if (!download.stopped) { + this.#toggleOpenWhenDone(download); + return; + } if (download.succeeded) { lazy.DownloadsCommon.openDownload(download).catch(console.error); } else if (download.source?.url) { @@ -274,14 +298,29 @@ class ZenLibraryDownloadStack { ); entry.querySelector(".zen-library-download-list-title").textContent = this.#fileName(download); - entry.querySelector(".zen-library-download-list-subtitle").textContent = - this.#statusText(download); + const subtitle = entry.querySelector( + ".zen-library-download-list-subtitle" + ); + if (this.#opensWhenDone(download)) { + this.#window.document.l10n.setAttributes( + subtitle, + "library-downloads-open-when-done" + ); + } else { + subtitle.removeAttribute("data-l10n-id"); + subtitle.textContent = this.#statusText(download); + } entry.toggleAttribute("downloading", !download.stopped); + entry.toggleAttribute("open-when-done", this.#opensWhenDone(download)); }); - this.#tabs?.style.setProperty( - "--zen-library-stack-height", - `${this.#list.getBoundingClientRect().height}px` - ); + this.#window + .promiseDocumentFlushed(() => this.#list.getBoundingClientRect().height) + .then(height => { + this.#tabs?.style.setProperty( + "--zen-library-stack-height", + `${height}px` + ); + }); } #updateBadge(badge, download) { diff --git a/src/zen/library/sections/ZenLibraryDownloadsSection.mjs b/src/zen/library/sections/ZenLibraryDownloadsSection.mjs index 65cb1a1fe..18bd55084 100644 --- a/src/zen/library/sections/ZenLibraryDownloadsSection.mjs +++ b/src/zen/library/sections/ZenLibraryDownloadsSection.mjs @@ -326,7 +326,34 @@ export class ZenLibraryDownloadsSection extends ZenLibrarySearchSection { lazy.DownloadsCommon.showDownloadedFile(this.#file(download)); } + /** + * Whether a download that is still coming in will be opened the moment it + * finishes. + * + * @param {object} download + * @returns {boolean} + */ + #opensWhenDone(download) { + return !download.stopped && !!download.launchWhenSucceeded; + } + + /** + * Clicking a download that is still coming in asks for it to be opened as + * soon as it is here, the way the downloads panel does. + * + * @param {object} download + */ + #toggleOpenWhenDone(download) { + download.launchWhenSucceeded = !download.launchWhenSucceeded; + download._launchedFromPanel = download.launchWhenSucceeded; + this.requestUpdate(); + } + #onRowClick(download) { + if (!download.stopped) { + this.#toggleOpenWhenDone(download); + return; + } if (!this.#hasFile(download)) { return; } @@ -516,15 +543,22 @@ export class ZenLibraryDownloadsSection extends ZenLibrarySearchSection { // Rendering #renderSubtitle(download) { - const statusNode = - download === this.#openingDownload - ? html`` - : html`${this.#statusText(download)}`; + let statusNode; + if (download === this.#openingDownload) { + statusNode = html``; + } else if (this.#opensWhenDone(download)) { + statusNode = html``; + } else { + statusNode = html`${this.#statusText(download)}`; + } return html` ${statusNode} @@ -584,6 +618,7 @@ export class ZenLibraryDownloadsSection extends ZenLibrarySearchSection { ?pending=${pending} ?indeterminate=${pending && !download.hasProgress} ?paused=${pending && download.stopped} + ?open-when-done=${this.#opensWhenDone(download)} ?opening=${download === this.#openingDownload} @click=${() => this.#onRowClick(download)} @contextmenu=${event => { diff --git a/src/zen/library/zen-library.css b/src/zen/library/zen-library.css index b5c10d76c..6189d26c7 100644 --- a/src/zen/library/zen-library.css +++ b/src/zen/library/zen-library.css @@ -882,6 +882,11 @@ zen-library:not([open]) :is(.zen-library-search-header, .zen-library-filter-head display: none; } +.zen-library-row[pending][open-when-done]:not(:hover) + .zen-library-download-status { + display: inline; +} + .zen-library-row[pending]:not(:hover) .zen-library-row-subtitle::before { content: ""; display: block;