summaryrefslogtreecommitdiff
path: root/overlays/spm/frontend/src/app/app.component.ts
blob: 11c0f77227223740bf5607f3d84d8c7785bf5639 (plain)
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();
  }
}