refactor: modal manager types ()

This commit is contained in:
Daniel Dietzler 2025-05-08 00:08:19 +02:00 committed by GitHub
parent 5250269fa4
commit 894545aeed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 32 additions and 26 deletions
web/src
lib
routes/admin
jobs-status
user-management

View file

@ -196,7 +196,7 @@
} }
const handleSettingsClick = async () => { const handleSettingsClick = async () => {
const settings = await modalManager.open(MapSettingsModal, { settings: { ...$mapSettings } }); const settings = await modalManager.show(MapSettingsModal, { settings: { ...$mapSettings } });
if (settings) { if (settings) {
const shouldUpdate = !isEqual(omit(settings, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode')); const shouldUpdate = !isEqual(omit(settings, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode'));
$mapSettings = settings; $mapSettings = settings;

View file

@ -132,7 +132,7 @@
variant="ghost" variant="ghost"
size="medium" size="medium"
icon={mdiHelpCircleOutline} icon={mdiHelpCircleOutline}
onclick={() => info && modalManager.open(HelpAndFeedbackModal, { info })} onclick={() => info && modalManager.show(HelpAndFeedbackModal, { info })}
aria-label={$t('support_and_feedback')} aria-label={$t('support_and_feedback')}
/> />
</div> </div>

View file

@ -28,7 +28,7 @@
const { isPurchased } = purchaseStore; const { isPurchased } = purchaseStore;
const openPurchaseModal = async () => { const openPurchaseModal = async () => {
await modalManager.open(PurchaseModal); await modalManager.show(PurchaseModal, {});
showMessage = false; showMessage = false;
}; };

View file

@ -56,7 +56,7 @@
{#if $connected && version} {#if $connected && version}
<button <button
type="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" class="dark:text-immich-gray flex gap-1"
> >
{#if isMain} {#if isMain}

View file

@ -99,7 +99,7 @@
}; };
const handleCreatePartners = async () => { const handleCreatePartners = async () => {
const users = await modalManager.open(PartnerSelectionModal, { user }); const users = await modalManager.show(PartnerSelectionModal, { user });
if (!users) { if (!users) {
return; return;

View file

@ -37,7 +37,7 @@
} }
const handleCreate = async () => { const handleCreate = async () => {
const result = await modalManager.open(ApiKeyModal, { const result = await modalManager.show(ApiKeyModal, {
title: $t('new_api_key'), title: $t('new_api_key'),
apiKey: { name: 'API Key' }, apiKey: { name: 'API Key' },
submitText: $t('create'), submitText: $t('create'),
@ -55,7 +55,7 @@
}, },
}); });
await modalManager.open(ApiKeySecretModal, { secret }); await modalManager.show(ApiKeySecretModal, { secret });
} catch (error) { } catch (error) {
handleError(error, $t('errors.unable_to_create_api_key')); handleError(error, $t('errors.unable_to_create_api_key'));
} finally { } finally {
@ -64,7 +64,7 @@
}; };
const handleUpdate = async (key: ApiKeyResponseDto) => { const handleUpdate = async (key: ApiKeyResponseDto) => {
const result = await modalManager.open(ApiKeyModal, { const result = await modalManager.show(ApiKeyModal, {
title: $t('api_key'), title: $t('api_key'),
submitText: $t('save'), submitText: $t('save'),
apiKey: key, apiKey: key,

View file

@ -1,19 +1,20 @@
import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte'; import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte';
import { mount, unmount, type Component, type ComponentProps } from '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 { class ModalManager {
open<T = { onClose: (data: unknown) => void }, K = OnCloseData<T>>( show<T extends object>(Component: Component<T>, props: ExtendsEmptyObject<Omit<T, 'onClose'>>) {
Component: Component<{ onClose: T }>, return this.open(Component, props).onClose;
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 = {};
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); await unmount(modal);
resolve(data); resolve(data);
}; };
@ -21,15 +22,20 @@ class ModalManager {
modal = mount(Component, { modal = mount(Component, {
target: document.body, target: document.body,
props: { props: {
...((props ?? {}) as T), ...(props as T),
onClose, onClose,
}, },
}); });
}); });
return {
onClose: deferred,
close: () => onClose(),
};
} }
openDialog(options: Omit<ComponentProps<typeof ConfirmDialog>, 'onClose'>) { openDialog(options: Omit<ComponentProps<typeof ConfirmDialog>, 'onClose'>) {
return this.open(ConfirmDialog, options); return this.show(ConfirmDialog, options);
} }
} }

View file

@ -39,7 +39,7 @@
<HStack gap={0}> <HStack gap={0}>
<Button <Button
leadingIcon={mdiPlus} leadingIcon={mdiPlus}
onclick={() => modalManager.open(JobCreateModal)} onclick={() => modalManager.show(JobCreateModal, {})}
size="small" size="small"
variant="ghost" variant="ghost"
color="secondary" color="secondary"

View file

@ -59,15 +59,15 @@
}; };
const handleCreate = async () => { const handleCreate = async () => {
await modalManager.open(UserCreateModal); await modalManager.show(UserCreateModal, {});
await refresh(); await refresh();
}; };
const handleEdit = async (dto: UserAdminResponseDto) => { 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) { switch (result?.action) {
case 'resetPassword': { case 'resetPassword': {
await modalManager.open(PasswordResetSuccess, { newPassword: result.data }); await modalManager.show(PasswordResetSuccess, { newPassword: result.data });
break; break;
} }
case 'update': { case 'update': {
@ -78,14 +78,14 @@
}; };
const handleDelete = async (user: UserAdminResponseDto) => { const handleDelete = async (user: UserAdminResponseDto) => {
const result = await modalManager.open(UserDeleteConfirmModal, { user }); const result = await modalManager.show(UserDeleteConfirmModal, { user });
if (result) { if (result) {
await refresh(); await refresh();
} }
}; };
const handleRestore = async (user: UserAdminResponseDto) => { const handleRestore = async (user: UserAdminResponseDto) => {
const result = await modalManager.open(UserRestoreConfirmModal, { user }); const result = await modalManager.show(UserRestoreConfirmModal, { user });
if (result) { if (result) {
await refresh(); await refresh();
} }