fix(web): close edit faces panel on Escape key press (#27519)

Move `showEditFaces` state to `assetViewerManager` so the edit faces
panel open/close state is globally accessible. Add Escape key handling
to `PersonSidePanel` that closes the assign-face sub-panel first, then
the edit faces panel. Guard the asset viewer's global Escape-to-close
action so it doesn't fire while either face panel is open.

Change-Id: I0c947fe0758aef0eac473f4cc72f6a3b6a6a6964
This commit is contained in:
Min Idzelis 2026-04-15 21:22:20 -04:00 committed by GitHub
parent 37abbeba52
commit d5d2ebd9bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 40 additions and 7 deletions

View File

@ -91,7 +91,7 @@
const Close: ActionItem = $derived({
title: $t('go_back'),
icon: languageManager.rtl ? mdiArrowRight : mdiArrowLeft,
$if: () => !!onClose && !assetViewerManager.isFaceEditMode,
$if: () => !!onClose && !assetViewerManager.isFaceEditMode && !assetViewerManager.isEditFacesPanelOpen,
onAction: () => onClose?.(),
shortcuts: [{ key: 'Escape' }],
});

View File

@ -173,7 +173,7 @@
onDestroy(() => {
activityManager.reset();
assetViewerManager.closeEditor();
assetViewerManager.resetPanelState();
syncAssetViewerOpenClass(false);
preloadManager.destroy();
});

View File

@ -54,7 +54,7 @@
let { asset, currentAlbum = null }: Props = $props();
let showEditFaces = $state(false);
let showEditFaces = $derived(assetViewerManager.isEditFacesPanelOpen);
let isOwner = $derived(authManager.authenticated && authManager.user.id === asset.ownerId);
let people = $derived(asset.people || []);
let unassignedFaces = $derived(asset.unassignedFaces || []);
@ -103,7 +103,7 @@
return;
}
showEditFaces = false;
assetViewerManager.closeEditFacesPanel();
previousId = asset.id;
});
@ -119,7 +119,7 @@
const handleRefreshPeople = async () => {
asset = await getAssetInfo({ id: asset.id });
showEditFaces = false;
assetViewerManager.closeEditFacesPanel();
};
const getAssetFolderHref = (asset: AssetResponseDto) => {
@ -214,7 +214,7 @@
shape="round"
color="secondary"
variant="ghost"
onclick={() => (showEditFaces = true)}
onclick={() => assetViewerManager.openEditFacesPanel()}
/>
{/if}
</div>
@ -572,7 +572,7 @@
<PersonSidePanel
assetId={asset.id}
assetType={asset.type}
onClose={() => (showEditFaces = false)}
onClose={() => assetViewerManager.closeEditFacesPanel()}
onRefresh={handleRefreshPeople}
/>
{/if}

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { shortcut } from '$lib/actions/shortcut';
import OnEvents from '$lib/components/OnEvents.svelte';
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
@ -187,6 +188,19 @@
<OnEvents {onPersonThumbnailReady} />
<svelte:document
use:shortcut={{
shortcut: { key: 'Escape' },
onShortcut: () => {
if (showSelectedFaces) {
showSelectedFaces = false;
} else {
onClose();
}
},
}}
/>
<section
transition:fly={{ x: 360, duration: 100, easing: linear }}
class="absolute top-0 h-full w-90 overflow-x-hidden p-2 dark:text-immich-dark-fg bg-light"

View File

@ -44,6 +44,7 @@ class AssetViewerManager extends BaseEventManager<Events> {
isPlayingMotionPhoto = $state(false);
isShowEditor = $state(false);
#isFaceEditMode = $state(false);
#isEditFacesPanelOpen = $state(false);
#viewingAssetStoreState = $state<AssetResponseDto>();
#viewState = $state<boolean>(false);
gridScrollTarget = $state<AssetGridRouteSearchParams | null | undefined>();
@ -72,6 +73,10 @@ class AssetViewerManager extends BaseEventManager<Events> {
return this.#isFaceEditMode;
}
get isEditFacesPanelOpen() {
return this.#isEditFacesPanelOpen;
}
get zoomState() {
return this.#zoomState;
}
@ -186,6 +191,20 @@ class AssetViewerManager extends BaseEventManager<Events> {
this.#isFaceEditMode = false;
}
openEditFacesPanel() {
this.#isEditFacesPanelOpen = true;
}
closeEditFacesPanel() {
this.#isEditFacesPanelOpen = false;
}
resetPanelState() {
this.closeEditor();
this.closeFaceEditMode();
this.closeEditFacesPanel();
}
setAsset(asset: AssetResponseDto) {
this.#viewingAssetStoreState = asset;
this.#viewState = true;