diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts index 743584a0..1e5bd0e6 100644 --- a/src/common/config/public/ClientConfig.ts +++ b/src/common/config/public/ClientConfig.ts @@ -489,6 +489,20 @@ export class ClientLightboxConfig { loopVideos: boolean = false; } +@SubConfigClass({tags: {client: true}, softReadonly: true}) +export class ThemesConfig { + + @ConfigProperty({ + tags: { + name: $localize`Enable`, + experimental: true + } as TAGS, + description: $localize`Enable themes and color modes.` + }) + enabled: boolean = false; +} + + @SubConfigClass({tags: {client: true}, softReadonly: true}) export class ClientGalleryConfig { @ConfigProperty({ @@ -578,6 +592,7 @@ export class ClientGalleryConfig { description: $localize`Adds a button to flattens the file structure, by listing the content of all subdirectories. (Won't work if the gallery has multiple folders with the same path.)` }) enableDirectoryFlattening: boolean = false; + @ConfigProperty({ tags: { name: $localize`Lightbox`, @@ -585,6 +600,14 @@ export class ClientGalleryConfig { }, }) Lightbox: ClientLightboxConfig = new ClientLightboxConfig(); + + @ConfigProperty({ + tags: { + name: $localize`ThemesConfig`, + priority: ConfigPriority.advanced, + }, + }) + Themes: ThemesConfig = new ThemesConfig(); } @SubConfigClass({tags: {client: true}, softReadonly: true}) diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 30c42715..9ec6ccd0 100644 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -104,6 +104,7 @@ import {JobProgressComponent} from './ui/settings/workflow/progress/job-progress import {SettingsEntryComponent} from './ui/settings/template/settings-entry/settings-entry.component'; import {UsersComponent} from './ui/settings/users/users.component'; import {SharingsListComponent} from './ui/settings/sharings-list/sharings-list.component'; +import {ThemeService} from './model/theme.service'; @Injectable() export class MyHammerConfig extends HammerGestureConfig { @@ -267,6 +268,7 @@ Marker.prototype.options.icon = iconDefault; SeededRandomService, OverlayService, QueryService, + ThemeService, DuplicateService, FacesService, VersionService, diff --git a/src/frontend/app/model/theme.service.ts b/src/frontend/app/model/theme.service.ts new file mode 100644 index 00000000..f97f7e78 --- /dev/null +++ b/src/frontend/app/model/theme.service.ts @@ -0,0 +1,82 @@ +import {Injectable} from '@angular/core'; +import {BehaviorSubject} from 'rxjs'; +import {Config} from '../../../common/config/public/Config'; + + +@Injectable({ + providedIn: 'root' +}) +export class ThemeService { + mode: ThemeMode = ThemeMode.light; + public readonly darkMode: BehaviorSubject = new BehaviorSubject(false); + public readonly matcher = window.matchMedia('(prefers-color-scheme: dark)'); + + constructor() { + this.darkMode.subscribe((darkMode: boolean) => { + this.applyMode(darkMode); + } + ); + } + + listenToModePreference() { + if (this.mode !== ThemeMode.auto) { + return; + } + this.darkMode.next(window.matchMedia('(prefers-color-scheme: dark)').matches); + + this.matcher.addEventListener('change', event => { + this.darkMode.next(event.matches); + }); + } + + stopListening() { + this.matcher.removeAllListeners(); + } + + applyMode(darkMode: boolean) { + if (!Config.Gallery.Themes.enabled) { + return; + } + if (!darkMode) { + document.documentElement.removeAttribute('data-bs-theme'); + } else { + document.documentElement.setAttribute('data-bs-theme', 'dark'); + } + } + + setMode(mode: ThemeMode) { + this.mode = mode; + if (this.mode === ThemeMode.light) { + this.darkMode.next(false); + this.stopListening(); + } else if (this.mode === ThemeMode.dark) { + this.darkMode.next(true); + this.stopListening(); + } else if (this.mode === ThemeMode.auto) { + this.listenToModePreference(); + } + } + + + toggleMode() { + switch (this.mode) { + case ThemeMode.light: + this.setMode(ThemeMode.dark); + break; + case ThemeMode.dark: + this.setMode(ThemeMode.auto); + break; + case ThemeMode.auto: + this.setMode(ThemeMode.light); + break; + } + } +} + +export enum ThemeMode { + light = 1, dark, auto +} + +export enum AppliedThemeMode { + light = 1, dark +} diff --git a/src/frontend/app/ui/frame/frame.component.html b/src/frontend/app/ui/frame/frame.component.html index 930b0ce7..899d50b7 100644 --- a/src/frontend/app/ui/frame/frame.component.html +++ b/src/frontend/app/ui/frame/frame.component.html @@ -63,7 +63,7 @@ -
+
  • + +
  • +