@whereby.com/react-native-sdk 0.3.1 → 0.5.0

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.
@@ -1,74 +1,88 @@
1
1
  import * as React from "react";
2
2
  import { WebView, WebViewMessageEvent, WebViewProps } from "react-native-webview";
3
3
 
4
+ type SettingsPane = "theme" | "integrations" | "streaming" | "effects" | "notifications" | "advanced" | "media" | null;
5
+
6
+ function getInjectableJSMessage(command: string, args: unknown[]) {
7
+ const parsedArgs = args.length === 1 && args[0] === undefined ? "" : `, args: ${JSON.stringify(args)}`;
8
+ return `(function() { window.postMessage({ command: ${JSON.stringify(command)}${parsedArgs} }) })()`;
9
+ }
10
+
4
11
  interface WherebyEmbedElementAttributes {
5
- aec?: string;
6
- agc?: string;
7
- audio?: string;
12
+ aec?: boolean;
13
+ agc?: boolean;
14
+ audio?: boolean;
8
15
  /**
9
16
  * Automatically spotlight the local participant on room join. Can only be used with users joining with host privileges.
10
17
  */
11
- audioDenoiser?: string;
12
- autoHideSelfView?: string;
13
- autoSpotlight?: string;
18
+ audioDenoiser?: boolean;
19
+ autoHideSelfView?: boolean;
20
+ autoSpotlight?: boolean;
14
21
  avatarUrl?: string;
15
- background?: string;
16
- bottomToolbar?: string;
17
- breakout?: string;
18
- callQualityMonitoring?: string;
19
- cameraAccess?: string;
20
- cameraEffect?: string;
21
- chat?: string;
22
+ background?: boolean;
23
+ bottomToolbar?: boolean;
24
+ breakout?: boolean;
25
+ callQualityMonitoring?: boolean;
26
+ cameraAccess?: boolean;
27
+ cameraEffect?:
28
+ | "slight-blur"
29
+ | "blur"
30
+ | "image-cabin"
31
+ | "image-concrete"
32
+ | "image-brick"
33
+ | "image-sunrise"
34
+ | "image-day"
35
+ | "image-night";
36
+ chat?: boolean;
22
37
  displayName?: string;
23
- emptyRoomInvitation?: string;
38
+ emptyRoomInvitation?: boolean;
24
39
  emojiSkinTone?: string;
25
40
  externalId?: string;
26
- floatSelf?: string;
41
+ floatSelf?: boolean;
27
42
  groups?: string;
28
- help?: string;
29
- lang?: string;
30
- leaveButton?: string;
31
- locking?: string;
32
- localization?: string;
33
- logo?: string;
34
- lowData?: string;
43
+ help?: boolean;
44
+ lang?: "fr" | "it" | "de" | "nb" | "da" | "nl" | "pt" | "pl" | "es" | "hi" | "cs" | "zh-hant" | "jp";
45
+ leaveButton?: boolean;
46
+ locking?: boolean;
47
+ localization?: boolean;
48
+ logo?: boolean;
49
+ lowData?: boolean;
35
50
  metadata?: string;
36
- minimal?: string;
37
- moreButton?: string;
38
- participantCount?: string;
39
- people?: string;
40
- pipButton?: string;
51
+ minimal?: boolean;
52
+ moreButton?: boolean;
53
+ participantCount?: boolean;
54
+ people?: boolean;
55
+ pipButton?: boolean;
41
56
  /**
42
57
  * Displays a device and connectivity test for the user. Is dependent on precallReview being enabled
43
58
  */
44
- precallCeremony?: string;
45
- precallReview?: string;
59
+ precallCeremony?: boolean;
60
+ precallReview?: boolean;
46
61
  precallPermissionsHelpLink?: string;
47
- precallCeremonyCanSkip?: string;
48
- reactions?: string;
49
- recording?: string;
50
- room?: string;
62
+ precallCeremonyCanSkip?: boolean;
63
+ reactions?: boolean;
64
+ recording?: boolean;
51
65
  /**
52
66
  * Enables the use of supported room integrations (Miro and YouTube)
53
67
  */
54
- roomIntegrations?: string;
55
- settingsButton?: string;
56
- screenshare?: string;
68
+ roomIntegrations?: boolean;
69
+ settingsButton?: boolean;
70
+ screenshare?: boolean;
57
71
  /**
58
72
  * Skips the Whereby permissions UI and causes browser to automatically request device permissions. Required for Android app integrations.
59
73
  */
60
- skipMediaPermissionPrompt?: string;
61
- subgridLabels?: string;
62
- timer?: string;
74
+ skipMediaPermissionPrompt?: boolean;
75
+ subgridLabels?: boolean;
76
+ timer?: boolean;
63
77
  title?: string;
64
78
  /**
65
79
  * Use dark text for bottom toolbar items.
66
80
  *
67
81
  * Use this attribute when the room background is light and the bottom toolbar items are hard to read.
68
82
  */
69
- toolbarDarkText?: string;
70
- topToolbar?: string;
71
- video?: string;
83
+ toolbarDarkText?: boolean;
84
+ topToolbar?: boolean;
85
+ video?: boolean;
72
86
  virtualBackgroundUrl?: string;
73
87
  }
