@@ -2,6 +2,8 @@ import { action } from '@ember/object';
2
2
import Service from '@ember/service' ;
3
3
import { tracked } from '@glimmer/tracking' ;
4
4
5
+ import { restartableTask , waitForEvent } from 'ember-concurrency' ;
6
+
5
7
import * as localStorage from '../utils/local-storage' ;
6
8
7
9
const DEFAULT_SCHEME = 'light' ;
@@ -10,6 +12,16 @@ const LS_KEY = 'color-scheme';
10
12
11
13
export default class DesignService extends Service {
12
14
@tracked _scheme = localStorage . getItem ( LS_KEY ) ;
15
+ @tracked resolvedScheme ;
16
+
17
+ constructor ( ) {
18
+ super ( ...arguments ) ;
19
+ this . restartWatcherTask ( ) ;
20
+ }
21
+
22
+ get isDark ( ) {
23
+ return this . resolvedScheme === 'dark' ;
24
+ }
13
25
14
26
get scheme ( ) {
15
27
return VALID_SCHEMES . has ( this . _scheme ) ? this . _scheme : DEFAULT_SCHEME ;
@@ -18,5 +30,32 @@ export default class DesignService extends Service {
18
30
@action set ( scheme ) {
19
31
this . _scheme = scheme ;
20
32
localStorage . setItem ( LS_KEY , scheme ) ;
33
+ this . restartWatcherTask ( ) ;
21
34
}
35
+
36
+ restartWatcherTask ( ) {
37
+ this . watcherTask . perform ( ) . catch ( ( ) => {
38
+ // Ignore Promise rejections. This shouldn't be able to fail, and task cancellations are expected.
39
+ } ) ;
40
+ }
41
+
42
+ /**
43
+ * This task watches for changes in the system color scheme and updates the `resolvedScheme` property accordingly.
44
+ */
45
+ watcherTask = restartableTask ( async ( ) => {
46
+ let mediaQueryList = window . matchMedia ( '(prefers-color-scheme: dark)' ) ;
47
+ // eslint-disable-next-line no-constant-condition
48
+ while ( true ) {
49
+ let scheme = this . scheme ;
50
+ if ( scheme === 'system' ) {
51
+ scheme = mediaQueryList . matches ? 'dark' : 'light' ;
52
+ }
53
+
54
+ if ( this . resolvedScheme !== scheme ) {
55
+ this . resolvedScheme = scheme ;
56
+ }
57
+
58
+ await waitForEvent ( mediaQueryList , 'change' ) ;
59
+ }
60
+ } ) ;
22
61
}
0 commit comments