mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 20:25:32 -04:00
refactor: date of birth modal (#18283)
This commit is contained in:
parent
c9d45eee86
commit
4efc41d5d9
@ -1,48 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Button from '../elements/buttons/button.svelte';
|
|
||||||
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
|
|
||||||
import { mdiCake } from '@mdi/js';
|
|
||||||
import DateInput from '../elements/date-input.svelte';
|
|
||||||
import { t } from 'svelte-i18n';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
birthDate: string;
|
|
||||||
onClose: () => void;
|
|
||||||
onUpdate: (birthDate: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { birthDate = $bindable(), onClose, onUpdate }: Props = $props();
|
|
||||||
|
|
||||||
const todayFormatted = new Date().toISOString().split('T')[0];
|
|
||||||
|
|
||||||
const onSubmit = (event: Event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
onUpdate(birthDate);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<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 onsubmit={(e) => onSubmit(e)} autocomplete="off" id="set-birth-date-form">
|
|
||||||
<div class="my-4 flex flex-col gap-2">
|
|
||||||
<DateInput
|
|
||||||
class="immich-form-input"
|
|
||||||
id="birthDate"
|
|
||||||
name="birthDate"
|
|
||||||
type="date"
|
|
||||||
bind:value={birthDate}
|
|
||||||
max={todayFormatted}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{#snippet stickyBottom()}
|
|
||||||
<Button color="gray" fullwidth onclick={onClose}>{$t('cancel')}</Button>
|
|
||||||
<Button type="submit" fullwidth form="set-birth-date-form">{$t('set')}</Button>
|
|
||||||
{/snippet}
|
|
||||||
</FullScreenModal>
|
|
@ -384,7 +384,6 @@ export enum PersonPageViewMode {
|
|||||||
SELECT_PERSON = 'select-person',
|
SELECT_PERSON = 'select-person',
|
||||||
MERGE_PEOPLE = 'merge-people',
|
MERGE_PEOPLE = 'merge-people',
|
||||||
SUGGEST_MERGE = 'suggest-merge',
|
SUGGEST_MERGE = 'suggest-merge',
|
||||||
BIRTH_DATE = 'birth-date',
|
|
||||||
UNASSIGN_ASSETS = 'unassign-faces',
|
UNASSIGN_ASSETS = 'unassign-faces',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
67
web/src/lib/modals/PersonEditBirthDateModal.svelte
Normal file
67
web/src/lib/modals/PersonEditBirthDateModal.svelte
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
notificationController,
|
||||||
|
NotificationType,
|
||||||
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
import { updatePerson, type PersonResponseDto } from '@immich/sdk';
|
||||||
|
import { Modal, ModalBody, ModalFooter } from '@immich/ui';
|
||||||
|
import { mdiCake } from '@mdi/js';
|
||||||
|
import { t } from 'svelte-i18n';
|
||||||
|
import Button from '../components/elements/buttons/button.svelte';
|
||||||
|
import DateInput from '../components/elements/date-input.svelte';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
person: PersonResponseDto;
|
||||||
|
onClose: (updatedPerson?: PersonResponseDto) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { person, onClose }: Props = $props();
|
||||||
|
let birthDate = $state(person.birthDate ?? '');
|
||||||
|
|
||||||
|
const todayFormatted = new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
|
const handleUpdateBirthDate = async () => {
|
||||||
|
try {
|
||||||
|
const updatedPerson = await updatePerson({
|
||||||
|
id: person.id,
|
||||||
|
personUpdateDto: { birthDate: birthDate.length > 0 ? birthDate : null },
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationController.show({ message: $t('date_of_birth_saved'), type: NotificationType.Info });
|
||||||
|
onClose(updatedPerson);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, $t('errors.unable_to_save_date_of_birth'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal title={$t('set_date_of_birth')} icon={mdiCake} {onClose} size="small">
|
||||||
|
<ModalBody>
|
||||||
|
<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 onsubmit={() => handleUpdateBirthDate()} autocomplete="off" id="set-birth-date-form">
|
||||||
|
<div class="my-4 flex flex-col gap-2">
|
||||||
|
<DateInput
|
||||||
|
class="immich-form-input"
|
||||||
|
id="birthDate"
|
||||||
|
name="birthDate"
|
||||||
|
type="date"
|
||||||
|
bind:value={birthDate}
|
||||||
|
max={todayFormatted}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<div class="flex gap-3 w-full">
|
||||||
|
<Button color="secondary" fullwidth onclick={() => onClose()}>{$t('cancel')}</Button>
|
||||||
|
<Button type="submit" fullwidth form="set-birth-date-form">{$t('set')}</Button>
|
||||||
|
</div>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
@ -9,13 +9,14 @@
|
|||||||
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
||||||
import PeopleInfiniteScroll from '$lib/components/faces-page/people-infinite-scroll.svelte';
|
import PeopleInfiniteScroll from '$lib/components/faces-page/people-infinite-scroll.svelte';
|
||||||
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
|
import SearchPeople from '$lib/components/faces-page/people-search.svelte';
|
||||||
import SetBirthDateModal from '$lib/components/faces-page/set-birth-date-modal.svelte';
|
|
||||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||||
import {
|
import {
|
||||||
notificationController,
|
notificationController,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { ActionQueryParameterValue, AppRoute, QueryParameter, SessionStorageKey } from '$lib/constants';
|
import { ActionQueryParameterValue, AppRoute, QueryParameter, SessionStorageKey } from '$lib/constants';
|
||||||
|
import { modalManager } from '$lib/managers/modal-manager.svelte';
|
||||||
|
import PersonEditBirthDateModal from '$lib/modals/PersonEditBirthDateModal.svelte';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { websocketEvents } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import { handlePromiseError } from '$lib/utils';
|
import { handlePromiseError } from '$lib/utils';
|
||||||
@ -45,7 +46,6 @@
|
|||||||
|
|
||||||
let selectHidden = $state(false);
|
let selectHidden = $state(false);
|
||||||
let searchName = $state('');
|
let searchName = $state('');
|
||||||
let showSetBirthDateModal = $state(false);
|
|
||||||
let showMergeModal = $state(false);
|
let showMergeModal = $state(false);
|
||||||
let newName = $state('');
|
let newName = $state('');
|
||||||
let currentPage = $state(1);
|
let currentPage = $state(1);
|
||||||
@ -53,7 +53,7 @@
|
|||||||
let personMerge1 = $state<PersonResponseDto>();
|
let personMerge1 = $state<PersonResponseDto>();
|
||||||
let personMerge2 = $state<PersonResponseDto>();
|
let personMerge2 = $state<PersonResponseDto>();
|
||||||
let potentialMergePeople: PersonResponseDto[] = $state([]);
|
let potentialMergePeople: PersonResponseDto[] = $state([]);
|
||||||
let edittingPerson: PersonResponseDto | null = $state(null);
|
let editingPerson: PersonResponseDto | null = $state(null);
|
||||||
let searchedPeopleLocal: PersonResponseDto[] = $state([]);
|
let searchedPeopleLocal: PersonResponseDto[] = $state([]);
|
||||||
let innerHeight = $state(0);
|
let innerHeight = $state(0);
|
||||||
let searchPeopleElement = $state<ReturnType<typeof SearchPeople>>();
|
let searchPeopleElement = $state<ReturnType<typeof SearchPeople>>();
|
||||||
@ -135,7 +135,7 @@
|
|||||||
const [personToMerge, personToBeMergedIn] = response;
|
const [personToMerge, personToBeMergedIn] = response;
|
||||||
showMergeModal = false;
|
showMergeModal = false;
|
||||||
|
|
||||||
if (!edittingPerson) {
|
if (!editingPerson) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -155,7 +155,7 @@
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_save_name'));
|
handleError(error, $t('errors.unable_to_save_name'));
|
||||||
}
|
}
|
||||||
if (personToBeMergedIn.name !== newName && edittingPerson.id === personToBeMergedIn.id) {
|
if (personToBeMergedIn.name !== newName && editingPerson.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
|
* If the user merges one of the suggested people into the person he's editing it, it's merging the suggested person AND renames
|
||||||
@ -181,11 +181,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetBirthDate = (detail: PersonResponseDto) => {
|
|
||||||
showSetBirthDateModal = true;
|
|
||||||
edittingPerson = detail;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleHidePerson = async (detail: PersonResponseDto) => {
|
const handleHidePerson = async (detail: PersonResponseDto) => {
|
||||||
try {
|
try {
|
||||||
const updatedPerson = await updatePerson({
|
const updatedPerson = await updatePerson({
|
||||||
@ -234,31 +229,19 @@
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitBirthDateChange = async (value: string) => {
|
const handleChangeBirthDate = async (person: PersonResponseDto) => {
|
||||||
showSetBirthDateModal = false;
|
const updatedPerson = await modalManager.show(PersonEditBirthDateModal, { person });
|
||||||
if (!edittingPerson || value === edittingPerson.birthDate) {
|
|
||||||
|
if (!updatedPerson) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
people = people.map((person: PersonResponseDto) => {
|
||||||
const updatedPerson = await updatePerson({
|
if (person.id === updatedPerson.id) {
|
||||||
id: edittingPerson.id,
|
return updatedPerson;
|
||||||
personUpdateDto: { birthDate: value.length > 0 ? value : null },
|
}
|
||||||
});
|
return person;
|
||||||
|
});
|
||||||
people = people.map((person: PersonResponseDto) => {
|
|
||||||
if (person.id === updatedPerson.id) {
|
|
||||||
return updatedPerson;
|
|
||||||
}
|
|
||||||
return person;
|
|
||||||
});
|
|
||||||
notificationController.show({
|
|
||||||
message: $t('birthdate_saved'),
|
|
||||||
type: NotificationType.Info,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_save_name'));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onResetSearchBar = async () => {
|
const onResetSearchBar = async () => {
|
||||||
@ -274,7 +257,7 @@
|
|||||||
let showPeople = $derived(searchName ? searchedPeopleLocal : visiblePeople);
|
let showPeople = $derived(searchName ? searchedPeopleLocal : visiblePeople);
|
||||||
|
|
||||||
const onNameChangeInputFocus = (person: PersonResponseDto) => {
|
const onNameChangeInputFocus = (person: PersonResponseDto) => {
|
||||||
edittingPerson = person;
|
editingPerson = person;
|
||||||
newName = person.name;
|
newName = person.name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -414,7 +397,7 @@
|
|||||||
>
|
>
|
||||||
<PeopleCard
|
<PeopleCard
|
||||||
{person}
|
{person}
|
||||||
onSetBirthDate={() => handleSetBirthDate(person)}
|
onSetBirthDate={() => handleChangeBirthDate(person)}
|
||||||
onMergePeople={() => handleMergePeople(person)}
|
onMergePeople={() => handleMergePeople(person)}
|
||||||
onHidePerson={() => handleHidePerson(person)}
|
onHidePerson={() => handleHidePerson(person)}
|
||||||
onToggleFavorite={() => handleToggleFavorite(person)}
|
onToggleFavorite={() => handleToggleFavorite(person)}
|
||||||
@ -444,14 +427,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if showSetBirthDateModal}
|
|
||||||
<SetBirthDateModal
|
|
||||||
birthDate={edittingPerson?.birthDate ?? ''}
|
|
||||||
onClose={() => (showSetBirthDateModal = false)}
|
|
||||||
onUpdate={submitBirthDateChange}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</UserPageLayout>
|
</UserPageLayout>
|
||||||
|
|
||||||
{#if selectHidden}
|
{#if selectHidden}
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
import EditNameInput from '$lib/components/faces-page/edit-name-input.svelte';
|
import EditNameInput from '$lib/components/faces-page/edit-name-input.svelte';
|
||||||
import MergeFaceSelector from '$lib/components/faces-page/merge-face-selector.svelte';
|
import MergeFaceSelector from '$lib/components/faces-page/merge-face-selector.svelte';
|
||||||
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
||||||
import SetBirthDateModal from '$lib/components/faces-page/set-birth-date-modal.svelte';
|
|
||||||
import UnMergeFaceSelector from '$lib/components/faces-page/unmerge-face-selector.svelte';
|
import UnMergeFaceSelector from '$lib/components/faces-page/unmerge-face-selector.svelte';
|
||||||
import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte';
|
import AddToAlbum from '$lib/components/photos-page/actions/add-to-album.svelte';
|
||||||
import ArchiveAction from '$lib/components/photos-page/actions/archive-action.svelte';
|
import ArchiveAction from '$lib/components/photos-page/actions/archive-action.svelte';
|
||||||
@ -31,6 +30,8 @@
|
|||||||
notificationController,
|
notificationController,
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { AppRoute, PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
import { AppRoute, PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
||||||
|
import { modalManager } from '$lib/managers/modal-manager.svelte';
|
||||||
|
import PersonEditBirthDateModal from '$lib/modals/PersonEditBirthDateModal.svelte';
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||||
@ -322,27 +323,19 @@
|
|||||||
await changeName();
|
await changeName();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetBirthDate = async (birthDate: string) => {
|
const handleSetBirthDate = async () => {
|
||||||
try {
|
const updatedPerson = await modalManager.show(PersonEditBirthDateModal, { person });
|
||||||
viewMode = PersonPageViewMode.VIEW_ASSETS;
|
|
||||||
person.birthDate = birthDate;
|
|
||||||
|
|
||||||
const updatedPerson = await updatePerson({
|
if (!updatedPerson) {
|
||||||
id: person.id,
|
return;
|
||||||
personUpdateDto: { birthDate: birthDate.length > 0 ? birthDate : null },
|
|
||||||
});
|
|
||||||
|
|
||||||
people = people.map((person: PersonResponseDto) => {
|
|
||||||
if (person.id === updatedPerson.id) {
|
|
||||||
return updatedPerson;
|
|
||||||
}
|
|
||||||
return person;
|
|
||||||
});
|
|
||||||
|
|
||||||
notificationController.show({ message: $t('date_of_birth_saved'), type: NotificationType.Info });
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_save_date_of_birth'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
people = people.map((person: PersonResponseDto) => {
|
||||||
|
if (person.id === updatedPerson.id) {
|
||||||
|
return updatedPerson;
|
||||||
|
}
|
||||||
|
return person;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGoBack = async () => {
|
const handleGoBack = async () => {
|
||||||
@ -389,7 +382,7 @@
|
|||||||
onSelect={handleSelectFeaturePhoto}
|
onSelect={handleSelectFeaturePhoto}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
>
|
>
|
||||||
{#if viewMode === PersonPageViewMode.VIEW_ASSETS || viewMode === PersonPageViewMode.SUGGEST_MERGE || viewMode === PersonPageViewMode.BIRTH_DATE}
|
{#if viewMode === PersonPageViewMode.VIEW_ASSETS || viewMode === PersonPageViewMode.SUGGEST_MERGE}
|
||||||
<!-- Person information block -->
|
<!-- Person information block -->
|
||||||
<div
|
<div
|
||||||
class="relative w-fit p-4 sm:px-6"
|
class="relative w-fit p-4 sm:px-6"
|
||||||
@ -515,14 +508,6 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if viewMode === PersonPageViewMode.BIRTH_DATE}
|
|
||||||
<SetBirthDateModal
|
|
||||||
birthDate={person.birthDate ?? ''}
|
|
||||||
onClose={() => (viewMode = PersonPageViewMode.VIEW_ASSETS)}
|
|
||||||
onUpdate={handleSetBirthDate}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if viewMode === PersonPageViewMode.MERGE_PEOPLE}
|
{#if viewMode === PersonPageViewMode.MERGE_PEOPLE}
|
||||||
<MergeFaceSelector {person} onBack={handleGoBack} onMerge={handleMerge} />
|
<MergeFaceSelector {person} onBack={handleGoBack} onMerge={handleMerge} />
|
||||||
{/if}
|
{/if}
|
||||||
@ -568,7 +553,7 @@
|
|||||||
</ButtonContextMenu>
|
</ButtonContextMenu>
|
||||||
</AssetSelectControlBar>
|
</AssetSelectControlBar>
|
||||||
{:else}
|
{:else}
|
||||||
{#if viewMode === PersonPageViewMode.VIEW_ASSETS || viewMode === PersonPageViewMode.SUGGEST_MERGE || viewMode === PersonPageViewMode.BIRTH_DATE}
|
{#if viewMode === PersonPageViewMode.VIEW_ASSETS || viewMode === PersonPageViewMode.SUGGEST_MERGE}
|
||||||
<ControlAppBar showBackButton backIcon={mdiArrowLeft} onClose={() => goto(previousRoute)}>
|
<ControlAppBar showBackButton backIcon={mdiArrowLeft} onClose={() => goto(previousRoute)}>
|
||||||
{#snippet trailing()}
|
{#snippet trailing()}
|
||||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||||
@ -582,11 +567,7 @@
|
|||||||
icon={person.isHidden ? mdiEyeOutline : mdiEyeOffOutline}
|
icon={person.isHidden ? mdiEyeOutline : mdiEyeOffOutline}
|
||||||
onClick={() => toggleHidePerson()}
|
onClick={() => toggleHidePerson()}
|
||||||
/>
|
/>
|
||||||
<MenuOption
|
<MenuOption text={$t('set_date_of_birth')} icon={mdiCalendarEditOutline} onClick={handleSetBirthDate} />
|
||||||
text={$t('set_date_of_birth')}
|
|
||||||
icon={mdiCalendarEditOutline}
|
|
||||||
onClick={() => (viewMode = PersonPageViewMode.BIRTH_DATE)}
|
|
||||||
/>
|
|
||||||
<MenuOption
|
<MenuOption
|
||||||
text={$t('merge_people')}
|
text={$t('merge_people')}
|
||||||
icon={mdiAccountMultipleCheckOutline}
|
icon={mdiAccountMultipleCheckOutline}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user