@cloudflare/realtimekit-react-native 0.0.0 → 0.0.1
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/LICENSE +13 -0
- package/README.md +83 -0
- package/RealtimeKitCore.podspec +19 -0
- package/android/build.gradle +58 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/android/gradlew +185 -0
- package/android/gradlew.bat +89 -0
- package/android/src/main/AndroidManifest.xml +23 -0
- package/android/src/main/java/com/reactnativecore/BackgroundTimer/RNBackgroundTimer.java +97 -0
- package/android/src/main/java/com/reactnativecore/Constants.java +14 -0
- package/android/src/main/java/com/reactnativecore/CoreModule.java +49 -0
- package/android/src/main/java/com/reactnativecore/CorePackage.java +34 -0
- package/android/src/main/java/com/reactnativecore/DyteHelperModule.java +268 -0
- package/android/src/main/java/com/reactnativecore/DyteHolder.java +7 -0
- package/android/src/main/java/com/reactnativecore/ForegroundService.java +48 -0
- package/android/src/main/java/com/reactnativecore/NotificationHelper.java +127 -0
- package/android/src/main/java/com/reactnativecore/RNPermissionsModule.java +226 -0
- package/android/src/main/java/com/reactnativecore/incallmanager/AppRTCBluetoothManager.java +637 -0
- package/android/src/main/java/com/reactnativecore/incallmanager/AppRTCProximitySensor.java +133 -0
- package/android/src/main/java/com/reactnativecore/incallmanager/AppRTCUtils.java +41 -0
- package/android/src/main/java/com/reactnativecore/incallmanager/InCallManagerModule.java +1881 -0
- package/android/src/main/java/com/reactnativecore/incallmanager/InCallProximityManager.java +162 -0
- package/android/src/main/java/com/reactnativecore/incallmanager/InCallWakeLockUtils.java +116 -0
- package/android/src/main/java/com/reactnativecore/incallmanager/ThreadUtils.java +39 -0
- package/app.plugin.js +1 -0
- package/ios/BroadcastEventEmitter.h +11 -0
- package/ios/BroadcastEventEmitter.m +72 -0
- package/ios/Core.h +5 -0
- package/ios/Core.m +28 -0
- package/ios/Core.xcodeproj/project.pbxproj +287 -0
- package/ios/DyteRNBackgroundTimer.h +22 -0
- package/ios/DyteRNBackgroundTimer.m +90 -0
- package/ios/DyteRNPermissionHandlerNotifications.h +17 -0
- package/ios/DyteRNPermissionHandlerNotifications.m +127 -0
- package/ios/DyteRNPermissions.h +50 -0
- package/ios/DyteRNPermissions.m +275 -0
- package/ios/DyteScreensharePickerView.h +8 -0
- package/ios/DyteScreensharePickerView.m +27 -0
- package/ios/PrivacyInfo.xcprivacy +60 -0
- package/ios/RNInCallManager.h +16 -0
- package/ios/RNInCallManager.m +1354 -0
- package/ios/permissions/DyteRNPermissionHandlerBluetoothPeripheral.h +4 -0
- package/ios/permissions/DyteRNPermissionHandlerBluetoothPeripheral.m +87 -0
- package/ios/permissions/DyteRNPermissionHandlerCamera.h +5 -0
- package/ios/permissions/DyteRNPermissionHandlerCamera.m +38 -0
- package/ios/permissions/DyteRNPermissionHandlerMicrophone.h +5 -0
- package/ios/permissions/DyteRNPermissionHandlerMicrophone.m +34 -0
- package/ios/permissions/DyteRNPermissionHandlerPhotoLibrary.h +8 -0
- package/ios/permissions/DyteRNPermissionHandlerPhotoLibrary.m +70 -0
- package/ios/screenshare/DyteDarwinNotificationCenter.swift +21 -0
- package/ios/screenshare/DyteScreenshareAtomic.swift +29 -0
- package/ios/screenshare/DyteScreenshareHandler.swift +103 -0
- package/ios/screenshare/DyteScreenshareUploader.swift +139 -0
- package/ios/screenshare/DyteSocketConnection.swift +190 -0
- package/lib/BackgroundHandler.d.ts +20 -0
- package/lib/BackgroundHandler.js +87 -0
- package/lib/LocalMediaError.d.ts +4 -0
- package/lib/LocalMediaError.js +6 -0
- package/lib/LocalMediaHandler.d.ts +118 -0
- package/lib/LocalMediaHandler.js +853 -0
- package/lib/LocalMediaInterfaces.d.ts +76 -0
- package/lib/LocalMediaInterfaces.js +1 -0
- package/lib/LocalMediaUtils.d.ts +93 -0
- package/lib/LocalMediaUtils.js +276 -0
- package/lib/NativeAudioManager.d.ts +16 -0
- package/lib/NativeAudioManager.js +93 -0
- package/lib/PermissionHandler.d.ts +34 -0
- package/lib/PermissionHandler.js +179 -0
- package/lib/ReactContext.d.ts +42 -0
- package/lib/ReactContext.js +96 -0
- package/lib/ReactHooks.d.ts +11 -0
- package/lib/ReactHooks.js +73 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +40 -0
- package/lib/utils/crypto.d.ts +17 -0
- package/lib/utils/crypto.js +82 -0
- package/package.json +51 -4
- package/plugin/build/withDyte.d.ts +25 -0
- package/plugin/build/withDyte.js +100 -0
- package/plugin/src/withDyte.ts +150 -0
- package/plugin/tsconfig.json +10 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { Platform, NativeModules } from 'react-native';
|
|
11
|
+
const ANDROID = Object.freeze({
|
|
12
|
+
CAMERA: 'android.permission.CAMERA',
|
|
13
|
+
BLUETOOTH: 'android.permission.BLUETOOTH_CONNECT',
|
|
14
|
+
RECORD_AUDIO: 'android.permission.RECORD_AUDIO',
|
|
15
|
+
LOCATION: 'android.permission.ACCESS_FINE_LOCATION',
|
|
16
|
+
});
|
|
17
|
+
const IOS = Object.freeze({
|
|
18
|
+
APP_TRACKING_TRANSPARENCY: 'ios.permission.APP_TRACKING_TRANSPARENCY',
|
|
19
|
+
BLUETOOTH_PERIPHERAL: 'ios.permission.BLUETOOTH_PERIPHERAL',
|
|
20
|
+
CAMERA: 'ios.permission.CAMERA',
|
|
21
|
+
MICROPHONE: 'ios.permission.MICROPHONE',
|
|
22
|
+
});
|
|
23
|
+
export const PERMISSIONS = Object.freeze({
|
|
24
|
+
ANDROID: ANDROID,
|
|
25
|
+
IOS: IOS,
|
|
26
|
+
});
|
|
27
|
+
export const RESULTS = Object.freeze({
|
|
28
|
+
UNAVAILABLE: 'unavailable',
|
|
29
|
+
BLOCKED: 'blocked',
|
|
30
|
+
DENIED: 'denied',
|
|
31
|
+
GRANTED: 'granted',
|
|
32
|
+
LIMITED: 'limited',
|
|
33
|
+
PROMPTED: 'prompted',
|
|
34
|
+
});
|
|
35
|
+
const { DyteRNPermissions } = NativeModules;
|
|
36
|
+
function request(permission) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
return DyteRNPermissions.available.includes(permission)
|
|
39
|
+
? yield DyteRNPermissions.request(permission)
|
|
40
|
+
: RESULTS.UNAVAILABLE;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function check(permission) {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
return DyteRNPermissions.available.includes(permission)
|
|
46
|
+
? yield DyteRNPermissions.check(permission)
|
|
47
|
+
: RESULTS.UNAVAILABLE;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function uniq(array) {
|
|
51
|
+
return array.filter((item, index) => item != null && array.indexOf(item) === index);
|
|
52
|
+
}
|
|
53
|
+
function requestMultiplePermissions(permissions) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
if (Platform.OS === 'ios') {
|
|
56
|
+
const output = {};
|
|
57
|
+
const dedup = uniq(permissions);
|
|
58
|
+
for (let index = 0; index < dedup.length; index += 1) {
|
|
59
|
+
const permission = dedup[index];
|
|
60
|
+
output[permission] = yield request(permission);
|
|
61
|
+
}
|
|
62
|
+
return output;
|
|
63
|
+
}
|
|
64
|
+
const output = yield DyteRNPermissions.requestMultiplePermissions(permissions, true);
|
|
65
|
+
return output;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function checkMultiplePermissions(permissions) {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
if (Platform.OS === 'ios') {
|
|
71
|
+
const output = {};
|
|
72
|
+
const dedup = uniq(permissions);
|
|
73
|
+
for (let index = 0; index < dedup.length; index += 1) {
|
|
74
|
+
const permission = dedup[index];
|
|
75
|
+
output[permission] = yield check(permission);
|
|
76
|
+
}
|
|
77
|
+
return output;
|
|
78
|
+
}
|
|
79
|
+
const output = yield DyteRNPermissions.checkMultiplePermissions(permissions);
|
|
80
|
+
return output;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const getPlatformEquiPermission = (permission, reverse = false) => {
|
|
84
|
+
const android = {
|
|
85
|
+
camera: PERMISSIONS.ANDROID.CAMERA,
|
|
86
|
+
microphone: PERMISSIONS.ANDROID.RECORD_AUDIO,
|
|
87
|
+
bluetooth: PERMISSIONS.ANDROID.BLUETOOTH,
|
|
88
|
+
location: PERMISSIONS.ANDROID.LOCATION,
|
|
89
|
+
};
|
|
90
|
+
const ios = {
|
|
91
|
+
camera: PERMISSIONS.IOS.CAMERA,
|
|
92
|
+
microphone: PERMISSIONS.IOS.MICROPHONE,
|
|
93
|
+
bluetooth: PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL,
|
|
94
|
+
};
|
|
95
|
+
if (!reverse) {
|
|
96
|
+
if (Platform.OS === 'android') {
|
|
97
|
+
return android[permission];
|
|
98
|
+
}
|
|
99
|
+
return ios[permission];
|
|
100
|
+
}
|
|
101
|
+
if (Platform.OS === 'android') {
|
|
102
|
+
const androidReverse = {};
|
|
103
|
+
Object.keys(android).forEach((key) => {
|
|
104
|
+
androidReverse[android[key]] = key;
|
|
105
|
+
});
|
|
106
|
+
return androidReverse[permission];
|
|
107
|
+
}
|
|
108
|
+
const iosReverse = {};
|
|
109
|
+
Object.keys(ios).forEach((key) => {
|
|
110
|
+
iosReverse[ios[key]] = key;
|
|
111
|
+
});
|
|
112
|
+
return iosReverse[permission];
|
|
113
|
+
};
|
|
114
|
+
let timeout;
|
|
115
|
+
const processPermissionResult = (systemPermissionRequired, audio, video) => (status) => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
|
+
const permissionState = {
|
|
117
|
+
camera: 'NOT_REQUESTED',
|
|
118
|
+
microphone: 'NOT_REQUESTED',
|
|
119
|
+
bluetooth: 'NOT_REQUESTED',
|
|
120
|
+
location: 'NOT_REQUESTED',
|
|
121
|
+
};
|
|
122
|
+
systemPermissionRequired.forEach((permission) => {
|
|
123
|
+
const normalPermission = getPlatformEquiPermission(permission, true);
|
|
124
|
+
if (status[permission] === RESULTS.GRANTED) {
|
|
125
|
+
permissionState[normalPermission] = 'ACCEPTED';
|
|
126
|
+
}
|
|
127
|
+
else if (status[permission] !== RESULTS.PROMPTED) {
|
|
128
|
+
permissionState[normalPermission] = 'DENIED';
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
const isBluetoothGranted = Platform.OS === 'ios' || permissionState.bluetooth !== 'NOT_REQUESTED';
|
|
132
|
+
if ((permissionState.camera !== 'NOT_REQUESTED' || !video) &&
|
|
133
|
+
(permissionState.microphone !== 'NOT_REQUESTED' || !audio) &&
|
|
134
|
+
isBluetoothGranted &&
|
|
135
|
+
timeout)
|
|
136
|
+
clearInterval(timeout);
|
|
137
|
+
else {
|
|
138
|
+
yield requestMultiplePermissions(systemPermissionRequired);
|
|
139
|
+
const checkResponse = yield checkMultiplePermissions(systemPermissionRequired);
|
|
140
|
+
yield processPermissionResult(systemPermissionRequired, audio, video)(checkResponse);
|
|
141
|
+
}
|
|
142
|
+
return permissionState;
|
|
143
|
+
});
|
|
144
|
+
export const setupPermissions = (audio, video) => __awaiter(void 0, void 0, void 0, function* () {
|
|
145
|
+
return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
|
|
146
|
+
let statusCheck;
|
|
147
|
+
const systemPermissionRequired = [];
|
|
148
|
+
const isIOS = Platform.OS === 'ios';
|
|
149
|
+
const permissionsRequired = []
|
|
150
|
+
.concat(audio ? ['microphone'] : [])
|
|
151
|
+
.concat(isIOS ? [] : ['bluetooth'])
|
|
152
|
+
.concat(video ? ['camera'] : []);
|
|
153
|
+
permissionsRequired.forEach((permission) => {
|
|
154
|
+
const systemPermissionEquivalent = getPlatformEquiPermission(permission);
|
|
155
|
+
if (systemPermissionEquivalent) {
|
|
156
|
+
systemPermissionRequired.push(systemPermissionEquivalent);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
const checkPermLoop = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
160
|
+
timeout = id;
|
|
161
|
+
statusCheck = yield processPermissionResult(systemPermissionRequired, audio, video)(requestResponse);
|
|
162
|
+
if (statusCheck !== undefined)
|
|
163
|
+
resolve(statusCheck);
|
|
164
|
+
});
|
|
165
|
+
// Requesting permission status
|
|
166
|
+
const requestResponse = yield requestMultiplePermissions(systemPermissionRequired);
|
|
167
|
+
const timeout_sample = setInterval(() => checkPermLoop(timeout_sample), 1000);
|
|
168
|
+
if (timeout)
|
|
169
|
+
statusCheck = yield processPermissionResult(systemPermissionRequired, audio, video)(requestResponse);
|
|
170
|
+
if (timeout) {
|
|
171
|
+
clearInterval(timeout);
|
|
172
|
+
}
|
|
173
|
+
if (statusCheck)
|
|
174
|
+
resolve(statusCheck);
|
|
175
|
+
setTimeout(() => {
|
|
176
|
+
reject(statusCheck);
|
|
177
|
+
}, 20000);
|
|
178
|
+
}));
|
|
179
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { ReactChild } from 'react';
|
|
2
|
+
import RealtimeKitClient from '@cloudflare/realtimekit';
|
|
3
|
+
type Listener = () => void;
|
|
4
|
+
declare class RealtimeKitUpdates {
|
|
5
|
+
private meeting;
|
|
6
|
+
private l;
|
|
7
|
+
constructor(meeting: RealtimeKitClient);
|
|
8
|
+
clean(): void;
|
|
9
|
+
addListener(listener: Listener): void;
|
|
10
|
+
removeListener(listener: Listener): void;
|
|
11
|
+
private update;
|
|
12
|
+
}
|
|
13
|
+
export declare const RealtimeKitContext: React.Context<{
|
|
14
|
+
meeting: RealtimeKitClient | undefined;
|
|
15
|
+
updates: RealtimeKitUpdates | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Provider component which makes the DyteClient object
|
|
19
|
+
* available to nested components
|
|
20
|
+
* @component
|
|
21
|
+
* @param value The DyteClient instance from `useDyteClient()`
|
|
22
|
+
* @param renderBeforeLoad The provider will not render children by default
|
|
23
|
+
* until it gets a client object, renderBeforeLoad can be used to override that
|
|
24
|
+
*/
|
|
25
|
+
export declare function RealtimeKitProvider({ value, children, renderBeforeLoad, }: {
|
|
26
|
+
value: RealtimeKitClient | undefined;
|
|
27
|
+
children: ReactChild | ReactChild[];
|
|
28
|
+
renderBeforeLoad?: boolean;
|
|
29
|
+
}): JSX.Element;
|
|
30
|
+
export declare namespace RealtimeKitProvider {
|
|
31
|
+
var defaultProps: {
|
|
32
|
+
renderBeforeLoad: boolean;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Hook which returns the reference to the DyteClient object
|
|
37
|
+
* @returns meeting instance from `useDyteClient()`
|
|
38
|
+
*/
|
|
39
|
+
export declare const useRealtimeKitMeeting: () => {
|
|
40
|
+
meeting: RealtimeKitClient;
|
|
41
|
+
};
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import React, { createContext, useContext, useEffect, useRef, useState, } from 'react';
|
|
3
|
+
class RealtimeKitUpdates {
|
|
4
|
+
constructor(meeting) {
|
|
5
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
6
|
+
this.meeting = meeting;
|
|
7
|
+
this.l = new Set();
|
|
8
|
+
(_a = this.meeting.self) === null || _a === void 0 ? void 0 : _a.addListener('*', this.update.bind(this));
|
|
9
|
+
(_b = this.meeting.meta) === null || _b === void 0 ? void 0 : _b.addListener('*', this.update.bind(this));
|
|
10
|
+
(_c = this.meeting.participants) === null || _c === void 0 ? void 0 : _c.addListener('*', this.update.bind(this));
|
|
11
|
+
(_e = (_d = this.meeting.participants) === null || _d === void 0 ? void 0 : _d.joined) === null || _e === void 0 ? void 0 : _e.addListener('*', this.update.bind(this));
|
|
12
|
+
(_g = (_f = this.meeting.participants) === null || _f === void 0 ? void 0 : _f.active) === null || _g === void 0 ? void 0 : _g.addListener('*', this.update.bind(this));
|
|
13
|
+
(_j = (_h = this.meeting.participants) === null || _h === void 0 ? void 0 : _h.pinned) === null || _j === void 0 ? void 0 : _j.addListener('*', this.update.bind(this));
|
|
14
|
+
(_k = this.meeting.chat) === null || _k === void 0 ? void 0 : _k.addListener('*', this.update.bind(this));
|
|
15
|
+
(_l = this.meeting.polls) === null || _l === void 0 ? void 0 : _l.addListener('*', this.update.bind(this));
|
|
16
|
+
(_o = (_m = this.meeting.plugins) === null || _m === void 0 ? void 0 : _m.all) === null || _o === void 0 ? void 0 : _o.addListener('*', this.update.bind(this));
|
|
17
|
+
(_q = (_p = this.meeting.plugins) === null || _p === void 0 ? void 0 : _p.active) === null || _q === void 0 ? void 0 : _q.addListener('*', this.update.bind(this));
|
|
18
|
+
(_r = this.meeting.recording) === null || _r === void 0 ? void 0 : _r.addListener('*', this.update.bind(this));
|
|
19
|
+
(_s = this.meeting.stage) === null || _s === void 0 ? void 0 : _s.addListener('*', this.update.bind(this));
|
|
20
|
+
(_t = this.meeting.livestream) === null || _t === void 0 ? void 0 : _t.addListener('*', this.update.bind(this));
|
|
21
|
+
}
|
|
22
|
+
clean() {
|
|
23
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
24
|
+
(_a = this.meeting.self) === null || _a === void 0 ? void 0 : _a.removeListener('*', this.update.bind(this));
|
|
25
|
+
(_b = this.meeting.meta) === null || _b === void 0 ? void 0 : _b.removeListener('*', this.update.bind(this));
|
|
26
|
+
(_c = this.meeting.participants) === null || _c === void 0 ? void 0 : _c.removeListener('*', this.update.bind(this));
|
|
27
|
+
(_e = (_d = this.meeting.participants) === null || _d === void 0 ? void 0 : _d.joined) === null || _e === void 0 ? void 0 : _e.removeListener('*', this.update.bind(this));
|
|
28
|
+
(_g = (_f = this.meeting.participants) === null || _f === void 0 ? void 0 : _f.active) === null || _g === void 0 ? void 0 : _g.removeListener('*', this.update.bind(this));
|
|
29
|
+
(_j = (_h = this.meeting.participants) === null || _h === void 0 ? void 0 : _h.pinned) === null || _j === void 0 ? void 0 : _j.removeListener('*', this.update.bind(this));
|
|
30
|
+
(_k = this.meeting.chat) === null || _k === void 0 ? void 0 : _k.removeListener('*', this.update.bind(this));
|
|
31
|
+
(_l = this.meeting.polls) === null || _l === void 0 ? void 0 : _l.removeListener('*', this.update.bind(this));
|
|
32
|
+
(_o = (_m = this.meeting.plugins) === null || _m === void 0 ? void 0 : _m.all) === null || _o === void 0 ? void 0 : _o.removeListener('*', this.update.bind(this));
|
|
33
|
+
(_q = (_p = this.meeting.plugins) === null || _p === void 0 ? void 0 : _p.active) === null || _q === void 0 ? void 0 : _q.removeListener('*', this.update.bind(this));
|
|
34
|
+
(_r = this.meeting.recording) === null || _r === void 0 ? void 0 : _r.removeListener('*', this.update.bind(this));
|
|
35
|
+
(_s = this.meeting.stage) === null || _s === void 0 ? void 0 : _s.removeListener('*', this.update.bind(this));
|
|
36
|
+
(_t = this.meeting.livestream) === null || _t === void 0 ? void 0 : _t.removeListener('*', this.update.bind(this));
|
|
37
|
+
}
|
|
38
|
+
addListener(listener) {
|
|
39
|
+
this.l.add(listener);
|
|
40
|
+
}
|
|
41
|
+
removeListener(listener) {
|
|
42
|
+
this.l.delete(listener);
|
|
43
|
+
}
|
|
44
|
+
update() {
|
|
45
|
+
this.l.forEach((l) => {
|
|
46
|
+
l();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export const RealtimeKitContext = createContext({
|
|
51
|
+
meeting: undefined,
|
|
52
|
+
updates: undefined,
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Provider component which makes the DyteClient object
|
|
56
|
+
* available to nested components
|
|
57
|
+
* @component
|
|
58
|
+
* @param value The DyteClient instance from `useDyteClient()`
|
|
59
|
+
* @param renderBeforeLoad The provider will not render children by default
|
|
60
|
+
* until it gets a client object, renderBeforeLoad can be used to override that
|
|
61
|
+
*/
|
|
62
|
+
export function RealtimeKitProvider({ value, children,
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
64
|
+
renderBeforeLoad, }) {
|
|
65
|
+
const [updates, setUpdates] = useState();
|
|
66
|
+
const updatesRef = useRef();
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (value) {
|
|
69
|
+
setUpdates(new RealtimeKitUpdates(value));
|
|
70
|
+
}
|
|
71
|
+
return () => {
|
|
72
|
+
var _a;
|
|
73
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
+
(_a = updatesRef.current) === null || _a === void 0 ? void 0 : _a.clean();
|
|
75
|
+
};
|
|
76
|
+
}, [value]);
|
|
77
|
+
if (!value)
|
|
78
|
+
return null;
|
|
79
|
+
return (React.createElement(RealtimeKitContext.Provider, { value: {
|
|
80
|
+
meeting: value,
|
|
81
|
+
updates,
|
|
82
|
+
} }, children));
|
|
83
|
+
}
|
|
84
|
+
RealtimeKitProvider.defaultProps = {
|
|
85
|
+
renderBeforeLoad: false,
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Hook which returns the reference to the DyteClient object
|
|
89
|
+
* @returns meeting instance from `useDyteClient()`
|
|
90
|
+
*/
|
|
91
|
+
export const useRealtimeKitMeeting = () => {
|
|
92
|
+
const { meeting } = useContext(RealtimeKitContext);
|
|
93
|
+
if (!meeting)
|
|
94
|
+
throw new Error('useRealtimeKitMeeting must be used within the DyteProvider');
|
|
95
|
+
return { meeting };
|
|
96
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import RealtimeKitClient from '@cloudflare/realtimekit';
|
|
2
|
+
/**
|
|
3
|
+
* Hook which manages a RealtimeKitClient instance
|
|
4
|
+
*/
|
|
5
|
+
export declare const useRealtimeKitClient: () => [RealtimeKitClient | undefined, (options: any) => {}];
|
|
6
|
+
type StateSelector<T extends object, U> = (state: T) => U;
|
|
7
|
+
/**
|
|
8
|
+
* Hook which extracts data from the DyteClient object
|
|
9
|
+
*/
|
|
10
|
+
export declare const useRTKSelector: <StateSlice>(selector: StateSelector<RealtimeKitClient, StateSlice>) => StateSlice;
|
|
11
|
+
export * from './ReactContext';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { useState, useEffect, useRef, useReducer, useContext } from 'react';
|
|
11
|
+
import RealtimeKitClient from '@cloudflare/realtimekit';
|
|
12
|
+
import { RealtimeKitContext } from './ReactContext';
|
|
13
|
+
/**
|
|
14
|
+
* Hook which manages a RealtimeKitClient instance
|
|
15
|
+
*/
|
|
16
|
+
export const useRealtimeKitClient = () => {
|
|
17
|
+
const [client, setClient] = useState();
|
|
18
|
+
const initClient = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
const cli = yield RealtimeKitClient.init(options);
|
|
20
|
+
setClient(cli);
|
|
21
|
+
return cli;
|
|
22
|
+
});
|
|
23
|
+
return [client, initClient];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Utility function which determines whether to trigger update or not
|
|
27
|
+
*/
|
|
28
|
+
const shouldUpdate = (slice, newSlice) => {
|
|
29
|
+
if (slice !== newSlice) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(slice) && Array.isArray(newSlice)) {
|
|
33
|
+
if (slice.length !== newSlice.length) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Hook which extracts data from the DyteClient object
|
|
41
|
+
*/
|
|
42
|
+
export const useRTKSelector = (selector) => {
|
|
43
|
+
const { meeting, updates } = useContext(RealtimeKitContext);
|
|
44
|
+
if (!meeting)
|
|
45
|
+
throw new Error('useRTKSelector must be used within the RealtimeKitProvider');
|
|
46
|
+
const state = useRef(selector(meeting));
|
|
47
|
+
const [, forceUpdate] = useReducer((c) => c + 1, 0);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const currentState = meeting && selector(meeting);
|
|
50
|
+
const listener = () => {
|
|
51
|
+
const newStateSlice = meeting && selector(meeting);
|
|
52
|
+
if (shouldUpdate(state.current, newStateSlice)) {
|
|
53
|
+
state.current = newStateSlice;
|
|
54
|
+
forceUpdate();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
if (currentState &&
|
|
58
|
+
typeof currentState === 'object' &&
|
|
59
|
+
'addListener' in currentState) {
|
|
60
|
+
currentState.addListener('*', forceUpdate);
|
|
61
|
+
return () => {
|
|
62
|
+
currentState.removeListener('*', forceUpdate);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
updates === null || updates === void 0 ? void 0 : updates.addListener(listener);
|
|
66
|
+
return () => {
|
|
67
|
+
updates === null || updates === void 0 ? void 0 : updates.removeListener(listener);
|
|
68
|
+
};
|
|
69
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
+
}, [meeting, updates]);
|
|
71
|
+
return state.current;
|
|
72
|
+
};
|
|
73
|
+
export * from './ReactContext';
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import RealtimeKitMeeting from '@cloudflare/realtimekit';
|
|
2
|
+
import './utils/crypto';
|
|
3
|
+
import 'text-encoding-polyfill';
|
|
4
|
+
import 'node-libs-react-native/globals';
|
|
5
|
+
import 'react-native-url-polyfill/auto';
|
|
6
|
+
export { RealtimeKitProvider, useRealtimeKitClient, useRTKSelector, useRealtimeKitMeeting, } from './ReactHooks';
|
|
7
|
+
declare global {
|
|
8
|
+
var navigator: any;
|
|
9
|
+
}
|
|
10
|
+
export default RealtimeKitMeeting;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
import RealtimeKitMeeting from '@cloudflare/realtimekit';
|
|
3
|
+
import LocalMediaHandler from './LocalMediaHandler';
|
|
4
|
+
import { registerGlobals } from '@dyteinternals/react-native-webrtc';
|
|
5
|
+
import './utils/crypto';
|
|
6
|
+
import 'text-encoding-polyfill';
|
|
7
|
+
import 'node-libs-react-native/globals';
|
|
8
|
+
import 'react-native-url-polyfill/auto';
|
|
9
|
+
export { RealtimeKitProvider, useRealtimeKitClient, useRTKSelector, useRealtimeKitMeeting, } from './ReactHooks';
|
|
10
|
+
registerGlobals();
|
|
11
|
+
const LINKING_ERROR = `The package '@cloudflare/realtimekit-react-native' doesn't seem to be linked. Make sure: \n\n` +
|
|
12
|
+
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
13
|
+
'- You rebuilt the app after installing the package\n' +
|
|
14
|
+
'- You are not using Expo managed workflow\n';
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
16
|
+
const Core = NativeModules.Core
|
|
17
|
+
? NativeModules.Core
|
|
18
|
+
: new Proxy({}, {
|
|
19
|
+
get() {
|
|
20
|
+
throw new Error(LINKING_ERROR);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(navigator, 'isReactNative', {
|
|
24
|
+
get: function () {
|
|
25
|
+
return true;
|
|
26
|
+
},
|
|
27
|
+
configurable: true,
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(navigator, 'userAgent', {
|
|
30
|
+
get: function () {
|
|
31
|
+
return 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4753.0 Safari/537.36'; // customized user agent
|
|
32
|
+
},
|
|
33
|
+
configurable: true,
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(navigator, 'RNLocalMediaHandlerImpl', {
|
|
36
|
+
get: function () {
|
|
37
|
+
return LocalMediaHandler;
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
export default RealtimeKitMeeting;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const base64Decode: any;
|
|
2
|
+
declare const NativeModules: any;
|
|
3
|
+
declare class TypeMismatchError extends Error {
|
|
4
|
+
}
|
|
5
|
+
declare class QuotaExceededError extends Error {
|
|
6
|
+
}
|
|
7
|
+
declare let warned: boolean;
|
|
8
|
+
declare function insecureRandomValues(array: any): any;
|
|
9
|
+
/**
|
|
10
|
+
* @param {number} byteLength
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
declare function getRandomBase64(byteLength: any): any;
|
|
14
|
+
/**
|
|
15
|
+
* @param {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Uint8ClampedArray} array
|
|
16
|
+
*/
|
|
17
|
+
declare function getRandomValues(array: any): any;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const base64Decode = require('fast-base64-decode');
|
|
2
|
+
const { NativeModules } = require('react-native');
|
|
3
|
+
class TypeMismatchError extends Error {
|
|
4
|
+
}
|
|
5
|
+
class QuotaExceededError extends Error {
|
|
6
|
+
}
|
|
7
|
+
let warned = false;
|
|
8
|
+
function insecureRandomValues(array) {
|
|
9
|
+
if (!warned) {
|
|
10
|
+
console.warn('Using an insecure random number generator, this should only happen when running in a debugger without support for crypto.getRandomValues');
|
|
11
|
+
warned = true;
|
|
12
|
+
}
|
|
13
|
+
for (let i = 0, r; i < array.length; i++) {
|
|
14
|
+
if ((i & 0x03) === 0)
|
|
15
|
+
r = Math.random() * 0x100000000;
|
|
16
|
+
array[i] = (r >>> ((i & 0x03) << 3)) & 0xff;
|
|
17
|
+
}
|
|
18
|
+
return array;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @param {number} byteLength
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
function getRandomBase64(byteLength) {
|
|
25
|
+
if (NativeModules.Core) {
|
|
26
|
+
return NativeModules.Core.getRandomBase64(byteLength);
|
|
27
|
+
}
|
|
28
|
+
else if (NativeModules.ExpoRandom) {
|
|
29
|
+
// Expo SDK 41-44
|
|
30
|
+
return NativeModules.ExpoRandom.getRandomBase64String(byteLength);
|
|
31
|
+
}
|
|
32
|
+
else if (global.ExpoModules) {
|
|
33
|
+
// Expo SDK 45+
|
|
34
|
+
return global.ExpoModules.ExpoRandom.getRandomBase64String(byteLength);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
throw new Error('Native module not found');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @param {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Uint8ClampedArray} array
|
|
42
|
+
*/
|
|
43
|
+
function getRandomValues(array) {
|
|
44
|
+
if (!(array instanceof Int8Array ||
|
|
45
|
+
array instanceof Uint8Array ||
|
|
46
|
+
array instanceof Int16Array ||
|
|
47
|
+
array instanceof Uint16Array ||
|
|
48
|
+
array instanceof Int32Array ||
|
|
49
|
+
array instanceof Uint32Array ||
|
|
50
|
+
array instanceof Uint8ClampedArray)) {
|
|
51
|
+
throw new TypeMismatchError('Expected an integer array');
|
|
52
|
+
}
|
|
53
|
+
if (array.byteLength > 65536) {
|
|
54
|
+
throw new QuotaExceededError('Can only request a maximum of 65536 bytes');
|
|
55
|
+
}
|
|
56
|
+
// Expo SDK 48+
|
|
57
|
+
if (global.expo &&
|
|
58
|
+
global.expo.modules &&
|
|
59
|
+
global.expo.modules.ExpoCrypto &&
|
|
60
|
+
global.expo.modules.ExpoCrypto.getRandomValues) {
|
|
61
|
+
// ExpoCrypto.getRandomValues doesn't return the array
|
|
62
|
+
global.expo.modules.ExpoCrypto.getRandomValues(array);
|
|
63
|
+
return array;
|
|
64
|
+
}
|
|
65
|
+
// Calling getRandomBase64 in debug mode leads to the error
|
|
66
|
+
// "Calling synchronous methods on native modules is not supported in Chrome".
|
|
67
|
+
// So in that specific case we fall back to just using Math.random.
|
|
68
|
+
if (__DEV__) {
|
|
69
|
+
if (typeof global.nativeCallSyncHook === 'undefined') {
|
|
70
|
+
return insecureRandomValues(array);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
base64Decode(getRandomBase64(array.byteLength), new Uint8Array(array.buffer, array.byteOffset, array.byteLength));
|
|
74
|
+
return array;
|
|
75
|
+
}
|
|
76
|
+
if (typeof global.crypto !== 'object') {
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
global.crypto = {};
|
|
79
|
+
}
|
|
80
|
+
if (typeof global.crypto.getRandomValues !== 'function') {
|
|
81
|
+
global.crypto.getRandomValues = getRandomValues;
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
"name": "@cloudflare/realtimekit-react-native",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Cloudflare RealtimeKit SDK for react native",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"author": "Dyte <dev@dyte.io> (https://github.com/dyte-in/)",
|
|
7
|
+
"homepage": "https://github.com/dyte-in/react-native-core#readme",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"lib",
|
|
11
|
+
"android",
|
|
12
|
+
"ios",
|
|
13
|
+
"cpp",
|
|
14
|
+
"plugin",
|
|
15
|
+
"app.plugin.js",
|
|
16
|
+
"RealtimeKitCore.podspec",
|
|
17
|
+
"!android/build",
|
|
18
|
+
"!ios/build",
|
|
19
|
+
"!**/__tests__",
|
|
20
|
+
"!**/__fixtures__",
|
|
21
|
+
"!**/__mocks__"
|
|
22
|
+
],
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/dyte-in/react-native-core/issues"
|
|
25
|
+
},
|
|
26
|
+
"private": false,
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@cloudflare/realtimekit": "0.5.0-staging.98",
|
|
29
|
+
"events": "^3.3.0",
|
|
30
|
+
"fast-base64-decode": "1.0.0",
|
|
31
|
+
"node-libs-react-native": "^1.2.1",
|
|
32
|
+
"react-native-url-polyfill": "1.3.0",
|
|
33
|
+
"text-encoding-polyfill": "^0.6.7"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"react-native",
|
|
37
|
+
"ios",
|
|
38
|
+
"android"
|
|
39
|
+
],
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@dyteinternals/react-native-webrtc": "114.0.16",
|
|
42
|
+
"@expo/config-plugins": "*",
|
|
43
|
+
"react": "*",
|
|
44
|
+
"react-native": "*"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"tag": "staging"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"postpublish": "mv package.json.bak package.json"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ConfigPlugin } from '@expo/config-plugins';
|
|
2
|
+
export type Props = {
|
|
3
|
+
/**
|
|
4
|
+
* `NSMicrophoneUsageDescription` message.
|
|
5
|
+
*/
|
|
6
|
+
microphonePermission?: string | false;
|
|
7
|
+
/**
|
|
8
|
+
* `NSCameraUsageDescription` message.
|
|
9
|
+
*/
|
|
10
|
+
cameraPermission?: string | false;
|
|
11
|
+
/**
|
|
12
|
+
* `NSBluetoothPeripheralUsageDescription` message.
|
|
13
|
+
*/
|
|
14
|
+
bluetoothPermission?: string | false;
|
|
15
|
+
/**
|
|
16
|
+
* `NSBluetoothPeripheralUsageDescription` message.
|
|
17
|
+
*/
|
|
18
|
+
bluetoothAlwaysPermission?: string | false;
|
|
19
|
+
/**
|
|
20
|
+
* `NSPhotoLibraryUsageDescription` message.
|
|
21
|
+
*/
|
|
22
|
+
libraryPermission?: string | false;
|
|
23
|
+
};
|
|
24
|
+
declare const _default: ConfigPlugin<void | Props>;
|
|
25
|
+
export default _default;
|