mirror of
https://github.com/immich-app/immich.git
synced 2025-06-01 04:36:19 -04:00
feat(web): swap between people when merging faces (#4089)
* feat: swap between people when merging faces * rename * fix: remove url parameter when closing * chore: handler naming --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
parent
49ef86173f
commit
a0163d8df0
@ -12,7 +12,9 @@
|
|||||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||||
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { invalidateAll } from '$app/navigation';
|
import { goto, invalidateAll } from '$app/navigation';
|
||||||
|
import { AppRoute } from '$lib/constants';
|
||||||
|
import SwapHorizontal from 'svelte-material-icons/SwapHorizontal.svelte';
|
||||||
|
|
||||||
export let person: PersonResponseDto;
|
export let person: PersonResponseDto;
|
||||||
let people: PersonResponseDto[] = [];
|
let people: PersonResponseDto[] = [];
|
||||||
@ -22,8 +24,9 @@
|
|||||||
let dispatch = createEventDispatcher();
|
let dispatch = createEventDispatcher();
|
||||||
|
|
||||||
$: hasSelection = selectedPeople.length > 0;
|
$: hasSelection = selectedPeople.length > 0;
|
||||||
$: unselectedPeople = people.filter((source) => !selectedPeople.includes(source) && source.id !== person.id);
|
$: unselectedPeople = people.filter(
|
||||||
|
(source) => !selectedPeople.some((selected) => selected.id === source.id) && source.id !== person.id,
|
||||||
|
);
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
||||||
people = data.people;
|
people = data.people;
|
||||||
@ -33,6 +36,11 @@
|
|||||||
dispatch('go-back');
|
dispatch('go-back');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSwapPeople = () => {
|
||||||
|
[person, selectedPeople[0]] = [selectedPeople[0], person];
|
||||||
|
goto(`${AppRoute.PEOPLE}/${person.id}?action=merge`);
|
||||||
|
};
|
||||||
|
|
||||||
const onSelect = (selected: PersonResponseDto) => {
|
const onSelect = (selected: PersonResponseDto) => {
|
||||||
if (selectedPeople.includes(selected)) {
|
if (selectedPeople.includes(selected)) {
|
||||||
selectedPeople = selectedPeople.filter((person) => person.id !== selected.id);
|
selectedPeople = selectedPeople.filter((person) => person.id !== selected.id);
|
||||||
@ -112,7 +120,14 @@
|
|||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if hasSelection}
|
{#if hasSelection}
|
||||||
<span><CallMerge size={48} class="rotate-90 dark:text-white" /> </span>
|
<span class="grid grid-cols-1"
|
||||||
|
><CallMerge size={48} class="rotate-90 dark:text-white" />
|
||||||
|
{#if selectedPeople.length === 1}
|
||||||
|
<button class="flex justify-center" on:click={handleSwapPeople}
|
||||||
|
><SwapHorizontal size={24} class="dark:text-white" />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
<FaceThumbnail {person} border circle selectable={false} thumbnailSize={180} />
|
<FaceThumbnail {person} border circle selectable={false} thumbnailSize={180} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
BIRTH_DATE = 'birth-date',
|
BIRTH_DATE = 'birth-date',
|
||||||
}
|
}
|
||||||
|
|
||||||
const assetStore = new AssetStore({
|
let assetStore = new AssetStore({
|
||||||
size: TimeBucketSize.Month,
|
size: TimeBucketSize.Month,
|
||||||
isArchived: false,
|
isArchived: false,
|
||||||
personId: data.person.id,
|
personId: data.person.id,
|
||||||
@ -54,6 +54,7 @@
|
|||||||
let viewMode: ViewMode = ViewMode.VIEW_ASSETS;
|
let viewMode: ViewMode = ViewMode.VIEW_ASSETS;
|
||||||
let isEditingName = false;
|
let isEditingName = false;
|
||||||
let previousRoute: string = AppRoute.EXPLORE;
|
let previousRoute: string = AppRoute.EXPLORE;
|
||||||
|
let previousPersonId: string = data.person.id;
|
||||||
let people = data.people.people;
|
let people = data.people.people;
|
||||||
let personMerge1: PersonResponseDto;
|
let personMerge1: PersonResponseDto;
|
||||||
let personMerge2: PersonResponseDto;
|
let personMerge2: PersonResponseDto;
|
||||||
@ -74,6 +75,14 @@
|
|||||||
if (from && from.route.id !== $page.route.id) {
|
if (from && from.route.id !== $page.route.id) {
|
||||||
previousRoute = from.url.href;
|
previousRoute = from.url.href;
|
||||||
}
|
}
|
||||||
|
if (previousPersonId !== data.person.id) {
|
||||||
|
assetStore = new AssetStore({
|
||||||
|
size: TimeBucketSize.Month,
|
||||||
|
isArchived: false,
|
||||||
|
personId: data.person.id,
|
||||||
|
});
|
||||||
|
previousPersonId = data.person.id;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const hideFace = async () => {
|
const hideFace = async () => {
|
||||||
@ -215,6 +224,14 @@
|
|||||||
handleError(error, 'Unable to save date of birth');
|
handleError(error, 'Unable to save date of birth');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleGoBack = () => {
|
||||||
|
viewMode = ViewMode.VIEW_ASSETS;
|
||||||
|
if ($page.url.searchParams.has('action')) {
|
||||||
|
$page.url.searchParams.delete('action');
|
||||||
|
goto($page.url);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if viewMode === ViewMode.SUGGEST_MERGE}
|
{#if viewMode === ViewMode.SUGGEST_MERGE}
|
||||||
@ -237,7 +254,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if viewMode === ViewMode.MERGE_FACES}
|
{#if viewMode === ViewMode.MERGE_FACES}
|
||||||
<MergeFaceSelector person={data.person} on:go-back={() => (viewMode = ViewMode.VIEW_ASSETS)} />
|
<MergeFaceSelector person={data.person} on:go-back={handleGoBack} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
@ -279,48 +296,50 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="relative h-screen overflow-hidden bg-immich-bg pt-[var(--navbar-height)] dark:bg-immich-dark-bg">
|
<main class="relative h-screen overflow-hidden bg-immich-bg pt-[var(--navbar-height)] dark:bg-immich-dark-bg">
|
||||||
<AssetGrid
|
{#key previousPersonId}
|
||||||
{assetStore}
|
<AssetGrid
|
||||||
{assetInteractionStore}
|
{assetStore}
|
||||||
isSelectionMode={viewMode === ViewMode.SELECT_FACE}
|
{assetInteractionStore}
|
||||||
singleSelect={viewMode === ViewMode.SELECT_FACE}
|
isSelectionMode={viewMode === ViewMode.SELECT_FACE}
|
||||||
on:select={({ detail: asset }) => handleSelectFeaturePhoto(asset)}
|
singleSelect={viewMode === ViewMode.SELECT_FACE}
|
||||||
>
|
on:select={({ detail: asset }) => handleSelectFeaturePhoto(asset)}
|
||||||
{#if viewMode === ViewMode.VIEW_ASSETS || viewMode === ViewMode.SUGGEST_MERGE || viewMode === ViewMode.BIRTH_DATE}
|
>
|
||||||
<!-- Face information block -->
|
{#if viewMode === ViewMode.VIEW_ASSETS || viewMode === ViewMode.SUGGEST_MERGE || viewMode === ViewMode.BIRTH_DATE}
|
||||||
<section class="flex place-items-center p-4 sm:px-6">
|
<!-- Face information block -->
|
||||||
{#if isEditingName}
|
<section class="flex place-items-center p-4 sm:px-6">
|
||||||
<EditNameInput
|
{#if isEditingName}
|
||||||
person={data.person}
|
<EditNameInput
|
||||||
on:change={(event) => handleNameChange(event.detail)}
|
person={data.person}
|
||||||
on:cancel={() => handleCancelEditName()}
|
on:change={(event) => handleNameChange(event.detail)}
|
||||||
/>
|
on:cancel={() => handleCancelEditName()}
|
||||||
{:else}
|
|
||||||
<button on:click={() => (viewMode = ViewMode.VIEW_ASSETS)}>
|
|
||||||
<ImageThumbnail
|
|
||||||
circle
|
|
||||||
shadow
|
|
||||||
url={api.getPeopleThumbnailUrl(data.person.id)}
|
|
||||||
altText={data.person.name}
|
|
||||||
widthStyle="3.375rem"
|
|
||||||
heightStyle="3.375rem"
|
|
||||||
/>
|
/>
|
||||||
</button>
|
{:else}
|
||||||
|
<button on:click={() => (viewMode = ViewMode.VIEW_ASSETS)}>
|
||||||
|
<ImageThumbnail
|
||||||
|
circle
|
||||||
|
shadow
|
||||||
|
url={api.getPeopleThumbnailUrl(data.person.id)}
|
||||||
|
altText={data.person.name}
|
||||||
|
widthStyle="3.375rem"
|
||||||
|
heightStyle="3.375rem"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
title="Edit name"
|
title="Edit name"
|
||||||
class="px-4 text-immich-primary dark:text-immich-dark-primary"
|
class="px-4 text-immich-primary dark:text-immich-dark-primary"
|
||||||
on:click={() => (isEditingName = true)}
|
on:click={() => (isEditingName = true)}
|
||||||
>
|
>
|
||||||
{#if data.person.name}
|
{#if data.person.name}
|
||||||
<p class="py-2 font-medium">{data.person.name}</p>
|
<p class="py-2 font-medium">{data.person.name}</p>
|
||||||
{:else}
|
{:else}
|
||||||
<p class="w-fit font-medium">Add a name</p>
|
<p class="w-fit font-medium">Add a name</p>
|
||||||
<p class="text-sm text-gray-500 dark:text-immich-gray">Find them fast by name with search</p>
|
<p class="text-sm text-gray-500 dark:text-immich-gray">Find them fast by name with search</p>
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
</AssetGrid>
|
</AssetGrid>
|
||||||
|
{/key}
|
||||||
</main>
|
</main>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user