From 0b942094cba98411ccc4739b2545aaf484dce123 Mon Sep 17 00:00:00 2001 From: Eugene <91225197+jewhyena@users.noreply.github> Date: Thu, 10 Oct 2024 00:36:17 +0300 Subject: [PATCH 1/3] feat: respect-users-preferred-color-scheme --- .../theme-mode-toggle.component.html | 12 ++++- .../theme-mode-toggle.component.ts | 48 +++++++++++-------- 2 files changed, 37 insertions(+), 23 deletions(-) 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..3547f10292 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 @@ -3,13 +3,15 @@ import { DOCUMENT } from '@angular/common'; import { MediaMatcher } from '@angular/cdk/layout'; 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) @@ -19,31 +21,35 @@ export class ThemeModeToggleComponent implements OnInit { ) {} 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 preferredScheme = this.mediaMatcher.matchMedia( + '(prefers-color-scheme: dark)', + ); + + preferredScheme.onchange = () => { + if (!this.getStoredTheme()) this.toggleTheme(true); + }; + + const isDarkSchemePreferred = preferredScheme.matches; + const storedTheme = this.getStoredTheme(); + + this.theme = storedTheme ?? (isDarkSchemePreferred ? 'dark' : 'light'); + this.setTheme(this.theme); } - toggleThemeMode() { - const isDarkMode = !this.isDarkMode; - this.storageService.set('theme-mode', isDarkMode.toString()); - this.setThemeMode(isDarkMode); + toggleTheme(skipStorage = false) { + this.theme = 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', this.theme); + this.setTheme(this.theme); } - 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.document.documentElement.setAttribute('mode', theme); } } From 858a6e4e6b3cb3ac7edf317bb2b33625fe4a63df Mon Sep 17 00:00:00 2001 From: Eugene <91225197+jewhyena@users.noreply.github> Date: Mon, 14 Oct 2024 22:58:00 +0300 Subject: [PATCH 2/3] fix: theme toggles wrong way after clearing `localStorage` --- .../theme-mode-toggle/theme-mode-toggle.component.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 3547f10292..8bf804e0d7 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 @@ -21,18 +21,18 @@ export class ThemeModeToggleComponent implements OnInit { ) {} ngOnInit() { - const preferredScheme = this.mediaMatcher.matchMedia( + const darkSchemeMatcher = this.mediaMatcher.matchMedia( '(prefers-color-scheme: dark)', ); - preferredScheme.onchange = () => { - if (!this.getStoredTheme()) this.toggleTheme(true); + darkSchemeMatcher.onchange = ({ matches }) => { + if (!this.getStoredTheme()) this.setTheme(matches ? 'dark' : 'light'); }; - const isDarkSchemePreferred = preferredScheme.matches; + const preferredScheme = darkSchemeMatcher.matches ? 'dark' : 'light'; const storedTheme = this.getStoredTheme(); - this.theme = storedTheme ?? (isDarkSchemePreferred ? 'dark' : 'light'); + this.theme = storedTheme ?? preferredScheme; this.setTheme(this.theme); } From 4a523ac800417ffcc802c36041e394ea6ebae795 Mon Sep 17 00:00:00 2001 From: Eugene <91225197+jewhyena@users.noreply.github> Date: Mon, 14 Oct 2024 23:34:42 +0300 Subject: [PATCH 3/3] fix: theme button don't change on matcher change --- .../theme-mode-toggle.component.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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 8bf804e0d7..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,6 +1,6 @@ -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'; @@ -18,6 +18,7 @@ export class ThemeModeToggleComponent implements OnInit { private readonly document: Document, private readonly mediaMatcher: MediaMatcher, private readonly storageService: StorageService, + private readonly changeDetector: ChangeDetectorRef, ) {} ngOnInit() { @@ -32,17 +33,16 @@ export class ThemeModeToggleComponent implements OnInit { const preferredScheme = darkSchemeMatcher.matches ? 'dark' : 'light'; const storedTheme = this.getStoredTheme(); - this.theme = storedTheme ?? preferredScheme; - this.setTheme(this.theme); + this.setTheme(storedTheme ?? preferredScheme); } toggleTheme(skipStorage = false) { - this.theme = this.theme === 'dark' ? 'light' : 'dark'; + 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', this.theme); - this.setTheme(this.theme); + if (!skipStorage) this.storageService.set('theme', newTheme); + this.setTheme(newTheme); } private getStoredTheme() { @@ -50,6 +50,8 @@ export class ThemeModeToggleComponent implements OnInit { } private setTheme(theme: Theme) { + this.theme = theme; this.document.documentElement.setAttribute('mode', theme); + this.changeDetector.detectChanges(); } }