You've already forked immich
mirror of
https://github.com/immich-app/immich.git
synced 2025-06-26 05:01:05 +02:00
feat(server): Merge Faces sorted by Similarity (#14635)
* Merge Faces sorted by Similarity * Adds face sorting to the side panel face merger * run make open-api * Make it one query * Only have the single order by when sorting by closest face
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { timeBeforeShowLoadingSpinner } from '$lib/constants';
|
||||
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
|
||||
import { getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { AssetTypeEnum, type AssetFaceResponseDto, type PersonResponseDto } from '@immich/sdk';
|
||||
import { getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||
import { AssetTypeEnum, type AssetFaceResponseDto, type PersonResponseDto, getAllPeople } from '@immich/sdk';
|
||||
import { mdiArrowLeftThin, mdiClose, mdiMagnify, mdiPlus } from '@mdi/js';
|
||||
import { linear } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
@ -13,9 +13,10 @@
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import { zoomImageToBase64 } from '$lib/utils/people-utils';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
allPeople: PersonResponseDto[];
|
||||
editedFace: AssetFaceResponseDto;
|
||||
assetId: string;
|
||||
assetType: AssetTypeEnum;
|
||||
@ -24,7 +25,24 @@
|
||||
onReassign: (person: PersonResponseDto) => void;
|
||||
}
|
||||
|
||||
let { allPeople, editedFace, assetId, assetType, onClose, onCreatePerson, onReassign }: Props = $props();
|
||||
let { editedFace, assetId, assetType, onClose, onCreatePerson, onReassign }: Props = $props();
|
||||
|
||||
let allPeople: PersonResponseDto[] = $state([]);
|
||||
|
||||
let isShowLoadingPeople = $state(false);
|
||||
|
||||
async function loadPeople() {
|
||||
const timeout = setTimeout(() => (isShowLoadingPeople = true), timeBeforeShowLoadingSpinner);
|
||||
try {
|
||||
const { people } = await getAllPeople({ withHidden: true, closestAssetId: editedFace.id });
|
||||
allPeople = people;
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.cant_get_faces'));
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
isShowLoadingPeople = false;
|
||||
}
|
||||
|
||||
// loading spinners
|
||||
let isShowLoadingNewPerson = $state(false);
|
||||
@ -37,6 +55,10 @@
|
||||
|
||||
let showPeople = $derived(searchName ? searchedPeople : allPeople.filter((person) => !person.isHidden));
|
||||
|
||||
onMount(() => {
|
||||
handlePromiseError(loadPeople());
|
||||
});
|
||||
|
||||
const handleCreatePerson = async () => {
|
||||
const timeout = setTimeout(() => (isShowLoadingNewPerson = true), timeBeforeShowLoadingSpinner);
|
||||
|
||||
@ -96,31 +118,40 @@
|
||||
</div>
|
||||
<div class="px-4 py-4 text-sm">
|
||||
<h2 class="mb-8 mt-4 uppercase">{$t('all_people')}</h2>
|
||||
<div class="immich-scrollbar mt-4 flex flex-wrap gap-2 overflow-y-auto">
|
||||
{#each showPeople as person (person.id)}
|
||||
{#if !editedFace.person || person.id !== editedFace.person.id}
|
||||
<div class="w-fit">
|
||||
<button type="button" class="w-[90px]" onclick={() => onReassign(person)}>
|
||||
<div class="relative">
|
||||
<ImageThumbnail
|
||||
curve
|
||||
shadow
|
||||
url={getPeopleThumbnailUrl(person)}
|
||||
altText={$getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
title={$getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
widthStyle="90px"
|
||||
heightStyle="90px"
|
||||
hidden={person.isHidden}
|
||||
/>
|
||||
</div>
|
||||
{#if isShowLoadingPeople}
|
||||
<div class="flex w-full justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="immich-scrollbar mt-4 flex flex-wrap gap-2 overflow-y-auto">
|
||||
{#each showPeople as person (person.id)}
|
||||
{#if !editedFace.person || person.id !== editedFace.person.id}
|
||||
<div class="w-fit">
|
||||
<button type="button" class="w-[90px]" onclick={() => onReassign(person)}>
|
||||
<div class="relative">
|
||||
<ImageThumbnail
|
||||
curve
|
||||
shadow
|
||||
url={getPeopleThumbnailUrl(person)}
|
||||
altText={$getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
title={$getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
widthStyle="90px"
|
||||
heightStyle="90px"
|
||||
hidden={person.isHidden}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="mt-1 truncate font-medium" title={$getPersonNameWithHiddenValue(person.name, person.isHidden)}>
|
||||
{person.name}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<p
|
||||
class="mt-1 truncate font-medium"
|
||||
title={$getPersonNameWithHiddenValue(person.name, person.isHidden)}
|
||||
>
|
||||
{person.name}
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
@ -35,7 +35,7 @@
|
||||
let peopleToNotShow = $derived([...selectedPeople, person]);
|
||||
|
||||
onMount(async () => {
|
||||
const data = await getAllPeople({ withHidden: false });
|
||||
const data = await getAllPeople({ withHidden: false, closestPersonId: person.id });
|
||||
people = data.people;
|
||||
});
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
|
||||
import {
|
||||
createPerson,
|
||||
getAllPeople,
|
||||
getFaces,
|
||||
reassignFacesById,
|
||||
AssetTypeEnum,
|
||||
@ -53,7 +52,6 @@
|
||||
|
||||
// search people
|
||||
let showSelectedFaces = $state(false);
|
||||
let allPeople: PersonResponseDto[] = $state([]);
|
||||
|
||||
// timers
|
||||
let loaderLoadingDoneTimeout: ReturnType<typeof setTimeout>;
|
||||
@ -64,8 +62,6 @@
|
||||
async function loadPeople() {
|
||||
const timeout = setTimeout(() => (isShowLoadingPeople = true), timeBeforeShowLoadingSpinner);
|
||||
try {
|
||||
const { people } = await getAllPeople({ withHidden: true });
|
||||
allPeople = people;
|
||||
peopleWithFaces = await getFaces({ id: assetId });
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.cant_get_faces'));
|
||||
@ -322,7 +318,6 @@
|
||||
|
||||
{#if showSelectedFaces && editedFace}
|
||||
<AssignFaceSidePanel
|
||||
{allPeople}
|
||||
{editedFace}
|
||||
{assetId}
|
||||
{assetType}
|
||||
|
Reference in New Issue
Block a user