fix(web): refresh people after setting person thumbnail (#31744)

This commit is contained in:
Mees Frensel
2026-09-23 13:44:03 +00:00
committed by GitHub
parent 3e2e8e0fe8
commit 20862f1d4a
6 changed files with 44 additions and 55 deletions
@@ -177,6 +177,14 @@
}
};
const onPersonThumbnailReady = async ({ id: personId }: { id: string }) => {
if (person && person.id !== personId) {
return;
}
faceManager.clear();
await faceManager.getAssetFaces(asset.id);
};
onMount(() => {
syncAssetViewerOpenClass(true);
const slideshowStateUnsubscribe = slideshowState.subscribe((value) => {
@@ -345,19 +353,13 @@
preAction?.(action);
};
const handleAction = async (action: Action) => {
const handleAction = (action: Action) => {
switch (action.type) {
case AssetAction.DELETE:
case AssetAction.TRASH: {
eventManager.emit('AssetsDelete', [asset.id]);
break;
}
case AssetAction.SET_PERSON_FEATURED_PHOTO: {
const assetInfo = await getAssetInfo({ id: asset.id });
cursor.current = { ...asset, people: assetInfo.people };
eventManager.emit('AssetUpdate', cursor.current);
break;
}
case AssetAction.RATING: {
cursor.current = {
...asset,
@@ -490,7 +492,14 @@
</script>
<CommandPaletteDefaultProvider name={$t('assets')} actions={[Tag, TagPeople]} />
<OnEvents {onAssetUpdate} {onAssetsUndoArchive} {onStackCreate} onStackDelete={() => closeViewer()} {onStackUpdate} />
<OnEvents
{onAssetUpdate}
{onAssetsUndoArchive}
{onStackCreate}
onStackDelete={() => closeViewer()}
{onStackUpdate}
{onPersonThumbnailReady}
/>
<svelte:document
bind:fullscreenElement
@@ -5,7 +5,6 @@
import DeleteAction from '$lib/components/asset-viewer/actions/DeleteAction.svelte';
import RatingAction from '$lib/components/asset-viewer/actions/RatingAction.svelte';
import RestoreAction from '$lib/components/asset-viewer/actions/RestoreAction.svelte';
import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/SetPersonFeaturedAction.svelte';
import SetVisibilityAction from '$lib/components/asset-viewer/actions/SetVisibilityAction.svelte';
import LoadingDots from '$lib/components/LoadingDots.svelte';
import ButtonContextMenu from '$lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
@@ -15,6 +14,7 @@
import { getAlbumAssetActions } from '$lib/services/album.service';
import { getGlobalActions } from '$lib/services/app.service';
import { getAssetActions } from '$lib/services/asset.service';
import { getPersonAssetActions } from '$lib/services/person.service';
import { getStackActions } from '$lib/services/stack.service';
import { getSharedLink, withoutIcons } from '$lib/utils';
import type { OnUndoDelete } from '$lib/utils/actions';
@@ -152,7 +152,8 @@
<ActionMenuItem action={SetCover} />
{/if}
{#if person}
<SetFeaturedPhotoAction {asset} {person} {onAction} />
{@const { SetFeaturedPhoto } = getPersonAssetActions($t, person, asset)}
<ActionMenuItem action={SetFeaturedPhoto} />
{/if}
<ActionMenuItem action={Actions.SetProfilePicture} />
@@ -1,41 +0,0 @@
<script lang="ts">
import type { OnAction } from '$lib/components/asset-viewer/actions/action';
import MenuOption from '$lib/components/shared-components/context-menu/MenuOption.svelte';
import { AssetAction } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
import { updatePerson, type AssetResponseDto, type PersonResponseDto } from '@immich/sdk';
import { toastManager } from '@immich/ui';
import { mdiFaceManProfile } from '@mdi/js';
import { t } from 'svelte-i18n';
interface Props {
asset: AssetResponseDto;
person: PersonResponseDto;
onAction?: OnAction;
}
let { asset, person, onAction }: Props = $props();
const handleSelectFeaturePhoto = async () => {
try {
const updatedPerson = await updatePerson({
id: person.id,
personUpdateDto: { featureFaceAssetId: asset.id },
});
person = { ...person, ...updatedPerson };
onAction?.({
type: AssetAction.SET_PERSON_FEATURED_PHOTO,
asset,
person,
});
toastManager.primary($t('feature_photo_updated'));
} catch (error) {
handleError(error, $t('errors.unable_to_set_feature_photo'));
}
};
</script>
<MenuOption text={$t('set_as_featured_photo')} icon={mdiFaceManProfile} onClick={handleSelectFeaturePhoto} />
@@ -1,4 +1,3 @@
import type { AssetResponseDto, PersonResponseDto } from '@immich/sdk';
import type { AssetAction } from '$lib/constants';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
@@ -10,7 +9,6 @@ type ActionMap = {
[AssetAction.RESTORE]: { asset: TimelineAsset };
[AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset };
[AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset };
[AssetAction.SET_PERSON_FEATURED_PHOTO]: { asset: AssetResponseDto; person: PersonResponseDto };
[AssetAction.RATING]: { asset: TimelineAsset; rating: number | null };
};
-1
View File
@@ -8,7 +8,6 @@ export enum AssetAction {
RESTORE = 'restore',
SET_VISIBILITY_LOCKED = 'set-visibility-locked',
SET_VISIBILITY_TIMELINE = 'set-visibility-timeline',
SET_PERSON_FEATURED_PHOTO = 'set-person-featured-photo',
RATING = 'rating',
}
+24 -1
View File
@@ -1,9 +1,10 @@
import { updatePerson, type PersonResponseDto } from '@immich/sdk';
import { updatePerson, type AssetResponseDto, type PersonResponseDto } from '@immich/sdk';
import { modalManager, toastManager, type ActionItem } from '@immich/ui';
import {
mdiCalendarEditOutline,
mdiEyeOffOutline,
mdiEyeOutline,
mdiFaceManProfile,
mdiHeartMinusOutline,
mdiHeartOutline,
} from '@mdi/js';
@@ -51,6 +52,16 @@ export const getPersonActions = ($t: MessageFormatter, person: PersonResponseDto
return { SetDateOfBirth, Favorite, Unfavorite, HidePerson, ShowPerson };
};
export const getPersonAssetActions = ($t: MessageFormatter, person: PersonResponseDto, asset: AssetResponseDto) => {
const SetFeaturedPhoto: ActionItem = {
title: $t('set_as_featured_photo'),
icon: mdiFaceManProfile,
onAction: () => handleSetFeaturedPhoto(person, asset.id),
};
return { SetFeaturedPhoto };
};
const handleFavoritePerson = async (person: { id: string }) => {
const $t = await getFormatter();
@@ -111,3 +122,15 @@ export const handleUpdatePersonBirthDate = async (person: PersonResponseDto, bir
handleError(error, $t('errors.unable_to_save_date_of_birth'));
}
};
const handleSetFeaturedPhoto = async (person: PersonResponseDto, featureFaceAssetId: string) => {
const $t = await getFormatter();
try {
const response = await updatePerson({ id: person.id, personUpdateDto: { featureFaceAssetId } });
toastManager.primary($t('feature_photo_updated'));
eventManager.emit('PersonUpdate', response);
} catch (error) {
handleError(error, $t('errors.unable_to_set_feature_photo'));
}
};