diff --git a/web/src/lib/components/shared-components/map/map.svelte b/web/src/lib/components/shared-components/map/map.svelte index 5742799409..bf66018eb6 100644 --- a/web/src/lib/components/shared-components/map/map.svelte +++ b/web/src/lib/components/shared-components/map/map.svelte @@ -196,7 +196,7 @@ } const handleSettingsClick = async () => { - const settings = await modalManager.open(MapSettingsModal, { settings: { ...$mapSettings } }); + const settings = await modalManager.show(MapSettingsModal, { settings: { ...$mapSettings } }); if (settings) { const shouldUpdate = !isEqual(omit(settings, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode')); $mapSettings = settings; diff --git a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte index 915b041d4e..8cf2fb9dfc 100644 --- a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte @@ -132,7 +132,7 @@ variant="ghost" size="medium" icon={mdiHelpCircleOutline} - onclick={() => info && modalManager.open(HelpAndFeedbackModal, { info })} + onclick={() => info && modalManager.show(HelpAndFeedbackModal, { info })} aria-label={$t('support_and_feedback')} /> </div> diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index fe48a68009..aafb430046 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -28,7 +28,7 @@ const { isPurchased } = purchaseStore; const openPurchaseModal = async () => { - await modalManager.open(PurchaseModal); + await modalManager.show(PurchaseModal, {}); showMessage = false; }; diff --git a/web/src/lib/components/shared-components/side-bar/server-status.svelte b/web/src/lib/components/shared-components/side-bar/server-status.svelte index 665decc44f..0a9f3d9d8c 100644 --- a/web/src/lib/components/shared-components/side-bar/server-status.svelte +++ b/web/src/lib/components/shared-components/side-bar/server-status.svelte @@ -56,7 +56,7 @@ {#if $connected && version} <button type="button" - onclick={() => info && modalManager.open(ServerAboutModal, { versions, info })} + onclick={() => info && modalManager.show(ServerAboutModal, { versions, info })} class="dark:text-immich-gray flex gap-1" > {#if isMain} 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 9d187b6adf..b18390e28f 100644 --- a/web/src/lib/components/user-settings-page/partner-settings.svelte +++ b/web/src/lib/components/user-settings-page/partner-settings.svelte @@ -99,7 +99,7 @@ }; const handleCreatePartners = async () => { - const users = await modalManager.open(PartnerSelectionModal, { user }); + const users = await modalManager.show(PartnerSelectionModal, { user }); if (!users) { return; 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 823f5a0a96..ab9ffb294c 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 @@ -37,7 +37,7 @@ } const handleCreate = async () => { - const result = await modalManager.open(ApiKeyModal, { + const result = await modalManager.show(ApiKeyModal, { title: $t('new_api_key'), apiKey: { name: 'API Key' }, submitText: $t('create'), @@ -55,7 +55,7 @@ }, }); - await modalManager.open(ApiKeySecretModal, { secret }); + await modalManager.show(ApiKeySecretModal, { secret }); } catch (error) { handleError(error, $t('errors.unable_to_create_api_key')); } finally { @@ -64,7 +64,7 @@ }; const handleUpdate = async (key: ApiKeyResponseDto) => { - const result = await modalManager.open(ApiKeyModal, { + const result = await modalManager.show(ApiKeyModal, { title: $t('api_key'), submitText: $t('save'), apiKey: key, diff --git a/web/src/lib/managers/modal-manager.svelte.ts b/web/src/lib/managers/modal-manager.svelte.ts index 055df14502..12f9224018 100644 --- a/web/src/lib/managers/modal-manager.svelte.ts +++ b/web/src/lib/managers/modal-manager.svelte.ts @@ -1,19 +1,20 @@ import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte'; import { mount, unmount, type Component, type ComponentProps } from 'svelte'; -type OnCloseData<T> = T extends { onClose: (data: infer R) => void | Promise<void> } ? R : never; +type OnCloseData<T> = T extends { onClose: (data?: infer R) => void } ? R : never; +type ExtendsEmptyObject<T> = keyof T extends never ? Record<string, never> : T; class ModalManager { - open<T = { onClose: (data: unknown) => void }, K = OnCloseData<T>>( - Component: Component<{ onClose: T }>, - props?: Record<string, never>, - ): Promise<K>; - open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props: Omit<T, 'onClose'>): Promise<K>; - open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props?: Omit<T, 'onClose'>) { - return new Promise<K>((resolve) => { - let modal: object = {}; + show<T extends object>(Component: Component<T>, props: ExtendsEmptyObject<Omit<T, 'onClose'>>) { + return this.open(Component, props).onClose; + } - const onClose = async (data: K) => { + open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props: ExtendsEmptyObject<Omit<T, 'onClose'>>) { + let modal: object = {}; + let onClose: () => Promise<void>; + + const deferred = new Promise<K | undefined>((resolve) => { + onClose = async (data?: K) => { await unmount(modal); resolve(data); }; @@ -21,15 +22,20 @@ class ModalManager { modal = mount(Component, { target: document.body, props: { - ...((props ?? {}) as T), + ...(props as T), onClose, }, }); }); + + return { + onClose: deferred, + close: () => onClose(), + }; } openDialog(options: Omit<ComponentProps<typeof ConfirmDialog>, 'onClose'>) { - return this.open(ConfirmDialog, options); + return this.show(ConfirmDialog, options); } } diff --git a/web/src/routes/admin/jobs-status/+page.svelte b/web/src/routes/admin/jobs-status/+page.svelte index c5327a4271..6636d748cf 100644 --- a/web/src/routes/admin/jobs-status/+page.svelte +++ b/web/src/routes/admin/jobs-status/+page.svelte @@ -39,7 +39,7 @@ <HStack gap={0}> <Button leadingIcon={mdiPlus} - onclick={() => modalManager.open(JobCreateModal)} + onclick={() => modalManager.show(JobCreateModal, {})} size="small" variant="ghost" color="secondary" diff --git a/web/src/routes/admin/user-management/+page.svelte b/web/src/routes/admin/user-management/+page.svelte index 8ac3793046..dcf82bd10d 100644 --- a/web/src/routes/admin/user-management/+page.svelte +++ b/web/src/routes/admin/user-management/+page.svelte @@ -59,15 +59,15 @@ }; const handleCreate = async () => { - await modalManager.open(UserCreateModal); + await modalManager.show(UserCreateModal, {}); await refresh(); }; const handleEdit = async (dto: UserAdminResponseDto) => { - const result = await modalManager.open(UserEditModal, { user: dto, canResetPassword: dto.id !== $user.id }); + const result = await modalManager.show(UserEditModal, { user: dto, canResetPassword: dto.id !== $user.id }); switch (result?.action) { case 'resetPassword': { - await modalManager.open(PasswordResetSuccess, { newPassword: result.data }); + await modalManager.show(PasswordResetSuccess, { newPassword: result.data }); break; } case 'update': { @@ -78,14 +78,14 @@ }; const handleDelete = async (user: UserAdminResponseDto) => { - const result = await modalManager.open(UserDeleteConfirmModal, { user }); + const result = await modalManager.show(UserDeleteConfirmModal, { user }); if (result) { await refresh(); } }; const handleRestore = async (user: UserAdminResponseDto) => { - const result = await modalManager.open(UserRestoreConfirmModal, { user }); + const result = await modalManager.show(UserRestoreConfirmModal, { user }); if (result) { await refresh(); }