1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
import { Component, OnInit, OnDestroy, Inject, Renderer2, RendererFactory2 } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { MediaMatcher } from '@angular/cdk/layout';
import { LocalStorage } from 'ngx-webstorage';
import { BehaviorSubject, Subscription, Subject } from 'rxjs';
import { Router, RouterEvent, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { map, filter } from 'rxjs/operators';
type ColorScheme = 'dark' | 'light';
export interface DomainResponse {
domain: string;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass']
})
export class AppComponent implements OnInit, OnDestroy {
private renderer: Renderer2;
colorSchemeMatcher: MediaQueryList;
title = 'spm-frontend';
public activeColorScheme$: BehaviorSubject<ColorScheme> = new BehaviorSubject<ColorScheme>('dark');
@LocalStorage('prefers-color-scheme')
public colorSchemeOverride?: ColorScheme;
colorSchemeSubscription?: Subscription;
router: Router;
public routerLoading$: Subject<boolean> = new Subject<boolean>();
routerEventSubscription?: Subscription;
http: HttpClient;
apiDomain$: Subject<string> = new Subject<string>();
constructor( rendererFactory: RendererFactory2,
mediaMatcher: MediaMatcher,
@Inject(DOCUMENT) private document: Document,
router: Router,
http: HttpClient,
) {
this.renderer = rendererFactory.createRenderer(null, null);
this.colorSchemeMatcher = mediaMatcher.matchMedia('(prefers-color-scheme: dark)');
this.document = document;
this.colorSchemeListener = this.colorSchemeListener.bind(this);
this.colorSchemeMatcher.addEventListener('change', this.colorSchemeListener);
this.activeColorScheme$.next(this.getColorScheme());
this.router = router;
this.http = http;
}
private colorSchemeListener(event: { matches: boolean; }) {
this.activeColorScheme$.next(this.getColorScheme(event));
}
private getColorScheme(event?: { matches: boolean; }): ColorScheme {
if (this.colorSchemeOverride) {
return this.colorSchemeOverride;
} else if (event) {
return event.matches ? 'dark' : 'light';
} else {
return this.colorSchemeMatcher.matches ? 'dark' : 'light';
}
}
updateColorScheme(scheme: ColorScheme) {
this.colorSchemeOverride = scheme;
this.activeColorScheme$.next(scheme);
}
ngOnInit() {
this.colorSchemeSubscription = this.activeColorScheme$.subscribe(scheme => { this.renderer.setAttribute(this.document.body, 'data-color-scheme', scheme); });
this.routerEventSubscription = this.router.events.pipe(
filter(event =>
event instanceof NavigationStart ||
event instanceof NavigationEnd ||
event instanceof NavigationCancel ||
event instanceof NavigationError
),
map(event => event instanceof NavigationStart)
).subscribe(this.routerLoading$);
this.http.get<DomainResponse>('/domain', { headers: new HttpHeaders({ 'Accept': 'application/json' }) }).pipe(map((resp: DomainResponse) => resp.domain)).subscribe(this.apiDomain$);
}
ngOnDestroy() {
this.colorSchemeMatcher.removeEventListener('change', this.colorSchemeListener);
this.colorSchemeSubscription?.unsubscribe();
this.routerEventSubscription?.unsubscribe();
}
}
|