diff --git a/web/src/lib/components/asset-viewer/AssetViewer.svelte b/web/src/lib/components/asset-viewer/AssetViewer.svelte index 3b69f8b2f..4873b0b41 100644 --- a/web/src/lib/components/asset-viewer/AssetViewer.svelte +++ b/web/src/lib/components/asset-viewer/AssetViewer.svelte @@ -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 @@ - closeViewer()} {onStackUpdate} /> + closeViewer()} + {onStackUpdate} + {onPersonThumbnailReady} +/> {/if} {#if person} - + {@const { SetFeaturedPhoto } = getPersonAssetActions($t, person, asset)} + {/if} diff --git a/web/src/lib/components/asset-viewer/actions/SetPersonFeaturedAction.svelte b/web/src/lib/components/asset-viewer/actions/SetPersonFeaturedAction.svelte deleted file mode 100644 index 86e201c61..000000000 --- a/web/src/lib/components/asset-viewer/actions/SetPersonFeaturedAction.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - - diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts index 6cfd68dc3..c02fadf81 100644 --- a/web/src/lib/components/asset-viewer/actions/action.ts +++ b/web/src/lib/components/asset-viewer/actions/action.ts @@ -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 }; }; diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 2c3e7b889..5079e44c0 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -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', } diff --git a/web/src/lib/services/person.service.ts b/web/src/lib/services/person.service.ts index c51a7d42b..9835c5e82 100644 --- a/web/src/lib/services/person.service.ts +++ b/web/src/lib/services/person.service.ts @@ -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')); + } +};