@whereby.com/browser-sdk 2.0.0-alpha23 → 2.0.0-alpha26

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 (83) hide show
  1. package/README.md +14 -0
  2. package/dist/LocalMedia.d.ts +53 -0
  3. package/dist/LocalMedia.js +154 -0
  4. package/dist/RoomConnection.d.ts +174 -0
  5. package/dist/RoomConnection.js +617 -0
  6. package/dist/RoomParticipant.d.ts +50 -0
  7. package/dist/RoomParticipant.js +48 -0
  8. package/dist/api/ApiClient.d.ts +26 -0
  9. package/dist/api/ApiClient.js +63 -0
  10. package/dist/api/Credentials.d.ts +17 -0
  11. package/dist/api/Credentials.js +16 -0
  12. package/dist/api/HttpClient.d.ts +16 -0
  13. package/dist/api/HttpClient.js +53 -0
  14. package/dist/api/MultipartHttpClient.d.ts +10 -0
  15. package/dist/api/MultipartHttpClient.js +25 -0
  16. package/dist/api/OrganizationApiClient.d.ts +16 -0
  17. package/dist/api/OrganizationApiClient.js +30 -0
  18. package/dist/api/Response.d.ts +29 -0
  19. package/dist/api/Response.js +9 -0
  20. package/dist/api/credentialsService/index.d.ts +27 -0
  21. package/dist/api/credentialsService/index.js +90 -0
  22. package/dist/api/deviceService/index.d.ts +9 -0
  23. package/dist/api/deviceService/index.js +25 -0
  24. package/dist/api/extractUtils.d.ts +16 -0
  25. package/dist/api/extractUtils.js +51 -0
  26. package/dist/api/index.d.ts +7 -0
  27. package/dist/api/index.js +7 -0
  28. package/dist/api/localStorageWrapper/index.d.ts +2 -0
  29. package/dist/api/localStorageWrapper/index.js +15 -0
  30. package/dist/api/models/Account.d.ts +20 -0
  31. package/dist/api/models/Account.js +24 -0
  32. package/dist/api/models/Meeting.d.ts +12 -0
  33. package/dist/api/models/Meeting.js +29 -0
  34. package/dist/api/models/Organization.d.ts +102 -0
  35. package/dist/api/models/Organization.js +81 -0
  36. package/dist/api/models/Room.d.ts +4 -0
  37. package/dist/api/models/Room.js +38 -0
  38. package/dist/api/models/account/EmbeddedFreeTierStatus.d.ts +13 -0
  39. package/dist/api/models/account/EmbeddedFreeTierStatus.js +17 -0
  40. package/dist/api/modules/AbstractStore.d.ts +5 -0
  41. package/dist/api/modules/AbstractStore.js +1 -0
  42. package/dist/api/modules/ChromeStorageStore.d.ts +10 -0
  43. package/dist/api/modules/ChromeStorageStore.js +21 -0
  44. package/dist/api/modules/LocalStorageStore.d.ts +9 -0
  45. package/dist/api/modules/LocalStorageStore.js +35 -0
  46. package/dist/api/modules/tests/__mocks__/storage.d.ts +10 -0
  47. package/dist/api/modules/tests/__mocks__/storage.js +19 -0
  48. package/dist/api/organizationService/index.d.ts +46 -0
  49. package/dist/api/organizationService/index.js +159 -0
  50. package/dist/api/organizationServiceCache/index.d.ts +13 -0
  51. package/dist/api/organizationServiceCache/index.js +20 -0
  52. package/dist/api/parameterAssertUtils.d.ts +13 -0
  53. package/dist/api/parameterAssertUtils.js +64 -0
  54. package/dist/api/roomService/index.d.ts +54 -0
  55. package/dist/api/roomService/index.js +160 -0
  56. package/dist/api/test/helpers.d.ts +5 -0
  57. package/dist/api/test/helpers.js +32 -0
  58. package/dist/api/types.d.ts +5 -0
  59. package/dist/api/types.js +1 -0
  60. package/dist/embed/index.d.ts +31 -0
  61. package/dist/embed/index.js +123 -0
  62. package/dist/index.d.ts +5 -0
  63. package/dist/index.js +5 -0
  64. package/dist/react/VideoView.d.ts +15 -0
  65. package/dist/react/VideoView.js +37 -0
  66. package/dist/react/index.d.ts +3 -0
  67. package/dist/react/index.js +3 -0
  68. package/dist/react/useLocalMedia.d.ts +28 -0
  69. package/dist/react/useLocalMedia.js +109 -0
  70. package/dist/react/useRoomConnection.d.ts +47 -0
  71. package/dist/react/useRoomConnection.js +283 -0
  72. package/dist/utils/debounce.d.ts +9 -0
  73. package/dist/utils/debounce.js +20 -0
  74. package/dist/utils/fakeAudioStream.d.ts +1 -0
  75. package/dist/utils/fakeAudioStream.js +18 -0
  76. package/dist/utils/fakeWebcamFrame.d.ts +1 -0
  77. package/dist/utils/fakeWebcamFrame.js +49 -0
  78. package/dist/v2-alpha26.js +1993 -0
  79. package/package.json +24 -12
  80. package/dist/lib.cjs +0 -6200
  81. package/dist/lib.esm.js +0 -6182
  82. package/dist/types.d.ts +0 -382
  83. package/dist/v2-alpha23.js +0 -43
