refactor(web): migrate isFaceEditMode from standalone store to assetViewerManager (#27307)

This commit is contained in:
Min Idzelis
2026-03-27 08:20:15 -04:00
committed by GitHub
parent e2d26ebdea
commit 4b9ebc2cff
10 changed files with 35 additions and 34 deletions
@@ -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' }],
});
@@ -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 @@
</div>
{/if}
{#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !isFaceEditMode.value && previousAsset}
{#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !assetViewerManager.isFaceEditMode && previousAsset}
<div class="my-auto col-span-1 col-start-1 row-span-full row-start-1 justify-self-start">
<PreviousAssetAction onPreviousAsset={() => navigateAsset('previous')} />
</div>
@@ -571,7 +570,7 @@
{/if}
</div>
{#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !isFaceEditMode.value && nextAsset}
{#if $slideshowState === SlideshowState.None && showNavigation && !assetViewerManager.isShowEditor && !assetViewerManager.isFaceEditMode && nextAsset}
<div class="my-auto col-span-1 col-start-4 row-span-full row-start-1 justify-self-end">
<NextAssetAction onNextAsset={() => navigateAsset('next')} />
</div>
@@ -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}
@@ -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();
});
</script>
<svelte:document use:shortcut={{ shortcut: { key: 'Escape' }, onShortcut: cancel, ignoreInputFields: false }} />
<svelte:document use:shortcut={{ shortcut: { key: 'Escape' }, onShortcut: onClose, ignoreInputFields: false }} />
<div
id="face-editor-data"
@@ -350,6 +353,6 @@
{/if}
</div>
<Button size="small" fullWidth onclick={cancel} color="danger" class="mt-2">{$t('cancel')}</Button>
<Button size="small" fullWidth onclick={onClose} color="danger" class="mt-2">{$t('cancel')}</Button>
</div>
</div>
@@ -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))}
>
<AdaptiveImage
@@ -265,7 +264,7 @@
{/snippet}
</AdaptiveImage>
{#if isFaceEditMode.value && assetViewerManager.imgRef}
{#if assetViewerManager.isFaceEditMode && assetViewerManager.imgRef}
<FaceEditor htmlElement={assetViewerManager.imgRef} {containerWidth} {containerHeight} assetId={asset.id} />
{/if}
</div>
@@ -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 @@
</div>
{/if}
{#if isFaceEditMode.value}
{#if assetViewerManager.isFaceEditMode}
<FaceEditor htmlElement={videoPlayer} {containerWidth} {containerHeight} {assetId} />
{/if}
{/if}
@@ -42,7 +42,7 @@ class AssetViewerManager extends BaseEventManager<Events> {
isShowActivityPanel = $state(false);
isPlayingMotionPhoto = $state(false);
isShowEditor = $state(false);
#isFaceEditMode = $state(false);
#viewingAssetStoreState = $state<AssetResponseDto>();
#viewState = $state<boolean>(false);
gridScrollTarget = $state<AssetGridRouteSearchParams | null | undefined>();
@@ -63,6 +63,10 @@ class AssetViewerManager extends BaseEventManager<Events> {
return isShowDetailPanel.current;
}
get isFaceEditMode() {
return this.#isFaceEditMode;
}
get zoomState() {
return this.#zoomState;
}
@@ -161,6 +165,14 @@ class AssetViewerManager extends BaseEventManager<Events> {
this.isShowEditor = false;
}
toggleFaceEditMode() {
this.#isFaceEditMode = !this.#isFaceEditMode;
}
closeFaceEditMode() {
this.#isFaceEditMode = false;
}
setAsset(asset: AssetResponseDto) {
this.#viewingAssetStoreState = asset;
this.#viewState = true;
+1 -4
View File
@@ -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' },
};
-1
View File
@@ -1 +0,0 @@
export const isFaceEditMode = $state({ value: false });
@@ -1,5 +1,4 @@
<script lang="ts">
import { beforeNavigate } from '$app/navigation';
import ActionMenuItem from '$lib/components/ActionMenuItem.svelte';
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
@@ -26,7 +25,6 @@
import { Route } from '$lib/route';
import { getAssetBulkActions } from '$lib/services/asset.service';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
import { preferences, user } from '$lib/stores/user.store';
import { getAssetMediaUrl, memoryLaneTitle } from '$lib/utils';
import {
@@ -85,10 +83,6 @@
assetInteraction.clearMultiselect();
};
beforeNavigate(() => {
isFaceEditMode.value = false;
});
const items = $derived(
memoryManager.memories.map((memory) => ({
id: memory.id,