Skip to content

Commit f864332

Browse files
Merge pull request #3102 from jewhyena/fix/respect-users-preferred-color-scheme
feat: respect user's preferred color scheme (dark mode repair)
2 parents 6007703 + 4a523ac commit f864332

File tree

2 files changed

+41
-25
lines changed

2 files changed

+41
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
<button class="theme-mode-toggle">
2-
<span class="material-icons" *ngIf="!isDarkMode" (click)="toggleThemeMode()"> light_mode </span>
3-
<span class="material-icons" *ngIf="isDarkMode" (click)="toggleThemeMode()"> dark_mode </span>
2+
<span class="material-icons" *ngIf="theme === 'dark'" (click)="toggleTheme()">
3+
light_mode
4+
</span>
5+
<span
6+
class="material-icons"
7+
*ngIf="theme === 'light'"
8+
(click)="toggleTheme()"
9+
>
10+
dark_mode
11+
</span>
412
</button>
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,57 @@
1-
import { Component, Inject, OnInit } from '@angular/core';
2-
import { DOCUMENT } from '@angular/common';
31
import { MediaMatcher } from '@angular/cdk/layout';
2+
import { DOCUMENT } from '@angular/common';
3+
import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core';
44
import { StorageService } from '../../services/storage.service';
55

6+
type Theme = 'light' | 'dark';
7+
68
@Component({
79
selector: 'app-theme-mode-toggle',
810
templateUrl: './theme-mode-toggle.component.html',
911
styleUrls: ['./theme-mode-toggle.component.scss'],
1012
})
1113
export class ThemeModeToggleComponent implements OnInit {
12-
isDarkMode: boolean;
14+
theme: Theme;
1315

1416
constructor(
1517
@Inject(DOCUMENT)
1618
private readonly document: Document,
1719
private readonly mediaMatcher: MediaMatcher,
1820
private readonly storageService: StorageService,
21+
private readonly changeDetector: ChangeDetectorRef,
1922
) {}
2023

2124
ngOnInit() {
22-
// This is commented out because by default the theme mode is set to light (at least for now)
23-
// const userPrefersTheme =
24-
// this.mediaMatcher.matchMedia &&
25-
// this.mediaMatcher.matchMedia('(prefers-color-scheme: light)').matches;
26-
// this.setThemeMode(this.getUserSettingsIsDarkMode() || userPrefersTheme);
27-
28-
const isDarkMode = this.getUserSettingsIsDarkMode();
29-
this.setThemeMode(isDarkMode);
25+
const darkSchemeMatcher = this.mediaMatcher.matchMedia(
26+
'(prefers-color-scheme: dark)',
27+
);
28+
29+
darkSchemeMatcher.onchange = ({ matches }) => {
30+
if (!this.getStoredTheme()) this.setTheme(matches ? 'dark' : 'light');
31+
};
32+
33+
const preferredScheme = darkSchemeMatcher.matches ? 'dark' : 'light';
34+
const storedTheme = this.getStoredTheme();
35+
36+
this.setTheme(storedTheme ?? preferredScheme);
3037
}
3138

32-
toggleThemeMode() {
33-
const isDarkMode = !this.isDarkMode;
34-
this.storageService.set('theme-mode', isDarkMode.toString());
35-
this.setThemeMode(isDarkMode);
39+
toggleTheme(skipStorage = false) {
40+
const newTheme = this.theme === 'dark' ? 'light' : 'dark';
41+
// NOTE: We should skip saving theme in storage when toggle is caused by matchMedia change event
42+
// Otherwise, once saved, it'll no longer correspond to the system preferences,
43+
// despite the user not touching the toggle button themselves
44+
if (!skipStorage) this.storageService.set('theme', newTheme);
45+
this.setTheme(newTheme);
3646
}
3747

38-
private getUserSettingsIsDarkMode(): boolean {
39-
return this.storageService.get('theme-mode') === 'true';
48+
private getStoredTheme() {
49+
return this.storageService.get('theme') as Theme | null;
4050
}
4151

42-
private setThemeMode(isDarkMode: boolean) {
43-
this.isDarkMode = isDarkMode;
44-
this.document.documentElement.setAttribute(
45-
'mode',
46-
isDarkMode ? 'dark' : 'light',
47-
);
52+
private setTheme(theme: Theme) {
53+
this.theme = theme;
54+
this.document.documentElement.setAttribute('mode', theme);
55+
this.changeDetector.detectChanges();
4856
}
4957
}

0 commit comments

Comments
 (0)