74
88
 
@@ -132,6 +146,25 @@ export type WherebyEvent =
132
146
  };
133
147
  };
134
148
 
149
+ type WherebyWebView = WebView & {
150
+ endMeeting: () => void;
151
+ knock: () => void;
152
+ leaveRoom: () => void;
153
+ openSettings: (settingsPane?: SettingsPane) => void;
154
+ startRecording: () => void;
155
+ stopRecording: () => void;
156
+ startStreaming: () => void;
157
+ stopStreaming: () => void;
158
+ startLiveTranscription: () => void;
159
+ stopLiveTranscription: () => void;
160
+ toggleBreakout: (enabled?: boolean) => void;
161
+ toggleCamera: (enabled?: boolean) => void;
162
+ toggleMicrophone: (enabled?: boolean) => void;
163
+ togglePeople: (enabled?: boolean) => void;
164
+ toggleScreenshare: (enabled?: boolean) => void;
165
+ toggleChat: (enabled?: boolean) => void;
166
+ };
167
+
135
168
  interface WherebyEmbedProps extends WebViewProps, WherebyEmbedElementAttributes {
136
169
  room: string;
137
170
  // Catch-all for any Whereby event
@@ -159,9 +192,12 @@ interface WherebyEmbedProps extends WebViewProps, WherebyEmbedElementAttributes
159
192
  onPrecallCheckCompleted?: (data: Extract<WherebyEvent, { type: "precall_check_completed" }>["payload"]) => void;
160
193
  }
161
194
 
