1
1
import React , { Component } from 'react' ;
2
2
import Routes from './pages/Routes.js' ;
3
+ import messaging from '@react-native-firebase/messaging' ;
4
+ import { Alert } from 'react-native'
5
+ import notifee , { AuthorizationStatus , EventType , AndroidImportance , AndroidVisibility } from '@notifee/react-native' ;
6
+
7
+ var killedIncomingCallRoomId = '' ;
8
+
9
+ // For android
10
+ notifee . createChannel ( {
11
+ id : 'callinvite' ,
12
+ name : 'Call Invite' ,
13
+ badge : false ,
14
+ vibration : true ,
15
+ vibrationPattern : [ 300 , 500 ] ,
16
+ importance : AndroidImportance . HIGH ,
17
+ visibility : AndroidVisibility . PUBLIC ,
18
+ // https://clideo.com/merge-audio
19
+ // https://www.zedge.net/find/ringtones/sound-effects
20
+ sound : 'call_invite' ,
21
+ } ) ;
22
+
23
+ async function onBackgroundMessageReceived ( message ) {
24
+ // invokeApp();
25
+ console . log ( ">>>>>>>>>>Message: " , message , message . data . callerUserName ) ;
26
+ killedIncomingCallRoomId = message . data . roomID ;
27
+ notifee . displayNotification ( {
28
+ title : '<p style="color: #4caf50;"><b>' + '📞 ' + message . data . callerUserName + ' incoming call..' + '</span></p></b></p>' ,
29
+ body : 'Tap to view contact.' ,
30
+ data : { "roomID" : message . data . roomID , "callType" : message . data . callType } ,
31
+ android : {
32
+ channelId : 'callinvite' ,
33
+ largeIcon : message . data . callerIconUrl ,
34
+ // Launch the app on lock screen
35
+ fullScreenAction : {
36
+ // For Android Activity other than the default:
37
+ id : 'full_screen_body_press' ,
38
+ launchActivity : 'default' ,
39
+ } ,
40
+ pressAction : {
41
+ id : 'body_press' ,
42
+ launchActivity : 'default' ,
43
+ } ,
44
+ actions : [
45
+ {
46
+ title : 'Denied' ,
47
+ // icon: 'https://my-cdn.com/icons/snooze.png',
48
+ pressAction : {
49
+ id : 'denied' ,
50
+ } ,
51
+ } ,
52
+ {
53
+ title : 'Accept' ,
54
+ // icon: 'https://my-cdn.com/icons/snooze.png',
55
+ pressAction : {
56
+ id : 'accept' ,
57
+ launchActivity : 'default' ,
58
+ } ,
59
+ } ,
60
+ ] ,
61
+ } ,
62
+ } ) ;
63
+ console . log ( 'Show completed.' )
64
+ }
65
+ messaging ( ) . setBackgroundMessageHandler ( onBackgroundMessageReceived ) ;
66
+
67
+ const config = {
68
+ // Get your AppID from ZEGOCLOUD Console [My Projects] : https://console.zegocloud.com/project
69
+ appID : ,
70
+ // Heroku server url for example
71
+ // Get the server from: https://github.com/ZEGOCLOUD/dynamic_token_server_nodejs
72
+ serverUrl : ''
73
+ }
3
74
4
75
class App extends Component {
76
+ routesInstance ;
77
+ messageListener ;
78
+ backgroundMessageListener ;
79
+
80
+ state = {
81
+ userID : '' ,
82
+ zegoToken : '' ,
83
+ fcmToken : '' ,
84
+ }
85
+
86
+ componentDidMount ( ) {
87
+ this . onAppBootstrap ( ) ;
88
+ }
89
+
90
+ componentWillUnmount ( ) {
91
+ this . messageListener ;
92
+ // this.backgroundMessageListener;
93
+ }
94
+
95
+ async onAppBootstrap ( ) {
96
+ await this . checkPermission ( ) ;
97
+
98
+ await this . prepareBasicData ( ) ;
99
+
100
+ await this . setupNotification ( ) ;
101
+
102
+ if ( killedIncomingCallRoomId != '' ) {
103
+ this . routesInstance . handleIncomingCall ( killedIncomingCallRoomId ) ;
104
+ }
105
+ }
106
+
107
+ //////////////////////////// notification /////////////////////////
108
+ async setupNotification ( ) {
109
+ notifee . onForegroundEvent ( async ( { type, detail } ) => {
110
+ if ( type === EventType . PRESS ) {
111
+ console . log ( 'User press on froeground event: ' , detail )
112
+ await notifee . cancelAllNotifications ( ) ;
113
+ } else if ( type == EventType . ACTION_PRESS && detail . pressAction . id ) {
114
+ if ( detail . pressAction . id == 'accept' ) {
115
+ console . log ( 'Accept the call...' , detail . notification . data . roomID )
116
+ if ( this . routesInstance != undefined ) {
117
+ this . routesInstance . handleIncomingCall ( detail . notification . data . roomID ) ;
118
+ }
119
+ }
120
+ await notifee . cancelAllNotifications ( ) ;
121
+ }
122
+ } ) ;
123
+ notifee . onBackgroundEvent ( async ( { type, detail } ) => {
124
+ if ( type === EventType . PRESS ) {
125
+ console . log ( 'User press on background event: ' , detail )
126
+ // await notifee.cancelNotification(detail.notification.id);
127
+ await notifee . cancelAllNotifications ( ) ;
128
+ } else if ( type == EventType . ACTION_PRESS && detail . pressAction . id ) {
129
+ if ( detail . pressAction . id == 'accept' ) {
130
+ console . log ( 'Accept the call...' , detail . notification . data . roomID )
131
+ if ( this . routesInstance != undefined ) {
132
+ this . routesInstance . handleIncomingCall ( detail . notification . data . roomID ) ;
133
+ }
134
+ }
135
+ await notifee . cancelAllNotifications ( ) ;
136
+ }
137
+ } ) ;
138
+
139
+ this . messageListener = messaging ( ) . onMessage ( this . onMessageReceived ) ;
140
+ }
141
+ async onMessageReceived ( message ) {
142
+ // invokeApp();
143
+ console . log ( ">>>>>>>>>>Foreground Message: " , message , message . data . callerUserName ) ;
144
+ notifee . displayNotification ( {
145
+ title : '<p style="color: #4caf50;"><b>' + '📞 ' + message . data . callerUserName + ' incoming call..' + '</span></p></b></p>' ,
146
+ body : 'Tap to view contact.' ,
147
+ data : { "roomID" : message . data . roomID , "callType" : message . data . callType } ,
148
+ android : {
149
+ channelId : 'callinvite' ,
150
+ largeIcon : message . data . callerIconUrl ,
151
+ // Launch the app on lock screen
152
+ fullScreenAction : {
153
+ // For Android Activity other than the default:
154
+ id : 'full_screen_body_press' ,
155
+ launchActivity : 'default' ,
156
+ } ,
157
+ pressAction : {
158
+ id : 'body_press' ,
159
+ launchActivity : 'default' ,
160
+ } ,
161
+ actions : [
162
+ {
163
+ title : 'Denied' ,
164
+ // icon: 'https://my-cdn.com/icons/snooze.png',
165
+ pressAction : {
166
+ id : 'denied' ,
167
+ launchActivity : 'default' ,
168
+ } ,
169
+ } ,
170
+ {
171
+ title : 'Accept' ,
172
+ // icon: 'https://my-cdn.com/icons/snooze.png',
173
+ pressAction : {
174
+ id : 'accept' ,
175
+ launchActivity : 'default' ,
176
+ } ,
177
+ } ,
178
+ ] ,
179
+ } ,
180
+ } ) ;
181
+ console . log ( 'Show completed.' )
182
+ }
183
+ //////////////////////////// notification /////////////////////////
184
+
185
+ //////////////////////////// basic data /////////////////////////
186
+ async prepareBasicData ( ) {
187
+ // Get fcm token
188
+ await this . updateFcmToken ( ) ;
189
+ // Generate user id
190
+ this . setState ( {
191
+ userID : Math . floor ( Math . random ( ) * 1000000 ) . toString ( )
192
+ } )
193
+ // Save the fcm token with user id to server
194
+ const requestOptions = {
195
+ method : 'POST' ,
196
+ headers : { 'Content-Type' : 'application/json' } ,
197
+ body : JSON . stringify ( { userID : this . state . userID , token : this . state . fcmToken } )
198
+ } ;
199
+ const reps = await fetch ( `${ config . serverUrl } /store_fcm_token` , requestOptions ) ;
200
+ console . log ( 'Store fcm token reps: ' , reps ) ;
201
+ await this . updateZegoToken ( ) ;
202
+ }
203
+ async updateZegoToken ( ) {
204
+ // Obtain the token interface provided by the App Server
205
+ const reps = await fetch (
206
+ `${ config . serverUrl } /access_token?uid=${ this . state . userID } ` ,
207
+ {
208
+ method : 'GET' ,
209
+ headers : {
210
+ 'Content-Type' : 'application/json; charset=utf-8' ,
211
+ } ,
212
+ } ,
213
+ ) ;
214
+ if ( reps . ok ) {
215
+ const tokenObj = await reps . json ( ) ;
216
+ console . log ( 'Get zego token succeed' ) ;
217
+ this . setState ( {
218
+ zegoToken : tokenObj [ 'token' ]
219
+ } ) ;
220
+ } else {
221
+ console . warn ( 'Get zego token error: ' , reps . text ) ;
222
+ }
223
+ } ;
224
+ async updateFcmToken ( ) {
225
+ // Register the device with FCM
226
+ await messaging ( ) . registerDeviceForRemoteMessages ( ) ;
227
+
228
+ // Get the token
229
+ const token = await messaging ( ) . getToken ( ) ;
230
+ console . log ( 'Fcm token: ' , token ) ;
231
+ this . setState ( {
232
+ fcmToken : token
233
+ } ) ;
234
+
235
+ }
236
+ //////////////////////////// basic data /////////////////////////
237
+
238
+ //////////////////////////// permission stuffs /////////////////////////
239
+ async checkPermission ( ) {
240
+ // For ios
241
+ await this . requestiOSUserPermission ( ) ;
242
+ // For android
243
+ await this . checkAndroidNotificationPermission ( ) ;
244
+ await this . checkAndroidChannelPermission ( 'callinvite' ) ;
245
+ await this . checkBatteryOptimization ( ) ;
246
+ await this . checkPowerManagerRestrictions ( ) ;
247
+ }
248
+ async checkAndroidNotificationPermission ( ) {
249
+ const settings = await notifee . getNotificationSettings ( ) ;
250
+
251
+ if ( settings . authorizationStatus == AuthorizationStatus . AUTHORIZED ) {
252
+ console . log ( 'Notification permissions has been authorized' ) ;
253
+ } else if ( settings . authorizationStatus == AuthorizationStatus . DENIED ) {
254
+ console . log ( 'Notification permissions has been denied' ) ;
255
+ }
256
+ }
257
+ async checkAndroidChannelPermission ( channelId ) {
258
+ const channel = await notifee . getChannel ( channelId ) ;
259
+
260
+ if ( channel . blocked ) {
261
+ console . log ( 'Channel is disabled' ) ;
262
+ Alert . alert (
263
+ 'Restrictions Detected' ,
264
+ 'To ensure notifications are delivered, please enable notification for the app.' ,
265
+ [
266
+ // 3. launch intent to navigate the user to the appropriate screen
267
+ {
268
+ text : 'OK, open settings' ,
269
+ onPress : async ( ) => await notifee . openNotificationSettings ( ) ,
270
+ } ,
271
+ {
272
+ text : "Cancel" ,
273
+ onPress : ( ) => console . log ( "Cancel Pressed" ) ,
274
+ style : "cancel"
275
+ } ,
276
+ ] ,
277
+ { cancelable : false }
278
+ ) ;
279
+ } else {
280
+ console . log ( 'Channel is enabled' ) ;
281
+ }
282
+ }
283
+ // Need for background message
284
+ async requestiOSUserPermission ( ) {
285
+ const authStatus = await messaging ( ) . requestPermission ( ) ;
286
+ const enabled =
287
+ authStatus === messaging . AuthorizationStatus . AUTHORIZED ||
288
+ authStatus === messaging . AuthorizationStatus . PROVISIONAL ;
289
+
290
+ if ( enabled ) {
291
+ console . log ( 'Authorization status:' , authStatus ) ;
292
+ }
293
+ }
294
+ async checkBatteryOptimization ( ) {
295
+ const batteryOptimizationEnabled = await notifee . isBatteryOptimizationEnabled ( ) ;
296
+ console . log ( "batteryOptimizationEnabled" , batteryOptimizationEnabled )
297
+ if ( batteryOptimizationEnabled ) {
298
+ // 2. ask your users to disable the feature
299
+ Alert . alert (
300
+ 'Restrictions Detected' ,
301
+ 'To ensure notifications are delivered, please disable battery optimization for the app.' ,
302
+ [
303
+ // 3. launch intent to navigate the user to the appropriate screen
304
+ {
305
+ text : 'OK, open settings' ,
306
+ onPress : async ( ) => await notifee . openBatteryOptimizationSettings ( ) ,
307
+ } ,
308
+ {
309
+ text : "Cancel" ,
310
+ onPress : ( ) => console . log ( "Cancel Pressed" ) ,
311
+ style : "cancel"
312
+ } ,
313
+ ] ,
314
+ { cancelable : false }
315
+ ) ;
316
+ } ;
317
+ }
318
+ async checkPowerManagerRestrictions ( ) {
319
+ const powerManagerInfo = await notifee . getPowerManagerInfo ( ) ;
320
+ console . log ( "powerManagerInfo" , powerManagerInfo . activity )
321
+ if ( powerManagerInfo . activity ) {
322
+ // 2. ask your users to adjust their settings
323
+ Alert . alert (
324
+ 'Restrictions Detected' ,
325
+ 'To ensure notifications are delivered, please adjust your settings to prevent the app from being killed' ,
326
+ [
327
+ // 3. launch intent to navigate the user to the appropriate screen
328
+ {
329
+ text : 'OK, open settings' ,
330
+ onPress : async ( ) => await notifee . openPowerManagerSettings ( ) ,
331
+ } ,
332
+ {
333
+ text : "Cancel" ,
334
+ onPress : ( ) => console . log ( "Cancel Pressed" ) ,
335
+ style : "cancel"
336
+ } ,
337
+ ] ,
338
+ { cancelable : false }
339
+ ) ;
340
+ } ;
341
+ }
342
+ //////////////////////////// permission stuffs /////////////////////////
343
+
5
344
render ( ) {
6
- return (
7
- < Routes />
8
- )
345
+ if ( this . state . userID != '' && this . state . zegoToken != '' && this . state . fcmToken != '' ) {
346
+ var appData = {
347
+ appID : config . appID ,
348
+ serverUrl : config . serverUrl ,
349
+ userID : this . state . userID ,
350
+ zegoToken : this . state . zegoToken ,
351
+ }
352
+ return (
353
+ < Routes appData = { appData } ref = { instance => { this . routesInstance = instance ; } } />
354
+ )
355
+ } else {
356
+ return ( null )
357
+ }
9
358
}
10
359
}
11
360
export default App
0 commit comments