@stream-io/video-react-native-sdk 1.10.13 → 1.10.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/commonjs/hooks/push/index.js +0 -2
  3. package/dist/commonjs/hooks/push/index.js.map +1 -1
  4. package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js +20 -23
  5. package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +1 -1
  6. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js +6 -103
  7. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  8. package/dist/commonjs/utils/StreamVideoRN/index.js +4 -0
  9. package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
  10. package/dist/commonjs/utils/push/ios.js +95 -1
  11. package/dist/commonjs/utils/push/ios.js.map +1 -1
  12. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js +150 -0
  13. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js.map +1 -0
  14. package/dist/commonjs/utils/push/setupIosVoipPushEvents.js +27 -0
  15. package/dist/commonjs/utils/push/setupIosVoipPushEvents.js.map +1 -0
  16. package/dist/commonjs/version.js +1 -1
  17. package/dist/module/hooks/push/index.js +0 -2
  18. package/dist/module/hooks/push/index.js.map +1 -1
  19. package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js +21 -24
  20. package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +1 -1
  21. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js +10 -108
  22. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  23. package/dist/module/utils/StreamVideoRN/index.js +4 -0
  24. package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
  25. package/dist/module/utils/push/ios.js +97 -4
  26. package/dist/module/utils/push/ios.js.map +1 -1
  27. package/dist/module/utils/push/setupIosCallKeepEvents.js +145 -0
  28. package/dist/module/utils/push/setupIosCallKeepEvents.js.map +1 -0
  29. package/dist/module/utils/push/setupIosVoipPushEvents.js +21 -0
  30. package/dist/module/utils/push/setupIosVoipPushEvents.js.map +1 -0
  31. package/dist/module/version.js +1 -1
  32. package/dist/typescript/hooks/push/index.d.ts.map +1 -1
  33. package/dist/typescript/hooks/push/useIosCallkeepWithCallingStateEffect.d.ts.map +1 -1
  34. package/dist/typescript/hooks/push/useIosVoipPushEventsSetupEffect.d.ts.map +1 -1
  35. package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
  36. package/dist/typescript/utils/push/ios.d.ts +1 -0
  37. package/dist/typescript/utils/push/ios.d.ts.map +1 -1
  38. package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts +6 -0
  39. package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts.map +1 -0
  40. package/dist/typescript/utils/push/setupIosVoipPushEvents.d.ts +3 -0
  41. package/dist/typescript/utils/push/setupIosVoipPushEvents.d.ts.map +1 -0
  42. package/dist/typescript/version.d.ts +1 -1
  43. package/ios/StreamVideoReactNative.m +72 -22
  44. package/package.json +3 -3
  45. package/src/hooks/push/index.ts +0 -2
  46. package/src/hooks/push/useIosCallkeepWithCallingStateEffect.ts +37 -31
  47. package/src/hooks/push/useIosVoipPushEventsSetupEffect.ts +10 -145
  48. package/src/utils/StreamVideoRN/index.ts +5 -0
  49. package/src/utils/push/ios.ts +135 -3
  50. package/src/utils/push/setupIosCallKeepEvents.ts +187 -0
  51. package/src/utils/push/setupIosVoipPushEvents.ts +29 -0
  52. package/src/version.ts +1 -1
  53. package/dist/commonjs/hooks/push/useIosCallKeepEventsSetupEffect.js +0 -109
  54. package/dist/commonjs/hooks/push/useIosCallKeepEventsSetupEffect.js.map +0 -1
  55. package/dist/module/hooks/push/useIosCallKeepEventsSetupEffect.js +0 -103
  56. package/dist/module/hooks/push/useIosCallKeepEventsSetupEffect.js.map +0 -1
  57. package/dist/typescript/hooks/push/useIosCallKeepEventsSetupEffect.d.ts +0 -5
  58. package/dist/typescript/hooks/push/useIosCallKeepEventsSetupEffect.d.ts.map +0 -1
  59. package/src/hooks/push/useIosCallKeepEventsSetupEffect.ts +0 -132
@@ -1,6 +1,6 @@
1
1
  import { CallingState, getLogger, RxUtils } from '@stream-io/video-client';
2
2
  import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
3
- import { Platform } from 'react-native';
3
+ import { NativeModules, Platform } from 'react-native';
4
4
  import { useEffect, useState } from 'react';
