|
@@ -5,7 +5,7 @@
|
|
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
|
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
|
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
|
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
|
import Button from '$lib/components/elements/buttons/button.svelte';
|
|
import Button from '$lib/components/elements/buttons/button.svelte';
|
|
- import { api, type PersonResponseDto } from '@api';
|
|
|
|
|
|
+ import { api, PeopleUpdateItem, type PersonResponseDto } from '@api';
|
|
import { goto } from '$app/navigation';
|
|
import { goto } from '$app/navigation';
|
|
import { AppRoute } from '$lib/constants';
|
|
import { AppRoute } from '$lib/constants';
|
|
import { handleError } from '$lib/utils/handle-error';
|
|
import { handleError } from '$lib/utils/handle-error';
|
|
@@ -17,41 +17,86 @@
|
|
import IconButton from '$lib/components/elements/buttons/icon-button.svelte';
|
|
import IconButton from '$lib/components/elements/buttons/icon-button.svelte';
|
|
import EyeOutline from 'svelte-material-icons/EyeOutline.svelte';
|
|
import EyeOutline from 'svelte-material-icons/EyeOutline.svelte';
|
|
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 { browser } from '$app/environment';
|
|
|
|
|
|
export let data: PageData;
|
|
export let data: PageData;
|
|
let selectHidden = false;
|
|
let selectHidden = false;
|
|
- let changeCounter = 0;
|
|
|
|
let initialHiddenValues: Record<string, boolean> = {};
|
|
let initialHiddenValues: Record<string, boolean> = {};
|
|
|
|
|
|
|
|
+ let eyeColorMap: Record<string, string> = {};
|
|
|
|
+
|
|
let people = data.people.people;
|
|
let people = data.people.people;
|
|
let countTotalPeople = data.people.total;
|
|
let countTotalPeople = data.people.total;
|
|
let countVisiblePeople = data.people.visible;
|
|
let countVisiblePeople = data.people.visible;
|
|
|
|
|
|
|
|
+ let showLoadingSpinner = false;
|
|
|
|
+ let toggleVisibility = false;
|
|
|
|
+
|
|
people.forEach((person: PersonResponseDto) => {
|
|
people.forEach((person: PersonResponseDto) => {
|
|
initialHiddenValues[person.id] = person.isHidden;
|
|
initialHiddenValues[person.id] = person.isHidden;
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
|
|
|
|
+
|
|
|
|
+ onMount(() => {
|
|
|
|
+ document.addEventListener('keydown', onKeyboardPress);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ onDestroy(() => {
|
|
|
|
+ if (browser) {
|
|
|
|
+ document.removeEventListener('keydown', onKeyboardPress);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const handleKeyboardPress = (event: KeyboardEvent) => {
|
|
|
|
+ switch (event.key) {
|
|
|
|
+ case 'Escape':
|
|
|
|
+ handleCloseClick();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
const handleCloseClick = () => {
|
|
const handleCloseClick = () => {
|
|
|
|
+ for (const person of people) {
|
|
|
|
+ person.isHidden = initialHiddenValues[person.id];
|
|
|
|
+ }
|
|
|
|
+ // trigger reactivity
|
|
|
|
+ people = people;
|
|
|
|
+
|
|
|
|
+ // Reset variables used on the "Show & hide faces" modal
|
|
|
|
+ showLoadingSpinner = false;
|
|
selectHidden = false;
|
|
selectHidden = false;
|
|
- people.forEach((person: PersonResponseDto) => {
|
|
|
|
|
|
+ toggleVisibility = false;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleResetVisibility = () => {
|
|
|
|
+ for (const person of people) {
|
|
person.isHidden = initialHiddenValues[person.id];
|
|
person.isHidden = initialHiddenValues[person.id];
|
|
- });
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trigger reactivity
|
|
|
|
+ people = people;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleToggleVisibility = () => {
|
|
|
|
+ toggleVisibility = !toggleVisibility;
|
|
|
|
+ for (const person of people) {
|
|
|
|
+ person.isHidden = toggleVisibility;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // trigger reactivity
|
|
|
|
+ people = people;
|
|
};
|
|
};
|
|
|
|
|
|
const handleDoneClick = async () => {
|
|
const handleDoneClick = async () => {
|
|
- selectHidden = false;
|
|
|
|
|
|
+ showLoadingSpinner = true;
|
|
|
|
+ let changed: PeopleUpdateItem[] = [];
|
|
try {
|
|
try {
|
|
- // Reset the counter before checking changes
|
|
|
|
- let changeCounter = 0;
|
|
|
|
-
|
|
|
|
// Check if the visibility for each person has been changed
|
|
// Check if the visibility for each person has been changed
|
|
for (const person of people) {
|
|
for (const person of people) {
|
|
if (person.isHidden !== initialHiddenValues[person.id]) {
|
|
if (person.isHidden !== initialHiddenValues[person.id]) {
|
|
- changeCounter++;
|
|
|
|
- await api.personApi.updatePerson({
|
|
|
|
- id: person.id,
|
|
|
|
- personUpdateDto: { isHidden: person.isHidden },
|
|
|
|
- });
|
|
|
|
|
|
+ changed.push({ id: person.id, isHidden: person.isHidden });
|
|
|
|
|
|
// Update the initial hidden values
|
|
// Update the initial hidden values
|
|
initialHiddenValues[person.id] = person.isHidden;
|
|
initialHiddenValues[person.id] = person.isHidden;
|
|
@@ -61,18 +106,34 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (changeCounter > 0) {
|
|
|
|
|
|
+ if (changed.length > 0) {
|
|
|
|
+ const { data: results } = await api.personApi.updatePeople({
|
|
|
|
+ peopleUpdateDto: { people: changed },
|
|
|
|
+ });
|
|
|
|
+ const count = results.filter(({ success }) => success).length;
|
|
|
|
+ if (results.length - count > 0) {
|
|
|
|
+ notificationController.show({
|
|
|
|
+ type: NotificationType.Error,
|
|
|
|
+ message: `Unable to change the visibility for ${results.length - count} ${
|
|
|
|
+ results.length - count <= 1 ? 'person' : 'people'
|
|
|
|
+ }`,
|
|
|
|
+ });
|
|
|
|
+ }
|
|
notificationController.show({
|
|
notificationController.show({
|
|
type: NotificationType.Info,
|
|
type: NotificationType.Info,
|
|
- message: `Visibility changed for ${changeCounter} ${changeCounter <= 1 ? 'person' : 'people'}`,
|
|
|
|
|
|
+ message: `Visibility changed for ${count} ${count <= 1 ? 'person' : 'people'}`,
|
|
});
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
} catch (error) {
|
|
handleError(
|
|
handleError(
|
|
error,
|
|
error,
|
|
- `Unable to change the visibility for ${changeCounter} ${changeCounter <= 1 ? 'person' : 'people'}`,
|
|
|
|
|
|
+ `Unable to change the visibility for ${changed.length} ${changed.length <= 1 ? 'person' : 'people'}`,
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
+ // Reset variables used on the "Show & hide faces" modal
|
|
|
|
+ showLoadingSpinner = false;
|
|
|
|
+ selectHidden = false;
|
|
|
|
+ toggleVisibility = false;
|
|
};
|
|
};
|
|
|
|
|
|
let showChangeNameModal = false;
|
|
let showChangeNameModal = false;
|
|
@@ -85,6 +146,37 @@
|
|
edittingPerson = detail;
|
|
edittingPerson = detail;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ const handleHideFace = async (event: CustomEvent<PersonResponseDto>) => {
|
|
|
|
+ try {
|
|
|
|
+ const { data: updatedPerson } = await api.personApi.updatePerson({
|
|
|
|
+ id: event.detail.id,
|
|
|
|
+ personUpdateDto: { isHidden: true },
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ people = people.map((person: PersonResponseDto) => {
|
|
|
|
+ if (person.id === updatedPerson.id) {
|
|
|
|
+ return updatedPerson;
|
|
|
|
+ }
|
|
|
|
+ return person;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ people.forEach((person: PersonResponseDto) => {
|
|
|
|
+ initialHiddenValues[person.id] = person.isHidden;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ countVisiblePeople--;
|
|
|
|
+
|
|
|
|
+ showChangeNameModal = false;
|
|
|
|
+
|
|
|
|
+ notificationController.show({
|
|
|
|
+ message: 'Changed visibility succesfully',
|
|
|
|
+ type: NotificationType.Info,
|
|
|
|
+ });
|
|
|
|
+ } catch (error) {
|
|
|
|
+ handleError(error, 'Unable to hide person');
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
const handleMergeFaces = (event: CustomEvent<PersonResponseDto>) => {
|
|
const handleMergeFaces = (event: CustomEvent<PersonResponseDto>) => {
|
|
goto(`${AppRoute.PEOPLE}/${event.detail.id}?action=merge`);
|
|
goto(`${AppRoute.PEOPLE}/${event.detail.id}?action=merge`);
|
|
};
|
|
};
|
|
@@ -132,13 +224,16 @@
|
|
{#if countVisiblePeople > 0}
|
|
{#if countVisiblePeople > 0}
|
|
<div class="pl-4">
|
|
<div class="pl-4">
|
|
<div class="flex flex-row flex-wrap gap-1">
|
|
<div class="flex flex-row flex-wrap gap-1">
|
|
- {#key selectHidden}
|
|
|
|
- {#each people as person (person.id)}
|
|
|
|
- {#if !person.isHidden}
|
|
|
|
- <PeopleCard {person} on:change-name={handleChangeName} on:merge-faces={handleMergeFaces} />
|
|
|
|
- {/if}
|
|
|
|
- {/each}
|
|
|
|
- {/key}
|
|
|
|
|
|
+ {#each people as person (person.id)}
|
|
|
|
+ {#if !person.isHidden}
|
|
|
|
+ <PeopleCard
|
|
|
|
+ {person}
|
|
|
|
+ on:change-name={handleChangeName}
|
|
|
|
+ on:merge-faces={handleMergeFaces}
|
|
|
|
+ on:hide-face={handleHideFace}
|
|
|
|
+ />
|
|
|
|
+ {/if}
|
|
|
|
+ {/each}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{:else}
|
|
{:else}
|
|
@@ -184,32 +279,35 @@
|
|
{/if}
|
|
{/if}
|
|
</UserPageLayout>
|
|
</UserPageLayout>
|
|
{#if selectHidden}
|
|
{#if selectHidden}
|
|
- <ShowHide on:doneClick={handleDoneClick} on:closeClick={handleCloseClick}>
|
|
|
|
- <div class="pl-4">
|
|
|
|
- <div class="flex flex-row flex-wrap gap-1">
|
|
|
|
- {#each people as person (person.id)}
|
|
|
|
- <div class="relative">
|
|
|
|
- <div class="h-48 w-48 rounded-xl brightness-95 filter">
|
|
|
|
- <button class="h-full w-full" on:click={() => (person.isHidden = !person.isHidden)}>
|
|
|
|
- <ImageThumbnail
|
|
|
|
- bind:hidden={person.isHidden}
|
|
|
|
- shadow
|
|
|
|
- url={api.getPeopleThumbnailUrl(person.id)}
|
|
|
|
- altText={person.name}
|
|
|
|
- widthStyle="100%"
|
|
|
|
- />
|
|
|
|
- </button>
|
|
|
|
- </div>
|
|
|
|
- {#if person.name}
|
|
|
|
- <span
|
|
|
|
- class="w-100 absolute bottom-2 w-full text-ellipsis px-1 text-center font-medium text-white backdrop-blur-[1px] hover:cursor-pointer"
|
|
|
|
- >
|
|
|
|
- {person.name}
|
|
|
|
- </span>
|
|
|
|
- {/if}
|
|
|
|
- </div>
|
|
|
|
- {/each}
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
|
|
+ <ShowHide
|
|
|
|
+ on:doneClick={handleDoneClick}
|
|
|
|
+ on:closeClick={handleCloseClick}
|
|
|
|
+ on:reset-visibility={handleResetVisibility}
|
|
|
|
+ on:toggle-visibility={handleToggleVisibility}
|
|
|
|
+ bind:showLoadingSpinner
|
|
|
|
+ bind:toggleVisibility
|
|
|
|
+ >
|
|
|
|
+ {#each people as person (person.id)}
|
|
|
|
+ <button
|
|
|
|
+ class="relative h-36 w-36 md:h-48 md:w-48"
|
|
|
|
+ on:click={() => (person.isHidden = !person.isHidden)}
|
|
|
|
+ on:mouseenter={() => (eyeColorMap[person.id] = 'black')}
|
|
|
|
+ on:mouseleave={() => (eyeColorMap[person.id] = 'white')}
|
|
|
|
+ >
|
|
|
|
+ <ImageThumbnail
|
|
|
|
+ bind:hidden={person.isHidden}
|
|
|
|
+ shadow
|
|
|
|
+ url={api.getPeopleThumbnailUrl(person.id)}
|
|
|
|
+ altText={person.name}
|
|
|
|
+ widthStyle="100%"
|
|
|
|
+ bind:eyeColor={eyeColorMap[person.id]}
|
|
|
|
+ />
|
|
|
|
+ {#if person.name}
|
|
|
|
+ <span class="absolute bottom-2 left-0 w-full select-text px-1 text-center font-medium text-white">
|
|
|
|
+ {person.name}
|
|
|
|
+ </span>
|
|
|
|
+ {/if}
|
|
|
|
+ </button>
|
|
|
|
+ {/each}
|
|
</ShowHide>
|
|
</ShowHide>
|
|
{/if}
|
|
{/if}
|