@ -7,14 +7,16 @@
import { handleError } from '$lib/utils/handle-error';
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
import {
AssetTypeEnum,
createPerson,
getAllPeople,
getFaces,
reassignFacesById,
AssetTypeEnum,
type AssetFaceResponseDto,
type PersonResponseDto,
} from '@immich/sdk';
import { mdiAccountOff } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte';
import { mdiArrowLeftThin , mdiMinus , mdiRestart } from '@mdi/js';
import { createEventDispatcher , onMount } from 'svelte';
import { linear } from 'svelte/easing';
@ -23,6 +25,8 @@
import { NotificationType , notificationController } from '../shared-components/notification/notification';
import AssignFaceSidePanel from './assign-face-side-panel.svelte';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { zoomImageToBase64 } from '$lib/utils/people-utils';
import { photoViewer } from '$lib/stores/assets.store';
import { t } from 'svelte-i18n';
export let assetId: string;
@ -36,7 +40,6 @@
let peopleWithFaces: AssetFaceResponseDto[] = [];
let selectedPersonToReassign: Record< string , PersonResponseDto > = {} ;
let selectedPersonToCreate: Record< string , string > = {} ;
let editedPerson: PersonResponseDto;
let editedFace: AssetFaceResponseDto;
// loading spinners
@ -171,11 +174,8 @@
};
const handleFacePicker = (face: AssetFaceResponseDto) => {
if (face.person) {
editedFace = face;
editedPerson = face.person;
showSelectedFaces = true;
}
editedFace = face;
showSelectedFaces = true;
};
< / script >
@ -209,91 +209,125 @@
< / div >
{ : else }
{ #each peopleWithFaces as face , index }
{ #if face . person }
< div class = "relative z-[20001] h-[115px] w-[95px]" >
< div
role="button"
tabindex={ index }
class="absolute left-0 top-0 h-[90px] w-[90px] cursor-default"
on:focus={() => ( $boundingBoxesArray = [ peopleWithFaces [ index ]])}
on:mouseover={() => ( $boundingBoxesArray = [ peopleWithFaces [ index ]])}
on:mouseleave={() => ( $boundingBoxesArray = [])}
>
< div class = "relative" >
{ #if selectedPersonToCreate [ face . id ]}
< ImageThumbnail
curve
shadow
url={ selectedPersonToCreate [ face . id ]}
altText={ selectedPersonToCreate [ face . id ]}
title={ $t ( 'new_person' )}
widthStyle={ thumbnailWidth }
heightStyle={ thumbnailWidth }
/>
{ :else if selectedPersonToReassign [ face . id ]}
{ @const personName = face . person ? face . person ? . name : 'Unassigned' }
< div class = "relative z-[20001] h-[115px] w-[95px]" >
< div
role="button"
tabindex={ index }
class="absolute left-0 top-0 h-[90px] w-[90px] cursor-default"
on:focus={() => ( $boundingBoxesArray = [ peopleWithFaces [ index ]])}
on:mouseover={() => ( $boundingBoxesArray = [ peopleWithFaces [ index ]])}
on:mouseleave={() => ( $boundingBoxesArray = [])}
>
< div class = "relative" >
{ #if selectedPersonToCreate [ face . id ]}
< ImageThumbnail
curve
shadow
url={ selectedPersonToCreate [ face . id ]}
altText={ 'New person' }
title={ 'New person' }
widthStyle={ thumbnailWidth }
heightStyle={ thumbnailWidth }
/>
{ :else if selectedPersonToReassign [ face . id ]}
< ImageThumbnail
curve
shadow
url={ getPeopleThumbnailUrl ( selectedPersonToReassign [ face . id ]. id )}
altText={ selectedPersonToReassign [ face . id ]. name }
title={ getPersonNameWithHiddenValue (
selectedPersonToReassign[face.id].name,
selectedPersonToReassign[face.id]?.isHidden,
)}
widthStyle={ thumbnailWidth }
heightStyle={ thumbnailWidth }
hidden={ selectedPersonToReassign [ face . id ]. isHidden }
/>
{ :else if face . person }
< ImageThumbnail
curve
shadow
url={ getPeopleThumbnailUrl ( face . person . id )}
altText={ face . person . name }
title={ getPersonNameWithHiddenValue ( face . person . name , face . person . isHidden )}
widthStyle={ thumbnailWidth }
heightStyle={ thumbnailWidth }
hidden={ face . person . isHidden }
/>
{ : else }
{ #await zoomImageToBase64 ( face , assetId , assetType , $photoViewer )}
< ImageThumbnail
curve
shadow
url={ getPeopleThumbnailUrl ( selectedPersonToReassign [ face . id ]. id )}
altText={ selectedPersonToReassign [ face . id ] ? . name || selectedPersonToReassign [ face . id ]. id }
title={ getPersonNameWithHiddenValue (
selectedPersonToReassign[face.id].name,
face.person?.isHidden,
)}
widthStyle={ thumbnailWidth }
heightStyle={ thumbnailWidth }
hidden={ selectedPersonToReassign [ face . id ]. isHidden }
url="/src/lib/assets/no-thumbnail.png"
altText="Unassigned"
title="Unassigned"
widthStyle="90px"
heightStyle="90px"
thumbhash={ null }
hidden={ false }
/>
{ : else }
{ :then data }
< ImageThumbnail
curve
shadow
url={ getPeopleThumbnailUrl ( face . person . id )}
altText={ face . person . name || face . person . id }
title={ getPersonNameWithHiddenValue ( face . person . name , face . person . isHidden )}
widthStyle={ thumbnailWidth }
heightStyle={ thumbnailWidth }
hidden={ face . person . isHidden }
url={ data === null ? '/src/lib/assets/no-thumbnail.png' : data }
altText="Unassigned"
title="Unassigned"
widthStyle="90px"
heightStyle="90px"
thumbhash={ null }
hidden={ false }
/>
{ /if }
< / div >
{ #if ! selectedPersonToCreate [ face . id ]}
< p class = "relative mt-1 truncate font-medium" title = { face . person ? . name } >
{ #if selectedPersonToReassign [ face . id ] ? . id }
{ selectedPersonToReassign [ face . id ] ? . name }
{ : else }
{ face . person ? . name }
{ /if }
< / p >
{ /await }
{ /if }
< / div >
< div class = "absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full" >
{ #if selectedPersonToCreate [ face . id ] || selectedPersonToReassign [ face . id ]}
< CircleIconButton
color="primary"
icon={ mdiRestart }
title={ $t ( 'reset' )}
size="18"
padding="1"
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
on:click={() => handleReset ( face . id )}
/>
{ #if ! selectedPersonToCreate [ face . id ]}
< p class = "relative mt-1 truncate font-medium" title = { personName } >
{ #if selectedPersonToReassign [ face . id ] ? . id }
{ selectedPersonToReassign [ face . id ] ? . name }
{ : else }
< CircleIconButton
color="primary"
icon={ mdiMinus }
title={ $t ( 'select_new_face' )}
size="18"
padding="1"
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
on:click={() => handleFacePicker ( face )}
/>
< span class = { personName == 'Unassigned' ? 'dark:text-gray-500' : '' } > { personName } </span >
{ /if }
< / div >
< / p >
{ /if }
< div class = "absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full" >
{ #if selectedPersonToCreate [ face . id ] || selectedPersonToReassign [ face . id ]}
< CircleIconButton
color="primary"
icon={ mdiRestart }
title="Reset"
size="18"
padding="1"
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
on:click={() => handleReset ( face . id )}
/>
{ : else }
< CircleIconButton
color="primary"
icon={ mdiMinus }
title="Select new face"
size="18"
padding="1"
class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
on:click={() => handleFacePicker ( face )}
/>
{ /if }
< / div >
< div class = "absolute right-[25px] -top-[5px] h-[20px] w-[20px] rounded-full" >
{ #if ! selectedPersonToCreate [ face . id ] && ! selectedPersonToReassign [ face . id ] && ! face . person }
< div
class="flex place-content-center place-items-center rounded-full bg-[#d3d3d3] p-1 transition-all absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
>
< Icon color = "primary" path = { mdiAccountOff } ariaLabel="Just a face " size = "18" />
< / div >
{ /if }
< / div >
< / div >
{ /if }
</ div >
{ /each }
{ /if }
< / div >
@ -302,11 +336,10 @@
{ #if showSelectedFaces }
< AssignFaceSidePanel
{ peopleWithFaces }
{ allPeople }
{ editedPerson }
{ assetType }
{ editedFace }
{ assetId }
{ assetType }
on:close={() => ( showSelectedFaces = false )}
on:createPerson={( event ) => handleCreatePerson ( event . detail )}
on:reassign={( event ) => handleReassignFace ( event . detail )}