162
- const WherebyEmbed = React.forwardRef<React.ElementRef<typeof WebView>, WherebyEmbedProps>(
195
+ const WherebyEmbed = React.forwardRef<WherebyWebView, WherebyEmbedProps>(
163
196
  ({ room, ...props }: WherebyEmbedProps, ref) => {
197
+ const webviewRef = React.useRef<WebView>(null);
164
198
  const roomUrl = new URL(room);
199
+ const ON = "on";
200
+ const OFF = "off";
165
201
 
166
202
  Object.entries({
167
203
  ...(props.displayName && { displayName: props.displayName }),
@@ -174,57 +210,61 @@ const WherebyEmbed = React.forwardRef<React.ElementRef<typeof WebView>, WherebyE
174
210
  ...(props.avatarUrl && { avatarUrl: props.avatarUrl }),
175
211
  ...(props.cameraEffect && { cameraEffect: props.cameraEffect }),
176
212
  // the original ?embed name was confusing, so we give minimal
177
- ...(props.minimal != null && { embed: props.minimal }),
178
- ...(props.aec != null && { aec: props.aec }),
179
- ...(props.agc != null && { agc: props.agc }),
180
- ...(props.audio != null && { audio: props.audio }),
181
- ...(props.audioDenoiser != null && { audioDenoiser: props.audioDenoiser }),
182
- ...(props.autoHideSelfView != null && { autoHideSelfView: props.autoHideSelfView }),
183
- ...(props.autoSpotlight != null && { autoSpotlight: props.autoSpotlight }),
184
- ...(props.background != null && { background: props.background }),
185
- ...(props.bottomToolbar != null && { bottomToolbar: props.bottomToolbar }),
186
- ...(props.breakout != null && { breakout: props.breakout }),
187
- ...(props.callQualityMonitoring != null && { callQualityMonitoring: props.callQualityMonitoring }),
188
- ...(props.cameraAccess != null && { cameraAccess: props.cameraAccess }),
213
+ ...(props.minimal != null && { embed: props.minimal ? ON : OFF }),
214
+ ...(props.aec != null && { aec: props.aec ? ON : OFF }),
215
+ ...(props.agc != null && { agc: props.agc ? ON : OFF }),
216
+ ...(props.audio != null && { audio: props.audio ? ON : OFF }),
217
+ ...(props.audioDenoiser != null && { audioDenoiser: props.audioDenoiser ? ON : OFF }),
218
+ ...(props.autoHideSelfView != null && { autoHideSelfView: props.autoHideSelfView ? ON : OFF }),
219
+ ...(props.autoSpotlight != null && { autoSpotlight: props.autoSpotlight ? ON : OFF }),
220
+ ...(props.background != null && { background: props.background ? ON : OFF }),
221
+ ...(props.bottomToolbar != null && { bottomToolbar: props.bottomToolbar ? ON : OFF }),
222
+ ...(props.breakout != null && { breakout: props.breakout ? ON : OFF }),
223
+ ...(props.callQualityMonitoring != null && {
224
+ callQualityMonitoring: props.callQualityMonitoring ? ON : OFF,
225
+ }),
226
+ ...(props.cameraAccess != null && { cameraAccess: props.cameraAccess ? ON : OFF }),
189
227
  ...(props.cameraEffect != null && { cameraEffect: props.cameraEffect }),
190
- ...(props.chat != null && { chat: props.chat }),
228
+ ...(props.chat != null && { chat: props.chat ? ON : OFF }),
191
229
  ...(props.displayName != null && { displayName: props.displayName }),
192
- ...(props.emptyRoomInvitation != null && { emptyRoomInvitation: props.emptyRoomInvitation }),
230
+ ...(props.emptyRoomInvitation != null && { emptyRoomInvitation: props.emptyRoomInvitation ? ON : OFF }),
193
231
  ...(props.emojiSkinTone != null && { emojiSkinTone: props.emojiSkinTone }),
194
232
  ...(props.externalId != null && { externalId: props.externalId }),
195
- ...(props.floatSelf != null && { floatSelf: props.floatSelf }),
233
+ ...(props.floatSelf != null && { floatSelf: props.floatSelf ? ON : OFF }),
196
234
  ...(props.groups != null && { groups: props.groups }),
197
- ...(props.help != null && { help: props.help }),
235
+ ...(props.help != null && { help: props.help ? ON : OFF }),
198
236
  ...(props.lang != null && { lang: props.lang }),
199
- ...(props.leaveButton != null && { leaveButton: props.leaveButton }),
200
- ...(props.locking != null && { locking: props.locking }),
201
- ...(props.localization != null && { localization: props.localization }),
202
- ...(props.logo != null && { logo: props.logo }),
203
- ...(props.lowData != null && { lowData: props.lowData }),
204
- ...(props.moreButton != null && { moreButton: props.moreButton }),
205
- ...(props.participantCount != null && { participantCount: props.participantCount }),
206
- ...(props.people != null && { people: props.people }),
207
- ...(props.pipButton != null && { pipButton: props.pipButton }),
208
- ...(props.precallCeremony != null && { precallCeremony: props.precallCeremony }),
209
- ...(props.precallReview != null && { precallReview: props.precallReview }),
237
+ ...(props.leaveButton != null && { leaveButton: props.leaveButton ? ON : OFF }),
238
+ ...(props.locking != null && { locking: props.locking ? ON : OFF }),
239
+ ...(props.localization != null && { localization: props.localization ? ON : OFF }),
240
+ ...(props.logo != null && { logo: props.logo ? ON : OFF }),
241
+ ...(props.lowData != null && { lowData: props.lowData ? ON : OFF }),
242
+ ...(props.moreButton != null && { moreButton: props.moreButton ? ON : OFF }),
243
+ ...(props.participantCount != null && { participantCount: props.participantCount ? ON : OFF }),
244
+ ...(props.people != null && { people: props.people ? ON : OFF }),
245
+ ...(props.pipButton != null && { pipButton: props.pipButton ? ON : OFF }),
246
+ ...(props.precallCeremony != null && { precallCeremony: props.precallCeremony ? ON : OFF }),
247
+ ...(props.precallReview != null && { precallReview: props.precallReview ? ON : OFF }),
210
248
  ...(props.precallPermissionsHelpLink != null && {
211
249
  precallPermissionsHelpLink: props.precallPermissionsHelpLink,
212
250
  }),
213
- ...(props.precallCeremonyCanSkip != null && { precallCeremonyCanSkip: props.precallCeremonyCanSkip }),
214
- ...(props.reactions != null && { reactions: props.reactions }),
215
- ...(props.recording != null && { recording: props.recording }),
216
- ...(props.roomIntegrations != null && { roomIntegrations: props.roomIntegrations }),
217
- ...(props.settingsButton != null && { settingsButton: props.settingsButton }),
218
- ...(props.screenshare != null && { screenshare: props.screenshare }),
251
+ ...(props.precallCeremonyCanSkip != null && {
252
+ precallCeremonyCanSkip: props.precallCeremonyCanSkip ? ON : OFF,
253
+ }),
254
+ ...(props.reactions != null && { reactions: props.reactions ? ON : OFF }),
255
+ ...(props.recording != null && { recording: props.recording ? ON : OFF }),
256
+ ...(props.roomIntegrations != null && { roomIntegrations: props.roomIntegrations ? ON : OFF }),
257
+ ...(props.settingsButton != null && { settingsButton: props.settingsButton ? ON : OFF }),
258
+ ...(props.screenshare != null && { screenshare: props.screenshare ? ON : OFF }),
219
259
  ...(props.skipMediaPermissionPrompt != null && {
220
- skipMediaPermissionPrompt: props.skipMediaPermissionPrompt,
260
+ skipMediaPermissionPrompt: props.skipMediaPermissionPrompt ? ON : OFF,
221
261
  }),
222
- ...(props.subgridLabels != null && { subgridLabels: props.subgridLabels }),
223
- ...(props.timer != null && { timer: props.timer }),
262
+ ...(props.subgridLabels != null && { subgridLabels: props.subgridLabels ? ON : OFF }),
263
+ ...(props.timer != null && { timer: props.timer ? ON : OFF }),
224
264
  ...(props.title != null && { title: props.title }),
225
- ...(props.toolbarDarkText != null && { toolbarDarkText: props.toolbarDarkText }),
226
- ...(props.topToolbar != null && { topToolbar: props.topToolbar }),
227
- ...(props.video != null && { video: props.video }),
265
+ ...(props.toolbarDarkText != null && { toolbarDarkText: props.toolbarDarkText ? ON : OFF }),
266
+ ...(props.topToolbar != null && { topToolbar: props.topToolbar ? ON : OFF }),
267
+ ...(props.video != null && { video: props.video ? ON : OFF }),
228
268
  ...(props.virtualBackgroundUrl != null && { virtualBackgroundUrl: props.virtualBackgroundUrl }),
229
269
  }).forEach(([k, v]) => {
230
270
  if (!roomUrl.searchParams.has(k)) {
@@ -232,6 +272,60 @@ const WherebyEmbed = React.forwardRef<React.ElementRef<typeof WebView>, WherebyE
232
272
  }
233
273
  });
234
274
 
275
+ React.useImperativeHandle(ref, () => {
276
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
277
+ return Object.assign(webviewRef.current!, {
278
+ endMeeting: () => {
279
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("end_meeting", []));
280
+ },
281
+ knock: () => {
282
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("knock", []));
283
+ },
284
+ leaveRoom: () => {
285
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("leave_room", []));
286
+ },
287
+ openSettings: (settingsPane: SettingsPane = "media") => {
288
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("open_settings", [settingsPane]));
289
+ },
290
+ startRecording: () => {
291
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("start_recording", []));
292
+ },
293
+ stopRecording: () => {
294
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("stop_recording", []));
295
+ },
296
+ startStreaming: () => {
297
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("start_streaming", []));
298
+ },
299
+ stopStreaming: () => {
300
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("stop_streaming", []));
301
+ },
302
+ startLiveTranscription: () => {
303
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("start_live_transcription", []));
304
+ },
305
+ stopLiveTranscription: () => {
306
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("stop_live_transcription", []));
307
+ },
308
+ toggleBreakout: (enabled?: boolean) => {
309
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("toggle_breakout", [enabled]));
310
+ },
311
+ toggleCamera: (enabled?: boolean) => {
312
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("toggle_camera", [enabled]));
313
+ },
314
+ toggleMicrophone: (enabled?: boolean) => {
315
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("toggle_microphone", [enabled]));
316
+ },
317
+ togglePeople: (enabled?: boolean) => {
318
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("toggle_people", [enabled]));
319
+ },
320
+ toggleScreenshare: (enabled?: boolean) => {
321
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("toggle_screenshare", [enabled]));
322
+ },
323
+ toggleChat: (enabled?: boolean) => {
324
+ webviewRef.current?.injectJavaScript(getInjectableJSMessage("toggle_chat", [enabled]));
325
+ },
326
+ });
327
+ });
328
+
235
329
  const handleMessage = React.useCallback(
236
330
  (event: WebViewMessageEvent) => {
237
331
  const data = JSON.parse(event.nativeEvent.data);
@@ -310,7 +404,7 @@ const WherebyEmbed = React.forwardRef<React.ElementRef<typeof WebView>, WherebyE
310
404
 
311
405
  return (
312
406
  <WebView
313
- ref={ref}
407
+ ref={webviewRef}
314
408
  source={{ uri: roomUrl.href }}
315
409
  startInLoadingState
316
410
  originWhitelist={["*"]}
@@ -325,6 +419,4 @@ const WherebyEmbed = React.forwardRef<React.ElementRef<typeof WebView>, WherebyE
325
419
  },
326
420
  );
327
421
 
328
- type WherebyEmbedRef = React.ElementRef<typeof WebView>;
329
-
330
- export { WherebyEmbed, type WherebyEmbedRef };
422
+ export { WherebyEmbed, type WherebyWebView };