@@ -0,0 +1,109 @@
1
+ import { __awaiter } from "tslib";
2
+ import { useEffect, useReducer, useState } from "react";
3
+ import LocalMedia from "../LocalMedia";
4
+ const initialState = {
5
+ cameraDeviceError: null,
6
+ cameraDevices: [],
7
+ isSettingCameraDevice: false,
8
+ isSettingMicrophoneDevice: false,
9
+ isStarting: false,
10
+ microphoneDeviceError: null,
11
+ microphoneDevices: [],
12
+ speakerDevices: [],
13
+ startError: null,
14
+ };
15
+ function reducer(state, action) {
16
+ switch (action.type) {
17
+ case "DEVICE_LIST_UPDATED":
18
+ return Object.assign(Object.assign({}, state), action.payload);
19
+ case "LOCAL_STREAM_UPDATED":
20
+ return Object.assign(Object.assign({}, state), { currentCameraDeviceId: action.payload.currentCameraDeviceId, currentMicrophoneDeviceId: action.payload.currentMicrophoneDeviceId, localStream: action.payload.stream });
21
+ case "SET_CAMERA_DEVICE":
22
+ return Object.assign(Object.assign({}, state), { cameraDeviceError: null, isSettingCameraDevice: true });
23
+ case "SET_CAMERA_DEVICE_COMPLETE":
24
+ return Object.assign(Object.assign({}, state), { isSettingCameraDevice: false });
25
+ case "SET_CAMERA_DEVICE_ERROR":
26
+ return Object.assign(Object.assign({}, state), { cameraDeviceError: action.payload, isSettingCameraDevice: false });
27
+ case "SET_MICROPHONE_DEVICE":
28
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: true, microphoneDeviceError: null });
29
+ case "SET_MICROPHONE_DEVICE_COMPLETE":
30
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: false });
31
+ case "SET_MICROPHONE_DEVICE_ERROR":
32
+ return Object.assign(Object.assign({}, state), { isSettingMicrophoneDevice: false, microphoneDeviceError: action.payload });
33
+ case "START":
34
+ return Object.assign(Object.assign({}, state), { isStarting: true, startError: null });
35
+ case "START_COMPLETE":
36
+ return Object.assign(Object.assign({}, state), { isStarting: false });
37
+ case "START_ERROR":
38
+ return Object.assign(Object.assign({}, state), { isStarting: false, startError: action.payload });
39
+ default:
40
+ return state;
41
+ }
42
+ }
43
+ export default function useLocalMedia(constraintsOrStream = { audio: true, video: true }) {
44
+ const [localMedia] = useState(() => new LocalMedia(constraintsOrStream));
45
+ const [state, dispatch] = useReducer(reducer, initialState);
46
+ useEffect(() => {
47
+ localMedia.addEventListener("device_list_updated", (e) => {
48
+ const { cameraDevices, microphoneDevices, speakerDevices } = e.detail;
49
+ dispatch({ type: "DEVICE_LIST_UPDATED", payload: { cameraDevices, microphoneDevices, speakerDevices } });
50
+ });
51
+ localMedia.addEventListener("stream_updated", (e) => {
52
+ const { stream } = e.detail;
53
+ dispatch({
54
+ type: "LOCAL_STREAM_UPDATED",
55
+ payload: {
56
+ stream,
57
+ currentCameraDeviceId: localMedia.getCameraDeviceId(),
58
+ currentMicrophoneDeviceId: localMedia.getMicrophoneDeviceId(),
59
+ },
60
+ });
61
+ });
62
+ const start = () => __awaiter(this, void 0, void 0, function* () {
63
+ dispatch({ type: "START" });
64
+ try {
65
+ yield localMedia.start();
66
+ dispatch({ type: "START_COMPLETE" });
67
+ }
68
+ catch (error) {
69
+ dispatch({ type: "START_ERROR", payload: error });
70
+ }
71
+ });
72
+ start();
73
+ return () => {
74
+ localMedia.stop();
75
+ };
76
+ }, []);
77
+ return {
78
+ state,
79
+ actions: {
80
+ setCameraDevice: (...args) => __awaiter(this, void 0, void 0, function* () {
81
+ dispatch({ type: "SET_CAMERA_DEVICE" });
82
+ try {
83
+ yield localMedia.setCameraDevice(...args);
84
+ dispatch({ type: "SET_CAMERA_DEVICE_COMPLETE" });
85
+ }
86
+ catch (error) {
87
+ dispatch({ type: "SET_CAMERA_DEVICE_ERROR", payload: error });
88
+ }
89
+ }),
90
+ setMicrophoneDevice: (...args) => __awaiter(this, void 0, void 0, function* () {
91
+ dispatch({ type: "SET_MICROPHONE_DEVICE" });
92
+ try {
93
+ yield localMedia.setMicrophoneDevice(...args);
94
+ dispatch({ type: "SET_MICROPHONE_DEVICE_COMPLETE" });
95
+ }
96
+ catch (error) {
97
+ dispatch({ type: "SET_MICROPHONE_DEVICE_ERROR", payload: error });
98
+ }
99
+ }),
100
+ toggleCameraEnabled: (...args) => {
101
+ return localMedia.toggleCameraEnabled(...args);
102
+ },
103
+ toggleMicrophoneEnabled: (...args) => {
104
+ return localMedia.toggleMichrophoneEnabled(...args);
105
+ },
106
+ },
107
+ _ref: localMedia,
108
+ };
109
+ }
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+ import VideoView from "./VideoView";
3
+ import { LocalMediaRef } from "./useLocalMedia";
4
+ import RoomConnection, { ChatMessage, CloudRecordingState, RoomConnectionOptions, RoomConnectionStatus, StreamingState } from "../RoomConnection";
5
+ import { LocalParticipant, RemoteParticipant, Screenshare, WaitingParticipant } from "../RoomParticipant";
6
+ type RemoteParticipantState = Omit<RemoteParticipant, "updateStreamState">;
7
+ export interface RoomConnectionState {
8
+ chatMessages: ChatMessage[];
9
+ cloudRecording: CloudRecordingState;
10
+ isJoining: boolean;
11
+ isStartingScreenshare: boolean;
12
+ joinError: unknown;
13
+ startScreenshareError: unknown;
14
+ localParticipant?: LocalParticipant;
15
+ mostRecentChatMessage: ChatMessage | null;
16
+ remoteParticipants: RemoteParticipantState[];
17
+ screenshares: Screenshare[];
18
+ roomConnectionStatus: RoomConnectionStatus;
19
+ streaming: StreamingState;
20
+ waitingParticipants: WaitingParticipant[];
21
+ }
22
+ interface UseRoomConnectionOptions extends Omit<RoomConnectionOptions, "localMedia"> {
23
+ localMedia?: LocalMediaRef;
24
+ }
25
+ interface RoomConnectionActions {
26
+ sendChatMessage(text: string): void;
27
+ knock(): void;
28
+ setDisplayName(displayName: string): void;
29
+ toggleCamera(enabled?: boolean): void;
30
+ toggleMicrophone(enabled?: boolean): void;
31
+ acceptWaitingParticipant(participantId: string): void;
32
+ rejectWaitingParticipant(participantId: string): void;
33
+ startScreenshare(): void;
34
+ stopScreenshare(): void;
35
+ }
36
+ type VideoViewComponentProps = Omit<React.ComponentProps<typeof VideoView>, "onResize">;
37
+ interface RoomConnectionComponents {
38
+ VideoView: (props: VideoViewComponentProps) => ReturnType<typeof VideoView>;
39
+ }
40
+ export type RoomConnectionRef = {
41
+ state: RoomConnectionState;
42
+ actions: RoomConnectionActions;
43
+ components: RoomConnectionComponents;
44
+ _ref: RoomConnection;
45
+ };
46
+ export declare function useRoomConnection(roomUrl: string, roomConnectionOptions: UseRoomConnectionOptions): RoomConnectionRef;
47
+ export {};
@@ -0,0 +1,283 @@
1
+ import React, { useEffect, useReducer, useState } from "react";
2
+ import VideoView from "./VideoView";
3
+ import RoomConnection from "../RoomConnection";
4
+ const initialState = {
5
+ chatMessages: [],
6
+ cloudRecording: {
7
+ status: "",
8
+ startedAt: null,
9
+ },
10
+ isJoining: false,
11
+ isStartingScreenshare: false,
12
+ joinError: null,
13
+ startScreenshareError: null,
14
+ mostRecentChatMessage: null,
15
+ remoteParticipants: [],
16
+ roomConnectionStatus: "",
17
+ screenshares: [],
18
+ streaming: {
19
+ status: "",
20
+ startedAt: null,
21
+ },
22
+ waitingParticipants: [],
23
+ };
24
+ function updateParticipant(remoteParticipants, participantId, updates) {
25
+ const existingParticipant = remoteParticipants.find((p) => p.id === participantId);
26
+ if (!existingParticipant) {
27
+ return remoteParticipants;
28
+ }
29
+ const index = remoteParticipants.indexOf(existingParticipant);
30
+ return [
31
+ ...remoteParticipants.slice(0, index),
32
+ Object.assign(Object.assign({}, existingParticipant), updates),
33
+ ...remoteParticipants.slice(index + 1),
34
+ ];
35
+ }
36
+ function addScreenshare(screenshares, screenshare) {
37
+ const existingScreenshare = screenshares.find((ss) => ss.id === screenshare.id);
38
+ if (existingScreenshare) {
39
+ return screenshares;
40
+ }
41
+ return [...screenshares, screenshare];
42
+ }
43
+ function reducer(state, action) {
44
+ switch (action.type) {
45
+ case "CHAT_MESSAGE":
46
+ return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, action.payload], mostRecentChatMessage: action.payload });
47
+ case "CLOUD_RECORDING_STARTED":
48
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
49
+ status: action.payload.status,
50
+ startedAt: action.payload.startedAt,
51
+ } });
52
+ case "CLOUD_RECORDING_STOPPED":
53
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
54
+ status: "",
55
+ startedAt: null,
56
+ } });
57
+ case "ROOM_JOINED":
58
+ return Object.assign(Object.assign({}, state), { localParticipant: action.payload.localParticipant, remoteParticipants: action.payload.remoteParticipants, waitingParticipants: action.payload.waitingParticipants, roomConnectionStatus: "connected" });
59
+ case "ROOM_CONNECTION_STATUS_CHANGED":
60
+ return Object.assign(Object.assign({}, state), { roomConnectionStatus: action.payload.roomConnectionStatus });
61
+ case "PARTICIPANT_AUDIO_ENABLED":
62
+ return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
63
+ isAudioEnabled: action.payload.isAudioEnabled,
64
+ }) });
65
+ case "PARTICIPANT_JOINED":
66
+ return Object.assign(Object.assign({}, state), { remoteParticipants: [...state.remoteParticipants, action.payload.paritipant] });
67
+ case "PARTICIPANT_LEFT":
68
+ return Object.assign(Object.assign({}, state), { remoteParticipants: [...state.remoteParticipants.filter((p) => p.id !== action.payload.participantId)] });
69
+ case "PARTICIPANT_STREAM_ADDED":
70
+ return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
71
+ stream: action.payload.stream,
72
+ }) });
73
+ case "PARTICIPANT_VIDEO_ENABLED":
74
+ return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
75
+ isVideoEnabled: action.payload.isVideoEnabled,
76
+ }) });
77
+ case "PARTICIPANT_METADATA_CHANGED":
78
+ return Object.assign(Object.assign({}, state), { remoteParticipants: [
79
+ ...state.remoteParticipants.map((p) => p.id === action.payload.participantId ? Object.assign(Object.assign({}, p), { displayName: action.payload.displayName }) : p),
80
+ ] });
81
+ case "LOCAL_CLIENT_DISPLAY_NAME_CHANGED":
82
+ if (!state.localParticipant)
83
+ return state;
84
+ return Object.assign(Object.assign({}, state), { localParticipant: Object.assign(Object.assign({}, state.localParticipant), { displayName: action.payload.displayName }) });
85
+ case "SCREENSHARE_STARTED":
86
+ return Object.assign(Object.assign({}, state), { screenshares: addScreenshare(state.screenshares, {
87
+ participantId: action.payload.participantId,
88
+ id: action.payload.id,
89
+ hasAudioTrack: action.payload.hasAudioTrack,
90
+ stream: action.payload.stream,
91
+ isLocal: action.payload.isLocal,
92
+ }) });
93
+ case "SCREENSHARE_STOPPED":
94
+ return Object.assign(Object.assign({}, state), { screenshares: state.screenshares.filter((ss) => ss.id !== action.payload.id) });
95
+ case "LOCAL_SCREENSHARE_START_ERROR":
96
+ return Object.assign(Object.assign({}, state), { isStartingScreenshare: false, startScreenshareError: action.payload });
97
+ case "LOCAL_SCREENSHARE_STARTING":
98
+ return Object.assign(Object.assign({}, state), { isStartingScreenshare: true });
99
+ case "LOCAL_SCREENSHARE_STOPPED":
100
+ return Object.assign(Object.assign({}, state), { screenshares: state.screenshares.filter((ss) => !ss.isLocal) });
101
+ case "STREAMING_STARTED":
102
+ return Object.assign(Object.assign({}, state), { streaming: {
103
+ status: action.payload.status,
104
+ startedAt: action.payload.startedAt,
105
+ } });
106
+ case "STREAMING_STOPPED":
107
+ return Object.assign(Object.assign({}, state), { streaming: {
108
+ status: "",
109
+ startedAt: null,
110
+ } });
111
+ case "WAITING_PARTICIPANT_JOINED":
112
+ return Object.assign(Object.assign({}, state), { waitingParticipants: [
113
+ ...state.waitingParticipants,
114
+ { id: action.payload.participantId, displayName: action.payload.displayName },
115
+ ] });
116
+ case "WAITING_PARTICIPANT_LEFT":
117
+ return Object.assign(Object.assign({}, state), { waitingParticipants: state.waitingParticipants.filter((wp) => wp.id !== action.payload.participantId) });
118
+ default:
119
+ throw state;
120
+ }
121
+ }
122
+ export function useRoomConnection(roomUrl, roomConnectionOptions) {
123
+ const [roomConnection] = useState(() => {
124
+ var _a;
125
+ return new RoomConnection(roomUrl, Object.assign(Object.assign({}, roomConnectionOptions), { localMedia: ((_a = roomConnectionOptions === null || roomConnectionOptions === void 0 ? void 0 : roomConnectionOptions.localMedia) === null || _a === void 0 ? void 0 : _a._ref) || undefined }));
126
+ });
127
+ const [state, dispatch] = useReducer(reducer, initialState);
128
+ function createEventListener(eventName, listener, options) {
129
+ return {
130
+ eventName,
131
+ listener,
132
+ options,
133
+ };
134
+ }
135
+ const eventListeners = React.useMemo(() => [
136
+ createEventListener("chat_message", (e) => {
137
+ dispatch({ type: "CHAT_MESSAGE", payload: e.detail });
138
+ }),
139
+ createEventListener("cloud_recording_started", (e) => {
140
+ const { status, startedAt } = e.detail;
141
+ dispatch({ type: "CLOUD_RECORDING_STARTED", payload: { status, startedAt } });
142
+ }),
143
+ createEventListener("cloud_recording_stopped", () => {
144
+ dispatch({ type: "CLOUD_RECORDING_STOPPED" });
145
+ }),
146
+ createEventListener("participant_audio_enabled", (e) => {
147
+ const { participantId, isAudioEnabled } = e.detail;
148
+ dispatch({ type: "PARTICIPANT_AUDIO_ENABLED", payload: { participantId, isAudioEnabled } });
149
+ }),
150
+ createEventListener("participant_joined", (e) => {
151
+ const { remoteParticipant } = e.detail;
152
+ dispatch({ type: "PARTICIPANT_JOINED", payload: { paritipant: remoteParticipant } });
153
+ }),
154
+ createEventListener("participant_left", (e) => {
155
+ const { participantId } = e.detail;
156
+ dispatch({ type: "PARTICIPANT_LEFT", payload: { participantId } });
157
+ }),
158
+ createEventListener("participant_metadata_changed", (e) => {
159
+ const { participantId, displayName } = e.detail;
160
+ dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
161
+ }),
162
+ createEventListener("participant_stream_added", (e) => {
163
+ const { participantId, stream } = e.detail;
164
+ dispatch({ type: "PARTICIPANT_STREAM_ADDED", payload: { participantId, stream } });
165
+ }),
166
+ createEventListener("participant_video_enabled", (e) => {
167
+ const { participantId, isVideoEnabled } = e.detail;
168
+ dispatch({ type: "PARTICIPANT_VIDEO_ENABLED", payload: { participantId, isVideoEnabled } });
169
+ }),
170
+ createEventListener("room_connection_status_changed", (e) => {
171
+ const { roomConnectionStatus } = e.detail;
172
+ dispatch({
173
+ type: "ROOM_CONNECTION_STATUS_CHANGED",
174
+ payload: { roomConnectionStatus },
175
+ });
176
+ }),
177
+ createEventListener("room_joined", (e) => {
178
+ const { localParticipant, remoteParticipants, waitingParticipants } = e.detail;
179
+ dispatch({
180
+ type: "ROOM_JOINED",
181
+ payload: { localParticipant, remoteParticipants, waitingParticipants },
182
+ });
183
+ }),
184
+ createEventListener("screenshare_started", (e) => {
185
+ const { participantId, stream, id, hasAudioTrack, isLocal } = e.detail;
186
+ dispatch({
187
+ type: "SCREENSHARE_STARTED",
188
+ payload: { participantId, stream, id, hasAudioTrack, isLocal },
189
+ });
190
+ }),
191
+ createEventListener("screenshare_stopped", (e) => {
192
+ const { participantId, id } = e.detail;
193
+ dispatch({
194
+ type: "SCREENSHARE_STOPPED",
195
+ payload: { participantId, id },
196
+ });
197
+ }),
198
+ createEventListener("streaming_started", (e) => {
199
+ const { status, startedAt } = e.detail;
200
+ dispatch({ type: "STREAMING_STARTED", payload: { status, startedAt } });
201
+ }),
202
+ createEventListener("streaming_stopped", () => {
203
+ dispatch({ type: "STREAMING_STOPPED" });
204
+ }),
205
+ createEventListener("waiting_participant_joined", (e) => {
206
+ const { participantId, displayName } = e.detail;
207
+ dispatch({
208
+ type: "WAITING_PARTICIPANT_JOINED",
209
+ payload: { participantId, displayName },
210
+ });
211
+ }),
212
+ createEventListener("waiting_participant_left", (e) => {
213
+ const { participantId } = e.detail;
214
+ dispatch({
215
+ type: "WAITING_PARTICIPANT_LEFT",
216
+ payload: { participantId },
217
+ });
218
+ }),
219
+ ], []);
220
+ useEffect(() => {
221
+ eventListeners.forEach(({ eventName, listener }) => {
222
+ roomConnection.addEventListener(eventName, listener);
223
+ });
224
+ roomConnection.join();
225
+ return () => {
226
+ eventListeners.forEach(({ eventName, listener }) => {
227
+ roomConnection.removeEventListener(eventName, listener);
228
+ });
229
+ roomConnection.leave();
230
+ };
231
+ }, []);
232
+ return {
233
+ state,
234
+ actions: {
235
+ knock: () => {
236
+ roomConnection.knock();
237
+ },
238
+ sendChatMessage: (text) => {
239
+ roomConnection.sendChatMessage(text);
240
+ },
241
+ setDisplayName: (displayName) => {
242
+ roomConnection.setDisplayName(displayName);
243
+ dispatch({ type: "LOCAL_CLIENT_DISPLAY_NAME_CHANGED", payload: { displayName } });
244
+ },
245
+ toggleCamera: (enabled) => {
246
+ roomConnection.localMedia.toggleCameraEnabled(enabled);
247
+ },
248
+ toggleMicrophone: (enabled) => {
249
+ roomConnection.localMedia.toggleMichrophoneEnabled(enabled);
250
+ },
251
+ acceptWaitingParticipant: (participantId) => {
252
+ roomConnection.acceptWaitingParticipant(participantId);
253
+ },
254
+ rejectWaitingParticipant: (participantId) => {
255
+ roomConnection.rejectWaitingParticipant(participantId);
256
+ },
257
+ startScreenshare: () => {
258
+ dispatch({ type: "LOCAL_SCREENSHARE_STARTING" });
259
+ try {
260
+ roomConnection.startScreenshare();
261
+ }
262
+ catch (error) {
263
+ dispatch({ type: "LOCAL_SCREENSHARE_START_ERROR", payload: error });
264
+ }
265
+ },
266
+ stopScreenshare: () => {
267
+ roomConnection.stopScreenshare();
268
+ },
269
+ },
270
+ components: {
271
+ VideoView: (props) => React.createElement(VideoView, Object.assign({}, props, {
272
+ onResize: ({ stream, width, height, }) => {
273
+ roomConnection.updateStreamResolution({
274
+ streamId: stream.id,
275
+ width,
276
+ height,
277
+ });
278
+ },
279
+ })),
280
+ },
281
+ _ref: roomConnection,
282
+ };
283
+ }
@@ -0,0 +1,9 @@
1
+ interface Options {
2
+ delay?: number;
3
+ edges?: boolean;
4
+ }
5
+ interface DebouncedFunction {
6
+ (...args: any[]): void;
7
+ }
8
+ export default function debounce(fn: DebouncedFunction, { delay, edges }?: Options): DebouncedFunction;
9
+ export {};
@@ -0,0 +1,20 @@
1
+ import assert from "assert";
2
+ export default function debounce(fn, { delay = 500, edges } = {}) {
3
+ assert.ok(typeof fn === "function", "fn<function> is required");
4
+ let timeout;
5
+ let nCalls = 0;
6
+ return (...args) => {
7
+ nCalls += 1;
8
+ if (edges && nCalls === 1) {
9
+ fn(...args);
10
+ }
11
+ clearTimeout(timeout);
12
+ timeout = setTimeout(() => {
13
+ if (!edges || nCalls > 1) {
14
+ fn(...args);
15
+ }
16
+ timeout = undefined;
17
+ nCalls = 0;
18
+ }, delay);
19
+ };
20
+ }
@@ -0,0 +1 @@
1
+ export default function fakeAudioStream(): MediaStream;
@@ -0,0 +1,18 @@
1
+ export default function fakeAudioStream() {
2
+ const audioCtx = new AudioContext();
3
+ const oscillator = audioCtx.createOscillator();
4
+ const destination = audioCtx.createMediaStreamDestination();
5
+ oscillator.connect(destination);
6
+ oscillator.frequency.value = 400;
7
+ oscillator.type = "sine";
8
+ setInterval(() => {
9
+ if (oscillator.frequency.value <= 900) {
10
+ oscillator.frequency.value += 10;
11
+ }
12
+ else {
13
+ oscillator.frequency.value = 200;
14
+ }
15
+ }, 20);
16
+ oscillator.start();
17
+ return destination.stream;
18
+ }
@@ -0,0 +1 @@
1
+ export default function fakeWebcamFrame(canvas: HTMLCanvasElement): void;
@@ -0,0 +1,49 @@
1
+ let rotationAngle = 0;
2
+ function drawWebcamFrame(canvas) {
3
+ const context = canvas.getContext("2d");
4
+ if (!context) {
5
+ console.error("Canvas context not available");
6
+ return;
7
+ }
8
+ const wheelRadius = 100;
9
+ const wheelCenterX = canvas.width / 2;
10
+ const wheelCenterY = canvas.height / 2;
11
+ context.fillStyle = "darkgreen";
12
+ context.fillRect(0, 0, canvas.width, canvas.height);
13
+ context.save();
14
+ context.translate(wheelCenterX, wheelCenterY);
15
+ context.rotate(rotationAngle);
16
+ const numSlices = 12;
17
+ const sliceAngle = (2 * Math.PI) / numSlices;
18
+ const colors = ["red", "orange", "yellow", "green", "blue", "purple"];
19
+ for (let i = 0; i < numSlices; i++) {
20
+ context.beginPath();
21
+ context.moveTo(0, 0);
22
+ context.arc(0, 0, wheelRadius, i * sliceAngle, (i + 1) * sliceAngle);
23
+ context.fillStyle = colors[i % colors.length];
24
+ context.fill();
25
+ context.closePath();
26
+ }
27
+ context.restore();
28
+ context.fillStyle = "white";
29
+ context.font = "42px Arial";
30
+ const topText = "Whereby Media Stream";
31
+ const topTextWidth = context.measureText(topText).width;
32
+ context.fillText(topText, canvas.width / 2 - topTextWidth / 2, 50);
33
+ context.font = "32px Arial";
34
+ const now = new Date();
35
+ const timeText = `time: ${now.getHours().toString().padStart(2, "0")}:${now
36
+ .getMinutes()
37
+ .toString()
38
+ .padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}.${now
39
+ .getMilliseconds()
40
+ .toString()
41
+ .padStart(3, "0")}`;
42
+ context.fillText(timeText, 10, canvas.height - 20);
43
+ context.fillText(`rotation angle: ${rotationAngle.toFixed(2)}`, canvas.width - canvas.width / 2, canvas.height - 20);
44
+ rotationAngle += 0.01;
45
+ }
46
+ export default function fakeWebcamFrame(canvas) {
47
+ drawWebcamFrame(canvas);
48
+ requestAnimationFrame(() => fakeWebcamFrame(canvas));
49
+ }