0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-02-04 01:09:14 -05:00
This commit is contained in:
martabal 2023-12-10 02:51:48 +01:00
parent 5f98f12731
commit 565cac16d7
No known key found for this signature in database
GPG key ID: C00196E3148A52BD
4 changed files with 64 additions and 51 deletions

View file

@ -11,7 +11,7 @@
| 'transparent-gray'
| 'dark-gray'
| 'overlay-primary';
export type Size = 'tiny' | 'icon' | 'link' | 'sm' | 'base' | 'lg';
export type Size = 'tiny' | 'xs' | 'icon' | 'link' | 'sm' | 'base' | 'lg';
export type Rounded = 'lg' | '3xl' | 'full' | false;
export type Shadow = 'md' | false;
</script>
@ -47,6 +47,7 @@
const sizeClasses: Record<Size, string> = {
tiny: 'p-0 ml-2 mr-0 align-top',
xs: 'p-2',
icon: 'p-2.5',
link: 'p-2 font-medium',
sm: 'px-4 py-2 text-sm font-medium',

View file

@ -22,9 +22,19 @@
let searchedPeople: PersonResponseDto[] = [];
let searchedPeopleCopy: PersonResponseDto[] = [];
let searchWord: string;
let searchFaces = false;
let isSearchingPerson = false;
let searchName = '';
$: {
searchedPeople = searchedPeopleCopy.filter(
(person) => personWithFace.person && personWithFace.person.id !== person.id,
);
if (searchName) {
searchedPeople = searchNameLocal(searchName, searchedPeople, 10);
}
}
const dispatch = createEventDispatcher();
const handleBackButton = () => {
dispatch('close');
@ -63,10 +73,6 @@
isShowLoadingSearch = false;
};
$: {
searchedPeople = searchNameLocal(searchName, searchedPeopleCopy, 10);
}
const initInput = (element: HTMLInputElement) => {
element.focus();
};
@ -77,7 +83,7 @@
class="absolute top-0 z-[2002] h-full w-[360px] overflow-x-hidden p-2 bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg"
>
<div class="flex place-items-center justify-between gap-2">
{#if !searchFaces}
{#if !isSearchingPerson}
<div class="flex items-center gap-2">
<button
class="flex place-content-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
@ -94,7 +100,7 @@
class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
title="Search existing person"
on:click={() => {
searchFaces = true;
isSearchingPerson = true;
}}
>
<div>
@ -143,7 +149,7 @@
</div>
<button
class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
on:click={() => (searchFaces = false)}
on:click={() => (isSearchingPerson = false)}
>
<div>
<Icon path={mdiClose} size="24" />
@ -182,26 +188,24 @@
{/each}
{:else}
{#each searchedPeople as person (person.id)}
{#if person.id !== personWithFace.person?.id}
<div class="w-fit">
<button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
<div class="relative">
<ImageThumbnail
curve
shadow
url={api.getPeopleThumbnailUrl(person.id)}
altText={getPersonNameWithHiddenValue(person.name, person.isHidden)}
title={getPersonNameWithHiddenValue(person.name, person.isHidden)}
widthStyle="90px"
heightStyle="90px"
thumbhash={null}
hidden={person.isHidden}
/>
</div>
<p class="mt-1 truncate font-medium" title={person.name}>{person.name}</p>
</button>
</div>
{/if}
<div class="w-fit">
<button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
<div class="relative">
<ImageThumbnail
curve
shadow
url={api.getPeopleThumbnailUrl(person.id)}
altText={getPersonNameWithHiddenValue(person.name, person.isHidden)}
title={getPersonNameWithHiddenValue(person.name, person.isHidden)}
widthStyle="90px"
heightStyle="90px"
thumbhash={null}
hidden={person.isHidden}
/>
</div>
<p class="mt-1 truncate font-medium" title={person.name}>{person.name}</p>
</button>
</div>
{/each}
{/if}
</div>

View file

@ -16,6 +16,7 @@
import { currentAsset, photoViewer } from '$lib/stores/assets.store';
import UnassignedFacesSidePannel from './unassigned-faces-side-pannel.svelte';
import type { FaceWithGeneretedThumbnail } from '$lib/utils/people-utils';
import Button from '../elements/buttons/button.svelte';
// keep track of the changes
let idsOfPersonToCreate: string[] = [];
@ -28,7 +29,7 @@
let selectedPersonToAdd: FaceWithGeneretedThumbnail[] = [];
let selectedPersonToUnassign: FaceWithGeneretedThumbnail[] = [];
let selectedPersonToRemove: boolean[] = [];
let unassignedFaces: (FaceWithGeneretedThumbnail | null)[] = [];
let unassignedFaces: FaceWithGeneretedThumbnail[] = [];
let editedPersonIndex: number;
let shouldRefresh: boolean = false;
@ -83,16 +84,18 @@
selectedPersonToCreate = new Array<string | null>(peopleWithFaces.length);
selectedPersonToReassign = new Array<PersonResponseDto | null>(peopleWithFaces.length);
selectedPersonToRemove = new Array<boolean>(peopleWithFaces.length);
unassignedFaces = await Promise.all(
peopleWithFaces.map(async (personWithFace) => {
if (personWithFace.person || $currentAsset === null) {
return null;
} else {
const image = await zoomImageToBase64(personWithFace, $photoViewer, $currentAsset.type, $currentAsset.id);
return image ? { ...personWithFace, customThumbnail: image } : null;
}
}),
);
unassignedFaces = (
await Promise.all(
peopleWithFaces.map(async (personWithFace) => {
if (personWithFace.person || $currentAsset === null) {
return null;
} else {
const image = await zoomImageToBase64(personWithFace, $photoViewer, $currentAsset.type, $currentAsset.id);
return image ? { ...personWithFace, customThumbnail: image } : null;
}
}),
)
).filter((item): item is FaceWithGeneretedThumbnail => item !== null);
} catch (error) {
handleError(error, "Can't get faces");
} finally {
@ -149,9 +152,10 @@
const handleAddRemovedFace = (indexToRemove: number) => {
$boundingBoxesArray = [];
unassignedFaces = unassignedFaces.map((obj) =>
obj && obj.id === selectedPersonToUnassign[indexToRemove].id ? null : obj,
);
unassignedFaces = unassignedFaces
.map((obj) => (obj && obj.id === selectedPersonToUnassign[indexToRemove].id ? null : obj))
.filter((item): item is FaceWithGeneretedThumbnail => item !== null) as FaceWithGeneretedThumbnail[];
selectedPersonToUnassign = selectedPersonToUnassign.filter((_, index) => index !== indexToRemove);
};
@ -343,16 +347,20 @@
</div>
</div>
{:else}
<div>Visible faces</div>
<div>Faces visible</div>
{/if}
{#if isSelectingFaces && selectedPersonToRemove && selectedPersonToRemove.filter((value) => value).length > 0}
<button
class="justify-self-end rounded-lg p-2 hover:bg-immich-dark-primary hover:dark:bg-immich-dark-primary/50"
<Button
size="xs"
color="red"
title="Unassign faces"
shadow={false}
rounded="full"
on:click={handleUnassignFaces}
>
Unassign faces
</button>
Unassign Faces
</Button>
{/if}
</div>
<div class="mt-2 flex flex-wrap gap-2">
@ -362,7 +370,7 @@
</div>
{:else}
{#each peopleWithFaces as face, index}
{#if face.person && unassignedFaces[index] === null && !selectedPersonToUnassign.some((unassignedFace) => unassignedFace.id === face.id)}
{#if face.person && !unassignedFaces.some((unassignedFace) => unassignedFace.id === face.id) && !selectedPersonToUnassign.some((unassignedFace) => unassignedFace.id === face.id)}
<div class="relative z-[20001] h-[115px] w-[95px]">
<div
role="button"

View file

@ -13,7 +13,7 @@
import type { FaceWithGeneretedThumbnail } from '$lib/utils/people-utils';
import { boundingBoxesArray } from '$lib/stores/people.store';
export let unassignedFaces: (FaceWithGeneretedThumbnail | null)[];
export let unassignedFaces: FaceWithGeneretedThumbnail[];
export let allPeople: PersonResponseDto[];
export let selectedPersonToAdd: FaceWithGeneretedThumbnail[];
@ -74,7 +74,7 @@
<div class="px-4 py-4 text-sm">
<div class="mt-4 flex flex-wrap gap-2">
{#each unassignedFaces as face, index}
{#if face && !selectedPersonToAdd.some((faceToAdd) => face && faceToAdd.id === face.id)}
{#if !selectedPersonToAdd.some((faceToAdd) => face && faceToAdd.id === face.id)}
<div class="relative z-[20001] h-[115px] w-[95px]">
<button
tabindex={index}