From 4b9ebc2cfff9251769b6f41a39f066992a4b1468 Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Fri, 27 Mar 2026 08:20:15 -0400 Subject: [PATCH] refactor(web): migrate isFaceEditMode from standalone store to assetViewerManager (#27307) --- .../asset-viewer/asset-viewer-nav-bar.svelte | 3 +-- .../components/asset-viewer/asset-viewer.svelte | 5 ++--- .../components/asset-viewer/detail-panel.svelte | 3 +-- .../asset-viewer/face-editor/face-editor.svelte | 17 ++++++++++------- .../components/asset-viewer/photo-viewer.svelte | 9 ++++----- .../asset-viewer/video-native-viewer.svelte | 6 +++--- .../lib/managers/asset-viewer-manager.svelte.ts | 14 +++++++++++++- web/src/lib/services/asset.service.ts | 5 +---- web/src/lib/stores/face-edit.svelte.ts | 1 - .../(user)/photos/[[assetId=id]]/+page.svelte | 6 ------ 10 files changed, 35 insertions(+), 34 deletions(-) delete mode 100644 web/src/lib/stores/face-edit.svelte.ts diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index 00e845e8ec..29626ea265 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -25,7 +25,6 @@ import { Route } from '$lib/route'; import { getGlobalActions } from '$lib/services/app.service'; import { getAssetActions } from '$lib/services/asset.service'; - import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { user } from '$lib/stores/user.store'; import { getSharedLink, withoutIcons } from '$lib/utils'; import type { OnUndoDelete } from '$lib/utils/actions'; @@ -93,7 +92,7 @@ title: $t('go_back'), type: $t('assets'), icon: languageManager.rtl ? mdiArrowRight : mdiArrowLeft, - $if: () => !!onClose && !isFaceEditMode.value, + $if: () => !!onClose && !assetViewerManager.isFaceEditMode, onAction: () => onClose?.(), shortcuts: [{ key: 'Escape' }], }); diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index e2981e2e55..1072104d4d 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -14,7 +14,6 @@ import { editManager, EditToolType } from '$lib/managers/edit/edit-manager.svelte'; import { eventManager } from '$lib/managers/event-manager.svelte'; import { getAssetActions } from '$lib/services/asset.service'; - import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { ocrManager } from '$lib/stores/ocr.svelte'; import { alwaysLoadOriginalVideo } from '$lib/stores/preferences.store'; import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store'; @@ -498,7 +497,7 @@ {/if} - {#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !isFaceEditMode.value && previousAsset} + {#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !assetViewerManager.isFaceEditMode && previousAsset}
navigateAsset('previous')} />
@@ -571,7 +570,7 @@ {/if} - {#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !isFaceEditMode.value && nextAsset} + {#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !assetViewerManager.isFaceEditMode && nextAsset}
navigateAsset('next')} />
diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index e80d376f57..ba3ae559dc 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -10,7 +10,6 @@ import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte'; import AssetChangeDateModal from '$lib/modals/AssetChangeDateModal.svelte'; import { Route } from '$lib/route'; - import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { boundingBoxesArray } from '$lib/stores/people.store'; import { locale } from '$lib/stores/preferences.store'; import { preferences, user } from '$lib/stores/user.store'; @@ -208,7 +207,7 @@ shape="round" color="secondary" variant="ghost" - onclick={() => (isFaceEditMode.value = !isFaceEditMode.value)} + onclick={() => assetViewerManager.toggleFaceEditMode()} /> {#if people.length > 0 || unassignedFaces.length > 0} diff --git a/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte b/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte index c5b4ab5ce7..ea2babfc11 100644 --- a/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte +++ b/web/src/lib/components/asset-viewer/face-editor/face-editor.svelte @@ -2,7 +2,6 @@ import { shortcut } from '$lib/actions/shortcut'; import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte'; import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte'; - import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { getPeopleThumbnailUrl } from '$lib/utils'; import { getNaturalSize, scaleToFit } from '$lib/utils/container-utils'; import { handleError } from '$lib/utils/handle-error'; @@ -10,7 +9,7 @@ import { Button, Input, modalManager, toastManager } from '@immich/ui'; import { Canvas, InteractiveFabricObject, Rect } from 'fabric'; import { clamp } from 'lodash-es'; - import { onMount, tick } from 'svelte'; + import { onDestroy, onMount, tick } from 'svelte'; import { t } from 'svelte-i18n'; interface Props { @@ -139,8 +138,8 @@ ); }; - const cancel = () => { - isFaceEditMode.value = false; + const onClose = () => { + assetViewerManager.closeFaceEditMode(); }; const getPeople = async () => { @@ -291,12 +290,16 @@ } catch (error) { handleError(error, 'Error tagging face'); } finally { - isFaceEditMode.value = false; + onClose(); } }; + + onDestroy(() => { + onClose(); + }); - +
- +
diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte index 4a6a02cb4a..a26b66b0b6 100644 --- a/web/src/lib/components/asset-viewer/photo-viewer.svelte +++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte @@ -8,7 +8,6 @@ import AssetViewerEvents from '$lib/components/AssetViewerEvents.svelte'; import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte'; import { castManager } from '$lib/managers/cast-manager.svelte'; - import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { ocrManager } from '$lib/stores/ocr.svelte'; import { boundingBoxesArray, type Faces } from '$lib/stores/people.store'; import { SlideshowLook, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store'; @@ -106,7 +105,7 @@ const onPlaySlideshow = () => ($slideshowState = SlideshowState.PlaySlideshow); $effect(() => { - if (isFaceEditMode.value && assetViewerManager.zoom > 1) { + if (assetViewerManager.isFaceEditMode && assetViewerManager.zoom > 1) { onZoom(); } }); @@ -166,7 +165,7 @@ const handleImageMouseMove = (event: MouseEvent) => { $boundingBoxesArray = []; - if (!assetViewerManager.imgRef || !element || isFaceEditMode.value || ocrManager.showOverlay) { + if (!assetViewerManager.imgRef || !element || assetViewerManager.isFaceEditMode || ocrManager.showOverlay) { return; } @@ -215,7 +214,7 @@ ondblclick={onZoom} onmousemove={handleImageMouseMove} onmouseleave={handleImageMouseLeave} - use:zoomImageAction={{ disabled: isFaceEditMode.value || ocrManager.showOverlay }} + use:zoomImageAction={{ disabled: assetViewerManager.isFaceEditMode || ocrManager.showOverlay }} {...useSwipe((event) => onSwipe?.(event))} > - {#if isFaceEditMode.value && assetViewerManager.imgRef} + {#if assetViewerManager.isFaceEditMode && assetViewerManager.imgRef} {/if} diff --git a/web/src/lib/components/asset-viewer/video-native-viewer.svelte b/web/src/lib/components/asset-viewer/video-native-viewer.svelte index e53414be07..915d5ceb58 100644 --- a/web/src/lib/components/asset-viewer/video-native-viewer.svelte +++ b/web/src/lib/components/asset-viewer/video-native-viewer.svelte @@ -3,7 +3,7 @@ import VideoRemoteViewer from '$lib/components/asset-viewer/video-remote-viewer.svelte'; import { assetViewerFadeDuration } from '$lib/constants'; import { castManager } from '$lib/managers/cast-manager.svelte'; - import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; + import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte'; import { autoPlayVideo, loopVideo as loopVideoPreference, @@ -115,7 +115,7 @@ let containerHeight = $state(0); $effect(() => { - if (isFaceEditMode.value) { + if (assetViewerManager.isFaceEditMode) { videoPlayer?.pause(); } }); @@ -172,7 +172,7 @@ {/if} - {#if isFaceEditMode.value} + {#if assetViewerManager.isFaceEditMode} {/if} {/if} diff --git a/web/src/lib/managers/asset-viewer-manager.svelte.ts b/web/src/lib/managers/asset-viewer-manager.svelte.ts index f46be6b698..157ed31243 100644 --- a/web/src/lib/managers/asset-viewer-manager.svelte.ts +++ b/web/src/lib/managers/asset-viewer-manager.svelte.ts @@ -42,7 +42,7 @@ class AssetViewerManager extends BaseEventManager { isShowActivityPanel = $state(false); isPlayingMotionPhoto = $state(false); isShowEditor = $state(false); - + #isFaceEditMode = $state(false); #viewingAssetStoreState = $state(); #viewState = $state(false); gridScrollTarget = $state(); @@ -63,6 +63,10 @@ class AssetViewerManager extends BaseEventManager { return isShowDetailPanel.current; } + get isFaceEditMode() { + return this.#isFaceEditMode; + } + get zoomState() { return this.#zoomState; } @@ -161,6 +165,14 @@ class AssetViewerManager extends BaseEventManager { this.isShowEditor = false; } + toggleFaceEditMode() { + this.#isFaceEditMode = !this.#isFaceEditMode; + } + + closeFaceEditMode() { + this.#isFaceEditMode = false; + } + setAsset(asset: AssetResponseDto) { this.#viewingAssetStoreState = asset; this.#viewState = true; diff --git a/web/src/lib/services/asset.service.ts b/web/src/lib/services/asset.service.ts index bc16b65577..d2d847fc15 100644 --- a/web/src/lib/services/asset.service.ts +++ b/web/src/lib/services/asset.service.ts @@ -5,7 +5,6 @@ import { eventManager } from '$lib/managers/event-manager.svelte'; import AssetAddToAlbumModal from '$lib/modals/AssetAddToAlbumModal.svelte'; import AssetTagModal from '$lib/modals/AssetTagModal.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; -import { isFaceEditMode } from '$lib/stores/face-edit.svelte'; import { user as authUser, preferences } from '$lib/stores/user.store'; import type { AssetControlContext } from '$lib/types'; import { getAssetMediaUrl, getSharedLink, sleep } from '$lib/utils'; @@ -229,9 +228,7 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) = icon: mdiFaceRecognition, type: $t('assets'), $if: () => isOwner && asset.type === AssetTypeEnum.Image && !asset.isTrashed, - onAction: () => { - isFaceEditMode.value = !isFaceEditMode.value; - }, + onAction: () => assetViewerManager.toggleFaceEditMode(), shortcuts: { key: 'p' }, }; diff --git a/web/src/lib/stores/face-edit.svelte.ts b/web/src/lib/stores/face-edit.svelte.ts deleted file mode 100644 index 0b2f436099..0000000000 --- a/web/src/lib/stores/face-edit.svelte.ts +++ /dev/null @@ -1 +0,0 @@ -export const isFaceEditMode = $state({ value: false }); diff --git a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte index 21f51a8f49..462f8fa3b6 100644 --- a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte @@ -1,5 +1,4 @@