chore: migrate away from event dispatcher (#12820)

This commit is contained in:
Daniel Dietzler 2024-09-20 23:02:58 +02:00 committed by GitHub
parent 529d49471f
commit 124eb8251b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
72 changed files with 360 additions and 656 deletions

View file

@ -4,7 +4,6 @@
import { getPeopleThumbnailUrl } from '$lib/utils';
import { AssetTypeEnum, type AssetFaceResponseDto, type PersonResponseDto } from '@immich/sdk';
import { mdiArrowLeftThin, mdiClose, mdiMagnify, mdiPlus } from '@mdi/js';
import { createEventDispatcher } from 'svelte';
import { linear } from 'svelte/easing';
import { fly } from 'svelte/transition';
import { photoViewer } from '$lib/stores/assets.store';
@ -19,6 +18,9 @@
export let editedFace: AssetFaceResponseDto;
export let assetId: string;
export let assetType: AssetTypeEnum;
export let onClose: () => void;
export let onCreatePerson: (featurePhoto: string | null) => void;
export let onReassign: (person: PersonResponseDto) => void;
// loading spinners
let isShowLoadingNewPerson = false;
@ -31,25 +33,16 @@
$: showPeople = searchName ? searchedPeople : allPeople.filter((person) => !person.isHidden);
const dispatch = createEventDispatcher<{
close: void;
createPerson: string | null;
reassign: PersonResponseDto;
}>();
const handleBackButton = () => {
dispatch('close');
};
const handleCreatePerson = async () => {
const timeout = setTimeout(() => (isShowLoadingNewPerson = true), timeBeforeShowLoadingSpinner);
const newFeaturePhoto = await zoomImageToBase64(editedFace, assetId, assetType, $photoViewer);
dispatch('createPerson', newFeaturePhoto);
onCreatePerson(newFeaturePhoto);
clearTimeout(timeout);
isShowLoadingNewPerson = false;
dispatch('createPerson', newFeaturePhoto);
onCreatePerson(newFeaturePhoto);
};
</script>
@ -60,7 +53,7 @@
<div class="flex place-items-center justify-between gap-2">
{#if !searchFaces}
<div class="flex items-center gap-2">
<CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={handleBackButton} />
<CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={onClose} />
<p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">{$t('select_face')}</p>
</div>
<div class="flex justify-end gap-2">
@ -80,7 +73,7 @@
{/if}
</div>
{:else}
<CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={handleBackButton} />
<CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={onClose} />
<div class="w-full flex">
<SearchPeople
type="input"
@ -103,7 +96,7 @@
{#each showPeople as person (person.id)}
{#if !editedFace.person || person.id !== editedFace.person.id}
<div class="w-fit">
<button type="button" class="w-[90px]" on:click={() => dispatch('reassign', person)}>
<button type="button" class="w-[90px]" on:click={() => onReassign(person)}>
<div class="relative">
<ImageThumbnail
curve

View file

@ -1,6 +1,5 @@
<script lang="ts">
import { type PersonResponseDto } from '@immich/sdk';
import { createEventDispatcher } from 'svelte';
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
import Button from '../elements/buttons/button.svelte';
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
@ -11,10 +10,7 @@
export let suggestedPeople: PersonResponseDto[];
export let thumbnailData: string;
export let isSearchingPeople: boolean;
const dispatch = createEventDispatcher<{
change: string;
}>();
export let onChange: (name: string) => void;
</script>
<div
@ -26,7 +22,7 @@
<form
class="ml-4 flex w-full justify-between gap-16"
autocomplete="off"
on:submit|preventDefault={() => dispatch('change', name)}
on:submit|preventDefault={() => onChange(name)}
>
<SearchPeople
bind:searchName={name}

View file

@ -1,7 +1,6 @@
<script lang="ts">
import { getPeopleThumbnailUrl } from '$lib/utils';
import { type PersonResponseDto } from '@immich/sdk';
import { createEventDispatcher } from 'svelte';
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
export let person: PersonResponseDto;
@ -10,20 +9,13 @@
export let thumbnailSize: number | null = null;
export let circle = false;
export let border = false;
let dispatch = createEventDispatcher<{
click: PersonResponseDto;
}>();
const handleOnClicked = () => {
dispatch('click', person);
};
export let onClick: (person: PersonResponseDto) => void = () => {};
</script>
<button
type="button"
class="relative rounded-lg transition-all"
on:click={handleOnClicked}
on:click={() => onClick(person)}
disabled={!selectable}
style:width={thumbnailSize ? thumbnailSize + 'px' : '100%'}
style:height={thumbnailSize ? thumbnailSize + 'px' : '100%'}

View file

@ -6,7 +6,7 @@
import { handleError } from '$lib/utils/handle-error';
import { getAllPeople, getPerson, mergePerson, type PersonResponseDto } from '@immich/sdk';
import { mdiCallMerge, mdiMerge, mdiSwapHorizontal } from '@mdi/js';
import { createEventDispatcher, onMount } from 'svelte';
import { onMount } from 'svelte';
import { flip } from 'svelte/animate';
import { quintOut } from 'svelte/easing';
import { fly } from 'svelte/transition';
@ -20,15 +20,13 @@
import { t } from 'svelte-i18n';
export let person: PersonResponseDto;
export let onBack: () => void;
export let onMerge: (mergedPerson: PersonResponseDto) => void;
let people: PersonResponseDto[] = [];
let selectedPeople: PersonResponseDto[] = [];
let screenHeight: number;
let dispatch = createEventDispatcher<{
back: void;
merge: PersonResponseDto;
}>();
$: hasSelection = selectedPeople.length > 0;
$: peopleToNotShow = [...selectedPeople, person];
@ -37,10 +35,6 @@
people = data.people;
});
const onClose = () => {
dispatch('back');
};
const handleSwapPeople = async () => {
[person, selectedPeople[0]] = [selectedPeople[0], person];
$page.url.searchParams.set(QueryParameter.ACTION, ActionQueryParameterValue.MERGE);
@ -88,7 +82,7 @@
message: $t('merged_people_count', { values: { count } }),
type: NotificationType.Info,
});
dispatch('merge', mergedPerson);
onMerge(mergedPerson);
} catch (error) {
handleError(error, $t('cannot_merge_people'));
}
@ -101,7 +95,7 @@
transition:fly={{ y: 500, duration: 100, easing: quintOut }}
class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg"
>
<ControlAppBar on:close={onClose}>
<ControlAppBar onClose={onBack}>
<svelte:fragment slot="leading">
{#if hasSelection}
{$t('selected_count', { values: { count: selectedPeople.length } })}
@ -125,7 +119,7 @@
<div class="grid grid-flow-col-dense place-content-center place-items-center gap-4">
{#each selectedPeople as person (person.id)}
<div animate:flip={{ duration: 250, easing: quintOut }}>
<FaceThumbnail border circle {person} selectable thumbnailSize={120} on:click={() => onSelect(person)} />
<FaceThumbnail border circle {person} selectable thumbnailSize={120} onClick={() => onSelect(person)} />
</div>
{/each}
@ -152,7 +146,7 @@
</div>
</div>
<PeopleList {people} {peopleToNotShow} {screenHeight} on:select={({ detail }) => onSelect(detail)} />
<PeopleList {people} {peopleToNotShow} {screenHeight} {onSelect} />
</section>
</section>
</section>

View file

@ -4,7 +4,6 @@
import { getPeopleThumbnailUrl } from '$lib/utils';
import { type PersonResponseDto } from '@immich/sdk';
import { mdiArrowLeft, mdiMerge } from '@mdi/js';
import { createEventDispatcher } from 'svelte';
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
import Button from '../elements/buttons/button.svelte';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
@ -13,25 +12,22 @@
export let personMerge1: PersonResponseDto;
export let personMerge2: PersonResponseDto;
export let potentialMergePeople: PersonResponseDto[];
export let onReject: () => void;
export let onConfirm: ([personMerge1, personMerge2]: [PersonResponseDto, PersonResponseDto]) => void;
export let onClose: () => void;
let choosePersonToMerge = false;
const title = personMerge2.name;
const dispatch = createEventDispatcher<{
reject: void;
confirm: [PersonResponseDto, PersonResponseDto];
close: void;
}>();
const changePersonToMerge = (newperson: PersonResponseDto) => {
const index = potentialMergePeople.indexOf(newperson);
const changePersonToMerge = (newPerson: PersonResponseDto) => {
const index = potentialMergePeople.indexOf(newPerson);
[potentialMergePeople[index], personMerge2] = [personMerge2, potentialMergePeople[index]];
choosePersonToMerge = false;
};
</script>
<FullScreenModal title="{$t('merge_people')} - {title}" onClose={() => dispatch('close')}>
<FullScreenModal title="{$t('merge_people')} - {title}" {onClose}>
<div class="flex items-center justify-center py-4 md:h-36 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">
@ -105,7 +101,7 @@
<p class="text-sm text-gray-500 dark:text-gray-300">{$t('they_will_be_merged_together')}</p>
</div>
<svelte:fragment slot="sticky-bottom">
<Button fullwidth color="gray" on:click={() => dispatch('reject')}>{$t('no')}</Button>
<Button fullwidth on:click={() => dispatch('confirm', [personMerge1, personMerge2])}>{$t('yes')}</Button>
<Button fullwidth color="gray" on:click={onReject}>{$t('no')}</Button>
<Button fullwidth on:click={() => onConfirm([personMerge1, personMerge2])}>{$t('yes')}</Button>
</svelte:fragment>
</FullScreenModal>

View file

@ -9,7 +9,6 @@
mdiDotsVertical,
mdiEyeOffOutline,
} from '@mdi/js';
import { createEventDispatcher } from 'svelte';
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
import MenuOption from '../shared-components/context-menu/menu-option.svelte';
import { t } from 'svelte-i18n';
@ -18,19 +17,12 @@
export let person: PersonResponseDto;
export let preload = false;
type MenuItemEvent = 'change-name' | 'set-birth-date' | 'merge-people' | 'hide-person';
let dispatch = createEventDispatcher<{
'change-name': void;
'set-birth-date': void;
'merge-people': void;
'hide-person': void;
}>();
export let onChangeName: () => void;
export let onSetBirthDate: () => void;
export let onMergePeople: () => void;
export let onHidePerson: () => void;
let showVerticalDots = false;
const onMenuClick = (event: MenuItemEvent) => {
dispatch(event);
};
</script>
<div
@ -76,18 +68,10 @@
icon={mdiDotsVertical}
title={$t('show_person_options')}
>
<MenuOption onClick={() => onMenuClick('hide-person')} icon={mdiEyeOffOutline} text={$t('hide_person')} />
<MenuOption onClick={() => onMenuClick('change-name')} icon={mdiAccountEditOutline} text={$t('change_name')} />
<MenuOption
onClick={() => onMenuClick('set-birth-date')}
icon={mdiCalendarEditOutline}
text={$t('set_date_of_birth')}
/>
<MenuOption
onClick={() => onMenuClick('merge-people')}
icon={mdiAccountMultipleCheckOutline}
text={$t('merge_people')}
/>
<MenuOption onClick={onHidePerson} icon={mdiEyeOffOutline} text={$t('hide_person')} />
<MenuOption onClick={onChangeName} icon={mdiAccountEditOutline} text={$t('change_name')} />
<MenuOption onClick={onSetBirthDate} icon={mdiCalendarEditOutline} text={$t('set_date_of_birth')} />
<MenuOption onClick={onMergePeople} icon={mdiAccountMultipleCheckOutline} text={$t('merge_people')} />
</ButtonContextMenu>
</div>
{/if}

View file

@ -1,6 +1,5 @@
<script lang="ts">
import { type PersonResponseDto } from '@immich/sdk';
import { createEventDispatcher } from 'svelte';
import FaceThumbnail from './face-thumbnail.svelte';
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
import { t } from 'svelte-i18n';
@ -8,15 +7,13 @@
export let screenHeight: number;
export let people: PersonResponseDto[];
export let peopleToNotShow: PersonResponseDto[];
export let onSelect: (person: PersonResponseDto) => void;
let searchedPeopleLocal: PersonResponseDto[] = [];
let name = '';
let showPeople: PersonResponseDto[];
let dispatch = createEventDispatcher<{
select: PersonResponseDto;
}>();
$: {
showPeople = name ? searchedPeopleLocal : people;
showPeople = showPeople.filter(
@ -35,15 +32,7 @@
>
<div class="grid-col-2 grid gap-8 md:grid-cols-3 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10">
{#each showPeople as person (person.id)}
<FaceThumbnail
{person}
on:click={() => {
dispatch('select', person);
}}
circle
border
selectable
/>
<FaceThumbnail {person} on:click={() => onSelect(person)} circle border selectable />
{/each}
</div>
</div>

View file

@ -18,7 +18,7 @@
import { mdiAccountOff } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte';
import { mdiArrowLeftThin, mdiMinus, mdiRestart } from '@mdi/js';
import { createEventDispatcher, onMount } from 'svelte';
import { onMount } from 'svelte';
import { linear } from 'svelte/easing';
import { fly } from 'svelte/transition';
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
@ -31,6 +31,8 @@
export let assetId: string;
export let assetType: AssetTypeEnum;
export let onClose: () => void;
export let onRefresh: () => void;
// keep track of the changes
let peopleToCreate: string[] = [];
@ -56,11 +58,6 @@
const thumbnailWidth = '90px';
const dispatch = createEventDispatcher<{
close: void;
refresh: void;
}>();
async function loadPeople() {
const timeout = setTimeout(() => (isShowLoadingPeople = true), timeBeforeShowLoadingSpinner);
try {
@ -85,7 +82,7 @@
) {
clearTimeout(loaderLoadingDoneTimeout);
clearTimeout(automaticRefreshTimeout);
dispatch('refresh');
onRefresh();
}
};
@ -98,10 +95,6 @@
return b.every((valueB) => a.includes(valueB));
};
const handleBackButton = () => {
dispatch('close');
};
const handleReset = (id: string) => {
if (selectedPersonToReassign[id]) {
delete selectedPersonToReassign[id];
@ -153,9 +146,9 @@
isShowLoadingDone = false;
if (peopleToCreate.length === 0) {
clearTimeout(loaderLoadingDoneTimeout);
dispatch('refresh');
onRefresh();
} else {
automaticRefreshTimeout = setTimeout(() => dispatch('refresh'), 15_000);
automaticRefreshTimeout = setTimeout(onRefresh, 15_000);
}
};
@ -185,7 +178,7 @@
>
<div class="flex place-items-center justify-between gap-2">
<div class="flex items-center gap-2">
<CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={handleBackButton} />
<CircleIconButton icon={mdiArrowLeftThin} title={$t('back')} on:click={onClose} />
<p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">{$t('edit_faces')}</p>
</div>
{#if !isShowLoadingDone}
@ -336,8 +329,8 @@
{editedFace}
{assetId}
{assetType}
on:close={() => (showSelectedFaces = false)}
on:createPerson={(event) => handleCreatePerson(event.detail)}
on:reassign={(event) => handleReassignFace(event.detail)}
onClose={() => (showSelectedFaces = false)}
onCreatePerson={handleCreatePerson}
onReassign={handleReassignFace}
/>
{/if}

View file

@ -1,5 +1,4 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import Button from '../elements/buttons/button.svelte';
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
import { mdiCake } from '@mdi/js';
@ -7,28 +6,20 @@
import { t } from 'svelte-i18n';
export let birthDate: string;
const dispatch = createEventDispatcher<{
close: void;
updated: string;
}>();
export let onClose: () => void;
export let onUpdate: (birthDate: string) => void;
const todayFormatted = new Date().toISOString().split('T')[0];
const handleCancel = () => dispatch('close');
const handleSubmit = () => {
dispatch('updated', birthDate);
};
</script>
<FullScreenModal title={$t('set_date_of_birth')} icon={mdiCake} onClose={handleCancel}>
<FullScreenModal title={$t('set_date_of_birth')} icon={mdiCake} {onClose}>
<div class="text-immich-primary dark:text-immich-dark-primary">
<p class="text-sm dark:text-immich-dark-fg">
{$t('birthdate_set_description')}
</p>
</div>
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="set-birth-date-form">
<form on:submit|preventDefault={() => onUpdate(birthDate)} autocomplete="off" id="set-birth-date-form">
<div class="my-4 flex flex-col gap-2">
<DateInput
class="immich-form-input"
@ -41,7 +32,7 @@
</div>
</form>
<svelte:fragment slot="sticky-bottom">
<Button color="gray" fullwidth on:click={() => handleCancel()}>{$t('cancel')}</Button>
<Button color="gray" fullwidth on:click={onClose}>{$t('cancel')}</Button>
<Button type="submit" fullwidth form="set-birth-date-form">{$t('set')}</Button>
</svelte:fragment>
</FullScreenModal>

View file

@ -10,7 +10,7 @@
type PersonResponseDto,
} from '@immich/sdk';
import { mdiMerge, mdiPlus } from '@mdi/js';
import { createEventDispatcher, onMount } from 'svelte';
import { onMount } from 'svelte';
import { quintOut } from 'svelte/easing';
import { fly } from 'svelte/transition';
import Button from '../elements/buttons/button.svelte';
@ -23,6 +23,8 @@
export let assetIds: string[];
export let personAssets: PersonResponseDto;
export let onConfirm: () => void;
export let onClose: () => void;
let people: PersonResponseDto[] = [];
let selectedPerson: PersonResponseDto | null = null;
@ -34,11 +36,6 @@
$: peopleToNotShow = selectedPerson ? [personAssets, selectedPerson] : [personAssets];
let dispatch = createEventDispatcher<{
confirm: void;
close: void;
}>();
const selectedPeople: AssetFaceUpdateItem[] = [];
for (const assetId of assetIds) {
@ -50,10 +47,6 @@
people = data.people;
});
const onClose = () => {
dispatch('close');
};
const handleSelectedPerson = (person: PersonResponseDto) => {
if (selectedPerson && selectedPerson.id === person.id) {
handleRemoveSelectedPerson();
@ -87,7 +80,7 @@
}
showLoadingSpinnerCreate = false;
dispatch('confirm');
onConfirm();
};
const handleReassign = async () => {
@ -113,7 +106,7 @@
}
showLoadingSpinnerReassign = false;
dispatch('confirm');
onConfirm();
};
</script>
@ -123,7 +116,7 @@
transition:fly={{ y: 500, duration: 100, easing: quintOut }}
class="absolute left-0 top-0 z-[9999] h-full w-full bg-immich-bg dark:bg-immich-dark-bg"
>
<ControlAppBar on:close={onClose}>
<ControlAppBar {onClose}>
<svelte:fragment slot="leading">
<slot name="header" />
<div />
@ -180,7 +173,7 @@
</div>
</div>
{/if}
<PeopleList {people} {peopleToNotShow} {screenHeight} on:select={({ detail }) => handleSelectedPerson(detail)} />
<PeopleList {people} {peopleToNotShow} {screenHeight} onSelect={handleSelectedPerson} />
</section>
</section>
</section>