refactor: theme manager (#17976)

This commit is contained in:
Jason Rasmussen 2025-04-29 17:44:09 -04:00 committed by GitHub
parent 2c2dd01bf0
commit 038a82c4f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 197 additions and 85 deletions

View file

@ -1,3 +1,4 @@
import type { ThemeSetting } from '$lib/managers/theme-manager.svelte';
import type { LoginResponseDto } from '@immich/sdk';
type Listener<EventMap extends Record<string, unknown[]>, K extends keyof EventMap> = (...params: EventMap[K]) => void;
@ -56,4 +57,5 @@ export const eventManager = new EventManager<{
'auth.login': [LoginResponseDto];
'auth.logout': [];
'language.change': [{ name: string; code: string; rtl?: boolean }];
'theme.change': [ThemeSetting];
}>();

View file

@ -0,0 +1,78 @@
import { browser } from '$app/environment';
import { Theme } from '$lib/constants';
import { eventManager } from '$lib/managers/event-manager.svelte';
import { PersistedLocalStorage } from '$lib/utils/persisted';
export interface ThemeSetting {
value: Theme;
system: boolean;
}
const getDefaultTheme = () => {
if (!browser) {
return Theme.DARK;
}
return globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.DARK : Theme.LIGHT;
};
class ThemeManager {
#theme = new PersistedLocalStorage<ThemeSetting>(
'color-theme',
{ value: getDefaultTheme(), system: false },
{
valid: (value): value is ThemeSetting => {
return Object.values(Theme).includes((value as ThemeSetting)?.value);
},
},
);
get theme() {
return this.#theme.current;
}
value = $derived(this.theme.value);
isDark = $derived(this.value === Theme.DARK);
constructor() {
eventManager.on('app.init', () => this.#onAppInit());
}
setSystem(system: boolean) {
this.#update(system ? 'system' : getDefaultTheme());
}
setTheme(theme: Theme) {
this.#update(theme);
}
toggleTheme() {
this.#update(this.value === Theme.DARK ? Theme.LIGHT : Theme.DARK);
}
#onAppInit() {
globalThis.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (this.theme.system) {
this.#update('system');
}
});
}
#update(value: Theme | 'system') {
const theme: ThemeSetting =
value === 'system' ? { system: true, value: getDefaultTheme() } : { system: false, value };
if (theme.value === Theme.LIGHT) {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
this.#theme.current = theme;
eventManager.emit('theme.change', theme);
}
}
export const themeManager = new ThemeManager();