refactor: modal manager types (#18150)

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

View File

@ -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;

View File

@ -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>

View File

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

View File

@ -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}

View File

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

View File

@ -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,

View File

@ -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);
}
}

View File

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

View File

@ -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();
}