@@ -16,29 +16,69 @@ const Utils = require('./common/utils');
16
16
const Defaults = require ( './common/defaults' ) ;
17
17
const Constants = require ( './common/constants' ) ;
18
18
const sjcl = require ( 'sjcl' ) ;
19
+ const $ = require ( 'preconditions' ) . singleton ( ) ;
19
20
20
21
const PUSHNOTIFICATIONS_TYPES = {
21
22
NewCopayer : {
22
- filename : 'new_copayer'
23
+ filename : 'new_copayer' ,
24
+ notifyCreator : false ,
25
+ notifyCreatorForegroundOnly : true ,
26
+ notifyOthers : true
23
27
} ,
24
28
WalletComplete : {
25
- filename : 'wallet_complete'
29
+ filename : 'wallet_complete' ,
30
+ notifyCreator : true ,
31
+ notifyOthers : true
26
32
} ,
27
33
NewTxProposal : {
28
- filename : 'new_tx_proposal'
34
+ filename : 'new_tx_proposal' ,
35
+ notifyCreator : false ,
36
+ notifyCreatorForegroundOnly : true ,
37
+ notifyOthers : true
29
38
} ,
30
39
NewOutgoingTx : {
31
- filename : 'new_outgoing_tx'
40
+ filename : 'new_outgoing_tx' ,
41
+ notifyCreator : true ,
42
+ notifyOthers : true
32
43
} ,
33
44
NewIncomingTx : {
34
- filename : 'new_incoming_tx'
45
+ filename : 'new_incoming_tx' ,
46
+ notifyCreator : true ,
47
+ notifyOthers : true
35
48
} ,
36
49
TxProposalFinallyRejected : {
37
- filename : 'txp_finally_rejected'
50
+ filename : 'txp_finally_rejected' ,
51
+ notifyCreator : false ,
52
+ notifyCreatorForegroundOnly : true ,
53
+ notifyOthers : true
38
54
} ,
39
55
TxConfirmation : {
40
56
filename : 'tx_confirmation' ,
41
- notifyCreatorOnly : true
57
+ notifyCreator : true
58
+ } ,
59
+ NewAddress : {
60
+ filename : 'empty' , // TODO: create templates in case of implement in app notification (eg toast)
61
+ notifyCreatorForegroundOnly : true
62
+ } ,
63
+ NewBlock : {
64
+ notifyCreatorForegroundOnly : true ,
65
+ filename : 'empty' // TODO: ^
66
+ } ,
67
+ TxProposalAcceptedBy : {
68
+ notifyCreatorForegroundOnly : true ,
69
+ filename : 'empty' // TODO: ^
70
+ } ,
71
+ TxProposalFinallyAccepted : {
72
+ notifyCreatorForegroundOnly : true ,
73
+ filename : 'empty' // TODO: ^
74
+ } ,
75
+ TxProposalRejectedBy : {
76
+ notifyCreatorForegroundOnly : true ,
77
+ filename : 'empty' // TODO: ^
78
+ } ,
79
+ TxProposalRemoved : {
80
+ notifyCreatorForegroundOnly : true ,
81
+ filename : 'empty' // TODO: ^
42
82
}
43
83
} ;
44
84
@@ -153,51 +193,51 @@ export class PushNotificationsService {
153
193
this . _readAndApplyTemplates ( notification , notifType , recipientsList , next ) ;
154
194
} ,
155
195
( contents , next ) => {
156
- async . map (
157
- recipientsList ,
158
- ( recipient : IPreferences , next ) => {
159
- const content = contents [ recipient . language ] ;
196
+ this . _getSubscriptions ( notification , recipientsList , contents , next ) ;
197
+ } ,
198
+ ( subs , next ) => {
199
+ const notifications = _ . map ( subs , sub => {
200
+ const tokenAddress =
201
+ notification . data && notification . data . tokenAddress ? notification . data . tokenAddress : null ;
202
+ const multisigContractAddress =
203
+ notification . data && notification . data . multisigContractAddress
204
+ ? notification . data . multisigContractAddress
205
+ : null ;
206
+
207
+ const notificationData : any = {
208
+ to : sub . token ,
209
+ priority : 'high' ,
210
+ restricted_package_name : sub . packageName ,
211
+ data : {
212
+ walletId : sjcl . codec . hex . fromBits ( sjcl . hash . sha256 . hash ( notification . walletId || sub . walletId ) ) ,
213
+ tokenAddress,
214
+ multisigContractAddress,
215
+ copayerId : sjcl . codec . hex . fromBits ( sjcl . hash . sha256 . hash ( sub . copayerId ) ) ,
216
+ title : sub ?. plain ?. subject ,
217
+ body : sub ?. plain ?. body ,
218
+ notification_type : notification . type
219
+ }
220
+ } ;
221
+
222
+ if ( notifType . notifyOthers || notifType . notifyCreator ) {
223
+ notificationData . notification = {
224
+ title : sub ?. plain ?. subject ,
225
+ body : sub ?. plain ?. body ,
226
+ sound : 'default' ,
227
+ click_action : 'FCM_PLUGIN_ACTIVITY' ,
228
+ icon : 'fcm_push_icon'
229
+ } ;
230
+ }
231
+ return notificationData ;
232
+ } ) ;
160
233
161
- this . storage . fetchPushNotificationSubs ( recipient . copayerId , ( err , subs ) => {
162
- if ( err ) return next ( err ) ;
234
+ if ( notifications && notifications [ 0 ] && notifications [ 0 ] . notification )
235
+ $ . checkState (
236
+ subs . length < 10 ,
237
+ "'Failed state: The recipient list for this push notification is >= 10'"
238
+ ) ;
163
239
164
- const notifications = _ . map ( subs , sub => {
165
- const tokenAddress =
166
- notification . data && notification . data . tokenAddress ? notification . data . tokenAddress : null ;
167
- const multisigContractAddress =
168
- notification . data && notification . data . multisigContractAddress
169
- ? notification . data . multisigContractAddress
170
- : null ;
171
- return {
172
- to : sub . token ,
173
- priority : 'high' ,
174
- restricted_package_name : sub . packageName ,
175
- notification : {
176
- title : content . plain . subject ,
177
- body : content . plain . body ,
178
- sound : 'default' ,
179
- click_action : 'FCM_PLUGIN_ACTIVITY' ,
180
- icon : 'fcm_push_icon'
181
- } ,
182
- data : {
183
- walletId : sjcl . codec . hex . fromBits ( sjcl . hash . sha256 . hash ( notification . walletId ) ) ,
184
- tokenAddress,
185
- multisigContractAddress,
186
- copayerId : sjcl . codec . hex . fromBits ( sjcl . hash . sha256 . hash ( recipient . copayerId ) ) ,
187
- title : content . plain . subject ,
188
- body : content . plain . body ,
189
- notification_type : notification . type
190
- }
191
- } ;
192
- } ) ;
193
- return next ( err , notifications ) ;
194
- } ) ;
195
- } ,
196
- ( err , allNotifications ) => {
197
- if ( err ) return next ( err ) ;
198
- return next ( null , _ . flatten ( allNotifications ) ) ;
199
- }
200
- ) ;
240
+ return next ( err , notifications ) ;
201
241
} ,
202
242
( notifications , next ) => {
203
243
async . each (
@@ -238,57 +278,63 @@ export class PushNotificationsService {
238
278
}
239
279
240
280
_getRecipientsList ( notification , notificationType , cb ) {
241
- this . storage . fetchWallet ( notification . walletId , ( err , wallet ) => {
242
- if ( err ) return cb ( err ) ;
243
-
244
- let unit ;
245
- if ( wallet . coin != Defaults . COIN ) {
246
- unit = wallet . coin ;
247
- }
281
+ if ( notification . type !== 'NewBlock' ) {
282
+ this . storage . fetchWallet ( notification . walletId , ( err , wallet ) => {
283
+ if ( err ) return cb ( err ) ;
248
284
249
- this . storage . fetchPreferences ( notification . walletId , null , ( err , preferences ) => {
250
- if ( err ) logger . error ( err ) ;
251
- if ( _ . isEmpty ( preferences ) ) preferences = [ ] ;
285
+ let unit ;
286
+ if ( wallet && wallet . coin != Defaults . COIN ) {
287
+ unit = wallet . coin ;
288
+ }
252
289
253
- const recipientPreferences = _ . compact (
254
- _ . map ( preferences , p => {
255
- if ( ! _ . includes ( this . availableLanguages , p . language ) ) {
256
- if ( p . language ) logger . warn ( 'Language for notifications "' + p . language + '" not available.' ) ;
257
- p . language = this . defaultLanguage ;
258
- }
290
+ this . storage . fetchPreferences ( notification . walletId , null , ( err , preferences ) => {
291
+ if ( err ) logger . error ( err ) ;
292
+ if ( _ . isEmpty ( preferences ) ) preferences = [ ] ;
259
293
260
- return {
261
- copayerId : p . copayerId ,
262
- language : p . language ,
263
- unit : unit || p . unit || this . defaultUnit
264
- } ;
265
- } )
266
- ) ;
294
+ const recipientPreferences = _ . compact (
295
+ _ . map ( preferences , p => {
296
+ if ( ! _ . includes ( this . availableLanguages , p . language ) ) {
297
+ if ( p . language ) logger . warn ( 'Language for notifications "' + p . language + '" not available.' ) ;
298
+ p . language = this . defaultLanguage ;
299
+ }
267
300
268
- const copayers = _ . keyBy ( recipientPreferences , 'copayerId' ) ;
269
-
270
- const recipientsList = _ . compact (
271
- _ . map ( wallet . copayers , copayer => {
272
- if (
273
- ( copayer . id == notification . creatorId && notificationType . notifyCreatorOnly ) ||
274
- ( copayer . id != notification . creatorId && ! notificationType . notifyCreatorOnly )
275
- ) {
276
- const p = copayers [ copayer . id ] || {
277
- language : this . defaultLanguage ,
278
- unit : this . defaultUnit
279
- } ;
280
301
return {
281
- copayerId : copayer . id ,
282
- language : p . language || this . defaultLanguage ,
302
+ copayerId : p . copayerId ,
303
+ language : p . language ,
283
304
unit : unit || p . unit || this . defaultUnit
284
305
} ;
285
- }
286
- } )
287
- ) ;
288
-
289
- return cb ( null , recipientsList ) ;
306
+ } )
307
+ ) ;
308
+
309
+ const copayers = _ . keyBy ( recipientPreferences , 'copayerId' ) ;
310
+
311
+ const recipientsList = wallet
312
+ ? _ . compact (
313
+ _ . map ( wallet . copayers , copayer => {
314
+ if (
315
+ ( copayer . id == notification . creatorId &&
316
+ ( notificationType . notifyCreator || notificationType . notifyCreatorForegroundOnly ) ) ||
317
+ ( copayer . id != notification . creatorId &&
318
+ ( ! notificationType . notifyCreatorOnly || ! notificationType . notifyCreatorForegroundOnly ) )
319
+ ) {
320
+ const p = copayers [ copayer . id ] || {
321
+ language : this . defaultLanguage ,
322
+ unit : this . defaultUnit
323
+ } ;
324
+ return {
325
+ walletId : notification . walletId ,
326
+ copayerId : copayer . id ,
327
+ language : p . language || this . defaultLanguage ,
328
+ unit : unit || p . unit || this . defaultUnit
329
+ } ;
330
+ }
331
+ } )
332
+ )
333
+ : [ ] ;
334
+ return cb ( null , recipientsList ) ;
335
+ } ) ;
290
336
} ) ;
291
- } ) ;
337
+ } else return cb ( null , [ ] ) ;
292
338
}
293
339
294
340
_readAndApplyTemplates ( notification , notifType , recipientsList , cb ) {
@@ -443,6 +489,51 @@ export class PushNotificationsService {
443
489
} ;
444
490
}
445
491
492
+ _getSubscriptions ( notification , recipientsList , contents , cb ) {
493
+ if ( notification . type !== 'NewBlock' ) {
494
+ async . map (
495
+ recipientsList ,
496
+ ( recipient : IPreferences , next ) => {
497
+ const content = contents ? contents [ recipient . language ] : null ;
498
+
499
+ this . storage . fetchPushNotificationSubs ( recipient . copayerId , ( err , subs ) => {
500
+ if ( err ) return next ( err ) ;
501
+
502
+ subs [ 0 ] . plain = content . plain ;
503
+ return next ( err , subs ) ;
504
+ } ) ;
505
+ } ,
506
+ ( err , allSubs ) => {
507
+ if ( err ) return cb ( err ) ;
508
+ return cb ( null , _ . flatten ( allSubs ) ) ;
509
+ }
510
+ ) ;
511
+ } else {
512
+ this . storage . fetchLatestPushNotificationSubs ( ( err , subs ) => {
513
+ if ( err ) return cb ( err ) ;
514
+
515
+ logger . info (
516
+ `Sending NewBlock [${ notification . data . coin } /${ notification . data . network } ] notifications to: ${ subs . length } subscribers`
517
+ ) ;
518
+ async . map (
519
+ subs ,
520
+ ( sub : any , next ) => {
521
+ this . storage . fetchCopayerLookup ( sub . copayerId , ( err , wallet ) => {
522
+ if ( err ) return cb ( err ) ;
523
+
524
+ sub . walletId = wallet . walletId ;
525
+ return next ( err , sub ) ;
526
+ } ) ;
527
+ } ,
528
+ ( err , subs ) => {
529
+ if ( err ) return cb ( err ) ;
530
+ return cb ( null , _ . flatten ( subs ) ) ;
531
+ }
532
+ ) ;
533
+ } ) ;
534
+ }
535
+ }
536
+
446
537
_makeRequest ( opts , cb ) {
447
538
this . request (
448
539
{
0 commit comments