5
5
  import { StreamVideoRN } from '../../utils';
6
6
  import { getCallKeepLib } from '../../utils/push/libs';
@@ -25,7 +25,7 @@ const isAcceptedCallingState = (callingState: CallingState) => {
25
25
  );
26
26
  };
27
27
 
28
- const unsubscribeCallkeepEvents = (activeCallCid: string | undefined) => {
28
+ const unsubscribeCallkeepEvents = async (activeCallCid: string | undefined) => {
29
29
  const voipPushNotificationCallCId = RxUtils.getCurrentValue(
30
30
  voipPushNotificationCallCId$
31
31
  );
@@ -33,9 +33,15 @@ const unsubscribeCallkeepEvents = (activeCallCid: string | undefined) => {
33
33
  // callkeep events should not be listened anymore so clear the call cid
34
34
  voipPushNotificationCallCId$.next(undefined);
35
35
  }
36
+ return await NativeModules.StreamVideoReactNative?.removeIncomingCall(
37
+ activeCallCid
38
+ );
36
39
  };
37
40
 
38
41
  const logger = getLogger(['useIosCallkeepWithCallingStateEffect']);
42
+ const log = (message: string) => {
43
+ logger('warn', message);
44
+ };
39
45
 
40
46
  /**
41
47
  * This hook is used to inform the callkeep library that the call has been joined or ended.
@@ -59,13 +65,12 @@ export const useIosCallkeepWithCallingStateEffect = () => {
59
65
  const callkeep = getCallKeepLib();
60
66
  // if the component is unmounted and the callID was not reported to callkeep, then report it now
61
67
  if (acceptedForegroundCallkeepMap) {
62
- logger(
63
- 'debug',
68
+ log(
64
69
  `Ending call in callkeep: ${acceptedForegroundCallkeepMap.cid}, reason: component unmounted and call was present in acceptedForegroundCallkeepMap`
65
70
  );
66
- unsubscribeCallkeepEvents(acceptedForegroundCallkeepMap.cid);
67
- // this call should be ended in callkeep
68
- callkeep.endCall(acceptedForegroundCallkeepMap.uuid);
71
+ unsubscribeCallkeepEvents(acceptedForegroundCallkeepMap.cid).then(() =>
72
+ callkeep.endCall(acceptedForegroundCallkeepMap.uuid)
73
+ );
69
74
  }
70
75
  };
71
76
  }, [acceptedForegroundCallkeepMap]);
@@ -86,23 +91,21 @@ export const useIosCallkeepWithCallingStateEffect = () => {
86
91
  );
87
92
  const callkeep = getCallKeepLib();
88
93
  if (activeCallCid === nativeDialerAcceptedCallMap?.cid) {
89
- logger(
90
- 'debug',
94
+ log(
91
95
  `Ending call in callkeep: ${activeCallCid}, reason: activeCallCid changed or was removed and call was present in nativeDialerAcceptedCallMap`
92
96
  );
93
- unsubscribeCallkeepEvents(activeCallCid);
94
- callkeep.endCall(nativeDialerAcceptedCallMap.uuid);
97
+ unsubscribeCallkeepEvents(activeCallCid).then(() =>
98
+ callkeep.endCall(nativeDialerAcceptedCallMap.uuid)
99
+ );
95
100
  // no need to keep this reference anymore
96
101
  voipCallkeepAcceptedCallOnNativeDialerMap$.next(undefined);
97
102
  } else if (activeCallCid === foregroundIncomingCallkeepMap?.cid) {
98
- logger(
99
- 'debug',
103
+ log(
100
104
  `Ending call in callkeep: ${activeCallCid}, reason: activeCallCid changed or was removed and call was present in foregroundIncomingCallkeepMap`
101
105
  );
102
- unsubscribeCallkeepEvents(activeCallCid);
103
- callkeep.endCall(foregroundIncomingCallkeepMap.uuid);
104
- // no need to keep this reference anymore
105
- voipCallkeepCallOnForegroundMap$.next(undefined);
106
+ unsubscribeCallkeepEvents(activeCallCid).then(() =>
107
+ callkeep.endCall(foregroundIncomingCallkeepMap.uuid)
108
+ );
106
109
  }
107
110
  };
108
111
  }, [activeCallCid]);
@@ -126,15 +129,16 @@ export const useIosCallkeepWithCallingStateEffect = () => {
126
129
  voipCallkeepCallOnForegroundMap$
127
130
  );
128
131
  if (foregroundCallkeepMap && foregroundCallkeepMap.cid === activeCallCid) {
129
- logger(
130
- 'debug',
132
+ log(
131
133
  // @ts-ignore
132
134
  `Accepting call in callkeep: ${activeCallCid}, reason: callingstate went to ${CallingState[callingState]} and call was present in foregroundCallkeepMap`
133
135
  );
134
- // this call should be accepted in callkeep
135
- callkeep.answerIncomingCall(foregroundCallkeepMap.uuid);
136
136
  // no need to keep this reference anymore
137
137
  voipCallkeepCallOnForegroundMap$.next(undefined);
138
+ NativeModules.StreamVideoReactNative?.removeIncomingCall(
139
+ activeCallCid
140
+ ).then(() => callkeep.answerIncomingCall(foregroundCallkeepMap.uuid));
141
+ // this call should be accepted in callkeep
138
142
  setAcceptedForegroundCallkeepMap(foregroundCallkeepMap);
139
143
  }
140
144
  }
@@ -144,18 +148,18 @@ export const useIosCallkeepWithCallingStateEffect = () => {
144
148
  */
145
149
  if (isNonActiveCallingState(callingState)) {
146
150
  const callkeep = getCallKeepLib();
147
- unsubscribeCallkeepEvents(activeCallCid);
148
151
 
149
152
  // this was a previously joined call which had push notification displayed
150
153
  // the call was accepted through the app and not through native dialer
151
154
  // the call was left using the leave button in the app and not through native dialer
152
155
  if (activeCallCid === acceptedForegroundCallkeepMap?.cid) {
153
- logger(
154
- 'debug',
156
+ log(
155
157
  // @ts-ignore
156
158
  `Ending call in callkeep: ${activeCallCid}, reason: callingstate went to ${CallingState[callingState]} and call was present in acceptedForegroundCallkeepMap`
157
159
  );
158
- callkeep.endCall(acceptedForegroundCallkeepMap.uuid);
160
+ unsubscribeCallkeepEvents(activeCallCid).then(() =>
161
+ callkeep.endCall(acceptedForegroundCallkeepMap.uuid)
162
+ );
159
163
  setAcceptedForegroundCallkeepMap(undefined);
160
164
  return;
161
165
  }
@@ -165,12 +169,13 @@ export const useIosCallkeepWithCallingStateEffect = () => {
165
169
  voipCallkeepCallOnForegroundMap$
166
170
  );
167
171
  if (activeCallCid === foregroundIncomingCallkeepMap?.cid) {
168
- logger(
169
- 'debug',
172
+ log(
170
173
  // @ts-ignore
171
174
  `Ending call in callkeep: ${activeCallCid}, reason: callingstate went to ${CallingState[callingState]} and call was present in foregroundIncomingCallkeepMap`
172
175
  );
173
- callkeep.endCall(foregroundIncomingCallkeepMap.uuid);
176
+ unsubscribeCallkeepEvents(activeCallCid).then(() =>
177
+ callkeep.endCall(foregroundIncomingCallkeepMap.uuid)
178
+ );
174
179
  // no need to keep this reference anymore
175
180
  voipCallkeepCallOnForegroundMap$.next(undefined);
176
181
  return;
@@ -182,12 +187,13 @@ export const useIosCallkeepWithCallingStateEffect = () => {
182
187
  voipCallkeepAcceptedCallOnNativeDialerMap$
183
188
  );
184
189
  if (activeCallCid === nativeDialerAcceptedCallMap?.cid) {
185
- logger(
186
- 'debug',
190
+ log(
187
191
  // @ts-ignore
188
192
  `Ending call in callkeep: ${activeCallCid}, reason: callingstate went to ${CallingState[callingState]} and call was present in nativeDialerAcceptedCallMap`
189
193
  );
190
- callkeep.endCall(nativeDialerAcceptedCallMap.uuid);
194
+ unsubscribeCallkeepEvents(activeCallCid).then(() =>
195
+ callkeep.endCall(nativeDialerAcceptedCallMap.uuid)
196
+ );
191
197
  // no need to keep this reference anymore
192
198
  voipCallkeepAcceptedCallOnNativeDialerMap$.next(undefined);
193
199
  return;
@@ -1,26 +1,14 @@
1
1
  import { type MutableRefObject, useEffect, useRef, useState } from 'react';
2
- import {
3
- getCallKeepLib,
4
- getVoipPushNotificationLib,
5
- } from '../../utils/push/libs';
2
+ import { getVoipPushNotificationLib } from '../../utils/push/libs';
6
3
 
7
- import { AppState, Platform } from 'react-native';
8
- import { StreamVideoRN } from '../../utils';
4
+ import { Platform } from 'react-native';
5
+ import { onVoipNotificationReceived, StreamVideoRN } from '../../utils';
9
6
  import {
10
7
  useConnectedUser,
11
8
  useStreamVideoClient,
12
9
  } from '@stream-io/video-react-bindings';
13
10
  import { setPushLogoutCallback } from '../../utils/internal/pushLogoutCallback';
14
- import { NativeModules } from 'react-native';
15
- import {
16
- canAddPushWSSubscriptionsRef,
17
- shouldCallBeEnded,
18
- } from '../../utils/push/internal/utils';
19
- import {
20
- pushUnsubscriptionCallbacks$,
21
- voipPushNotificationCallCId$,
22
- } from '../../utils/push/internal/rxSubjects';
23
- import { RxUtils, StreamVideoClient, getLogger } from '@stream-io/video-client';
11
+ import { StreamVideoClient, getLogger } from '@stream-io/video-client';
24
12
 
25
13
  const logger = getLogger(['useIosVoipPushEventsSetupEffect']);
26
14
 
@@ -59,6 +47,7 @@ export const useIosVoipPushEventsSetupEffect = () => {
59
47
  const lastVoipTokenRef = useRef({ token: '', userId: '' });
60
48
  const [unsentToken, setUnsentToken] = useState<string>();
61
49
 
50
+ // this effect is used to send the unsent token to stream
62
51
  useEffect(() => {
63
52
  const pushConfig = StreamVideoRN.getConfig().push;
64
53
  // we need to wait for user to be connected before we can send the push token
@@ -96,9 +85,11 @@ export const useIosVoipPushEventsSetupEffect = () => {
96
85
 
97
86
  useEffect(() => {
98
87
  const pushConfig = StreamVideoRN.getConfig().push;
99
- if (Platform.OS !== 'ios' || !pushConfig || !client) {
88
+ const pushProviderName = pushConfig?.ios.pushProviderName;
89
+ if (Platform.OS !== 'ios' || !pushConfig || !client || !pushProviderName) {
100
90
  return;
101
91
  }
92
+
102
93
  const voipPushNotification = getVoipPushNotificationLib();
103
94
 
104
95
  // even though we do this natively, we have to still register here again
@@ -124,16 +115,12 @@ export const useIosVoipPushEventsSetupEffect = () => {
124
115
  );
125
116
  return;
126
117
  }
127
- const push_provider_name = pushConfig.ios.pushProviderName;
128
- if (!push_provider_name) {
129
- return;
130
- }
131
118
  logger(
132
119
  'debug',
133
120
  `Sending voip token to stream, token: ${token} userId: ${userId}`
134
121
  );
135
122
  client
136
- .addVoipDevice(token, 'apn', push_provider_name)
123
+ .addVoipDevice(token, 'apn', pushProviderName)
137
124
  .then(() => {
138
125
  logger(
139
126
  'debug',
@@ -152,10 +139,6 @@ export const useIosVoipPushEventsSetupEffect = () => {
152
139
  onTokenReceived(token);
153
140
  });
154
141
 
155
- voipPushNotification.addEventListener('notification', (notification) => {
156
- onNotificationReceived(notification);
157
- });
158
-
159
142
  // this will fire when there are events occured before js bridge initialized
160
143
  voipPushNotification.addEventListener('didLoadWithEvents', (events) => {
161
144
  if (!events || !Array.isArray(events) || events.length < 1) {
@@ -166,7 +149,7 @@ export const useIosVoipPushEventsSetupEffect = () => {
166
149
  if (name === 'RNVoipPushRemoteNotificationsRegisteredEvent') {
167
150
  onTokenReceived(data);
168
151
  } else if (name === 'RNVoipPushRemoteNotificationReceivedEvent') {
169
- onNotificationReceived(data);
152
+ onVoipNotificationReceived(data);
170
153
  }
171
154
  }
172
155
  });
@@ -185,124 +168,6 @@ export const useIosVoipPushEventsSetupEffect = () => {
185
168
  logger('debug', 'Voip event listeners are removed for user: ' + userId);
186
169
  voipPushNotification.removeEventListener('didLoadWithEvents');
187
170
  voipPushNotification.removeEventListener('register');
188
- voipPushNotification.removeEventListener('notification');
189
171
  };
190
172
  }, [client]);
191
173
  };
192
-
193
- const onNotificationReceived = async (notification: any) => {
194
- /* --- Example payload ---
195
- {
196
- "aps": {
197
- "alert": {
198
- "body": "",
199
- "title": "Vishal Narkhede is calling you"
200
- },
201
- "badge": 0,
202
- "category": "stream.video",
203
- "mutable-content": 1
204
- },
205
- "stream": {
206
- "call_cid": "default:ixbm7y0k74pbjnq",
207
- "call_display_name": "",
208
- "created_by_display_name": "Vishal Narkhede",
209
- "created_by_id": "vishalexpo",
210
- "receiver_id": "santhoshexpo",
211
- "sender": "stream.video",
212
- "type": "call.ring",
213
- "version": "v2"
214
- }
215
- } */
216
- const sender = notification?.stream?.sender;
217
- const type = notification?.stream?.type;
218
- // do not process any other notifications other than stream.video or ringing
219
- if (sender !== 'stream.video' && type !== 'call.ring') {
220
- return;
221
- }
222
- const call_cid = notification?.stream?.call_cid;
223
- const pushConfig = StreamVideoRN.getConfig().push;
224
- if (!call_cid || Platform.OS !== 'ios' || !pushConfig) {
225
- return;
226
- }
227
- const client = await pushConfig.createStreamVideoClient();
228
- if (!client) {
229
- return;
230
- }
231
- const callFromPush = await client.onRingingCall(call_cid);
232
- let uuid = '';
233
- try {
234
- uuid =
235
- await NativeModules?.StreamVideoReactNative?.getIncomingCallUUid(
236
- call_cid
237
- );
238
- } catch (error) {
239
- logger('error', 'Error in getting call uuid from native module', error);
240
- }
241
- if (!uuid) {
242
- logger(
243
- 'error',
244
- `Not processing call.ring push notification, as no uuid found for call_cid: ${call_cid}`
245
- );
246
- return;
247
- }
248
- const created_by_id = notification?.stream?.created_by_id;
249
- const receiver_id = notification?.stream?.receiver_id;
250
- function closeCallIfNecessary() {
251
- const { mustEndCall, callkeepReason } = shouldCallBeEnded(
252
- callFromPush,
253
- created_by_id,
254
- receiver_id
255
- );
256
- if (mustEndCall) {
257
- const callkeep = getCallKeepLib();
258
- logger(
259
- 'debug',
260
- `callkeep.reportEndCallWithUUID for uuid: ${uuid}, call_cid: ${call_cid}, reason: ${callkeepReason}`
261
- );
262
- callkeep.reportEndCallWithUUID(uuid, callkeepReason);
263
- const voipPushNotification = getVoipPushNotificationLib();
264
- voipPushNotification.onVoipNotificationCompleted(uuid);
265
- return true;
266
- }
267
- return false;
268
- }
269
- const closed = closeCallIfNecessary();
270
- const canListenToWS = () =>
271
- canAddPushWSSubscriptionsRef.current && AppState.currentState !== 'active';
272
- if (!closed && canListenToWS()) {
273
- const unsubscribe = callFromPush.on('all', (event) => {
274
- const _canListenToWS = canListenToWS();
275
- if (!_canListenToWS) {
276
- logger(
277
- 'debug',
278
- `unsubscribe due to event callCid: ${call_cid} canListenToWS: ${_canListenToWS}`,
279
- event
280
- );
281
- unsubscribe();
282
- return;
283
- }
284
- const _closed = closeCallIfNecessary();
285
- if (_closed) {
286
- logger(
287
- 'debug',
288
- `unsubscribe due to event callCid: ${call_cid} canListenToWS: ${_canListenToWS} shouldCallBeClosed: ${_closed}`,
289
- event
290
- );
291
- unsubscribe();
292
- }
293
- });
294
- const unsubscriptionCallbacks =
295
- RxUtils.getCurrentValue(pushUnsubscriptionCallbacks$) ?? [];
296
- pushUnsubscriptionCallbacks$.next([
297
- ...unsubscriptionCallbacks,
298
- unsubscribe,
299
- ]);
300
- }
301
- // send the info to this subject, it is listened by callkeep events
302
- // callkeep events will then accept/reject the call
303
- logger(
304
- 'debug',
305
- `call_cid:${call_cid} uuid:${uuid} received and processed from call.ring push notification`
306
- );
307
- voipPushNotificationCallCId$.next(call_cid);
308
- };
@@ -3,6 +3,8 @@ import pushLogoutCallbacks from '../internal/pushLogoutCallback';
3
3
  import newNotificationCallbacks, {
4
4
  type NewCallNotificationCallback,
5
5
  } from '../internal/newNotificationCallbacks';
6
+ import { setupIosCallKeepEvents } from '../push/setupIosCallKeepEvents';
7
+ import { setupIosVoipPushEvents } from '../push/setupIosVoipPushEvents';
6
8
 
7
9
  const DEFAULT_STREAM_VIDEO_CONFIG: StreamVideoConfig = {
8
10
  foregroundService: {
@@ -83,6 +85,9 @@ export class StreamVideoRN {
83
85
  }
84
86
 
85
87
  this.config.push = pushConfig;
88
+
89
+ setupIosCallKeepEvents(pushConfig);
90
+ setupIosVoipPushEvents(pushConfig);
86
91
  }
87
92
 
88
93
  static getConfig() {
@@ -1,18 +1,28 @@
1
- import { Platform } from 'react-native';
1
+ import { AppState, NativeModules, Platform } from 'react-native';
2
2
  import type { StreamVideoConfig } from '../StreamVideoRN/types';
3
- import { pushNonRingingCallData$ } from './internal/rxSubjects';
3
+ import {
4
+ pushNonRingingCallData$,
5
+ pushUnsubscriptionCallbacks$,
6
+ voipPushNotificationCallCId$,
7
+ } from './internal/rxSubjects';
4
8
  import {
5
9
  type ExpoNotification,
10
+ getCallKeepLib,
6
11
  getExpoNotificationsLib,
7
12
  getNotifeeLibThrowIfNotInstalledForPush,
8
13
  getPushNotificationIosLib,
14
+ getVoipPushNotificationLib,
9
15
  type PushNotificationiOSType,
10
16
  } from './libs';
11
- import { StreamVideoClient, getLogger } from '@stream-io/video-client';
17
+ import { RxUtils, StreamVideoClient, getLogger } from '@stream-io/video-client';
12
18
  import { setPushLogoutCallback } from '../internal/pushLogoutCallback';
13
19
  import type { Event } from '@notifee/react-native';
14
20
  import { StreamVideoRN } from '../StreamVideoRN';
15
21
  import type { StreamPushPayload } from './utils';
22
+ import {
23
+ shouldCallBeEnded,
24
+ canAddPushWSSubscriptionsRef,
25
+ } from './internal/utils';
16
26
 
17
27
  type PushConfig = NonNullable<StreamVideoConfig['push']>;
18
28
 
@@ -208,3 +218,125 @@ export async function initIosNonVoipToken(
208
218
  });
209
219
  }
210
220
  }
221
+
222
+ export const onVoipNotificationReceived = async (notification: any) => {
223
+ /* --- Example payload ---
224
+ {
225
+ "aps": {
226
+ "alert": {
227
+ "body": "",
228
+ "title": "Vishal Narkhede is calling you"
229
+ },
230
+ "badge": 0,
231
+ "category": "stream.video",
232
+ "mutable-content": 1
233
+ },
234
+ "stream": {
235
+ "call_cid": "default:ixbm7y0k74pbjnq",
236
+ "call_display_name": "",
237
+ "created_by_display_name": "Vishal Narkhede",
238
+ "created_by_id": "vishalexpo",
239
+ "receiver_id": "santhoshexpo",
240
+ "sender": "stream.video",
241
+ "type": "call.ring",
242
+ "version": "v2"
243
+ }
244
+ } */
245
+ const sender = notification?.stream?.sender;
246
+ const type = notification?.stream?.type;
247
+ // do not process any other notifications other than stream.video or ringing
248
+ if (sender !== 'stream.video' && type !== 'call.ring') {
249
+ return;
250
+ }
251
+ const call_cid = notification?.stream?.call_cid;
252
+ const pushConfig = StreamVideoRN.getConfig().push;
253
+ if (!call_cid || Platform.OS !== 'ios' || !pushConfig) {
254
+ return;
255
+ }
256
+ const logger = getLogger(['setupIosVoipPushEvents']);
257
+ const client = await pushConfig.createStreamVideoClient();
258
+ if (!client) {
259
+ logger(
260
+ 'debug',
261
+ 'client not found, not processing call.ring voip push notification'
262
+ );
263
+ return;
264
+ }
265
+ const callFromPush = await client.onRingingCall(call_cid);
266
+ let uuid = '';
267
+ try {
268
+ uuid =
269
+ await NativeModules?.StreamVideoReactNative?.getIncomingCallUUid(
270
+ call_cid
271
+ );
272
+ } catch (error) {
273
+ logger('error', 'Error in getting call uuid from native module', error);
274
+ }
275
+ if (!uuid) {
276
+ logger(
277
+ 'error',
278
+ `Not processing call.ring push notification, as no uuid found for call_cid: ${call_cid}`
279
+ );
280
+ return;
281
+ }
282
+ const created_by_id = notification?.stream?.created_by_id;
283
+ const receiver_id = notification?.stream?.receiver_id;
284
+ function closeCallIfNecessary() {
285
+ const { mustEndCall, callkeepReason } = shouldCallBeEnded(
286
+ callFromPush,
287
+ created_by_id,
288
+ receiver_id
289
+ );
290
+ if (mustEndCall) {
291
+ const callkeep = getCallKeepLib();
292
+ logger(
293
+ 'debug',
294
+ `callkeep.reportEndCallWithUUID for uuid: ${uuid}, call_cid: ${call_cid}, reason: ${callkeepReason}`
295
+ );
296
+ callkeep.reportEndCallWithUUID(uuid, callkeepReason);
297
+ const voipPushNotification = getVoipPushNotificationLib();
298
+ voipPushNotification.onVoipNotificationCompleted(uuid);
299
+ return true;
300
+ }
301
+ return false;
302
+ }
303
+ const closed = closeCallIfNecessary();
304
+ const canListenToWS = () =>
305
+ canAddPushWSSubscriptionsRef.current && AppState.currentState !== 'active';
306
+ if (!closed && canListenToWS()) {
307
+ const unsubscribe = callFromPush.on('all', (event) => {
308
+ const _canListenToWS = canListenToWS();
309
+ if (!_canListenToWS) {
310
+ logger(
311
+ 'debug',
312
+ `unsubscribe due to event callCid: ${call_cid} canListenToWS: ${_canListenToWS}`,
313
+ event
314
+ );
315
+ unsubscribe();
316
+ return;
317
+ }
318
+ const _closed = closeCallIfNecessary();
319
+ if (_closed) {
320
+ logger(
321
+ 'debug',
322
+ `unsubscribe due to event callCid: ${call_cid} canListenToWS: ${_canListenToWS} shouldCallBeClosed: ${_closed}`,
323
+ event
324
+ );
325
+ unsubscribe();
326
+ }
327
+ });
328
+ const unsubscriptionCallbacks =
329
+ RxUtils.getCurrentValue(pushUnsubscriptionCallbacks$) ?? [];
330
+ pushUnsubscriptionCallbacks$.next([
331
+ ...unsubscriptionCallbacks,
332
+ unsubscribe,
333
+ ]);
334
+ }
335
+ // send the info to this subject, it is listened by callkeep events
336
+ // callkeep events will then accept/reject the call
337
+ logger(
338
+ 'debug',
339
+ `call_cid:${call_cid} uuid:${uuid} received and processed from call.ring push notification`
340
+ );
341
+ voipPushNotificationCallCId$.next(call_cid);
342
+ };