From dd393c83461a2d396bc2b9759fb3210059a76735 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:49:46 +0100 Subject: [PATCH] feat(web): event handler component (#23763) --- web/src/lib/components/OnEvents.svelte | 33 +++++++++++++++++++ web/src/lib/managers/auth-manager.svelte.ts | 2 +- web/src/lib/managers/event-manager.svelte.ts | 18 +++++----- .../lib/managers/language-manager.svelte.ts | 4 +-- web/src/lib/managers/theme-manager.svelte.ts | 4 +-- web/src/lib/managers/upload-manager.svelte.ts | 2 +- web/src/lib/stores/folders.svelte.ts | 2 +- web/src/lib/stores/memory.store.svelte.ts | 2 +- .../lib/stores/notification-manager.svelte.ts | 4 +-- web/src/lib/stores/search.svelte.ts | 2 +- web/src/lib/stores/user.store.ts | 2 +- web/src/lib/stores/user.svelte.ts | 2 +- web/src/routes/+layout.svelte | 2 +- web/src/routes/auth/login/+page.svelte | 2 +- 14 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 web/src/lib/components/OnEvents.svelte diff --git a/web/src/lib/components/OnEvents.svelte b/web/src/lib/components/OnEvents.svelte new file mode 100644 index 0000000000..3933f4df7b --- /dev/null +++ b/web/src/lib/components/OnEvents.svelte @@ -0,0 +1,33 @@ + diff --git a/web/src/lib/managers/auth-manager.svelte.ts b/web/src/lib/managers/auth-manager.svelte.ts index 0128892c3f..515fde0996 100644 --- a/web/src/lib/managers/auth-manager.svelte.ts +++ b/web/src/lib/managers/auth-manager.svelte.ts @@ -30,7 +30,7 @@ class AuthManager { globalThis.location.href = redirectUri; } } finally { - eventManager.emit('auth.logout'); + eventManager.emit('AuthLogout'); } } } diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts index f8e39411cf..7c7717a4d4 100644 --- a/web/src/lib/managers/event-manager.svelte.ts +++ b/web/src/lib/managers/event-manager.svelte.ts @@ -1,6 +1,15 @@ import type { ThemeSetting } from '$lib/managers/theme-manager.svelte'; import type { LoginResponseDto } from '@immich/sdk'; +export type Events = { + AppInit: []; + UserLogin: []; + AuthLogin: [LoginResponseDto]; + AuthLogout: []; + LanguageChange: [{ name: string; code: string; rtl?: boolean }]; + ThemeChange: [ThemeSetting]; +}; + type Listener, K extends keyof EventMap> = (...params: EventMap[K]) => void; class EventManager> { @@ -51,11 +60,4 @@ class EventManager> { } } -export const eventManager = new EventManager<{ - 'app.init': []; - 'user.login': []; - 'auth.login': [LoginResponseDto]; - 'auth.logout': []; - 'language.change': [{ name: string; code: string; rtl?: boolean }]; - 'theme.change': [ThemeSetting]; -}>(); +export const eventManager = new EventManager(); diff --git a/web/src/lib/managers/language-manager.svelte.ts b/web/src/lib/managers/language-manager.svelte.ts index 5acae27aa3..91d621f679 100644 --- a/web/src/lib/managers/language-manager.svelte.ts +++ b/web/src/lib/managers/language-manager.svelte.ts @@ -4,7 +4,7 @@ import { lang } from '$lib/stores/preferences.store'; class LanguageManager { constructor() { - eventManager.on('app.init', () => lang.subscribe((lang) => this.setLanguage(lang))); + eventManager.on('AppInit', () => lang.subscribe((lang) => this.setLanguage(lang))); } rtl = $state(false); @@ -19,7 +19,7 @@ class LanguageManager { document.body.setAttribute('dir', item.rtl ? 'rtl' : 'ltr'); - eventManager.emit('language.change', item); + eventManager.emit('LanguageChange', item); } } diff --git a/web/src/lib/managers/theme-manager.svelte.ts b/web/src/lib/managers/theme-manager.svelte.ts index 394c9850de..5095b5739e 100644 --- a/web/src/lib/managers/theme-manager.svelte.ts +++ b/web/src/lib/managers/theme-manager.svelte.ts @@ -36,7 +36,7 @@ class ThemeManager { isDark = $derived(this.value === Theme.DARK); constructor() { - eventManager.on('app.init', () => this.#onAppInit()); + eventManager.on('AppInit', () => this.#onAppInit()); } setSystem(system: boolean) { @@ -71,7 +71,7 @@ class ThemeManager { this.#theme.current = theme; - eventManager.emit('theme.change', theme); + eventManager.emit('ThemeChange', theme); } } diff --git a/web/src/lib/managers/upload-manager.svelte.ts b/web/src/lib/managers/upload-manager.svelte.ts index 61c6d73b53..b51756678b 100644 --- a/web/src/lib/managers/upload-manager.svelte.ts +++ b/web/src/lib/managers/upload-manager.svelte.ts @@ -6,7 +6,7 @@ class UploadManager { mediaTypes = $state({ image: [], sidecar: [], video: [] }); constructor() { - eventManager.on('app.init', () => void this.#loadExtensions()).on('auth.logout', () => void this.reset()); + eventManager.on('AppInit', () => void this.#loadExtensions()).on('AuthLogout', () => void this.reset()); } reset() { diff --git a/web/src/lib/stores/folders.svelte.ts b/web/src/lib/stores/folders.svelte.ts index f77b67bb7c..3480e95f28 100644 --- a/web/src/lib/stores/folders.svelte.ts +++ b/web/src/lib/stores/folders.svelte.ts @@ -19,7 +19,7 @@ class FoldersStore { private assets = $state({}); constructor() { - eventManager.on('auth.logout', () => this.clearCache()); + eventManager.on('AuthLogout', () => this.clearCache()); } async fetchTree(): Promise { diff --git a/web/src/lib/stores/memory.store.svelte.ts b/web/src/lib/stores/memory.store.svelte.ts index bc42053a21..05f45b3d7d 100644 --- a/web/src/lib/stores/memory.store.svelte.ts +++ b/web/src/lib/stores/memory.store.svelte.ts @@ -21,7 +21,7 @@ export type MemoryAsset = MemoryIndex & { class MemoryStoreSvelte { constructor() { - eventManager.on('auth.logout', () => this.clearCache()); + eventManager.on('AuthLogout', () => this.clearCache()); } memories = $state([]); diff --git a/web/src/lib/stores/notification-manager.svelte.ts b/web/src/lib/stores/notification-manager.svelte.ts index 3eba15deed..a0f0f6bb93 100644 --- a/web/src/lib/stores/notification-manager.svelte.ts +++ b/web/src/lib/stores/notification-manager.svelte.ts @@ -9,8 +9,8 @@ class NotificationStore { notifications = $state([]); constructor() { - eventManager.on('auth.login', () => handlePromiseError(this.refresh())); - eventManager.on('auth.logout', () => this.clear()); + eventManager.on('AuthLogin', () => handlePromiseError(this.refresh())); + eventManager.on('AuthLogout', () => this.clear()); } async refresh() { diff --git a/web/src/lib/stores/search.svelte.ts b/web/src/lib/stores/search.svelte.ts index 32f2741955..007c764fcf 100644 --- a/web/src/lib/stores/search.svelte.ts +++ b/web/src/lib/stores/search.svelte.ts @@ -5,7 +5,7 @@ class SearchStore { isSearchEnabled = $state(false); constructor() { - eventManager.on('auth.logout', () => this.clearCache()); + eventManager.on('AuthLogout', () => this.clearCache()); } clearCache() { diff --git a/web/src/lib/stores/user.store.ts b/web/src/lib/stores/user.store.ts index 0790788278..bc23917d22 100644 --- a/web/src/lib/stores/user.store.ts +++ b/web/src/lib/stores/user.store.ts @@ -16,4 +16,4 @@ export const resetSavedUser = () => { purchaseStore.setPurchaseStatus(false); }; -eventManager.on('auth.logout', () => resetSavedUser()); +eventManager.on('AuthLogout', () => resetSavedUser()); diff --git a/web/src/lib/stores/user.svelte.ts b/web/src/lib/stores/user.svelte.ts index 94d73efb9c..f9319fcfa1 100644 --- a/web/src/lib/stores/user.svelte.ts +++ b/web/src/lib/stores/user.svelte.ts @@ -26,4 +26,4 @@ const reset = () => { Object.assign(userInteraction, defaultUserInteraction); }; -eventManager.on('auth.logout', () => reset()); +eventManager.on('AuthLogout', () => reset()); diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 979c7b5c42..f5d4619943 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -58,7 +58,7 @@ // if the browser theme changes, changes the Immich theme too }); - eventManager.emit('app.init'); + eventManager.emit('AppInit'); beforeNavigate(({ from, to }) => { if (isAssetViewerRoute(from) && isAssetViewerRoute(to)) { diff --git a/web/src/routes/auth/login/+page.svelte b/web/src/routes/auth/login/+page.svelte index 9ed89a0c63..352eaed408 100644 --- a/web/src/routes/auth/login/+page.svelte +++ b/web/src/routes/auth/login/+page.svelte @@ -27,7 +27,7 @@ const onSuccess = async (user: LoginResponseDto) => { await goto(data.continueUrl, { invalidateAll: true }); - eventManager.emit('auth.login', user); + eventManager.emit('AuthLogin', user); }; const onFirstLogin = () => goto(AppRoute.AUTH_CHANGE_PASSWORD);