From c9c441a8ae4d155afbc550d11c0dc53734b02ef3 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 30 Dec 2024 21:53:35 +0400 Subject: [PATCH 1/8] Added size setting to the misc settings controller --- src/lib/extension/settings/MiscSettings.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/extension/settings/MiscSettings.ts b/src/lib/extension/settings/MiscSettings.ts index 94ea7d2..1e0e76d 100644 --- a/src/lib/extension/settings/MiscSettings.ts +++ b/src/lib/extension/settings/MiscSettings.ts @@ -1,7 +1,10 @@ import CacheableSettings from "$lib/extension/base/CacheableSettings.ts"; +export type FullscreenViewerSize = 'small' | 'medium' | 'large' | 'full'; + interface MiscSettingsFields { fullscreenViewer: boolean; + fullscreenViewerSize: FullscreenViewerSize; } export default class MiscSettings extends CacheableSettings { @@ -13,7 +16,15 @@ export default class MiscSettings extends CacheableSettings return this._resolveSetting("fullscreenViewer", true); } + async resolveFullscreenViewerPreviewSize() { + return this._resolveSetting('fullscreenViewerSize', 'large'); + } + async setFullscreenViewerEnabled(isEnabled: boolean) { return this._writeSetting("fullscreenViewer", isEnabled); } + + async setFullscreenViewerPreviewSize(size: FullscreenViewerSize | string) { + return this._writeSetting('fullscreenViewerSize', size as FullscreenViewerSize); + } } From 52a8b6e77889f5001abd007657857ff925bfce87 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 30 Dec 2024 21:57:35 +0400 Subject: [PATCH 2/8] Rendering selector for preview size in the fullscreen viewer element --- src/lib/components/FullscreenViewer.js | 69 ++++++++++++++++++++++++-- src/styles/content/listing.scss | 6 +++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/lib/components/FullscreenViewer.js b/src/lib/components/FullscreenViewer.js index 204f287..2b7b39a 100644 --- a/src/lib/components/FullscreenViewer.js +++ b/src/lib/components/FullscreenViewer.js @@ -1,13 +1,13 @@ import {BaseComponent} from "$lib/components/base/BaseComponent.js"; +import MiscSettings from "$lib/extension/settings/MiscSettings.ts"; export class FullscreenViewer extends BaseComponent { /** @type {HTMLVideoElement} */ #videoElement = document.createElement('video'); /** @type {HTMLImageElement} */ #imageElement = document.createElement('img'); - #spinnerElement = document.createElement('i'); - + #sizeSelectorElement = document.createElement('select'); /** @type {number|null} */ #touchId = null; /** @type {number|null} */ @@ -22,9 +22,22 @@ export class FullscreenViewer extends BaseComponent { */ build() { this.container.classList.add('fullscreen-viewer'); - this.container.append(this.#spinnerElement); + + this.container.append( + this.#spinnerElement, + this.#sizeSelectorElement, + ); this.#spinnerElement.classList.add('spinner', 'fa', 'fa-circle-notch', 'fa-spin'); + this.#sizeSelectorElement.classList.add('size-selector', 'input'); + + for (const [sizeKey, sizeName] of Object.entries(FullscreenViewer.#previewSizes)) { + const sizeOptionElement = document.createElement('option'); + sizeOptionElement.value = sizeKey; + sizeOptionElement.innerText = sizeName; + + this.#sizeSelectorElement.append(sizeOptionElement); + } } /** @@ -40,6 +53,11 @@ export class FullscreenViewer extends BaseComponent { this.#videoElement.addEventListener('loadeddata', this.#onLoaded.bind(this)); this.#imageElement.addEventListener('load', this.#onLoaded.bind(this)); + + FullscreenViewer.#miscSettings + .resolveFullscreenViewerPreviewSize() + .then(this.#onSizeResolved.bind(this)) + .then(this.#watchForSizeSelectionChanges.bind(this)); } #onLoaded() { @@ -163,6 +181,39 @@ export class FullscreenViewer extends BaseComponent { } } + /** + * @param {import("$lib/extension/settings/MiscSettings.js").FullscreenViewerSize} size + */ + #onSizeResolved(size) { + this.#sizeSelectorElement.value = size; + } + + #watchForSizeSelectionChanges() { + let lastActiveSize = this.#sizeSelectorElement.value; + + FullscreenViewer.#miscSettings.subscribe(settings => { + const targetSize = settings.fullscreenViewerSize; + + if (!targetSize || lastActiveSize === targetSize) { + return; + } + + lastActiveSize = targetSize; + this.#sizeSelectorElement.value = targetSize; + }); + + this.#sizeSelectorElement.addEventListener('input', () => { + const targetSize = this.#sizeSelectorElement.value; + + if (!targetSize || targetSize === lastActiveSize || !(targetSize in FullscreenViewer.#previewSizes)) { + return; + } + + lastActiveSize = targetSize; + void FullscreenViewer.#miscSettings.setFullscreenViewerPreviewSize(targetSize); + }); + } + #close() { this.container.classList.remove(FullscreenViewer.#shownState); document.body.style.overflow = null; @@ -214,9 +265,21 @@ export class FullscreenViewer extends BaseComponent { return url.endsWith('.mp4') || url.endsWith('.webm'); } + static #miscSettings = new MiscSettings(); + static #offsetProperty = '--offset'; static #opacityProperty = '--opacity'; static #shownState = 'shown'; static #swipeState = 'swiped'; static #minRequiredDistance = 50; + + /** + * @type {Record} + */ + static #previewSizes = { + full: 'Full', + large: 'Large', + medium: 'Medium', + small: 'Small' + } } diff --git a/src/styles/content/listing.scss b/src/styles/content/listing.scss index 37804f5..f963b82 100644 --- a/src/styles/content/listing.scss +++ b/src/styles/content/listing.scss @@ -199,6 +199,12 @@ transition: opacity .25s ease; } + .size-selector { + position: absolute; + top: 5px; + left: 5px; + } + &.shown { opacity: var(--opacity, 1); pointer-events: initial; From 98d3b1c696bab5548d41b58f4806f4630c28afe8 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 30 Dec 2024 22:40:17 +0400 Subject: [PATCH 3/8] Prevent viewer from closing when selecting sizes --- src/lib/components/FullscreenViewer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/components/FullscreenViewer.js b/src/lib/components/FullscreenViewer.js index 2b7b39a..6fa0a29 100644 --- a/src/lib/components/FullscreenViewer.js +++ b/src/lib/components/FullscreenViewer.js @@ -53,6 +53,7 @@ export class FullscreenViewer extends BaseComponent { this.#videoElement.addEventListener('loadeddata', this.#onLoaded.bind(this)); this.#imageElement.addEventListener('load', this.#onLoaded.bind(this)); + this.#sizeSelectorElement.addEventListener('click', event => event.stopPropagation()); FullscreenViewer.#miscSettings .resolveFullscreenViewerPreviewSize() From 3d1e0d6f06fa139c08765dff9f7482623c9535c4 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 30 Dec 2024 22:40:58 +0400 Subject: [PATCH 4/8] Updated viewer to receive all the image URLs and apply selected size --- src/app.d.ts | 7 +++ src/lib/components/FullscreenViewer.js | 43 ++++++++++++++++++- .../components/ImageShowFullscreenButton.js | 2 +- src/lib/components/MediaBoxWrapper.js | 9 +--- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/app.d.ts b/src/app.d.ts index 3735081..d28bfe2 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -25,6 +25,13 @@ declare global { interface EntityNamesMap { profiles: MaintenanceProfile; } + + interface ImageURIs { + full: string; + large: string; + medium: string; + small: string; + } } } diff --git a/src/lib/components/FullscreenViewer.js b/src/lib/components/FullscreenViewer.js index 6fa0a29..faa3551 100644 --- a/src/lib/components/FullscreenViewer.js +++ b/src/lib/components/FullscreenViewer.js @@ -16,6 +16,7 @@ export class FullscreenViewer extends BaseComponent { #startY = null; /** @type {boolean|null} */ #isClosingSwipeStarted = null; + #isSizeFetched = false; /** * @protected @@ -187,6 +188,9 @@ export class FullscreenViewer extends BaseComponent { */ #onSizeResolved(size) { this.#sizeSelectorElement.value = size; + this.#isSizeFetched = true; + + this.emit('size-loaded'); } #watchForSizeSelectionChanges() { @@ -227,9 +231,42 @@ export class FullscreenViewer extends BaseComponent { } /** - * @param {string} url + * @param {App.ImageURIs} imageUris + * @return {Promise} */ - show(url) { + async #resolveCurrentSelectedSizeUrl(imageUris) { + if (!this.#isSizeFetched) { + await new Promise(resolve => this.on('size-loaded', resolve)) + } + + let targetSize = this.#sizeSelectorElement.value; + + if (!imageUris.hasOwnProperty(targetSize)) { + targetSize = FullscreenViewer.#fallbackSize; + } + + if (!imageUris.hasOwnProperty(targetSize)) { + targetSize = Object.keys(imageUris)[0]; + } + + if (!targetSize) { + return null; + } + + return imageUris[targetSize]; + } + + /** + * @param {App.ImageURIs} imageUris + */ + async show(imageUris) { + const url = await this.#resolveCurrentSelectedSizeUrl(imageUris); + + if (!url) { + console.warn('Failed to resolve media for the viewer!'); + return; + } + this.container.classList.add('loading'); requestAnimationFrame(() => { @@ -283,4 +320,6 @@ export class FullscreenViewer extends BaseComponent { medium: 'Medium', small: 'Small' } + + static #fallbackSize = 'large'; } diff --git a/src/lib/components/ImageShowFullscreenButton.js b/src/lib/components/ImageShowFullscreenButton.js index f0cd8c7..1970504 100644 --- a/src/lib/components/ImageShowFullscreenButton.js +++ b/src/lib/components/ImageShowFullscreenButton.js @@ -47,7 +47,7 @@ export class ImageShowFullscreenButton extends BaseComponent { #onButtonClicked() { ImageShowFullscreenButton .#resolveViewer() - .show(this.#mediaBoxTools.mediaBox.imageLinks.large); + .show(this.#mediaBoxTools.mediaBox.imageLinks); } /** diff --git a/src/lib/components/MediaBoxWrapper.js b/src/lib/components/MediaBoxWrapper.js index cc4c6a2..5fb1a3f 100644 --- a/src/lib/components/MediaBoxWrapper.js +++ b/src/lib/components/MediaBoxWrapper.js @@ -56,7 +56,7 @@ export class MediaBoxWrapper extends BaseComponent { } /** - * @return {ImageURIs} + * @return {App.ImageURIs} */ get imageLinks() { return JSON.parse(this.#thumbnailContainer.dataset.uris); @@ -100,10 +100,3 @@ export function calculateMediaBoxesPositions(mediaBoxesList) { } }) } - -/** - * @typedef {Object} ImageURIs - * @property {string} full - * @property {string} large - * @property {string} small - */ From 112d60ac7843368758a47c07a58d6dbad6f9c6a4 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 30 Dec 2024 22:52:48 +0400 Subject: [PATCH 5/8] Fixed selector appearing behind the media and not being accessible --- src/styles/content/listing.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/content/listing.scss b/src/styles/content/listing.scss index f963b82..a770c00 100644 --- a/src/styles/content/listing.scss +++ b/src/styles/content/listing.scss @@ -203,6 +203,7 @@ position: absolute; top: 5px; left: 5px; + z-index: 1; } &.shown { From 757526ab52c010ace89673b56fb1aeaaa7141fae Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 30 Dec 2024 22:57:43 +0400 Subject: [PATCH 6/8] Fixed fullscreen button hiding itself on settings update --- src/lib/components/ImageShowFullscreenButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/ImageShowFullscreenButton.js b/src/lib/components/ImageShowFullscreenButton.js index 1970504..2f8c6e3 100644 --- a/src/lib/components/ImageShowFullscreenButton.js +++ b/src/lib/components/ImageShowFullscreenButton.js @@ -33,7 +33,7 @@ export class ImageShowFullscreenButton extends BaseComponent { }) .then(() => { ImageShowFullscreenButton.#miscSettings.subscribe(settings => { - this.#isFullscreenButtonEnabled = settings.fullscreenViewer; + this.#isFullscreenButtonEnabled = settings.fullscreenViewer ?? true; this.#updateFullscreenButtonVisibility(); }) }) From 309dd15598591c0427606cd4c0d4eea2172c9ad0 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Mon, 30 Dec 2024 23:08:06 +0400 Subject: [PATCH 7/8] Keep the current URIs and switch them when size is changed --- src/lib/components/FullscreenViewer.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib/components/FullscreenViewer.js b/src/lib/components/FullscreenViewer.js index faa3551..b74182d 100644 --- a/src/lib/components/FullscreenViewer.js +++ b/src/lib/components/FullscreenViewer.js @@ -17,6 +17,8 @@ export class FullscreenViewer extends BaseComponent { /** @type {boolean|null} */ #isClosingSwipeStarted = null; #isSizeFetched = false; + /** @type {App.ImageURIs|null} */ + #currentURIs = null; /** * @protected @@ -210,6 +212,10 @@ export class FullscreenViewer extends BaseComponent { this.#sizeSelectorElement.addEventListener('input', () => { const targetSize = this.#sizeSelectorElement.value; + if (this.#currentURIs) { + void this.show(this.#currentURIs); + } + if (!targetSize || targetSize === lastActiveSize || !(targetSize in FullscreenViewer.#previewSizes)) { return; } @@ -220,6 +226,8 @@ export class FullscreenViewer extends BaseComponent { } #close() { + this.#currentURIs = null; + this.container.classList.remove(FullscreenViewer.#shownState); document.body.style.overflow = null; @@ -260,6 +268,8 @@ export class FullscreenViewer extends BaseComponent { * @param {App.ImageURIs} imageUris */ async show(imageUris) { + this.#currentURIs = imageUris; + const url = await this.#resolveCurrentSelectedSizeUrl(imageUris); if (!url) { From ba10768496fad989b1f99cbeaa473b651933c50c Mon Sep 17 00:00:00 2001 From: KoloMl Date: Fri, 3 Jan 2025 06:28:09 +0400 Subject: [PATCH 8/8] Added visible close button --- src/lib/components/FullscreenViewer.js | 3 +++ src/styles/content/listing.scss | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/lib/components/FullscreenViewer.js b/src/lib/components/FullscreenViewer.js index b74182d..ee8b939 100644 --- a/src/lib/components/FullscreenViewer.js +++ b/src/lib/components/FullscreenViewer.js @@ -8,6 +8,7 @@ export class FullscreenViewer extends BaseComponent { #imageElement = document.createElement('img'); #spinnerElement = document.createElement('i'); #sizeSelectorElement = document.createElement('select'); + #closeButtonElement = document.createElement('i'); /** @type {number|null} */ #touchId = null; /** @type {number|null} */ @@ -29,9 +30,11 @@ export class FullscreenViewer extends BaseComponent { this.container.append( this.#spinnerElement, this.#sizeSelectorElement, + this.#closeButtonElement, ); this.#spinnerElement.classList.add('spinner', 'fa', 'fa-circle-notch', 'fa-spin'); + this.#closeButtonElement.classList.add('close', 'fa', 'fa-xmark'); this.#sizeSelectorElement.classList.add('size-selector', 'input'); for (const [sizeKey, sizeName] of Object.entries(FullscreenViewer.#previewSizes)) { diff --git a/src/styles/content/listing.scss b/src/styles/content/listing.scss index a770c00..a6ec9e9 100644 --- a/src/styles/content/listing.scss +++ b/src/styles/content/listing.scss @@ -206,6 +206,23 @@ z-index: 1; } + .close { + position: absolute; + top: 5px; + right: 5px; + z-index: 1; + padding: 5px; + background-color: colors.$text; + color: colors.$background; + font-size: 20px; + line-height: 20px; + width: 20px; + height: 20px; + text-align: center; + display: block; + cursor: pointer; + } + &.shown { opacity: var(--opacity, 1); pointer-events: initial;