1
- import { Inject , Injectable } from '@angular/core' ;
2
- import { defer , fromEvent , Observable , throwError } from 'rxjs' ;
1
+ import { Inject , inject , Injectable } from '@angular/core' ;
2
+ import { InjectionTokenType , SERVICE_WORKER } from '@ng-web-apis/common' ;
3
+ import {
4
+ filter ,
5
+ from ,
6
+ fromEvent ,
7
+ map ,
8
+ NEVER ,
9
+ Observable ,
10
+ shareReplay ,
11
+ switchMap ,
12
+ throwError ,
13
+ } from 'rxjs' ;
3
14
import { takeUntil } from 'rxjs/operators' ;
4
15
16
+ import { NOTIFICATION_SW_CLICKS } from '../tokens/notification-clicks' ;
17
+ import { NOTIFICATION_SW_CLOSES } from '../tokens/notification-closes' ;
18
+ import { NOTIFICATION_FACTORY } from '../tokens/notification-factory' ;
5
19
import { NOTIFICATION_SUPPORT } from '../tokens/support' ;
6
20
7
21
const NOT_SUPPORTED_ERROR$ = throwError (
8
22
( ) => new Error ( `Notification API is not supported in your browser` ) ,
9
23
) ;
10
24
25
+ const mapToVoid = map ( ( ) => undefined ) ;
26
+
11
27
@Injectable ( {
12
28
providedIn : `root` ,
13
29
} )
14
30
export class NotificationService {
15
- constructor ( @Inject ( NOTIFICATION_SUPPORT ) private readonly support : boolean ) { }
31
+ private readonly swRegistration$ = from (
32
+ inject ( SERVICE_WORKER ) . getRegistration ( ) ,
33
+ ) . pipe ( shareReplay ( { bufferSize : 1 , refCount : true } ) ) ;
34
+
35
+ constructor (
36
+ @Inject ( NOTIFICATION_SUPPORT ) private readonly support : boolean ,
37
+ @Inject ( NOTIFICATION_FACTORY )
38
+ private readonly createNotification : InjectionTokenType <
39
+ typeof NOTIFICATION_FACTORY
40
+ > ,
41
+ @Inject ( NOTIFICATION_SW_CLICKS )
42
+ private readonly notificationSwClicks$ : InjectionTokenType <
43
+ typeof NOTIFICATION_SW_CLICKS
44
+ > ,
45
+ @Inject ( NOTIFICATION_SW_CLOSES )
46
+ private readonly notificationSwCloses$ : InjectionTokenType <
47
+ typeof NOTIFICATION_SW_CLOSES
48
+ > ,
49
+ ) { }
16
50
17
51
requestPermission ( ) : Observable < NotificationPermission > {
18
52
if ( ! this . support ) {
@@ -36,15 +70,46 @@ export class NotificationService {
36
70
return NOT_SUPPORTED_ERROR$ ;
37
71
}
38
72
39
- return defer ( ( ) => {
40
- const notification = new Notification ( title , options ) ;
41
- const close$ = fromEvent ( notification , `close` ) ;
73
+ return from ( this . createNotification ( title , options ) ) . pipe (
74
+ switchMap ( notification => {
75
+ const close$ = this . fromEvent ( notification , `close` ) ;
42
76
43
- return new Observable < Notification > ( subscriber => {
44
- subscriber . next ( notification ) ;
77
+ return new Observable < Notification > ( subscriber => {
78
+ subscriber . next ( notification ) ;
45
79
46
- return ( ) => notification . close ( ) ;
47
- } ) . pipe ( takeUntil ( close$ ) ) ;
48
- } ) ;
80
+ return ( ) => notification . close ( ) ;
81
+ } ) . pipe ( takeUntil ( close$ ) ) ;
82
+ } ) ,
83
+ ) ;
84
+ }
85
+
86
+ fromEvent < E extends keyof NotificationEventMap > (
87
+ targetNotification : Notification & { timestamp ?: number } ,
88
+ eventName : E ,
89
+ ) : Observable < { action : string } | void > {
90
+ const isTargetNotification = ( { timestamp} : { timestamp ?: number } ) : boolean =>
91
+ timestamp === targetNotification . timestamp ;
92
+
93
+ return this . swRegistration$ . pipe (
94
+ switchMap ( swRegistration => {
95
+ if ( ! swRegistration ) {
96
+ return fromEvent ( targetNotification , eventName ) . pipe ( mapToVoid ) ;
97
+ }
98
+
99
+ switch ( eventName ) {
100
+ case `click` :
101
+ return this . notificationSwClicks$ . pipe (
102
+ filter ( x => isTargetNotification ( x . notification ) ) ,
103
+ ) ;
104
+ case `close` :
105
+ return this . notificationSwCloses$ . pipe (
106
+ filter ( isTargetNotification ) ,
107
+ mapToVoid ,
108
+ ) ;
109
+ default :
110
+ return NEVER ;
111
+ }
112
+ } ) ,
113
+ ) ;
49
114
}
50
115
}
0 commit comments