forked from Cutlery/immich
Compare commits
15 Commits
main
...
fix/edit-f
Author | SHA1 | Date | |
---|---|---|---|
|
8fd54e3994 | ||
|
ad9aafd484 | ||
|
8eca25007e | ||
|
86b200e870 | ||
|
5dcc4ddfc6 | ||
|
6b10994bd2 | ||
|
12ede06411 | ||
|
121e9f1f1c | ||
|
c263607515 | ||
|
4cb2e16549 | ||
|
0f8fb8d38b | ||
|
107157b856 | ||
|
87cbcc02c3 | ||
|
62531a75eb | ||
|
29c7663caa |
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
export let peopleWithFaces: AssetFaceResponseDto[];
|
export let peopleWithFaces: AssetFaceResponseDto[];
|
||||||
export let allPeople: PersonResponseDto[];
|
export let allPeople: PersonResponseDto[];
|
||||||
export let editedPersonIndex: number;
|
export let editedPerson: PersonResponseDto;
|
||||||
export let assetType: AssetTypeEnum;
|
export let assetType: AssetTypeEnum;
|
||||||
export let assetId: string;
|
export let assetId: string;
|
||||||
|
|
||||||
@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
const handleCreatePerson = async () => {
|
const handleCreatePerson = async () => {
|
||||||
const timeout = setTimeout(() => (isShowLoadingNewPerson = true), timeBeforeShowLoadingSpinner);
|
const timeout = setTimeout(() => (isShowLoadingNewPerson = true), timeBeforeShowLoadingSpinner);
|
||||||
const personToUpdate = peopleWithFaces.find((person) => person.id === peopleWithFaces[editedPersonIndex].id);
|
const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
|
||||||
|
|
||||||
const newFeaturePhoto = personToUpdate ? await zoomImageToBase64(personToUpdate) : null;
|
const newFeaturePhoto = personToUpdate ? await zoomImageToBase64(personToUpdate) : null;
|
||||||
|
|
||||||
@ -229,7 +229,7 @@
|
|||||||
<div class="immich-scrollbar mt-4 flex flex-wrap gap-2 overflow-y-auto">
|
<div class="immich-scrollbar mt-4 flex flex-wrap gap-2 overflow-y-auto">
|
||||||
{#if searchName == ''}
|
{#if searchName == ''}
|
||||||
{#each allPeople as person (person.id)}
|
{#each allPeople as person (person.id)}
|
||||||
{#if person.id !== peopleWithFaces[editedPersonIndex].person?.id}
|
{#if person.id !== editedPerson.id}
|
||||||
<div class="w-fit">
|
<div class="w-fit">
|
||||||
<button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
|
<button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@ -255,7 +255,7 @@
|
|||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
{#each searchedPeople as person (person.id)}
|
{#each searchedPeople as person (person.id)}
|
||||||
{#if person.id !== peopleWithFaces[editedPersonIndex].person?.id}
|
{#if person.id !== editedPerson.id}
|
||||||
<div class="w-fit">
|
<div class="w-fit">
|
||||||
<button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
|
<button class="w-[90px]" on:click={() => dispatch('reassign', person)}>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
|
@ -28,14 +28,14 @@
|
|||||||
export let assetType: AssetTypeEnum;
|
export let assetType: AssetTypeEnum;
|
||||||
|
|
||||||
// keep track of the changes
|
// keep track of the changes
|
||||||
let numberOfPersonToCreate: string[] = [];
|
let peopleToCreate: string[] = [];
|
||||||
let numberOfAssetFaceGenerated: string[] = [];
|
let assetFaceGenerated: string[] = [];
|
||||||
|
|
||||||
// faces
|
// faces
|
||||||
let peopleWithFaces: AssetFaceResponseDto[] = [];
|
let peopleWithFaces: AssetFaceResponseDto[] = [];
|
||||||
let selectedPersonToReassign: (PersonResponseDto | null)[];
|
let selectedPersonToReassign: Record<string, PersonResponseDto> = {};
|
||||||
let selectedPersonToCreate: (string | null)[];
|
let selectedPersonToCreate: Record<string, string> = {};
|
||||||
let editedPersonIndex: number;
|
let editedPerson: PersonResponseDto;
|
||||||
|
|
||||||
// loading spinners
|
// loading spinners
|
||||||
let isShowLoadingDone = false;
|
let isShowLoadingDone = false;
|
||||||
@ -49,6 +49,8 @@
|
|||||||
let loaderLoadingDoneTimeout: ReturnType<typeof setTimeout>;
|
let loaderLoadingDoneTimeout: ReturnType<typeof setTimeout>;
|
||||||
let automaticRefreshTimeout: ReturnType<typeof setTimeout>;
|
let automaticRefreshTimeout: ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
|
const thumbnailWidth = '90px';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
close: void;
|
close: void;
|
||||||
refresh: void;
|
refresh: void;
|
||||||
@ -60,8 +62,6 @@
|
|||||||
const { people } = await getAllPeople({ withHidden: true });
|
const { people } = await getAllPeople({ withHidden: true });
|
||||||
allPeople = people;
|
allPeople = people;
|
||||||
peopleWithFaces = await getFaces({ id: assetId });
|
peopleWithFaces = await getFaces({ id: assetId });
|
||||||
selectedPersonToCreate = Array.from({ length: peopleWithFaces.length });
|
|
||||||
selectedPersonToReassign = Array.from({ length: peopleWithFaces.length });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, "Can't get faces");
|
handleError(error, "Can't get faces");
|
||||||
} finally {
|
} finally {
|
||||||
@ -71,12 +71,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onPersonThumbnail = (personId: string) => {
|
const onPersonThumbnail = (personId: string) => {
|
||||||
numberOfAssetFaceGenerated.push(personId);
|
assetFaceGenerated.push(personId);
|
||||||
if (
|
if (
|
||||||
isEqual(numberOfAssetFaceGenerated, numberOfPersonToCreate) &&
|
isEqual(assetFaceGenerated, peopleToCreate) &&
|
||||||
loaderLoadingDoneTimeout &&
|
loaderLoadingDoneTimeout &&
|
||||||
automaticRefreshTimeout &&
|
automaticRefreshTimeout &&
|
||||||
selectedPersonToCreate.filter((person) => person !== null).length === numberOfPersonToCreate.length
|
Object.keys(selectedPersonToCreate).length === peopleToCreate.length
|
||||||
) {
|
) {
|
||||||
clearTimeout(loaderLoadingDoneTimeout);
|
clearTimeout(loaderLoadingDoneTimeout);
|
||||||
clearTimeout(automaticRefreshTimeout);
|
clearTimeout(automaticRefreshTimeout);
|
||||||
@ -97,36 +97,41 @@
|
|||||||
dispatch('close');
|
dispatch('close');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = (index: number) => {
|
const handleReset = (id: string) => {
|
||||||
if (selectedPersonToReassign[index]) {
|
if (selectedPersonToReassign[id]) {
|
||||||
selectedPersonToReassign[index] = null;
|
delete selectedPersonToReassign[id];
|
||||||
|
|
||||||
|
// trigger reactivity
|
||||||
|
selectedPersonToReassign = selectedPersonToReassign;
|
||||||
}
|
}
|
||||||
if (selectedPersonToCreate[index]) {
|
if (selectedPersonToCreate[id]) {
|
||||||
selectedPersonToCreate[index] = null;
|
delete selectedPersonToCreate[id];
|
||||||
|
|
||||||
|
// trigger reactivity
|
||||||
|
selectedPersonToCreate = selectedPersonToCreate;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditFaces = async () => {
|
const handleEditFaces = async () => {
|
||||||
loaderLoadingDoneTimeout = setTimeout(() => (isShowLoadingDone = true), timeBeforeShowLoadingSpinner);
|
loaderLoadingDoneTimeout = setTimeout(() => (isShowLoadingDone = true), timeBeforeShowLoadingSpinner);
|
||||||
const numberOfChanges =
|
const numberOfChanges = Object.keys(selectedPersonToCreate).length + Object.keys(selectedPersonToReassign).length;
|
||||||
selectedPersonToCreate.filter((person) => person !== null).length +
|
|
||||||
selectedPersonToReassign.filter((person) => person !== null).length;
|
|
||||||
if (numberOfChanges > 0) {
|
if (numberOfChanges > 0) {
|
||||||
try {
|
try {
|
||||||
for (const [index, peopleWithFace] of peopleWithFaces.entries()) {
|
for (const personWithFace of peopleWithFaces) {
|
||||||
const personId = selectedPersonToReassign[index]?.id;
|
const personId = selectedPersonToReassign[personWithFace.id]?.id;
|
||||||
|
|
||||||
if (personId) {
|
if (personId) {
|
||||||
await reassignFacesById({
|
await reassignFacesById({
|
||||||
id: personId,
|
id: personId,
|
||||||
faceDto: { id: peopleWithFace.id },
|
faceDto: { id: personWithFace.id },
|
||||||
});
|
});
|
||||||
} else if (selectedPersonToCreate[index]) {
|
} else if (selectedPersonToCreate[personWithFace.id]) {
|
||||||
const data = await createPerson({ personCreateDto: {} });
|
const data = await createPerson({ personCreateDto: {} });
|
||||||
numberOfPersonToCreate.push(data.id);
|
peopleToCreate.push(data.id);
|
||||||
await reassignFacesById({
|
await reassignFacesById({
|
||||||
id: data.id,
|
id: data.id,
|
||||||
faceDto: { id: peopleWithFace.id },
|
faceDto: { id: personWithFace.id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +146,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
isShowLoadingDone = false;
|
isShowLoadingDone = false;
|
||||||
if (numberOfPersonToCreate.length === 0) {
|
if (peopleToCreate.length === 0) {
|
||||||
clearTimeout(loaderLoadingDoneTimeout);
|
clearTimeout(loaderLoadingDoneTimeout);
|
||||||
dispatch('refresh');
|
dispatch('refresh');
|
||||||
} else {
|
} else {
|
||||||
@ -150,23 +155,26 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCreatePerson = (newFeaturePhoto: string | null) => {
|
const handleCreatePerson = (newFeaturePhoto: string | null) => {
|
||||||
const personToUpdate = peopleWithFaces.find((person) => person.id === peopleWithFaces[editedPersonIndex].id);
|
const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
|
||||||
if (newFeaturePhoto && personToUpdate) {
|
if (newFeaturePhoto && personToUpdate) {
|
||||||
selectedPersonToCreate[peopleWithFaces.indexOf(personToUpdate)] = newFeaturePhoto;
|
selectedPersonToCreate[personToUpdate.id] = newFeaturePhoto;
|
||||||
}
|
}
|
||||||
showSeletecFaces = false;
|
showSeletecFaces = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReassignFace = (person: PersonResponseDto | null) => {
|
const handleReassignFace = (person: PersonResponseDto | null) => {
|
||||||
if (person) {
|
const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
|
||||||
selectedPersonToReassign[editedPersonIndex] = person;
|
if (person && personToUpdate) {
|
||||||
|
selectedPersonToReassign[personToUpdate.id] = person;
|
||||||
showSeletecFaces = false;
|
showSeletecFaces = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePersonPicker = (index: number) => {
|
const handlePersonPicker = (person: PersonResponseDto | null) => {
|
||||||
editedPersonIndex = index;
|
if (person) {
|
||||||
showSeletecFaces = true;
|
editedPerson = person;
|
||||||
|
showSeletecFaces = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -217,35 +225,48 @@
|
|||||||
on:mouseleave={() => ($boundingBoxesArray = [])}
|
on:mouseleave={() => ($boundingBoxesArray = [])}
|
||||||
>
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<ImageThumbnail
|
{#if selectedPersonToCreate[face.id]}
|
||||||
curve
|
<ImageThumbnail
|
||||||
shadow
|
curve
|
||||||
url={selectedPersonToCreate[index] ||
|
shadow
|
||||||
getPeopleThumbnailUrl(selectedPersonToReassign[index]?.id || face.person.id)}
|
url={selectedPersonToCreate[face.id]}
|
||||||
altText={selectedPersonToReassign[index]
|
altText={selectedPersonToCreate[face.id]}
|
||||||
? selectedPersonToReassign[index]?.name
|
title={'New person'}
|
||||||
: selectedPersonToCreate[index]
|
widthStyle={thumbnailWidth}
|
||||||
? 'New person'
|
heightStyle={thumbnailWidth}
|
||||||
: getPersonNameWithHiddenValue(face.person?.name, face.person?.isHidden)}
|
/>
|
||||||
title={selectedPersonToReassign[index]
|
{:else if selectedPersonToReassign[face.id]}
|
||||||
? selectedPersonToReassign[index]?.name
|
<ImageThumbnail
|
||||||
: selectedPersonToCreate[index]
|
curve
|
||||||
? 'New person'
|
shadow
|
||||||
: getPersonNameWithHiddenValue(face.person?.name, face.person?.isHidden)}
|
url={getPeopleThumbnailUrl(selectedPersonToReassign[face.id].id)}
|
||||||
widthStyle="90px"
|
altText={selectedPersonToReassign[face.id]?.name || selectedPersonToReassign[face.id].id}
|
||||||
heightStyle="90px"
|
title={getPersonNameWithHiddenValue(
|
||||||
thumbhash={null}
|
selectedPersonToReassign[face.id].name,
|
||||||
hidden={selectedPersonToReassign[index]
|
face.person?.isHidden,
|
||||||
? selectedPersonToReassign[index]?.isHidden
|
)}
|
||||||
: selectedPersonToCreate[index]
|
widthStyle={thumbnailWidth}
|
||||||
? false
|
heightStyle={thumbnailWidth}
|
||||||
: face.person?.isHidden}
|
hidden={selectedPersonToReassign[face.id].isHidden}
|
||||||
/>
|
/>
|
||||||
|
{:else}
|
||||||
|
<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}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if !selectedPersonToCreate[index]}
|
|
||||||
|
{#if !selectedPersonToCreate[face.id]}
|
||||||
<p class="relative mt-1 truncate font-medium" title={face.person?.name}>
|
<p class="relative mt-1 truncate font-medium" title={face.person?.name}>
|
||||||
{#if selectedPersonToReassign[index]?.id}
|
{#if selectedPersonToReassign[face.id]?.id}
|
||||||
{selectedPersonToReassign[index]?.name}
|
{selectedPersonToReassign[face.id]?.name}
|
||||||
{:else}
|
{:else}
|
||||||
{face.person?.name}
|
{face.person?.name}
|
||||||
{/if}
|
{/if}
|
||||||
@ -253,8 +274,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full bg-blue-700">
|
<div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full bg-blue-700">
|
||||||
{#if selectedPersonToCreate[index] || selectedPersonToReassign[index]}
|
{#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]}
|
||||||
<button on:click={() => handleReset(index)} class="flex h-full w-full">
|
<button on:click={() => handleReset(face.id)} class="flex h-full w-full">
|
||||||
<div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform">
|
<div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform">
|
||||||
<div>
|
<div>
|
||||||
<Icon path={mdiRestart} size={18} />
|
<Icon path={mdiRestart} size={18} />
|
||||||
@ -262,7 +283,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<button on:click={() => handlePersonPicker(index)} class="flex h-full w-full">
|
<button on:click={() => handlePersonPicker(face.person)} class="flex h-full w-full">
|
||||||
<div
|
<div
|
||||||
class="absolute left-1/2 top-1/2 h-[2px] w-[14px] translate-x-[-50%] translate-y-[-50%] transform bg-white"
|
class="absolute left-1/2 top-1/2 h-[2px] w-[14px] translate-x-[-50%] translate-y-[-50%] transform bg-white"
|
||||||
/>
|
/>
|
||||||
@ -282,7 +303,7 @@
|
|||||||
<AssignFaceSidePanel
|
<AssignFaceSidePanel
|
||||||
{peopleWithFaces}
|
{peopleWithFaces}
|
||||||
{allPeople}
|
{allPeople}
|
||||||
{editedPersonIndex}
|
{editedPerson}
|
||||||
{assetType}
|
{assetType}
|
||||||
{assetId}
|
{assetId}
|
||||||
on:close={() => (showSeletecFaces = false)}
|
on:close={() => (showSeletecFaces = false)}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user