@stream-io/video-react-native-sdk 0.1.2 → 0.1.4
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.
- package/CHANGELOG.md +22 -0
- package/dist/commonjs/components/Call/CallControls/HangupCallButton.js +4 -6
- package/dist/commonjs/components/Call/CallControls/HangupCallButton.js.map +1 -1
- package/dist/commonjs/hooks/push/index.js +4 -0
- package/dist/commonjs/hooks/push/index.js.map +1 -1
- package/dist/commonjs/hooks/push/useInitAndroidTokenAndRest.js +8 -2
- package/dist/commonjs/hooks/push/useInitAndroidTokenAndRest.js.map +1 -1
- package/dist/commonjs/hooks/push/useIosInitRemoteNotifications.js +34 -0
- package/dist/commonjs/hooks/push/useIosInitRemoteNotifications.js.map +1 -0
- package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js +50 -4
- package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
- package/dist/commonjs/hooks/push/useProcessPushNonRingingCallEffect.js +50 -0
- package/dist/commonjs/hooks/push/useProcessPushNonRingingCallEffect.js.map +1 -0
- package/dist/commonjs/utils/StreamVideoRN/index.js +33 -0
- package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/commonjs/utils/internal/newNotificationCallbacks.js +18 -0
- package/dist/commonjs/utils/internal/newNotificationCallbacks.js.map +1 -0
- package/dist/commonjs/utils/internal/pushLogoutCallback.js +18 -0
- package/dist/commonjs/utils/internal/pushLogoutCallback.js.map +1 -0
- package/dist/commonjs/utils/push/android.js +182 -68
- package/dist/commonjs/utils/push/android.js.map +1 -1
- package/dist/commonjs/utils/push/ios.js +107 -1
- package/dist/commonjs/utils/push/ios.js.map +1 -1
- package/dist/commonjs/utils/push/libs.js +34 -1
- package/dist/commonjs/utils/push/libs.js.map +1 -1
- package/dist/commonjs/utils/push/rxSubjects.js +8 -1
- package/dist/commonjs/utils/push/rxSubjects.js.map +1 -1
- package/dist/commonjs/utils/push/utils.js +29 -1
- package/dist/commonjs/utils/push/utils.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/module/components/Call/CallControls/HangupCallButton.js +3 -4
- package/dist/module/components/Call/CallControls/HangupCallButton.js.map +1 -1
- package/dist/module/hooks/push/index.js +4 -0
- package/dist/module/hooks/push/index.js.map +1 -1
- package/dist/module/hooks/push/useInitAndroidTokenAndRest.js +8 -2
- package/dist/module/hooks/push/useInitAndroidTokenAndRest.js.map +1 -1
- package/dist/module/hooks/push/useIosInitRemoteNotifications.js +28 -0
- package/dist/module/hooks/push/useIosInitRemoteNotifications.js.map +1 -0
- package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js +49 -4
- package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
- package/dist/module/hooks/push/useProcessPushNonRingingCallEffect.js +44 -0
- package/dist/module/hooks/push/useProcessPushNonRingingCallEffect.js.map +1 -0
- package/dist/module/utils/StreamVideoRN/index.js +32 -0
- package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/module/utils/internal/newNotificationCallbacks.js +10 -0
- package/dist/module/utils/internal/newNotificationCallbacks.js.map +1 -0
- package/dist/module/utils/internal/pushLogoutCallback.js +10 -0
- package/dist/module/utils/internal/pushLogoutCallback.js.map +1 -0
- package/dist/module/utils/push/android.js +184 -70
- package/dist/module/utils/push/android.js.map +1 -1
- package/dist/module/utils/push/ios.js +103 -1
- package/dist/module/utils/push/ios.js.map +1 -1
- package/dist/module/utils/push/libs.js +31 -1
- package/dist/module/utils/push/libs.js.map +1 -1
- package/dist/module/utils/push/rxSubjects.js +5 -0
- package/dist/module/utils/push/rxSubjects.js.map +1 -1
- package/dist/module/utils/push/utils.js +27 -0
- package/dist/module/utils/push/utils.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/hooks/push/index.d.ts.map +1 -1
- package/dist/typescript/hooks/push/useInitAndroidTokenAndRest.d.ts +1 -1
- package/dist/typescript/hooks/push/useInitAndroidTokenAndRest.d.ts.map +1 -1
- package/dist/typescript/hooks/push/useIosInitRemoteNotifications.d.ts +5 -0
- package/dist/typescript/hooks/push/useIosInitRemoteNotifications.d.ts.map +1 -0
- package/dist/typescript/hooks/push/useIosVoipPushEventsSetupEffect.d.ts.map +1 -1
- package/dist/typescript/hooks/push/useProcessPushNonRingingCallEffect.d.ts +7 -0
- package/dist/typescript/hooks/push/useProcessPushNonRingingCallEffect.d.ts.map +1 -0
- package/dist/typescript/utils/StreamVideoRN/index.d.ts +12 -0
- package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
- package/dist/typescript/utils/StreamVideoRN/types.d.ts +40 -6
- package/dist/typescript/utils/StreamVideoRN/types.d.ts.map +1 -1
- package/dist/typescript/utils/internal/newNotificationCallbacks.d.ts +10 -0
- package/dist/typescript/utils/internal/newNotificationCallbacks.d.ts.map +1 -0
- package/dist/typescript/utils/internal/pushLogoutCallback.d.ts +8 -0
- package/dist/typescript/utils/internal/pushLogoutCallback.d.ts.map +1 -0
- package/dist/typescript/utils/push/android.d.ts +1 -1
- package/dist/typescript/utils/push/android.d.ts.map +1 -1
- package/dist/typescript/utils/push/ios.d.ts +4 -0
- package/dist/typescript/utils/push/ios.d.ts.map +1 -1
- package/dist/typescript/utils/push/libs.d.ts +6 -0
- package/dist/typescript/utils/push/libs.d.ts.map +1 -1
- package/dist/typescript/utils/push/rxSubjects.d.ts +9 -0
- package/dist/typescript/utils/push/rxSubjects.d.ts.map +1 -1
- package/dist/typescript/utils/push/utils.d.ts +9 -1
- package/dist/typescript/utils/push/utils.d.ts.map +1 -1
- package/dist/typescript/version.d.ts +1 -1
- package/expo-config-plugin/dist/index.d.ts +3 -2
- package/expo-config-plugin/dist/index.js +9 -5
- package/expo-config-plugin/dist/withPushAppDelegate.d.ts +4 -0
- package/expo-config-plugin/dist/withPushAppDelegate.js +119 -0
- package/expo-config-plugin/dist/withiOSInfoPlist.d.ts +2 -1
- package/expo-config-plugin/dist/withiOSInfoPlist.js +6 -1
- package/package.json +19 -3
- package/src/components/Call/CallControls/HangupCallButton.tsx +4 -4
- package/src/hooks/push/index.ts +4 -0
- package/src/hooks/push/useInitAndroidTokenAndRest.ts +8 -2
- package/src/hooks/push/useIosInitRemoteNotifications.ts +29 -0
- package/src/hooks/push/useIosVoipPushEventsSetupEffect.ts +50 -3
- package/src/hooks/push/useProcessPushNonRingingCallEffect.ts +44 -0
- package/src/utils/StreamVideoRN/index.ts +36 -0
- package/src/utils/StreamVideoRN/types.ts +47 -6
- package/src/utils/internal/newNotificationCallbacks.ts +29 -0
- package/src/utils/internal/pushLogoutCallback.ts +17 -0
- package/src/utils/push/android.ts +203 -74
- package/src/utils/push/ios.ts +120 -1
- package/src/utils/push/libs.ts +48 -2
- package/src/utils/push/rxSubjects.ts +9 -0
- package/src/utils/push/utils.ts +35 -1
- package/src/version.ts +1 -1
- /package/expo-config-plugin/dist/{withAppDelegate.d.ts → withStreamVideoReactNativeSDKAppDelegate.d.ts} +0 -0
- /package/expo-config-plugin/dist/{withAppDelegate.js → withStreamVideoReactNativeSDKAppDelegate.js} +0 -0
|
@@ -2,14 +2,23 @@ import notifee, { EventType, Event } from '@notifee/react-native';
|
|
|
2
2
|
import { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
|
|
3
3
|
import { StreamVideoClient } from '@stream-io/video-client';
|
|
4
4
|
import { Platform } from 'react-native';
|
|
5
|
-
import type {
|
|
6
|
-
|
|
5
|
+
import type {
|
|
6
|
+
NonRingingPushEvent,
|
|
7
|
+
StreamVideoConfig,
|
|
8
|
+
} from '../StreamVideoRN/types';
|
|
9
|
+
import {
|
|
10
|
+
getFirebaseMessagingLib,
|
|
11
|
+
getExpoNotificationsLib,
|
|
12
|
+
getExpoTaskManagerLib,
|
|
13
|
+
} from './libs';
|
|
7
14
|
import {
|
|
8
15
|
pushAcceptedIncomingCallCId$,
|
|
9
16
|
pushRejectedIncomingCallCId$,
|
|
10
17
|
pushTappedIncomingCallCId$,
|
|
18
|
+
pushNonRingingCallData$,
|
|
11
19
|
} from './rxSubjects';
|
|
12
20
|
import { processCallFromPushInBackground } from './utils';
|
|
21
|
+
import { setPushLogoutCallback } from '../internal/pushLogoutCallback';
|
|
13
22
|
|
|
14
23
|
const ACCEPT_CALL_ACTION_ID = 'accept';
|
|
15
24
|
const DECLINE_CALL_ACTION_ID = 'decline';
|
|
@@ -21,17 +30,59 @@ export function setupFirebaseHandlerAndroid(pushConfig: PushConfig) {
|
|
|
21
30
|
if (Platform.OS !== 'android') {
|
|
22
31
|
return;
|
|
23
32
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
if (pushConfig.isExpo) {
|
|
34
|
+
const Notifications = getExpoNotificationsLib();
|
|
35
|
+
const TaskManager = getExpoTaskManagerLib();
|
|
36
|
+
const BACKGROUND_NOTIFICATION_TASK =
|
|
37
|
+
'STREAM-VIDEO-SDK-INTERNAL-BACKGROUND-NOTIFICATION-TASK';
|
|
38
|
+
|
|
39
|
+
TaskManager.defineTask(BACKGROUND_NOTIFICATION_TASK, ({ data, error }) => {
|
|
40
|
+
if (error) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
const dataToProcess = data.notification?.data;
|
|
45
|
+
firebaseMessagingOnMessageHandler(dataToProcess, pushConfig);
|
|
46
|
+
});
|
|
47
|
+
// background handler
|
|
48
|
+
Notifications.registerTaskAsync(BACKGROUND_NOTIFICATION_TASK);
|
|
49
|
+
// foreground handler
|
|
50
|
+
Notifications.setNotificationHandler({
|
|
51
|
+
handleNotification: async (notification) => {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
const trigger = notification?.request?.trigger;
|
|
54
|
+
if (trigger.type === 'push') {
|
|
55
|
+
const data = trigger?.remoteMessage?.data;
|
|
56
|
+
if (data?.sender === 'stream.video') {
|
|
57
|
+
await firebaseMessagingOnMessageHandler(data, pushConfig);
|
|
58
|
+
return {
|
|
59
|
+
shouldShowAlert: false,
|
|
60
|
+
shouldPlaySound: false,
|
|
61
|
+
shouldSetBadge: false,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
shouldShowAlert: true,
|
|
67
|
+
shouldPlaySound: false,
|
|
68
|
+
shouldSetBadge: false,
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
const messaging = getFirebaseMessagingLib();
|
|
74
|
+
messaging().setBackgroundMessageHandler(
|
|
75
|
+
async (msg) =>
|
|
76
|
+
await firebaseMessagingOnMessageHandler(msg.data, pushConfig),
|
|
77
|
+
);
|
|
78
|
+
messaging().onMessage((msg) =>
|
|
79
|
+
firebaseMessagingOnMessageHandler(msg.data, pushConfig),
|
|
80
|
+
); // this is to listen to foreground messages, which we dont need for now
|
|
81
|
+
}
|
|
29
82
|
notifee.onBackgroundEvent(async (event) => {
|
|
30
|
-
// NOTE: When app was opened from a quit state, we will never hit this when on accept event as app will open and the click event will go to foreground
|
|
31
83
|
await onNotifeeEvent(event, pushConfig);
|
|
32
84
|
});
|
|
33
85
|
notifee.onForegroundEvent((event) => {
|
|
34
|
-
// NOTE: When app was opened from a quit state, we will never hit this when on accept event as app will open and go to foreground immediately
|
|
35
86
|
onNotifeeEvent(event, pushConfig);
|
|
36
87
|
});
|
|
37
88
|
}
|
|
@@ -40,23 +91,47 @@ export function setupFirebaseHandlerAndroid(pushConfig: PushConfig) {
|
|
|
40
91
|
export async function initAndroidPushToken(
|
|
41
92
|
client: StreamVideoClient,
|
|
42
93
|
pushConfig: PushConfig,
|
|
94
|
+
setUnsubscribeListener: (unsubscribe: () => void) => void,
|
|
43
95
|
) {
|
|
44
|
-
if (Platform.OS !== 'android') {
|
|
96
|
+
if (Platform.OS !== 'android' || !pushConfig.android.pushProviderName) {
|
|
45
97
|
return;
|
|
46
98
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
99
|
+
const setDeviceToken = async (token: string) => {
|
|
100
|
+
setPushLogoutCallback(() => {
|
|
101
|
+
client.removeDevice(token).catch((err) => {
|
|
102
|
+
console.warn('Failed to remove voip token from stream', err);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
const push_provider_name = pushConfig.android.pushProviderName;
|
|
106
|
+
await client.addDevice(token, 'firebase', push_provider_name);
|
|
107
|
+
};
|
|
108
|
+
if (pushConfig.isExpo) {
|
|
109
|
+
const expoNotificationsLib = getExpoNotificationsLib();
|
|
110
|
+
const subscription = expoNotificationsLib.addPushTokenListener(
|
|
111
|
+
(devicePushToken) => {
|
|
112
|
+
setDeviceToken(devicePushToken.data);
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
setUnsubscribeListener(() => subscription.remove());
|
|
116
|
+
const devicePushToken =
|
|
117
|
+
await expoNotificationsLib.getDevicePushTokenAsync();
|
|
118
|
+
const token = devicePushToken.data;
|
|
119
|
+
await setDeviceToken(token);
|
|
120
|
+
} else {
|
|
121
|
+
const messaging = getFirebaseMessagingLib();
|
|
122
|
+
const unsubscribe = messaging().onTokenRefresh((refreshedToken) =>
|
|
123
|
+
setDeviceToken(refreshedToken),
|
|
124
|
+
);
|
|
125
|
+
setUnsubscribeListener(unsubscribe);
|
|
126
|
+
const token = await messaging().getToken();
|
|
127
|
+
await setDeviceToken(token);
|
|
128
|
+
}
|
|
51
129
|
}
|
|
52
130
|
|
|
53
131
|
const firebaseMessagingOnMessageHandler = async (
|
|
54
|
-
|
|
132
|
+
data: FirebaseMessagingTypes.RemoteMessage['data'],
|
|
55
133
|
pushConfig: PushConfig,
|
|
56
134
|
) => {
|
|
57
|
-
if (Platform.OS !== 'android') {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
135
|
/* Example data from firebase
|
|
61
136
|
"message": {
|
|
62
137
|
"data": {
|
|
@@ -72,43 +147,86 @@ const firebaseMessagingOnMessageHandler = async (
|
|
|
72
147
|
// other stuff
|
|
73
148
|
}
|
|
74
149
|
*/
|
|
75
|
-
const data = message.data;
|
|
76
150
|
if (!data || data.sender !== 'stream.video') {
|
|
77
151
|
return;
|
|
78
152
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
pushConfig.android.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
153
|
+
|
|
154
|
+
if (data.type === 'call.ring') {
|
|
155
|
+
const incomingCallChannel = pushConfig.android.incomingCallChannel;
|
|
156
|
+
const incomingCallNotificationTextGetters =
|
|
157
|
+
pushConfig.android.incomingCallNotificationTextGetters;
|
|
158
|
+
if (!incomingCallChannel || !incomingCallNotificationTextGetters) {
|
|
159
|
+
console.debug(
|
|
160
|
+
"Can't show incoming call notification as either or both incomingCallChannel and was not provided",
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
await notifee.createChannel(incomingCallChannel);
|
|
165
|
+
const { getTitle, getBody } = incomingCallNotificationTextGetters;
|
|
166
|
+
const createdUserName = data.created_by_display_name;
|
|
167
|
+
|
|
168
|
+
const channelId = incomingCallChannel.id;
|
|
169
|
+
await notifee.displayNotification({
|
|
170
|
+
title: getTitle(createdUserName),
|
|
171
|
+
body: getBody(createdUserName),
|
|
172
|
+
data,
|
|
173
|
+
android: {
|
|
174
|
+
channelId,
|
|
175
|
+
pressAction: {
|
|
176
|
+
id: 'default',
|
|
177
|
+
launchActivity: 'default', // open the app when the notification is pressed
|
|
100
178
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
179
|
+
actions: [
|
|
180
|
+
{
|
|
181
|
+
title: 'Decline',
|
|
182
|
+
pressAction: {
|
|
183
|
+
id: DECLINE_CALL_ACTION_ID,
|
|
184
|
+
},
|
|
106
185
|
},
|
|
186
|
+
{
|
|
187
|
+
title: 'Accept',
|
|
188
|
+
pressAction: {
|
|
189
|
+
id: ACCEPT_CALL_ACTION_ID,
|
|
190
|
+
launchActivity: 'default', // open the app when the notification is pressed
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
timeoutAfter: 60000, // 60 seconds, after which the notification will be dismissed automatically
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
// the other types are call.live_started and call.notification
|
|
199
|
+
const callChannel = pushConfig.android.callChannel;
|
|
200
|
+
const callNotificationTextGetters =
|
|
201
|
+
pushConfig.android.callNotificationTextGetters;
|
|
202
|
+
if (!callChannel || !callNotificationTextGetters) {
|
|
203
|
+
console.debug(
|
|
204
|
+
"Can't show call notification as either or both callChannel and callNotificationTextGetters is not provided",
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
await notifee.createChannel(callChannel);
|
|
209
|
+
const channelId = callChannel.id;
|
|
210
|
+
const { getTitle, getBody } = callNotificationTextGetters;
|
|
211
|
+
const createdUserName = data.created_by_display_name;
|
|
212
|
+
// we can safely cast to string because the data is from "stream.video"
|
|
213
|
+
const type = data.type as NonRingingPushEvent;
|
|
214
|
+
await notifee.displayNotification({
|
|
215
|
+
title: getTitle(type, createdUserName),
|
|
216
|
+
body: getBody(type, createdUserName),
|
|
217
|
+
data,
|
|
218
|
+
android: {
|
|
219
|
+
channelId,
|
|
220
|
+
pressAction: {
|
|
221
|
+
id: 'default',
|
|
222
|
+
launchActivity: 'default', // open the app when the notification is pressed
|
|
107
223
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
224
|
+
timeoutAfter: 60000, // 60 seconds, after which the notification will be dismissed automatically
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
const cid = data.call_cid;
|
|
228
|
+
pushNonRingingCallData$.next({ cid, type });
|
|
229
|
+
}
|
|
112
230
|
};
|
|
113
231
|
|
|
114
232
|
const onNotifeeEvent = async (event: Event, pushConfig: PushConfig) => {
|
|
@@ -128,32 +246,43 @@ const onNotifeeEvent = async (event: Event, pushConfig: PushConfig) => {
|
|
|
128
246
|
// we can safely cast to string because the data is from "stream.video"
|
|
129
247
|
const call_cid = data.call_cid as string;
|
|
130
248
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
249
|
+
if (data.type === 'call.ring') {
|
|
250
|
+
// check if we have observers for the call cid (this means the app is in the foreground state)
|
|
251
|
+
const hasObservers =
|
|
252
|
+
pushAcceptedIncomingCallCId$.observed &&
|
|
253
|
+
pushRejectedIncomingCallCId$.observed;
|
|
135
254
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
255
|
+
// Check if we need to decline the call
|
|
256
|
+
const didPressDecline =
|
|
257
|
+
type === EventType.ACTION_PRESS &&
|
|
258
|
+
pressAction.id === DECLINE_CALL_ACTION_ID;
|
|
259
|
+
const didDismiss = type === EventType.DISMISSED;
|
|
260
|
+
const mustDecline = didPressDecline || didDismiss;
|
|
261
|
+
// Check if we need to accept the call
|
|
262
|
+
const mustAccept =
|
|
263
|
+
type === EventType.ACTION_PRESS &&
|
|
264
|
+
pressAction.id === ACCEPT_CALL_ACTION_ID;
|
|
265
|
+
if (mustAccept) {
|
|
266
|
+
pushAcceptedIncomingCallCId$.next(call_cid);
|
|
267
|
+
// NOTE: accept will be handled by the app with rxjs observers as the app will go to foreground always
|
|
268
|
+
} else if (mustDecline) {
|
|
269
|
+
pushRejectedIncomingCallCId$.next(call_cid);
|
|
270
|
+
if (hasObservers) {
|
|
271
|
+
// if we had observers we can return here as the observers will handle the call as the app is in the foreground state
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
await processCallFromPushInBackground(pushConfig, call_cid, 'decline');
|
|
275
|
+
} else if (type === EventType.PRESS) {
|
|
276
|
+
pushTappedIncomingCallCId$.next(call_cid);
|
|
277
|
+
// pressed state will be handled by the app with rxjs observers as the app will go to foreground always
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
if (type === EventType.PRESS) {
|
|
281
|
+
pushTappedIncomingCallCId$.next(call_cid);
|
|
282
|
+
pushConfig.onTapNonRingingCallNotification?.(
|
|
283
|
+
call_cid,
|
|
284
|
+
data.type as NonRingingPushEvent,
|
|
285
|
+
);
|
|
153
286
|
}
|
|
154
|
-
await processCallFromPushInBackground(pushConfig, call_cid, 'decline');
|
|
155
|
-
} else if (type === EventType.PRESS) {
|
|
156
|
-
pushTappedIncomingCallCId$.next(call_cid);
|
|
157
|
-
// pressed state will be handled by the app with rxjs observers as the app will go to foreground always
|
|
158
287
|
}
|
|
159
288
|
};
|
package/src/utils/push/ios.ts
CHANGED
|
@@ -1,14 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import type {
|
|
3
|
+
NonRingingPushEvent,
|
|
4
|
+
StreamVideoConfig,
|
|
5
|
+
} from '../StreamVideoRN/types';
|
|
2
6
|
import {
|
|
3
7
|
pushAcceptedIncomingCallCId$,
|
|
4
8
|
voipPushNotificationCallCId$,
|
|
5
9
|
voipCallkeepCallOnForegroundMap$,
|
|
6
10
|
voipCallkeepAcceptedCallOnNativeDialerMap$,
|
|
11
|
+
pushNonRingingCallData$,
|
|
7
12
|
} from './rxSubjects';
|
|
8
13
|
import { processCallFromPushInBackground } from './utils';
|
|
14
|
+
import { getExpoNotificationsLib, getPushNotificationIosLib } from './libs';
|
|
15
|
+
import { StreamVideoClient } from '@stream-io/video-client';
|
|
16
|
+
import { setPushLogoutCallback } from '../internal/pushLogoutCallback';
|
|
17
|
+
import notifee, { EventType } from '@notifee/react-native';
|
|
9
18
|
|
|
10
19
|
type PushConfig = NonNullable<StreamVideoConfig['push']>;
|
|
11
20
|
|
|
21
|
+
type StreamPayload =
|
|
22
|
+
| {
|
|
23
|
+
call_cid: string;
|
|
24
|
+
type: 'call.ring' | NonRingingPushEvent;
|
|
25
|
+
sender: string;
|
|
26
|
+
}
|
|
27
|
+
| undefined;
|
|
28
|
+
|
|
29
|
+
function processNonRingingNotificationStreamPayload(
|
|
30
|
+
streamPayload: StreamPayload,
|
|
31
|
+
) {
|
|
32
|
+
if (
|
|
33
|
+
streamPayload?.sender === 'stream.video' &&
|
|
34
|
+
streamPayload?.type !== 'call.ring'
|
|
35
|
+
) {
|
|
36
|
+
const cid = streamPayload.call_cid;
|
|
37
|
+
const type = streamPayload.type;
|
|
38
|
+
pushNonRingingCallData$.next({ cid, type });
|
|
39
|
+
return { cid, type };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
12
43
|
export const iosCallkeepAcceptCall = (
|
|
13
44
|
call_cid: string | undefined,
|
|
14
45
|
callUUIDFromCallkeep: string,
|
|
@@ -55,3 +86,91 @@ const shouldProcessCallFromCallkeep = (
|
|
|
55
86
|
}
|
|
56
87
|
return true;
|
|
57
88
|
};
|
|
89
|
+
|
|
90
|
+
export const setupRemoteNotificationsHandleriOS = (pushConfig: PushConfig) => {
|
|
91
|
+
if (Platform.OS !== 'ios') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
notifee.onForegroundEvent(({ type, detail }) => {
|
|
95
|
+
if (type === EventType.PRESS) {
|
|
96
|
+
const streamPayload = detail.notification?.data?.stream as
|
|
97
|
+
| StreamPayload
|
|
98
|
+
| undefined;
|
|
99
|
+
const result = processNonRingingNotificationStreamPayload(streamPayload);
|
|
100
|
+
if (result) {
|
|
101
|
+
pushConfig.onTapNonRingingCallNotification?.(result.cid, result.type);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
if (pushConfig.isExpo) {
|
|
106
|
+
const Notifications = getExpoNotificationsLib();
|
|
107
|
+
|
|
108
|
+
// foreground handler (just to show the notifications on foreground)
|
|
109
|
+
Notifications.setNotificationHandler({
|
|
110
|
+
handleNotification: async () => {
|
|
111
|
+
return {
|
|
112
|
+
shouldShowAlert: true,
|
|
113
|
+
shouldPlaySound: true,
|
|
114
|
+
shouldSetBadge: false,
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/** Send token to stream */
|
|
122
|
+
export async function initIosNonVoipToken(
|
|
123
|
+
client: StreamVideoClient,
|
|
124
|
+
pushConfig: PushConfig,
|
|
125
|
+
setUnsubscribeListener: (unsubscribe: () => void) => void,
|
|
126
|
+
) {
|
|
127
|
+
if (Platform.OS !== 'ios' || !pushConfig.android.pushProviderName) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const setDeviceToken = async (token: string) => {
|
|
131
|
+
setPushLogoutCallback(() => {
|
|
132
|
+
client.removeDevice(token).catch((err) => {
|
|
133
|
+
console.warn('Failed to remove voip token from stream', err);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
const push_provider_name = pushConfig.ios.pushProviderName;
|
|
137
|
+
await client.addDevice(token, 'apn', push_provider_name);
|
|
138
|
+
};
|
|
139
|
+
if (pushConfig.isExpo) {
|
|
140
|
+
const expoNotificationsLib = getExpoNotificationsLib();
|
|
141
|
+
const subscription = expoNotificationsLib.addPushTokenListener(
|
|
142
|
+
(devicePushToken) => {
|
|
143
|
+
setDeviceToken(devicePushToken.data);
|
|
144
|
+
},
|
|
145
|
+
);
|
|
146
|
+
const subscriptionForReceive =
|
|
147
|
+
expoNotificationsLib.addNotificationReceivedListener((event) => {
|
|
148
|
+
// listen to foreground notifications
|
|
149
|
+
if (event.request.trigger.type === 'push') {
|
|
150
|
+
const streamPayload = event.request.trigger.payload
|
|
151
|
+
?.stream as StreamPayload;
|
|
152
|
+
processNonRingingNotificationStreamPayload(streamPayload);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
setUnsubscribeListener(() => {
|
|
156
|
+
subscription.remove();
|
|
157
|
+
subscriptionForReceive.remove();
|
|
158
|
+
});
|
|
159
|
+
} else {
|
|
160
|
+
const pushNotificationIosLib = getPushNotificationIosLib();
|
|
161
|
+
pushNotificationIosLib.addEventListener('register', (token) => {
|
|
162
|
+
setDeviceToken(token);
|
|
163
|
+
});
|
|
164
|
+
pushNotificationIosLib.addEventListener('notification', (notification) => {
|
|
165
|
+
const data = notification.getData();
|
|
166
|
+
const streamPayload = data?.stream as StreamPayload;
|
|
167
|
+
// listen to foreground notifications
|
|
168
|
+
processNonRingingNotificationStreamPayload(streamPayload);
|
|
169
|
+
notification.finish(pushNotificationIosLib.FetchResult.NoData);
|
|
170
|
+
});
|
|
171
|
+
setUnsubscribeListener(() => {
|
|
172
|
+
pushNotificationIosLib.removeEventListener('register');
|
|
173
|
+
pushNotificationIosLib.removeEventListener('notification');
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
package/src/utils/push/libs.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
export type { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
|
|
2
|
-
|
|
3
2
|
export type RNCallKeepType = typeof import('react-native-callkeep').default;
|
|
4
3
|
export type FirebaseMessagingType =
|
|
5
4
|
typeof import('@react-native-firebase/messaging').default;
|
|
6
5
|
export type VoipPushNotificationType =
|
|
7
6
|
typeof import('react-native-voip-push-notification').default;
|
|
7
|
+
export type ExpoNotificationsLib = typeof import('expo-notifications');
|
|
8
|
+
export type ExpoTaskManagerLib = typeof import('expo-task-manager');
|
|
9
|
+
export type PushNotificationIosLib =
|
|
10
|
+
typeof import('@react-native-community/push-notification-ios').default;
|
|
8
11
|
|
|
9
12
|
let callkeep: RNCallKeepType | undefined;
|
|
10
13
|
let messaging: FirebaseMessagingType | undefined;
|
|
11
14
|
let voipPushNotification: VoipPushNotificationType | undefined;
|
|
15
|
+
let expoNotificationsLib: ExpoNotificationsLib | undefined;
|
|
16
|
+
let expoTaskManagerLib: ExpoTaskManagerLib | undefined;
|
|
17
|
+
let pushNotificationIosLib: PushNotificationIosLib | undefined;
|
|
12
18
|
|
|
13
19
|
try {
|
|
14
20
|
callkeep = require('react-native-callkeep').default;
|
|
@@ -22,6 +28,46 @@ try {
|
|
|
22
28
|
voipPushNotification = require('react-native-voip-push-notification').default;
|
|
23
29
|
} catch (e) {}
|
|
24
30
|
|
|
31
|
+
try {
|
|
32
|
+
expoNotificationsLib = require('expo-notifications');
|
|
33
|
+
} catch (e) {}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
expoTaskManagerLib = require('expo-task-manager');
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
pushNotificationIosLib =
|
|
41
|
+
require('@react-native-community/push-notification-ios').default;
|
|
42
|
+
} catch (e) {}
|
|
43
|
+
|
|
44
|
+
export function getExpoNotificationsLib() {
|
|
45
|
+
if (!expoNotificationsLib) {
|
|
46
|
+
throw Error(
|
|
47
|
+
'expo-notifications library is not installed. Please see https://docs.expo.dev/versions/latest/sdk/notifications/ for installation instructions',
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return expoNotificationsLib;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getExpoTaskManagerLib() {
|
|
54
|
+
if (!expoTaskManagerLib) {
|
|
55
|
+
throw Error(
|
|
56
|
+
'expo-task-manager library is not installed. Please see https://docs.expo.dev/versions/latest/sdk/task-manager/ for installation instructions',
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return expoTaskManagerLib;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getPushNotificationIosLib() {
|
|
63
|
+
if (!pushNotificationIosLib) {
|
|
64
|
+
throw Error(
|
|
65
|
+
'@react-native-community/push-notification-ios library is not installed. Please install it using "yarn add @react-native-community/push-notification-ios" or "npm i @react-native-community/push-notification-ios --save"',
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return pushNotificationIosLib;
|
|
69
|
+
}
|
|
70
|
+
|
|
25
71
|
export function getCallKeepLib() {
|
|
26
72
|
if (!callkeep) {
|
|
27
73
|
throw Error(
|
|
@@ -43,7 +89,7 @@ export function getFirebaseMessagingLib() {
|
|
|
43
89
|
export function getVoipPushNotificationLib() {
|
|
44
90
|
if (!voipPushNotification) {
|
|
45
91
|
throw Error(
|
|
46
|
-
"react-native-voip-push-notification library is not installed. Please install it using 'yarn add react-native-voip-push-notification' or 'npm
|
|
92
|
+
"react-native-voip-push-notification library is not installed. Please install it using 'yarn add react-native-voip-push-notification' or 'npm i react-native-voip-push-notification --save'",
|
|
47
93
|
);
|
|
48
94
|
}
|
|
49
95
|
return voipPushNotification;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { NonRingingPushEvent } from '../StreamVideoRN/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This rxjs subject is used to store the call cid of the accepted incoming call from push notification
|
|
6
|
+
* Note: it is should be subscribed only when a user has connected to the websocket of Stream
|
|
7
|
+
*/
|
|
8
|
+
export const pushNonRingingCallData$ = new BehaviorSubject<
|
|
9
|
+
{ cid: string; type: NonRingingPushEvent } | undefined
|
|
10
|
+
>(undefined);
|
|
2
11
|
|
|
3
12
|
/**
|
|
4
13
|
* This rxjs subject is used to store the call cid of the accepted incoming call from push notification
|
package/src/utils/push/utils.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Call, StreamVideoClient } from '@stream-io/video-client';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
NonRingingPushEvent,
|
|
4
|
+
StreamVideoConfig,
|
|
5
|
+
} from '../StreamVideoRN/types';
|
|
6
|
+
import { onNewCallNotification } from '../internal/newNotificationCallbacks';
|
|
3
7
|
|
|
4
8
|
type PushConfig = NonNullable<StreamVideoConfig['push']>;
|
|
5
9
|
|
|
@@ -56,3 +60,33 @@ export const processCallFromPush = async (
|
|
|
56
60
|
console.log('failed to process call from push notification', e, action);
|
|
57
61
|
}
|
|
58
62
|
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* This function is used process the call from push notifications due to non ringing calls
|
|
66
|
+
* It does the following steps:
|
|
67
|
+
* 1. Get the call from the client if present or create a new call
|
|
68
|
+
* 2. Fetch the latest state of the call from the server if its not already in ringing state
|
|
69
|
+
* 3. Call all the callbacks to inform the app about the call
|
|
70
|
+
*/
|
|
71
|
+
export const processNonIncomingCallFromPush = async (
|
|
72
|
+
client: StreamVideoClient,
|
|
73
|
+
call_cid: string,
|
|
74
|
+
nonRingingNotificationType: NonRingingPushEvent,
|
|
75
|
+
) => {
|
|
76
|
+
let callFromPush: Call;
|
|
77
|
+
try {
|
|
78
|
+
const _callFromPush = client.state.calls.find((c) => c.cid === call_cid);
|
|
79
|
+
if (_callFromPush) {
|
|
80
|
+
callFromPush = _callFromPush;
|
|
81
|
+
} else {
|
|
82
|
+
// if not it means that WS is not alive when receiving the push notifications and we need to fetch the call
|
|
83
|
+
const [callType, callId] = call_cid.split(':');
|
|
84
|
+
callFromPush = client.call(callType, callId);
|
|
85
|
+
await callFromPush.get();
|
|
86
|
+
}
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.log('failed to fetch call from push notification', e);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
onNewCallNotification(callFromPush, nonRingingNotificationType);
|
|
92
|
+
};
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.1.
|
|
1
|
+
export const version = '0.1.4';
|
|
File without changes
|
/package/expo-config-plugin/dist/{withAppDelegate.js → withStreamVideoReactNativeSDKAppDelegate.js}
RENAMED
|
File without changes
|