mirror of
https://github.com/immich-app/immich.git
synced 2024-12-26 10:50:29 +02:00
feat(web): suggest to merge people faces when renaming a person name (#3399)
* feat: propose to merge faced based on the name * responsive * drop down menu * add border * improvements * improvements * improvements * add comments * responsive * responsive * feat: use FullScreenModal * responsive * pr feeback * pr feeback * pr feeback * responsive * pr feeback * pr feeback * styling * fix test --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
26085ff82b
commit
afb0d0f54d
@ -14,6 +14,7 @@
|
|||||||
export let shadow = false;
|
export let shadow = false;
|
||||||
export let circle = false;
|
export let circle = false;
|
||||||
export let hidden = false;
|
export let hidden = false;
|
||||||
|
export let border = false;
|
||||||
let complete = false;
|
let complete = false;
|
||||||
|
|
||||||
export let eyeColor = 'white';
|
export let eyeColor = 'white';
|
||||||
@ -26,7 +27,9 @@
|
|||||||
style:opacity={hidden ? '0.5' : '1'}
|
style:opacity={hidden ? '0.5' : '1'}
|
||||||
src={url}
|
src={url}
|
||||||
alt={altText}
|
alt={altText}
|
||||||
class="object-cover transition duration-300"
|
class="object-cover transition duration-300 {border
|
||||||
|
? 'border-[3px] border-immich-dark-primary/80 hover:border-immich-primary'
|
||||||
|
: ''}"
|
||||||
class:rounded-lg={curve}
|
class:rounded-lg={curve}
|
||||||
class:shadow-lg={shadow}
|
class:shadow-lg={shadow}
|
||||||
class:rounded-full={circle}
|
class:rounded-full={circle}
|
||||||
|
128
web/src/lib/components/faces-page/merge-suggestion-modal.svelte
Normal file
128
web/src/lib/components/faces-page/merge-suggestion-modal.svelte
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import Close from 'svelte-material-icons/Close.svelte';
|
||||||
|
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||||
|
import type { PersonResponseDto } from '../../../api/open-api';
|
||||||
|
import { api } from '@api';
|
||||||
|
import Merge from 'svelte-material-icons/Merge.svelte';
|
||||||
|
import Button from '../elements/buttons/button.svelte';
|
||||||
|
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher<{
|
||||||
|
reject: void;
|
||||||
|
confirm: [PersonResponseDto, PersonResponseDto];
|
||||||
|
close: void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
export let personMerge1: PersonResponseDto;
|
||||||
|
export let personMerge2: PersonResponseDto;
|
||||||
|
export let people: PersonResponseDto[];
|
||||||
|
let potentialMergePeople: PersonResponseDto[] = people
|
||||||
|
.filter(
|
||||||
|
(person: PersonResponseDto) =>
|
||||||
|
personMerge2.name.toLowerCase() === person.name.toLowerCase() &&
|
||||||
|
person.id !== personMerge2.id &&
|
||||||
|
person.id !== personMerge1.id &&
|
||||||
|
!person.isHidden,
|
||||||
|
)
|
||||||
|
.slice(0, 3);
|
||||||
|
|
||||||
|
let choosePersonToMerge = false;
|
||||||
|
|
||||||
|
const title = personMerge2.name;
|
||||||
|
|
||||||
|
const changePersonToMerge = (newperson: PersonResponseDto) => {
|
||||||
|
const index = potentialMergePeople.indexOf(newperson);
|
||||||
|
[potentialMergePeople[index], personMerge2] = [personMerge2, potentialMergePeople[index]];
|
||||||
|
choosePersonToMerge = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex h-full w-full place-content-center place-items-center overflow-hidden">
|
||||||
|
<div
|
||||||
|
class="w-[250px] max-w-[125vw] rounded-3xl border bg-immich-bg shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg md:w-[375px]"
|
||||||
|
>
|
||||||
|
<div class="relative flex items-center justify-between">
|
||||||
|
<h1 class="truncate px-4 py-4 font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||||
|
Merge faces - {title}
|
||||||
|
</h1>
|
||||||
|
<CircleIconButton logo={Close} on:click={() => dispatch('close')} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-center px-2 py-4 md:h-36 md:px-4 md:py-4">
|
||||||
|
{#if !choosePersonToMerge}
|
||||||
|
<div class="flex h-20 w-20 items-center px-1 md:h-24 md:w-24 md:px-2">
|
||||||
|
<ImageThumbnail
|
||||||
|
circle
|
||||||
|
shadow
|
||||||
|
url={api.getPeopleThumbnailUrl(personMerge1.id)}
|
||||||
|
altText={personMerge1.name}
|
||||||
|
widthStyle="100%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mx-0.5 flex md:mx-2">
|
||||||
|
<CircleIconButton
|
||||||
|
logo={Merge}
|
||||||
|
on:click={() => ([personMerge1, personMerge2] = [personMerge2, personMerge1])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={potentialMergePeople.length === 0}
|
||||||
|
class="flex h-28 w-28 items-center rounded-full border-2 border-immich-primary px-1 dark:border-immich-dark-primary md:h-32 md:w-32 md:px-2"
|
||||||
|
on:click={() => {
|
||||||
|
if (potentialMergePeople.length > 0) {
|
||||||
|
choosePersonToMerge = !choosePersonToMerge;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ImageThumbnail
|
||||||
|
border={potentialMergePeople.length !== 0}
|
||||||
|
circle
|
||||||
|
shadow
|
||||||
|
url={api.getPeopleThumbnailUrl(personMerge2.id)}
|
||||||
|
altText={personMerge2.name}
|
||||||
|
widthStyle="100%"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<div class="grid w-full grid-cols-1 gap-2">
|
||||||
|
<div class="px-2">
|
||||||
|
<button on:click={() => (choosePersonToMerge = false)}> <ArrowLeft /></button>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<div class="flex flex-wrap justify-center md:grid md:grid-cols-{potentialMergePeople.length}">
|
||||||
|
{#each potentialMergePeople as person (person.id)}
|
||||||
|
<div class="h-24 w-24 md:h-28 md:w-28">
|
||||||
|
<button class="p-2" on:click={() => changePersonToMerge(person)}>
|
||||||
|
<ImageThumbnail
|
||||||
|
border={true}
|
||||||
|
circle
|
||||||
|
shadow
|
||||||
|
url={api.getPeopleThumbnailUrl(person.id)}
|
||||||
|
altText={person.name}
|
||||||
|
widthStyle="100%"
|
||||||
|
on:click={() => changePersonToMerge(person)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex px-4 md:px-8 md:pt-4">
|
||||||
|
<h1 class="text-xl text-gray-500 dark:text-gray-300">Are these the same face?</h1>
|
||||||
|
</div>
|
||||||
|
<div class="flex px-4 pt-2 md:px-8">
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-300">They will be merged together</p>
|
||||||
|
</div>
|
||||||
|
<div class="mt-8 flex w-full gap-4 px-4 pb-4">
|
||||||
|
<Button color="gray" fullwidth on:click={() => dispatch('reject')}>No</Button>
|
||||||
|
<Button fullwidth on:click={() => dispatch('confirm', [personMerge1, personMerge2])}>Yes</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -19,6 +19,7 @@
|
|||||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
|
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
let selectHidden = false;
|
let selectHidden = false;
|
||||||
@ -33,6 +34,13 @@
|
|||||||
let showLoadingSpinner = false;
|
let showLoadingSpinner = false;
|
||||||
let toggleVisibility = false;
|
let toggleVisibility = false;
|
||||||
|
|
||||||
|
let showChangeNameModal = false;
|
||||||
|
let showMergeModal = false;
|
||||||
|
let personName = '';
|
||||||
|
let personMerge1: PersonResponseDto;
|
||||||
|
let personMerge2: PersonResponseDto;
|
||||||
|
let edittingPerson: PersonResponseDto | null = null;
|
||||||
|
|
||||||
people.forEach((person: PersonResponseDto) => {
|
people.forEach((person: PersonResponseDto) => {
|
||||||
initialHiddenValues[person.id] = person.isHidden;
|
initialHiddenValues[person.id] = person.isHidden;
|
||||||
});
|
});
|
||||||
@ -136,13 +144,60 @@
|
|||||||
toggleVisibility = false;
|
toggleVisibility = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
let showChangeNameModal = false;
|
const handleMergeSameFace = async (response: [PersonResponseDto, PersonResponseDto]) => {
|
||||||
let personName = '';
|
const [personToMerge, personToBeMergedIn] = response;
|
||||||
let edittingPerson: PersonResponseDto | null = null;
|
showMergeModal = false;
|
||||||
|
|
||||||
|
if (!edittingPerson) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await api.personApi.mergePerson({
|
||||||
|
id: personMerge2.id,
|
||||||
|
mergePersonDto: { ids: [personToMerge.id] },
|
||||||
|
});
|
||||||
|
countVisiblePeople--;
|
||||||
|
people = people.filter((person: PersonResponseDto) => person.id !== personToMerge.id);
|
||||||
|
|
||||||
|
notificationController.show({
|
||||||
|
message: 'Merge faces succesfully',
|
||||||
|
type: NotificationType.Info,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, 'Unable to save name');
|
||||||
|
}
|
||||||
|
if (personToBeMergedIn.name !== personName && edittingPerson.id === personToBeMergedIn.id) {
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* If the user merges one of the suggested people into the person he's editing it, it's merging the suggested person AND renames
|
||||||
|
* the person he's editing
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
await api.personApi.updatePerson({ id: personToBeMergedIn.id, personUpdateDto: { name: personName } });
|
||||||
|
for (const person of people) {
|
||||||
|
if (person.id === personToBeMergedIn.id) {
|
||||||
|
person.name = personName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notificationController.show({
|
||||||
|
message: 'Change name succesfully',
|
||||||
|
type: NotificationType.Info,
|
||||||
|
});
|
||||||
|
|
||||||
|
// trigger reactivity
|
||||||
|
people = people;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, 'Unable to save name');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleChangeName = ({ detail }: CustomEvent<PersonResponseDto>) => {
|
const handleChangeName = ({ detail }: CustomEvent<PersonResponseDto>) => {
|
||||||
showChangeNameModal = true;
|
showChangeNameModal = true;
|
||||||
personName = detail.name;
|
personName = detail.name;
|
||||||
|
personMerge1 = detail;
|
||||||
edittingPerson = detail;
|
edittingPerson = detail;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -182,33 +237,73 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const submitNameChange = async () => {
|
const submitNameChange = async () => {
|
||||||
|
showChangeNameModal = false;
|
||||||
|
if (!edittingPerson) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (personName === edittingPerson.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We check if another person has the same name as the name entered by the user
|
||||||
|
|
||||||
|
const existingPerson = people.find(
|
||||||
|
(person: PersonResponseDto) =>
|
||||||
|
person.name.toLowerCase() === personName.toLowerCase() &&
|
||||||
|
edittingPerson &&
|
||||||
|
person.id !== edittingPerson.id &&
|
||||||
|
person.name,
|
||||||
|
);
|
||||||
|
if (existingPerson) {
|
||||||
|
personMerge2 = existingPerson;
|
||||||
|
showMergeModal = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeName();
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeName = async () => {
|
||||||
|
showMergeModal = false;
|
||||||
|
showChangeNameModal = false;
|
||||||
|
|
||||||
|
if (!edittingPerson) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (edittingPerson) {
|
const { data: updatedPerson } = await api.personApi.updatePerson({
|
||||||
const { data: updatedPerson } = await api.personApi.updatePerson({
|
id: edittingPerson.id,
|
||||||
id: edittingPerson.id,
|
personUpdateDto: { name: personName },
|
||||||
personUpdateDto: { name: personName },
|
});
|
||||||
});
|
|
||||||
|
|
||||||
people = people.map((person: PersonResponseDto) => {
|
people = people.map((person: PersonResponseDto) => {
|
||||||
if (person.id === updatedPerson.id) {
|
if (person.id === updatedPerson.id) {
|
||||||
return updatedPerson;
|
return updatedPerson;
|
||||||
}
|
}
|
||||||
return person;
|
return person;
|
||||||
});
|
});
|
||||||
|
|
||||||
showChangeNameModal = false;
|
notificationController.show({
|
||||||
|
message: 'Change name succesfully',
|
||||||
notificationController.show({
|
type: NotificationType.Info,
|
||||||
message: 'Change name succesfully',
|
});
|
||||||
type: NotificationType.Info,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, 'Unable to save name');
|
handleError(error, 'Unable to save name');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if showMergeModal}
|
||||||
|
<FullScreenModal on:clickOutside={() => (showMergeModal = false)}>
|
||||||
|
<MergeSuggestionModal
|
||||||
|
{personMerge1}
|
||||||
|
{personMerge2}
|
||||||
|
{people}
|
||||||
|
on:close={() => (showMergeModal = false)}
|
||||||
|
on:reject={() => changeName()}
|
||||||
|
on:confirm={(event) => handleMergeSameFace(event.detail)}
|
||||||
|
/>
|
||||||
|
</FullScreenModal>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<UserPageLayout user={data.user} title="People">
|
<UserPageLayout user={data.user} title="People">
|
||||||
<svelte:fragment slot="buttons">
|
<svelte:fragment slot="buttons">
|
||||||
{#if countTotalPeople > 0}
|
{#if countTotalPeople > 0}
|
||||||
|
@ -10,11 +10,13 @@ export const load = (async ({ locals, parent, params }) => {
|
|||||||
|
|
||||||
const { data: person } = await locals.api.personApi.getPerson({ id: params.personId });
|
const { data: person } = await locals.api.personApi.getPerson({ id: params.personId });
|
||||||
const { data: assets } = await locals.api.personApi.getPersonAssets({ id: params.personId });
|
const { data: assets } = await locals.api.personApi.getPersonAssets({ id: params.personId });
|
||||||
|
const { data: people } = await locals.api.personApi.getAllPeople({ withHidden: false });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
assets,
|
assets,
|
||||||
person,
|
person,
|
||||||
|
people,
|
||||||
meta: {
|
meta: {
|
||||||
title: person.name || 'Person',
|
title: person.name || 'Person',
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { afterNavigate, goto } from '$app/navigation';
|
import { afterNavigate, goto, invalidateAll } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte';
|
||||||
import EditNameInput from '$lib/components/faces-page/edit-name-input.svelte';
|
import EditNameInput from '$lib/components/faces-page/edit-name-input.svelte';
|
||||||
@ -15,7 +15,7 @@
|
|||||||
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
|
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { AssetResponseDto, api } from '@api';
|
import { AssetResponseDto, PersonResponseDto, api } from '@api';
|
||||||
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
||||||
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
|
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
|
||||||
import Plus from 'svelte-material-icons/Plus.svelte';
|
import Plus from 'svelte-material-icons/Plus.svelte';
|
||||||
@ -30,6 +30,8 @@
|
|||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import MergeFaceSelector from '$lib/components/faces-page/merge-face-selector.svelte';
|
import MergeFaceSelector from '$lib/components/faces-page/merge-face-selector.svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
||||||
|
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
let isEditingName = false;
|
let isEditingName = false;
|
||||||
@ -37,6 +39,13 @@
|
|||||||
let showMergeFacePanel = false;
|
let showMergeFacePanel = false;
|
||||||
let previousRoute: string = AppRoute.EXPLORE;
|
let previousRoute: string = AppRoute.EXPLORE;
|
||||||
let selectedAssets: Set<AssetResponseDto> = new Set();
|
let selectedAssets: Set<AssetResponseDto> = new Set();
|
||||||
|
let showMergeModal = false;
|
||||||
|
let people = data.people.people;
|
||||||
|
let personMerge1: PersonResponseDto;
|
||||||
|
let personMerge2: PersonResponseDto;
|
||||||
|
|
||||||
|
let personName = '';
|
||||||
|
|
||||||
$: isMultiSelectionMode = selectedAssets.size > 0;
|
$: isMultiSelectionMode = selectedAssets.size > 0;
|
||||||
$: isAllArchive = Array.from(selectedAssets).every((asset) => asset.isArchived);
|
$: isAllArchive = Array.from(selectedAssets).every((asset) => asset.isArchived);
|
||||||
$: isAllFavorite = Array.from(selectedAssets).every((asset) => asset.isFavorite);
|
$: isAllFavorite = Array.from(selectedAssets).every((asset) => asset.isFavorite);
|
||||||
@ -56,16 +65,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleNameChange = async (name: string) => {
|
|
||||||
try {
|
|
||||||
isEditingName = false;
|
|
||||||
data.person.name = name;
|
|
||||||
await api.personApi.updatePerson({ id: data.person.id, personUpdateDto: { name } });
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, 'Unable to save name');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onAssetDelete = (assetId: string) => {
|
const onAssetDelete = (assetId: string) => {
|
||||||
data.assets = data.assets.filter((asset: AssetResponseDto) => asset.id !== assetId);
|
data.assets = data.assets.filter((asset: AssetResponseDto) => asset.id !== assetId);
|
||||||
};
|
};
|
||||||
@ -91,8 +90,92 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMergeSameFace = async (response: [PersonResponseDto, PersonResponseDto]) => {
|
||||||
|
const [personToMerge, personToBeMergedIn] = response;
|
||||||
|
showMergeModal = false;
|
||||||
|
try {
|
||||||
|
await api.personApi.mergePerson({
|
||||||
|
id: personToBeMergedIn.id,
|
||||||
|
mergePersonDto: { ids: [personToMerge.id] },
|
||||||
|
});
|
||||||
|
notificationController.show({
|
||||||
|
message: 'Merge faces succesfully',
|
||||||
|
type: NotificationType.Info,
|
||||||
|
});
|
||||||
|
people = people.filter((person: PersonResponseDto) => person.id !== personToMerge.id);
|
||||||
|
if (personToBeMergedIn.name != personName && data.person.id === personToBeMergedIn.id) {
|
||||||
|
changeName();
|
||||||
|
invalidateAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
goto(`${AppRoute.PEOPLE}/${personToBeMergedIn.id}`, { replaceState: true });
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, 'Unable to save name');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeName = async () => {
|
||||||
|
showMergeModal = false;
|
||||||
|
data.person.name = personName;
|
||||||
|
try {
|
||||||
|
isEditingName = false;
|
||||||
|
|
||||||
|
const { data: updatedPerson } = await api.personApi.updatePerson({
|
||||||
|
id: data.person.id,
|
||||||
|
personUpdateDto: { name: personName },
|
||||||
|
});
|
||||||
|
|
||||||
|
people = people.map((person: PersonResponseDto) => {
|
||||||
|
if (person.id === updatedPerson.id) {
|
||||||
|
return updatedPerson;
|
||||||
|
}
|
||||||
|
return person;
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationController.show({
|
||||||
|
message: 'Change name succesfully',
|
||||||
|
type: NotificationType.Info,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, 'Unable to save name');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNameChange = async (name: string) => {
|
||||||
|
personName = name;
|
||||||
|
|
||||||
|
if (data.person.name === personName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingPerson = people.find(
|
||||||
|
(person: PersonResponseDto) =>
|
||||||
|
person.name.toLowerCase() === personName.toLowerCase() && person.id !== data.person.id && person.name,
|
||||||
|
);
|
||||||
|
if (existingPerson) {
|
||||||
|
personMerge2 = existingPerson;
|
||||||
|
personMerge1 = data.person;
|
||||||
|
showMergeModal = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeName();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if showMergeModal}
|
||||||
|
<FullScreenModal on:clickOutside={() => (showMergeModal = false)}>
|
||||||
|
<MergeSuggestionModal
|
||||||
|
{personMerge1}
|
||||||
|
{personMerge2}
|
||||||
|
{people}
|
||||||
|
on:close={() => (showMergeModal = false)}
|
||||||
|
on:reject={() => changeName()}
|
||||||
|
on:confirm={(event) => handleMergeSameFace(event.detail)}
|
||||||
|
/>
|
||||||
|
</FullScreenModal>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if isMultiSelectionMode}
|
{#if isMultiSelectionMode}
|
||||||
<AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}>
|
<AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}>
|
||||||
<CreateSharedLink />
|
<CreateSharedLink />
|
||||||
|
Loading…
Reference in New Issue
Block a user