diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts index 1e5bd0e6..d9dc95db 100644 --- a/src/common/config/public/ClientConfig.ts +++ b/src/common/config/public/ClientConfig.ts @@ -24,6 +24,11 @@ export enum ConfigPriority { basic = 0, advanced, underTheHood } + +export enum ThemeModes { + light = 1, dark, auto +} + export type TAGS = { client?: true, priority?: ConfigPriority, @@ -500,6 +505,17 @@ export class ThemesConfig { description: $localize`Enable themes and color modes.` }) enabled: boolean = false; + + @ConfigProperty({ + type: ThemeModes, + tags: { + name: $localize`Default theme mode`, + experimental: true, + uiDisabled: (sb: ThemesConfig) => !sb.enabled, + } as TAGS, + description: $localize`Sets the default theme mode that is used for the application.` + }) + defaultMode: ThemeModes = ThemeModes.light; } @@ -603,7 +619,7 @@ export class ClientGalleryConfig { @ConfigProperty({ tags: { - name: $localize`ThemesConfig`, + name: $localize`Themes`, priority: ConfigPriority.advanced, }, }) diff --git a/src/frontend/app/model/theme.service.ts b/src/frontend/app/model/theme.service.ts index f97f7e78..f9100449 100644 --- a/src/frontend/app/model/theme.service.ts +++ b/src/frontend/app/model/theme.service.ts @@ -1,17 +1,24 @@ import {Injectable} from '@angular/core'; import {BehaviorSubject} from 'rxjs'; +import {ThemeModes} from '../../../common/config/public/ClientConfig'; import {Config} from '../../../common/config/public/Config'; +import {GalleryCacheService} from '../ui/gallery/cache.gallery.service'; @Injectable({ providedIn: 'root' }) export class ThemeService { - mode: ThemeMode = ThemeMode.light; + mode: ThemeModes = ThemeModes.light; public readonly darkMode: BehaviorSubject = new BehaviorSubject(false); public readonly matcher = window.matchMedia('(prefers-color-scheme: dark)'); - constructor() { + constructor(private cachingService: GalleryCacheService) { + if (cachingService.getThemeMode()) { + this.setMode(cachingService.getThemeMode()); + } else { + this.setMode(Config.Gallery.Themes.defaultMode); + } this.darkMode.subscribe((darkMode: boolean) => { this.applyMode(darkMode); } @@ -19,7 +26,7 @@ export class ThemeService { } listenToModePreference() { - if (this.mode !== ThemeMode.auto) { + if (this.mode !== ThemeModes.auto) { return; } this.darkMode.next(window.matchMedia('(prefers-color-scheme: dark)').matches); @@ -44,39 +51,36 @@ export class ThemeService { } } - setMode(mode: ThemeMode) { + setMode(mode: ThemeModes) { + if (this.mode === mode) { + return; + } this.mode = mode; - if (this.mode === ThemeMode.light) { + if (this.mode === ThemeModes.light) { this.darkMode.next(false); this.stopListening(); - } else if (this.mode === ThemeMode.dark) { + } else if (this.mode === ThemeModes.dark) { this.darkMode.next(true); this.stopListening(); - } else if (this.mode === ThemeMode.auto) { + } else if (this.mode === ThemeModes.auto) { this.listenToModePreference(); } + this.cachingService.setThemeMode(this.mode); } toggleMode() { switch (this.mode) { - case ThemeMode.light: - this.setMode(ThemeMode.dark); + case ThemeModes.light: + this.setMode(ThemeModes.dark); break; - case ThemeMode.dark: - this.setMode(ThemeMode.auto); + case ThemeModes.dark: + this.setMode(ThemeModes.auto); break; - case ThemeMode.auto: - this.setMode(ThemeMode.light); + case ThemeModes.auto: + this.setMode(ThemeModes.light); break; } } } -export enum ThemeMode { - light = 1, dark, auto -} - -export enum AppliedThemeMode { - light = 1, dark -} diff --git a/src/frontend/app/ui/gallery/cache.gallery.service.ts b/src/frontend/app/ui/gallery/cache.gallery.service.ts index 20818b31..1fb6df1a 100644 --- a/src/frontend/app/ui/gallery/cache.gallery.service.ts +++ b/src/frontend/app/ui/gallery/cache.gallery.service.ts @@ -9,6 +9,7 @@ import {VersionService} from '../../model/version.service'; import {SearchQueryDTO, SearchQueryTypes,} from '../../../../common/entities/SearchQueryDTO'; import {ContentWrapper} from '../../../../common/entities/ConentWrapper'; import {ContentWrapperWithError} from './content.service'; +import {ThemeModes} from '../../../../common/config/public/ClientConfig'; interface CacheItem { timestamp: number; @@ -17,13 +18,14 @@ interface CacheItem { @Injectable() export class GalleryCacheService { - private static readonly CONTENT_PREFIX = 'content:'; - private static readonly AUTO_COMPLETE_PREFIX = 'autocomplete:'; - private static readonly INSTANT_SEARCH_PREFIX = 'instant_search:'; - private static readonly SEARCH_PREFIX = 'search:'; - private static readonly SORTING_PREFIX = 'sorting:'; - private static readonly VERSION = 'version'; - private static readonly SLIDESHOW_SPEED = 'slideshow_speed'; + private static readonly CONTENT_PREFIX = 'CONTENT:'; + private static readonly AUTO_COMPLETE_PREFIX = 'AUTOCOMPLETE:'; + private static readonly INSTANT_SEARCH_PREFIX = 'INSTANT_SEARCH:'; + private static readonly SEARCH_PREFIX = 'SEARCH:'; + private static readonly SORTING_PREFIX = 'SORTING:'; + private static readonly VERSION = 'VERSION'; + private static readonly SLIDESHOW_SPEED = 'SLIDESHOW_SPEED'; + private static THEME_MODE = 'THEME_MODE'; constructor(private versionService: VersionService) { // if it was a forced reload not a navigation, clear cache @@ -315,4 +317,24 @@ export class GalleryCacheService { console.error(e); } } + + getThemeMode(): ThemeModes { + const key = GalleryCacheService.THEME_MODE; + const tmp = localStorage.getItem(key) as keyof typeof ThemeModes; + if (tmp != null) { + return ThemeModes[tmp]; + } + return null; + } + + + setThemeMode(mode: ThemeModes): void { + try { + const key = GalleryCacheService.THEME_MODE; + localStorage.setItem(key, ThemeModes[mode]); + } catch (e) { + this.reset(); + console.error(e); + } + } }