fix: hide faces (#3352)

* fix: hide faces

* remove unused variable

* fix: work even if one fails

* better style for hidden people

* add hide face in the menu dropdown

* add buttons to toggle visibility for all faces

* add server test

* close modal with escape key

* fix: explore page

* improve show & hide faces modal

* keep name on people card

* simplify layout

* sticky app bar in show-hide page

* fix format

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
martin
2023-07-23 05:00:43 +02:00
committed by GitHub
parent c40aa4399b
commit ed64c91da6
25 changed files with 1097 additions and 72 deletions

View File

@@ -5,7 +5,7 @@
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.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 { AppRoute } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
@@ -17,41 +17,86 @@
import IconButton from '$lib/components/elements/buttons/icon-button.svelte';
import EyeOutline from 'svelte-material-icons/EyeOutline.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;
let selectHidden = false;
let changeCounter = 0;
let initialHiddenValues: Record<string, boolean> = {};
let eyeColorMap: Record<string, string> = {};
let people = data.people.people;
let countTotalPeople = data.people.total;
let countVisiblePeople = data.people.visible;
let showLoadingSpinner = false;
let toggleVisibility = false;
people.forEach((person: PersonResponseDto) => {
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 = () => {
selectHidden = false;
people.forEach((person: PersonResponseDto) => {
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;
toggleVisibility = false;
};
const handleResetVisibility = () => {
for (const person of people) {
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 () => {
selectHidden = false;
showLoadingSpinner = true;
let changed: PeopleUpdateItem[] = [];
try {
// Reset the counter before checking changes
let changeCounter = 0;
// Check if the visibility for each person has been changed
for (const person of people) {
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
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({
type: NotificationType.Info,
message: `Visibility changed for ${changeCounter} ${changeCounter <= 1 ? 'person' : 'people'}`,
message: `Visibility changed for ${count} ${count <= 1 ? 'person' : 'people'}`,
});
}
} catch (error) {
handleError(
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;
@@ -85,6 +146,37 @@
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>) => {
goto(`${AppRoute.PEOPLE}/${event.detail.id}?action=merge`);
};
@@ -132,13 +224,16 @@
{#if countVisiblePeople > 0}
<div class="pl-4">
<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>
{:else}
@@ -184,32 +279,35 @@
{/if}
</UserPageLayout>
{#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>
{/if}