14
14
* limitations under the License.
15
15
*/
16
16
17
+ import './sw-types' ;
18
+
17
19
import { FirebaseApp } from '@firebase/app-types' ;
18
20
19
21
import { ERROR_CODES } from '../models/errors' ;
20
22
import { DEFAULT_PUBLIC_VAPID_KEY } from '../models/fcm-details' ;
21
- import * as WorkerPageMessage from '../models/worker-page-message' ;
23
+ import {
24
+ InternalMessage ,
25
+ MessageParameter ,
26
+ MessageType
27
+ } from '../models/worker-page-message' ;
22
28
import { ControllerInterface } from './controller-interface' ;
23
29
30
+ // Let TS know that this is a service worker
31
+ declare const self : ServiceWorkerGlobalScope ;
32
+
24
33
const FCM_MSG = 'FCM_MSG' ;
25
34
26
- export type BgMessageHandler = ( input : any ) => Promise < any > ;
35
+ export type BgMessageHandler = ( input : Payload ) => Promise < void > ;
36
+
37
+ export interface NotificationDetails extends NotificationOptions {
38
+ title : string ;
39
+ click_action ?: string ;
40
+ }
41
+
42
+ export interface Payload {
43
+ notification ?: NotificationDetails ;
44
+ }
27
45
28
46
export class SWController extends ControllerInterface {
29
47
private bgMessageHandler_ : BgMessageHandler | null = null ;
30
48
31
49
constructor ( app : FirebaseApp ) {
32
50
super ( app ) ;
33
51
34
- self . addEventListener (
35
- 'push' ,
36
- ( e : any ) => {
37
- this . onPush ( e ) ;
38
- } ,
39
- false
40
- ) ;
41
- self . addEventListener (
42
- 'pushsubscriptionchange' ,
43
- ( e : any ) => {
44
- this . onSubChange ( e ) ;
45
- } ,
46
- false
47
- ) ;
48
- self . addEventListener (
49
- 'notificationclick' ,
50
- ( e : any ) => {
51
- this . onNotificationClick ( e ) ;
52
- } ,
53
- false
54
- ) ;
52
+ self . addEventListener ( 'push' , e => {
53
+ this . onPush ( e ) ;
54
+ } ) ;
55
+ self . addEventListener ( 'pushsubscriptionchange' , e => {
56
+ this . onSubChange ( e ) ;
57
+ } ) ;
58
+ self . addEventListener ( 'notificationclick' , e => {
59
+ this . onNotificationClick ( e ) ;
60
+ } ) ;
55
61
}
56
62
57
63
// Visible for testing
58
64
// TODO: Make private
59
- onPush ( event : any ) : void {
65
+ onPush ( event : PushEvent ) : void {
60
66
event . waitUntil ( this . onPush_ ( event ) ) ;
61
67
}
62
68
63
69
// Visible for testing
64
70
// TODO: Make private
65
- onSubChange ( event : any ) : void {
71
+ onSubChange ( event : PushSubscriptionChangeEvent ) : void {
66
72
event . waitUntil ( this . onSubChange_ ( event ) ) ;
67
73
}
68
74
69
75
// Visible for testing
70
76
// TODO: Make private
71
- onNotificationClick ( event : any ) : void {
77
+ onNotificationClick ( event : NotificationEvent ) : void {
72
78
event . waitUntil ( this . onNotificationClick_ ( event ) ) ;
73
79
}
74
80
@@ -84,8 +90,12 @@ export class SWController extends ControllerInterface {
84
90
* If there is no notification data in the payload then no notification will be
85
91
* shown.
86
92
*/
87
- private async onPush_ ( event : any ) : Promise < void > {
88
- let msgPayload : any ;
93
+ private async onPush_ ( event : PushEvent ) : Promise < void > {
94
+ if ( ! event . data ) {
95
+ return ;
96
+ }
97
+
98
+ let msgPayload : Payload ;
89
99
try {
90
100
msgPayload = event . data . json ( ) ;
91
101
} catch ( err ) {
@@ -105,15 +115,18 @@ export class SWController extends ControllerInterface {
105
115
106
116
const notificationDetails = this . getNotificationData_ ( msgPayload ) ;
107
117
if ( notificationDetails ) {
108
- const notificationTitle = ( notificationDetails as any ) . title || '' ;
118
+ const notificationTitle = notificationDetails . title || '' ;
109
119
const reg = await this . getSWRegistration_ ( ) ;
110
120
return reg . showNotification ( notificationTitle , notificationDetails ) ;
111
121
} else if ( this . bgMessageHandler_ ) {
112
- return this . bgMessageHandler_ ( msgPayload ) ;
122
+ await this . bgMessageHandler_ ( msgPayload ) ;
123
+ return ;
113
124
}
114
125
}
115
126
116
- private async onSubChange_ ( event : any ) : Promise < void > {
127
+ private async onSubChange_ (
128
+ event : PushSubscriptionChangeEvent
129
+ ) : Promise < void > {
117
130
let registration : ServiceWorkerRegistration ;
118
131
try {
119
132
registration = await this . getSWRegistration_ ( ) ;
@@ -140,12 +153,12 @@ export class SWController extends ControllerInterface {
140
153
}
141
154
142
155
// Attempt to delete the token if we know it's bad
143
- await this . deleteToken ( tokenDetails [ ' fcmToken' ] ) ;
156
+ await this . deleteToken ( tokenDetails . fcmToken ) ;
144
157
throw err ;
145
158
}
146
159
}
147
160
148
- private async onNotificationClick_ ( event : any ) : Promise < void > {
161
+ private async onNotificationClick_ ( event : NotificationEvent ) : Promise < void > {
149
162
if (
150
163
! event . notification ||
151
164
! event . notification . data ||
@@ -160,13 +173,13 @@ export class SWController extends ControllerInterface {
160
173
161
174
event . notification . close ( ) ;
162
175
163
- const msgPayload = event . notification . data [ FCM_MSG ] ;
164
- if ( ! msgPayload [ ' notification' ] ) {
176
+ const msgPayload : Payload = event . notification . data [ FCM_MSG ] ;
177
+ if ( ! msgPayload . notification ) {
165
178
// Nothing to do.
166
179
return ;
167
180
}
168
181
169
- const clickAction = msgPayload [ ' notification' ] [ ' click_action' ] ;
182
+ const clickAction = msgPayload . notification . click_action ;
170
183
if ( ! clickAction ) {
171
184
// Nothing to do.
172
185
return ;
@@ -175,7 +188,7 @@ export class SWController extends ControllerInterface {
175
188
let windowClient = await this . getWindowClient_ ( clickAction ) ;
176
189
if ( ! windowClient ) {
177
190
// Unable to find window client so need to open one.
178
- windowClient = await ( self as any ) . clients . openWindow ( clickAction ) ;
191
+ windowClient = await self . clients . openWindow ( clickAction ) ;
179
192
} else {
180
193
windowClient = await windowClient . focus ( ) ;
181
194
}
@@ -186,10 +199,10 @@ export class SWController extends ControllerInterface {
186
199
}
187
200
188
201
// Delete notification data from payload before sending to the page.
189
- delete msgPayload [ ' notification' ] ;
202
+ delete msgPayload . notification ;
190
203
191
- const internalMsg = WorkerPageMessage . createNewMsg (
192
- WorkerPageMessage . TYPES_OF_MSG . NOTIFICATION_CLICKED ,
204
+ const internalMsg = createNewMsg (
205
+ MessageType . NOTIFICATION_CLICKED ,
193
206
msgPayload
194
207
) ;
195
208
@@ -200,7 +213,7 @@ export class SWController extends ControllerInterface {
200
213
201
214
// Visible for testing
202
215
// TODO: Make private
203
- getNotificationData_ ( msgPayload : any ) : NotificationOptions | undefined {
216
+ getNotificationData_ ( msgPayload : Payload ) : NotificationDetails | undefined {
204
217
if ( ! msgPayload ) {
205
218
return ;
206
219
}
@@ -250,16 +263,16 @@ export class SWController extends ControllerInterface {
250
263
*/
251
264
// Visible for testing
252
265
// TODO: Make private
253
- async getWindowClient_ ( url : string ) : Promise < any > {
266
+ async getWindowClient_ ( url : string ) : Promise < WindowClient | null > {
254
267
// Use URL to normalize the URL when comparing to windowClients.
255
268
// This at least handles whether to include trailing slashes or not
256
- const parsedURL = new URL ( url , ( self as any ) . location ) . href ;
269
+ const parsedURL = new URL ( url , self . location . href ) . href ;
257
270
258
271
const clientList = await getClientList ( ) ;
259
272
260
- let suitableClient = null ;
273
+ let suitableClient : WindowClient | null = null ;
261
274
for ( let i = 0 ; i < clientList . length ; i ++ ) {
262
- const parsedClientUrl = new URL ( clientList [ i ] . url , ( self as any ) . location )
275
+ const parsedClientUrl = new URL ( clientList [ i ] . url , self . location . href )
263
276
. href ;
264
277
if ( parsedClientUrl === parsedURL ) {
265
278
suitableClient = clientList [ i ] ;
@@ -279,7 +292,10 @@ export class SWController extends ControllerInterface {
279
292
*/
280
293
// Visible for testing
281
294
// TODO: Make private
282
- async attemptToMessageClient_ ( client : any , message : any ) : Promise < void > {
295
+ async attemptToMessageClient_ (
296
+ client : WindowClient ,
297
+ message : InternalMessage
298
+ ) : Promise < void > {
283
299
// NOTE: This returns a promise in case this API is abstracted later on to
284
300
// do additional work
285
301
if ( ! client ) {
@@ -299,7 +315,7 @@ export class SWController extends ControllerInterface {
299
315
const clientList = await getClientList ( ) ;
300
316
301
317
return clientList . some (
302
- ( client : any ) => client . visibilityState === 'visible'
318
+ ( client : WindowClient ) => client . visibilityState === 'visible'
303
319
) ;
304
320
}
305
321
@@ -311,16 +327,13 @@ export class SWController extends ControllerInterface {
311
327
*/
312
328
// Visible for testing
313
329
// TODO: Make private
314
- async sendMessageToWindowClients_ ( msgPayload : any ) : Promise < void > {
330
+ async sendMessageToWindowClients_ ( msgPayload : Payload ) : Promise < void > {
315
331
const clientList = await getClientList ( ) ;
316
332
317
- const internalMsg = WorkerPageMessage . createNewMsg (
318
- WorkerPageMessage . TYPES_OF_MSG . PUSH_MSG_RECEIVED ,
319
- msgPayload
320
- ) ;
333
+ const internalMsg = createNewMsg ( MessageType . PUSH_MSG_RECEIVED , msgPayload ) ;
321
334
322
335
await Promise . all (
323
- clientList . map ( ( client : any ) =>
336
+ clientList . map ( client =>
324
337
this . attemptToMessageClient_ ( client , internalMsg )
325
338
)
326
339
) ;
@@ -331,7 +344,7 @@ export class SWController extends ControllerInterface {
331
344
* @return he service worker registration to be used for the push service.
332
345
*/
333
346
async getSWRegistration_ ( ) : Promise < ServiceWorkerRegistration > {
334
- return ( self as any ) . registration ;
347
+ return self . registration ;
335
348
}
336
349
337
350
/**
@@ -355,9 +368,17 @@ export class SWController extends ControllerInterface {
355
368
}
356
369
}
357
370
358
- function getClientList ( ) : Promise < any [ ] > {
359
- return ( self as any ) . clients . matchAll ( {
371
+ function getClientList ( ) : Promise < WindowClient [ ] > {
372
+ return self . clients . matchAll ( {
360
373
type : 'window' ,
361
374
includeUncontrolled : true
362
- } ) ;
375
+ // TS doesn't know that "type: 'window'" means it'll return WindowClient[]
376
+ } ) as Promise < WindowClient [ ] > ;
377
+ }
378
+
379
+ function createNewMsg ( msgType : MessageType , msgData : Payload ) : InternalMessage {
380
+ return {
381
+ [ MessageParameter . TYPE_OF_MSG ] : msgType ,
382
+ [ MessageParameter . DATA ] : msgData
383
+ } ;
363
384
}
0 commit comments