diff --git a/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.html b/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.html index 5c4f67ebe8..975c5c4aa6 100644 --- a/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.html +++ b/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.html @@ -1,4 +1,12 @@ diff --git a/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.ts b/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.ts index 36088c84d9..0d35fab221 100644 --- a/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.ts +++ b/src/app/shared/components/theme-mode-toggle/theme-mode-toggle.component.ts @@ -1,49 +1,57 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { DOCUMENT } from '@angular/common'; import { MediaMatcher } from '@angular/cdk/layout'; +import { DOCUMENT } from '@angular/common'; +import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core'; import { StorageService } from '../../services/storage.service'; +type Theme = 'light' | 'dark'; + @Component({ selector: 'app-theme-mode-toggle', templateUrl: './theme-mode-toggle.component.html', styleUrls: ['./theme-mode-toggle.component.scss'], }) export class ThemeModeToggleComponent implements OnInit { - isDarkMode: boolean; + theme: Theme; constructor( @Inject(DOCUMENT) private readonly document: Document, private readonly mediaMatcher: MediaMatcher, private readonly storageService: StorageService, + private readonly changeDetector: ChangeDetectorRef, ) {} ngOnInit() { - // This is commented out because by default the theme mode is set to light (at least for now) - // const userPrefersTheme = - // this.mediaMatcher.matchMedia && - // this.mediaMatcher.matchMedia('(prefers-color-scheme: light)').matches; - // this.setThemeMode(this.getUserSettingsIsDarkMode() || userPrefersTheme); - - const isDarkMode = this.getUserSettingsIsDarkMode(); - this.setThemeMode(isDarkMode); + const darkSchemeMatcher = this.mediaMatcher.matchMedia( + '(prefers-color-scheme: dark)', + ); + + darkSchemeMatcher.onchange = ({ matches }) => { + if (!this.getStoredTheme()) this.setTheme(matches ? 'dark' : 'light'); + }; + + const preferredScheme = darkSchemeMatcher.matches ? 'dark' : 'light'; + const storedTheme = this.getStoredTheme(); + + this.setTheme(storedTheme ?? preferredScheme); } - toggleThemeMode() { - const isDarkMode = !this.isDarkMode; - this.storageService.set('theme-mode', isDarkMode.toString()); - this.setThemeMode(isDarkMode); + toggleTheme(skipStorage = false) { + const newTheme = this.theme === 'dark' ? 'light' : 'dark'; + // NOTE: We should skip saving theme in storage when toggle is caused by matchMedia change event + // Otherwise, once saved, it'll no longer correspond to the system preferences, + // despite the user not touching the toggle button themselves + if (!skipStorage) this.storageService.set('theme', newTheme); + this.setTheme(newTheme); } - private getUserSettingsIsDarkMode(): boolean { - return this.storageService.get('theme-mode') === 'true'; + private getStoredTheme() { + return this.storageService.get('theme') as Theme | null; } - private setThemeMode(isDarkMode: boolean) { - this.isDarkMode = isDarkMode; - this.document.documentElement.setAttribute( - 'mode', - isDarkMode ? 'dark' : 'light', - ); + private setTheme(theme: Theme) { + this.theme = theme; + this.document.documentElement.setAttribute('mode', theme); + this.changeDetector.detectChanges(); } }