From bce916e4c8a1e1765a25078d0c59663f5b598a07 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 28 May 2024 09:10:43 +0700 Subject: [PATCH] refactor(web): ConfirmDialog and dialogController (#9716) * wrapper * no more callback * refactor: wip * refactor: wip * refactor: wip * pr feedback * fix * pr feedback --- .../admin-page/delete-confirm-dialogue.svelte | 8 ++-- .../admin-page/jobs/jobs-panel.svelte | 32 +++++-------- .../admin-page/restore-dialogue.svelte | 8 ++-- .../settings/auth/auth-settings.svelte | 8 ++-- .../components/album-page/albums-list.svelte | 31 +++++------- .../album-page/share-info-modal.svelte | 10 ++-- .../faces-page/merge-face-selector.svelte | 36 +++++--------- .../components/forms/edit-user-form.svelte | 34 +++++-------- .../actions/remove-from-album.svelte | 41 +++++----------- .../actions/remove-from-shared-link.svelte | 30 ++++++------ .../photos-page/delete-asset-dialog.svelte | 8 ++-- .../shared-components/change-date.svelte | 8 ++-- .../shared-components/change-location.svelte | 8 ++-- .../confirm-dialog.svelte} | 12 ++--- .../dialog/dialog-wrapper.svelte | 9 ++++ .../shared-components/dialog/dialog.ts | 48 +++++++++++++++++++ .../full-screen-modal.svelte | 2 +- .../user-settings-page/device-list.svelte | 48 ++++++++----------- .../partner-settings.svelte | 29 +++++------ .../user-api-key-list.svelte | 28 +++++------ .../[[assetId=id]]/+page.svelte | 39 +++++---------- .../(user)/sharing/sharedlinks/+page.svelte | 32 +++++-------- .../[[assetId=id]]/+page.svelte | 31 +++++------- web/src/routes/+layout.svelte | 2 + .../admin/library-management/+page.svelte | 48 +++++++++++-------- .../routes/admin/user-management/+page.svelte | 8 ++-- 26 files changed, 281 insertions(+), 317 deletions(-) rename web/src/lib/components/shared-components/{confirm-dialogue.svelte => dialog/confirm-dialog.svelte} (76%) create mode 100644 web/src/lib/components/shared-components/dialog/dialog-wrapper.svelte create mode 100644 web/src/lib/components/shared-components/dialog/dialog.ts diff --git a/web/src/lib/components/admin-page/delete-confirm-dialogue.svelte b/web/src/lib/components/admin-page/delete-confirm-dialogue.svelte index 94112a70ac..8436935cae 100644 --- a/web/src/lib/components/admin-page/delete-confirm-dialogue.svelte +++ b/web/src/lib/components/admin-page/delete-confirm-dialogue.svelte @@ -1,5 +1,5 @@ - dispatch('cancel')} + onCancel={() => dispatch('cancel')} disabled={deleteButtonDisabled} > @@ -96,4 +96,4 @@ {/if} - + diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte index c7b177b05a..dd1a3f7ab1 100644 --- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte +++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte @@ -20,9 +20,9 @@ mdiVideo, } from '@mdi/js'; import type { ComponentType } from 'svelte'; - import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte'; import JobTile from './job-tile.svelte'; import StorageMigrationDescription from './storage-migration-description.svelte'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let jobs: AllJobStatusResponseDto; @@ -38,25 +38,24 @@ handleCommand?: (jobId: JobName, jobCommand: JobCommandDto) => Promise; } - let confirmJob: JobName | null = null; - const handleConfirmCommand = async (jobId: JobName, dto: JobCommandDto) => { if (dto.force) { - confirmJob = jobId; + const isConfirmed = await dialogController.show({ + id: 'confirm-reprocess-all-faces', + prompt: 'Are you sure you want to reprocess all faces? This will also clear named people.', + }); + + if (isConfirmed) { + await handleCommand(jobId, { command: JobCommand.Start, force: true }); + return; + } + return; } await handleCommand(jobId, dto); }; - const onConfirm = async () => { - if (!confirmJob) { - return; - } - await handleCommand(confirmJob, { command: JobCommand.Start, force: true }); - confirmJob = null; - }; - $: jobDetails = >>{ [JobName.ThumbnailGeneration]: { icon: mdiFileJpgBox, @@ -152,15 +151,6 @@ } -{#if confirmJob} - (confirmJob = null)} - /> -{/if} -
{#each jobList as [jobName, { title, subtitle, disabled, allText, missingText, allowForceCommand, icon, component, handleCommand: handleCommandOverride }]} {@const { jobCounts, queueStatus } = jobs[jobName]} diff --git a/web/src/lib/components/admin-page/restore-dialogue.svelte b/web/src/lib/components/admin-page/restore-dialogue.svelte index 5b308d2f51..3c48b13970 100644 --- a/web/src/lib/components/admin-page/restore-dialogue.svelte +++ b/web/src/lib/components/admin-page/restore-dialogue.svelte @@ -1,5 +1,5 @@ - dispatch('cancel')} + onCancel={() => dispatch('cancel')} >

{user.name}'s account will be restored.

-
+ diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte index d9c879faff..b5862eda70 100644 --- a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte +++ b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte @@ -1,5 +1,5 @@ {#if isConfirmOpen} - (isConfirmOpen = false)} + onCancel={() => (isConfirmOpen = false)} onConfirm={() => handleSave(true)} > @@ -64,7 +64,7 @@

-
+ {/if}
diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 0d3c287a22..eb7530973d 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -5,7 +5,6 @@ import { mdiDeleteOutline, mdiShareVariantOutline, mdiFolderDownloadOutline, mdiRenameOutline } from '@mdi/js'; import Icon from '$lib/components/elements/icon.svelte'; import EditAlbumForm from '$lib/components/forms/edit-album-form.svelte'; - import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte'; import { NotificationType, @@ -33,6 +32,7 @@ } from '$lib/stores/preferences.store'; import { goto } from '$app/navigation'; import { AppRoute } from '$lib/constants'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let ownedAlbums: AlbumResponseDto[] = []; export let sharedAlbums: AlbumResponseDto[] = []; @@ -275,9 +275,10 @@ sharedAlbums = sharedAlbums.filter(({ id }) => id !== albumToDelete.id); }; - const setAlbumToDelete = () => { + const setAlbumToDelete = async () => { albumToDelete = contextMenuTargetAlbum ?? null; closeAlbumContextMenu(); + await deleteSelectedAlbum(); }; const handleEdit = (album: AlbumResponseDto) => { @@ -289,6 +290,16 @@ if (!albumToDelete) { return; } + + const isConfirmed = await dialogController.show({ + id: 'delete-album', + prompt: `Are you sure you want to delete the album ${albumToDelete.albumName}?\nIf this album is shared, other users will not be able to access it anymore.`, + }); + + if (!isConfirmed) { + return; + } + try { await handleDeleteAlbum(albumToDelete); } catch { @@ -456,20 +467,4 @@ /> {/if} {/if} - - - {#if albumToDelete} - (albumToDelete = null)} - > - -

Are you sure you want to delete the album {albumToDelete.albumName}?

-

If this album is shared, other users will not be able to access it anymore.

-
-
- {/if} {/if} diff --git a/web/src/lib/components/album-page/share-info-modal.svelte b/web/src/lib/components/album-page/share-info-modal.svelte index 790d4c1bc5..dba4cfdf8b 100644 --- a/web/src/lib/components/album-page/share-info-modal.svelte +++ b/web/src/lib/components/album-page/share-info-modal.svelte @@ -12,7 +12,7 @@ import { getContextMenuPosition } from '../../utils/context-menu'; import { handleError } from '../../utils/handle-error'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; - import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte'; + import ConfirmDialog from '../shared-components/dialog/confirm-dialog.svelte'; import ContextMenu from '../shared-components/context-menu/context-menu.svelte'; import MenuOption from '../shared-components/context-menu/menu-option.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; @@ -155,23 +155,23 @@ {/if} {#if selectedRemoveUser && selectedRemoveUser?.id === currentUser?.id} - (selectedRemoveUser = null)} + onCancel={() => (selectedRemoveUser = null)} /> {/if} {#if selectedRemoveUser && selectedRemoveUser?.id !== currentUser?.id} - (selectedRemoveUser = null)} + onCancel={() => (selectedRemoveUser = null)} /> {/if} diff --git a/web/src/lib/components/faces-page/merge-face-selector.svelte b/web/src/lib/components/faces-page/merge-face-selector.svelte index f92bff37aa..f2664c00c4 100644 --- a/web/src/lib/components/faces-page/merge-face-selector.svelte +++ b/web/src/lib/components/faces-page/merge-face-selector.svelte @@ -12,17 +12,16 @@ import { fly } from 'svelte/transition'; import Button from '../elements/buttons/button.svelte'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; - import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte'; import ControlAppBar from '../shared-components/control-app-bar.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; import FaceThumbnail from './face-thumbnail.svelte'; import PeopleList from './people-list.svelte'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let person: PersonResponseDto; let people: PersonResponseDto[] = []; let selectedPeople: PersonResponseDto[] = []; let screenHeight: number; - let isShowConfirmation = false; let dispatch = createEventDispatcher<{ back: void; @@ -65,6 +64,15 @@ }; const handleMerge = async () => { + const isConfirm = await dialogController.show({ + id: 'merge-people', + prompt: 'Do you want to merge these people?', + }); + + if (!isConfirm) { + return; + } + try { let results = await mergePerson({ id: person.id, @@ -79,8 +87,6 @@ dispatch('merge', mergedPerson); } catch (error) { handleError(error, 'Cannot merge people'); - } finally { - isShowConfirmation = false; } }; @@ -101,13 +107,7 @@
- @@ -150,19 +150,5 @@ onSelect(detail)} /> - - {#if isShowConfirmation} - (isShowConfirmation = false)} - > - -

Are you sure you want merge these people ?

-
- {/if} diff --git a/web/src/lib/components/forms/edit-user-form.svelte b/web/src/lib/components/forms/edit-user-form.svelte index 9222892701..20d1ad0fa1 100644 --- a/web/src/lib/components/forms/edit-user-form.svelte +++ b/web/src/lib/components/forms/edit-user-form.svelte @@ -1,5 +1,4 @@ {#if menuItem} - (isShowConfirmation = true)} /> + {:else} - (isShowConfirmation = true)} /> -{/if} - -{#if isShowConfirmation} - (isShowConfirmation = false)} - > - -

- Are you sure you want to remove - {#if getAssets().size > 1} - these {getAssets().size} assets - {:else} - this asset - {/if} - from the album? -

-
-
+ {/if} diff --git a/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte b/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte index 0521f1e8c5..c9c8a3c736 100644 --- a/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte +++ b/web/src/lib/components/photos-page/actions/remove-from-shared-link.svelte @@ -1,20 +1,29 @@ - (removing = true)} icon={mdiDeleteOutline} /> - -{#if removing} - handleRemove()} - onClose={() => (removing = false)} - /> -{/if} + diff --git a/web/src/lib/components/photos-page/delete-asset-dialog.svelte b/web/src/lib/components/photos-page/delete-asset-dialog.svelte index 22a1855ab4..58efb7302a 100644 --- a/web/src/lib/components/photos-page/delete-asset-dialog.svelte +++ b/web/src/lib/components/photos-page/delete-asset-dialog.svelte @@ -1,6 +1,6 @@ - dispatch('cancel')} + onCancel={() => dispatch('cancel')} >

@@ -44,4 +44,4 @@

-
+ diff --git a/web/src/lib/components/shared-components/change-date.svelte b/web/src/lib/components/shared-components/change-date.svelte index 0e53f0b261..d434ecd4eb 100644 --- a/web/src/lib/components/shared-components/change-date.svelte +++ b/web/src/lib/components/shared-components/change-date.svelte @@ -1,7 +1,7 @@ -
@@ -84,4 +84,4 @@ />
-
+ diff --git a/web/src/lib/components/shared-components/change-location.svelte b/web/src/lib/components/shared-components/change-location.svelte index b5961741b9..52fbcbed1f 100644 --- a/web/src/lib/components/shared-components/change-location.svelte +++ b/web/src/lib/components/shared-components/change-location.svelte @@ -1,6 +1,6 @@ -
- + diff --git a/web/src/lib/components/shared-components/confirm-dialogue.svelte b/web/src/lib/components/shared-components/dialog/confirm-dialog.svelte similarity index 76% rename from web/src/lib/components/shared-components/confirm-dialogue.svelte rename to web/src/lib/components/shared-components/dialog/confirm-dialog.svelte index c84419f622..b16dfb2bf5 100644 --- a/web/src/lib/components/shared-components/confirm-dialogue.svelte +++ b/web/src/lib/components/shared-components/dialog/confirm-dialog.svelte @@ -1,9 +1,9 @@ - +

{prompt}

@@ -33,7 +33,7 @@ {#if !hideCancelButton} - {/if} diff --git a/web/src/lib/components/shared-components/dialog/dialog-wrapper.svelte b/web/src/lib/components/shared-components/dialog/dialog-wrapper.svelte new file mode 100644 index 0000000000..65f194291b --- /dev/null +++ b/web/src/lib/components/shared-components/dialog/dialog-wrapper.svelte @@ -0,0 +1,9 @@ + + +{#if $dialog} + +{/if} diff --git a/web/src/lib/components/shared-components/dialog/dialog.ts b/web/src/lib/components/shared-components/dialog/dialog.ts new file mode 100644 index 0000000000..31517adc1e --- /dev/null +++ b/web/src/lib/components/shared-components/dialog/dialog.ts @@ -0,0 +1,48 @@ +import { writable } from 'svelte/store'; + +type DialogActions = { + onConfirm: () => void; + onCancel: () => void; +}; + +type DialogOptions = { + id?: string; + title?: string; + prompt?: string; + confirmText?: string; + cancelText?: string; + hideCancelButton?: boolean; + disable?: boolean; + width?: 'wide' | 'narrow' | undefined; +}; + +export type Dialog = DialogOptions & DialogActions; + +function createDialogWrapper() { + const dialog = writable(); + + async function show(options: DialogOptions) { + return new Promise((resolve) => { + const newDialog: Dialog = { + ...options, + onConfirm: () => { + dialog.set(undefined); + resolve(true); + }, + onCancel: () => { + dialog.set(undefined); + resolve(false); + }, + }; + + dialog.set(newDialog); + }); + } + + return { + dialog, + show, + }; +} + +export const dialogController = createDialogWrapper(); diff --git a/web/src/lib/components/shared-components/full-screen-modal.svelte b/web/src/lib/components/shared-components/full-screen-modal.svelte index afc465a32d..6cc2af1c45 100644 --- a/web/src/lib/components/shared-components/full-screen-modal.svelte +++ b/web/src/lib/components/shared-components/full-screen-modal.svelte @@ -47,7 +47,7 @@ role="presentation" in:fade={{ duration: 100 }} out:fade={{ duration: 100 }} - class="fixed left-0 top-0 z-[9990] flex h-screen w-screen place-content-center place-items-center bg-black/40" + class="fixed left-0 top-0 z-[9999] flex h-screen w-screen place-content-center place-items-center bg-black/40" on:keydown={(event) => { event.stopPropagation(); }} diff --git a/web/src/lib/components/user-settings-page/device-list.svelte b/web/src/lib/components/user-settings-page/device-list.svelte index b7bb05657f..923ea16dff 100644 --- a/web/src/lib/components/user-settings-page/device-list.svelte +++ b/web/src/lib/components/user-settings-page/device-list.svelte @@ -2,36 +2,47 @@ import { deleteAllSessions, deleteSession, getSessions, type SessionResponseDto } from '@immich/sdk'; import { handleError } from '../../utils/handle-error'; import Button from '../elements/buttons/button.svelte'; - import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte'; import { notificationController, NotificationType } from '../shared-components/notification/notification'; import DeviceCard from './device-card.svelte'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let devices: SessionResponseDto[]; - let deleteDevice: SessionResponseDto | null = null; - let deleteAll = false; const refresh = () => getSessions().then((_devices) => (devices = _devices)); $: currentDevice = devices.find((device) => device.current); $: otherDevices = devices.filter((device) => !device.current); - const handleDelete = async () => { - if (!deleteDevice) { + const handleDelete = async (device: SessionResponseDto) => { + const isConfirmed = await dialogController.show({ + id: 'log-out-device', + prompt: 'Are you sure you want to log out this device?', + }); + + if (!isConfirmed) { return; } try { - await deleteSession({ id: deleteDevice.id }); + await deleteSession({ id: device.id }); notificationController.show({ message: `Logged out device`, type: NotificationType.Info }); } catch (error) { handleError(error, 'Unable to log out device'); } finally { await refresh(); - deleteDevice = null; } }; const handleDeleteAll = async () => { + const isConfirmed = await dialogController.show({ + id: 'log-out-all-devices', + prompt: 'Are you sure you want to log out all devices?', + }); + + if (!isConfirmed) { + return; + } + try { await deleteAllSessions(); notificationController.show({ @@ -42,29 +53,10 @@ handleError(error, 'Unable to log out all devices'); } finally { await refresh(); - deleteAll = false; } }; -{#if deleteDevice} - handleDelete()} - onClose={() => (deleteDevice = null)} - /> -{/if} - -{#if deleteAll} - handleDeleteAll()} - onClose={() => (deleteAll = false)} - /> -{/if} -
{#if currentDevice}
@@ -76,7 +68,7 @@

OTHER DEVICES

{#each otherDevices as device, index} - (deleteDevice = device)} /> + handleDelete(device)} /> {#if index !== otherDevices.length - 1}
{/if} @@ -84,7 +76,7 @@

LOG OUT ALL DEVICES

- +
{/if}
diff --git a/web/src/lib/components/user-settings-page/partner-settings.svelte b/web/src/lib/components/user-settings-page/partner-settings.svelte index 8a34b3062a..e9c0119367 100644 --- a/web/src/lib/components/user-settings-page/partner-settings.svelte +++ b/web/src/lib/components/user-settings-page/partner-settings.svelte @@ -13,10 +13,10 @@ import Button from '../elements/buttons/button.svelte'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; import Icon from '../elements/icon.svelte'; - import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import UserAvatar from '$lib/components/shared-components/user-avatar.svelte'; import PartnerSelectionModal from './partner-selection-modal.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; interface PartnerSharing { user: UserResponseDto; @@ -28,7 +28,7 @@ export let user: UserResponseDto; let createPartnerFlag = false; - let removePartnerDto: PartnerResponseDto | null = null; + // let removePartnerDto: PartnerResponseDto | null = null; let partners: Array = []; onMount(async () => { @@ -75,14 +75,19 @@ } }; - const handleRemovePartner = async () => { - if (!removePartnerDto) { + const handleRemovePartner = async (partner: PartnerResponseDto) => { + const isConfirmed = await dialogController.show({ + id: 'remove-partner', + title: 'Stop sharing your photos?', + prompt: `${partner.name} will no longer be able to access your photos.`, + }); + + if (!isConfirmed) { return; } try { - await removePartner({ id: removePartnerDto.id }); - removePartnerDto = null; + await removePartner({ id: partner.id }); await refreshPartners(); } catch (error) { handleError(error, 'Unable to remove partner'); @@ -133,7 +138,7 @@ {#if partner.sharedByMe} (removePartnerDto = partner.user)} + on:click={() => handleRemovePartner(partner.user)} icon={mdiClose} size={'16'} title="Stop sharing your photos with this user" @@ -186,13 +191,3 @@ on:add-users={(event) => handleCreatePartners(event.detail)} /> {/if} - -{#if removePartnerDto} - (removePartnerDto = null)} - onConfirm={() => handleRemovePartner()} - /> -{/if} diff --git a/web/src/lib/components/user-settings-page/user-api-key-list.svelte b/web/src/lib/components/user-settings-page/user-api-key-list.svelte index b13d2a9019..11242cf95c 100644 --- a/web/src/lib/components/user-settings-page/user-api-key-list.svelte +++ b/web/src/lib/components/user-settings-page/user-api-key-list.svelte @@ -7,15 +7,14 @@ import Button from '../elements/buttons/button.svelte'; import APIKeyForm from '../forms/api-key-form.svelte'; import APIKeySecret from '../forms/api-key-secret.svelte'; - import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let keys: ApiKeyResponseDto[]; let newKey: Partial | null = null; let editKey: ApiKeyResponseDto | null = null; - let deleteKey: ApiKeyResponseDto | null = null; let secret = ''; const format: Intl.DateTimeFormatOptions = { @@ -59,22 +58,26 @@ } }; - const handleDelete = async () => { - if (!deleteKey) { + const handleDelete = async (key: ApiKeyResponseDto) => { + const isConfirmed = await dialogController.show({ + id: 'delete-api-key', + prompt: 'Are you sure you want to delete this API key?', + }); + + if (!isConfirmed) { return; } try { - await deleteApiKey({ id: deleteKey.id }); + await deleteApiKey({ id: key.id }); notificationController.show({ - message: `Removed API Key: ${deleteKey.name}`, + message: `Removed API Key: ${key.name}`, type: NotificationType.Info, }); } catch (error) { handleError(error, 'Unable to remove API Key'); } finally { await refreshKeys(); - deleteKey = null; } }; @@ -103,15 +106,6 @@ /> {/if} -{#if deleteKey} - handleDelete()} - onClose={() => (deleteKey = null)} - /> -{/if} -
@@ -156,7 +150,7 @@ icon={mdiTrashCanOutline} title="Delete key" size="16" - on:click={() => (deleteKey = key)} + on:click={() => handleDelete(key)} /> diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index cb6bac42b2..f51d6ed4cb 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -24,7 +24,6 @@ import AssetGrid from '$lib/components/photos-page/asset-grid.svelte'; import AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; - import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte'; import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; @@ -81,6 +80,7 @@ } from '@mdi/js'; import { fly } from 'svelte/transition'; import type { PageData } from './$types'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let data: PageData; @@ -98,7 +98,6 @@ } enum ViewMode { - CONFIRM_DELETE = 'confirm-delete', LINK_SHARING = 'link-sharing', SELECT_USERS = 'select-users', SELECT_THUMBNAIL = 'select-thumbnail', @@ -248,10 +247,7 @@ viewMode = ViewMode.VIEW; return; } - if (viewMode === ViewMode.CONFIRM_DELETE) { - viewMode = ViewMode.VIEW; - return; - } + if (viewMode === ViewMode.SELECT_ASSETS) { handleCloseSelectAssets(); return; @@ -353,6 +349,16 @@ }; const handleRemoveAlbum = async () => { + const isConfirmed = await dialogController.show({ + id: 'remove-album', + prompt: `Are you sure you want to delete the album ${album.albumName}?\nIf this album is shared, other users will not be able to access it anymore.`, + }); + + if (!isConfirmed) { + viewMode = ViewMode.VIEW; + return; + } + try { await deleteAlbum({ id: album.id }); await goto(backUrl); @@ -471,11 +477,7 @@ on:click={() => (viewMode = ViewMode.SELECT_THUMBNAIL)} /> (viewMode = ViewMode.OPTIONS)} /> - (viewMode = ViewMode.CONFIRM_DELETE)} - /> + handleRemoveAlbum()} /> {/if}
@@ -697,21 +699,6 @@ /> {/if} -{#if viewMode === ViewMode.CONFIRM_DELETE} - (viewMode = ViewMode.VIEW)} - > - -

Are you sure you want to delete the album {album.albumName}?

-

If this album is shared, other users will not be able to access it anymore.

-
-
-{/if} - {#if viewMode === ViewMode.OPTIONS && $user} import { goto } from '$app/navigation'; - import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; + import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte'; import { @@ -15,12 +15,11 @@ import { getAllSharedLinks, removeSharedLink, type SharedLinkResponseDto } from '@immich/sdk'; import { mdiArrowLeft } from '@mdi/js'; import { onMount } from 'svelte'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; let sharedLinks: SharedLinkResponseDto[] = []; let editSharedLink: SharedLinkResponseDto | null = null; - let deleteLinkId: string | null = null; - const refresh = async () => { sharedLinks = await getAllSharedLinks(); }; @@ -29,15 +28,21 @@ await refresh(); }); - const handleDeleteLink = async () => { - if (!deleteLinkId) { + const handleDeleteLink = async (id: string) => { + const isConfirmed = await dialogController.show({ + id: 'delete-shared-link', + title: 'Delete shared link', + prompt: 'Are you sure you want to delete this shared link?', + confirmText: 'Delete', + }); + + if (!isConfirmed) { return; } try { - await removeSharedLink({ id: deleteLinkId }); + await removeSharedLink({ id }); notificationController.show({ message: 'Deleted shared link', type: NotificationType.Info }); - deleteLinkId = null; await refresh(); } catch (error) { handleError(error, 'Unable to delete shared link'); @@ -73,7 +78,7 @@ {#each sharedLinks as link (link.id)} (deleteLinkId = link.id)} + on:delete={() => handleDeleteLink(link.id)} on:edit={() => (editSharedLink = link)} on:copy={() => handleCopyLink(link.key)} /> @@ -85,14 +90,3 @@ {#if editSharedLink} {/if} - -{#if deleteLinkId} - handleDeleteLink()} - onClose={() => (deleteLinkId = null)} - /> -{/if} diff --git a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte index 2728f25b02..b7481573b9 100644 --- a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -9,7 +9,6 @@ import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte'; import AssetGrid from '$lib/components/photos-page/asset-grid.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; - import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; import { NotificationType, @@ -24,6 +23,7 @@ import { mdiDeleteOutline, mdiHistory } from '@mdi/js'; import type { PageData } from './$types'; import { handlePromiseError } from '$lib/utils'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let data: PageData; @@ -32,10 +32,18 @@ const assetStore = new AssetStore({ isTrashed: true }); const assetInteractionStore = createAssetInteractionStore(); const { isMultiSelectState, selectedAssets } = assetInteractionStore; - let isShowEmptyConfirmation = false; const handleEmptyTrash = async () => { - isShowEmptyConfirmation = false; + const isConfirmed = await dialogController.show({ + id: 'empty-trash', + prompt: + 'Are you sure you want to empty the trash? This will remove all the assets in trash permanently from Immich.\nYou cannot undo this action!', + }); + + if (!isConfirmed) { + return; + } + try { await emptyTrash(); @@ -87,7 +95,7 @@ Restore all
- (isShowEmptyConfirmation = true)}> + handleEmptyTrash()}>
Empty trash @@ -103,18 +111,3 @@ {/if} - -{#if isShowEmptyConfirmation} - (isShowEmptyConfirmation = false)} - > - -

Are you sure you want to empty the trash? This will remove all the assets in trash permanently from Immich.

-

You cannot undo this action!

-
-
-{/if} diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 6fb98a4576..1303048645 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -18,6 +18,7 @@ import { onDestroy, onMount } from 'svelte'; import '../app.css'; import { isAssetViewerRoute, isSharedLinkRoute } from '$lib/utils/navigation'; + import DialogWrapper from '$lib/components/shared-components/dialog/dialog-wrapper.svelte'; let showNavigationLoadingBar = false; @@ -121,6 +122,7 @@ + {#if $user?.isAdmin} diff --git a/web/src/routes/admin/library-management/+page.svelte b/web/src/routes/admin/library-management/+page.svelte index fe5cb2686f..714c4f3710 100644 --- a/web/src/routes/admin/library-management/+page.svelte +++ b/web/src/routes/admin/library-management/+page.svelte @@ -6,7 +6,6 @@ import LibraryScanSettingsForm from '$lib/components/forms/library-scan-settings-form.svelte'; import LibraryUserPickerForm from '$lib/components/forms/library-user-picker-form.svelte'; import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; - import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte'; import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte'; @@ -37,6 +36,7 @@ import LinkButton from '../../../lib/components/elements/buttons/link-button.svelte'; import type { PageData } from './$types'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let data: PageData; @@ -282,29 +282,39 @@ const onDeleteLibraryClicked = async () => { closeAll(); - if (selectedLibrary && confirm(`Are you sure you want to delete ${selectedLibrary.name} library?`) == true) { - await refreshStats(selectedLibraryIndex); - if (totalCount[selectedLibraryIndex] > 0) { - deleteAssetCount = totalCount[selectedLibraryIndex]; - confirmDeleteLibrary = selectedLibrary; - } else { - deletedLibrary = selectedLibrary; - await handleDelete(); + if (!selectedLibrary) { + return; + } + + const isConfirmedLibrary = await dialogController.show({ + id: 'delete-library', + prompt: `Are you sure you want to delete ${selectedLibrary.name} library?`, + }); + + if (!isConfirmedLibrary) { + return; + } + + await refreshStats(selectedLibraryIndex); + if (totalCount[selectedLibraryIndex] > 0) { + deleteAssetCount = totalCount[selectedLibraryIndex]; + + const isConfirmedLibraryAssetCount = await dialogController.show({ + id: 'delete-library-assets', + prompt: `Are you sure you want to delete this library? This will delete all ${deleteAssetCount} contained assets from Immich and cannot be undone. Files will remain on disk.`, + }); + + if (!isConfirmedLibraryAssetCount) { + return; } + await handleDelete(); + } else { + deletedLibrary = selectedLibrary; + await handleDelete(); } }; -{#if confirmDeleteLibrary} - (confirmDeleteLibrary = null)} - /> -{/if} - {#if toCreateLibrary} handleCreate(detail.ownerId)} diff --git a/web/src/routes/admin/user-management/+page.svelte b/web/src/routes/admin/user-management/+page.svelte index 0875f819b9..d45c0be6c2 100644 --- a/web/src/routes/admin/user-management/+page.svelte +++ b/web/src/routes/admin/user-management/+page.svelte @@ -1,5 +1,6 @@