@whereby.com/browser-sdk 2.0.0-alpha9 → 2.0.0-beta2

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 (85) hide show
  1. package/README.md +82 -29
  2. package/dist/LocalMedia.d.ts +70 -0
  3. package/dist/LocalMedia.js +208 -0
  4. package/dist/RoomConnection.d.ts +190 -0
  5. package/dist/RoomConnection.js +646 -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 +61 -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 +52 -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 +28 -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 +89 -0
  22. package/dist/api/deviceService/index.d.ts +9 -0
  23. package/dist/api/deviceService/index.js +23 -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 +158 -0
  50. package/dist/api/organizationServiceCache/index.d.ts +13 -0
  51. package/dist/api/organizationServiceCache/index.js +16 -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 +7 -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 +32 -0
  61. package/dist/embed/index.js +124 -0
  62. package/dist/react/VideoView.d.ts +15 -0
  63. package/dist/react/VideoView.js +37 -0
  64. package/dist/react/index.d.ts +6 -0
  65. package/dist/react/index.js +4 -0
  66. package/dist/react/useLocalMedia.d.ts +29 -0
  67. package/dist/react/useLocalMedia.js +109 -0
  68. package/dist/react/useRoomConnection.d.ts +57 -0
  69. package/dist/react/useRoomConnection.js +340 -0
  70. package/dist/utils/debounce.d.ts +9 -0
  71. package/dist/utils/debounce.js +18 -0
  72. package/dist/utils/fakeAudioStream.d.ts +1 -0
  73. package/dist/utils/fakeAudioStream.js +18 -0
  74. package/dist/utils/fakeWebcamFrame.d.ts +1 -0
  75. package/dist/utils/fakeWebcamFrame.js +49 -0
  76. package/dist/utils/index.d.ts +2 -0
  77. package/dist/utils/index.js +2 -0
  78. package/dist/v2-beta2.js +2000 -0
  79. package/dist/version.d.ts +1 -0
  80. package/dist/version.js +1 -0
  81. package/package.json +110 -81
  82. package/dist/lib.cjs.js +0 -5868
  83. package/dist/lib.esm.js +0 -5850
  84. package/dist/types.d.ts +0 -308
  85. package/dist/v2-alpha9.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(optionsOrStream = { audio: true, video: true }) {
44
+ const [localMedia] = useState(() => new LocalMedia(optionsOrStream));
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,57 @@
1
+ import React from "react";
2
+ import VideoView from "./VideoView";
3
+ import { LocalMediaRef } from "./useLocalMedia";
4
+ import RoomConnection, { ChatMessage, CloudRecordingState, RoomConnectionOptions, ConnectionStatus, LiveStreamState } from "../RoomConnection";
5
+ import { LocalParticipant, RemoteParticipant, Screenshare } from "../RoomParticipant";
6
+ export type RemoteParticipantState = Omit<RemoteParticipant, "updateStreamState" | "newJoiner" | "streams" | "addStream" | "removeStream">;
7
+ export type LocalParticipantState = LocalParticipant;
8
+ export interface WaitingParticipantState {
9
+ id: string;
10
+ displayName: string | null;
11
+ }
12
+ export interface ChatMessageState {
13
+ senderId: string;
14
+ timestamp: string;
15
+ text: string;
16
+ }
17
+ export type ScreenshareState = Screenshare;
18
+ type LocalScreenshareStatus = "starting" | "active";
19
+ export interface RoomConnectionState {
20
+ chatMessages: ChatMessage[];
21
+ cloudRecording?: CloudRecordingState;
22
+ localScreenshareStatus?: LocalScreenshareStatus;
23
+ localParticipant?: LocalParticipantState;
24
+ remoteParticipants: RemoteParticipantState[];
25
+ screenshares: Screenshare[];
26
+ connectionStatus: ConnectionStatus;
27
+ liveStream?: LiveStreamState;
28
+ waitingParticipants: WaitingParticipantState[];
29
+ }
30
+ interface UseRoomConnectionOptions extends Omit<RoomConnectionOptions, "localMedia"> {
31
+ localMedia?: LocalMediaRef;
32
+ }
33
+ interface RoomConnectionActions {
34
+ sendChatMessage(text: string): void;
35
+ knock(): void;
36
+ setDisplayName(displayName: string): void;
37
+ toggleCamera(enabled?: boolean): void;
38
+ toggleMicrophone(enabled?: boolean): void;
39
+ acceptWaitingParticipant(participantId: string): void;
40
+ rejectWaitingParticipant(participantId: string): void;
41
+ startCloudRecording(): void;
42
+ startScreenshare(): void;
43
+ stopCloudRecording(): void;
44
+ stopScreenshare(): void;
45
+ }
46
+ type VideoViewComponentProps = Omit<React.ComponentProps<typeof VideoView>, "onResize">;
47
+ interface RoomConnectionComponents {
48
+ VideoView: (props: VideoViewComponentProps) => ReturnType<typeof VideoView>;
49
+ }
50
+ export type RoomConnectionRef = {
51
+ state: RoomConnectionState;
52
+ actions: RoomConnectionActions;
53
+ components: RoomConnectionComponents;
54
+ _ref: RoomConnection;
55
+ };
56
+ export declare function useRoomConnection(roomUrl: string, roomConnectionOptions?: UseRoomConnectionOptions): RoomConnectionRef;
57
+ export {};
@@ -0,0 +1,340 @@
1
+ import { __awaiter } from "tslib";
2
+ import React, { useEffect, useReducer, useState } from "react";
3
+ import VideoView from "./VideoView";
4
+ import RoomConnection from "../RoomConnection";
5
+ const initialState = {
6
+ chatMessages: [],
7
+ remoteParticipants: [],
8
+ connectionStatus: "initializing",
9
+ screenshares: [],
10
+ waitingParticipants: [],
11
+ };
12
+ function updateParticipant(remoteParticipants, participantId, updates) {
13
+ const existingParticipant = remoteParticipants.find((p) => p.id === participantId);
14
+ if (!existingParticipant) {
15
+ return remoteParticipants;
16
+ }
17
+ const index = remoteParticipants.indexOf(existingParticipant);
18
+ return [
19
+ ...remoteParticipants.slice(0, index),
20
+ Object.assign(Object.assign({}, existingParticipant), updates),
21
+ ...remoteParticipants.slice(index + 1),
22
+ ];
23
+ }
24
+ function convertRemoteParticipantToRemoteParticipantState(p) {
25
+ return {
26
+ displayName: p.displayName,
27
+ id: p.id,
28
+ isAudioEnabled: p.isAudioEnabled,
29
+ isLocalParticipant: p.isLocalParticipant,
30
+ isVideoEnabled: p.isVideoEnabled,
31
+ stream: p.stream,
32
+ };
33
+ }
34
+ function addScreenshare(screenshares, screenshare) {
35
+ const existingScreenshare = screenshares.find((ss) => ss.id === screenshare.id);
36
+ if (existingScreenshare) {
37
+ return screenshares;
38
+ }
39
+ return [...screenshares, screenshare];
40
+ }
41
+ function reducer(state, action) {
42
+ switch (action.type) {
43
+ case "CHAT_MESSAGE":
44
+ return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, action.payload] });
45
+ case "CLOUD_RECORDING_REQUEST_STARTED":
46
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
47
+ status: "requested",
48
+ } });
49
+ case "CLOUD_RECORDING_STARTED":
50
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
51
+ status: action.payload.status,
52
+ startedAt: action.payload.startedAt,
53
+ } });
54
+ case "CLOUD_RECORDING_STARTED_ERROR":
55
+ return Object.assign(Object.assign({}, state), { cloudRecording: {
56
+ status: action.payload.status,
57
+ error: action.payload.error,
58
+ } });
59
+ case "CLOUD_RECORDING_STOPPED":
60
+ delete state.cloudRecording;
61
+ return Object.assign({}, state);
62
+ case "ROOM_JOINED":
63
+ return Object.assign(Object.assign({}, state), { localParticipant: action.payload.localParticipant, remoteParticipants: action.payload.remoteParticipants, waitingParticipants: action.payload.waitingParticipants, connectionStatus: "connected" });
64
+ case "CONNECTION_STATUS_CHANGED":
65
+ return Object.assign(Object.assign({}, state), { connectionStatus: action.payload.connectionStatus });
66
+ case "PARTICIPANT_AUDIO_ENABLED":
67
+ return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
68
+ isAudioEnabled: action.payload.isAudioEnabled,
69
+ }) });
70
+ case "PARTICIPANT_JOINED":
71
+ return Object.assign(Object.assign({}, state), { remoteParticipants: [...state.remoteParticipants, action.payload.paritipant] });
72
+ case "PARTICIPANT_LEFT":
73
+ return Object.assign(Object.assign({}, state), { remoteParticipants: [...state.remoteParticipants.filter((p) => p.id !== action.payload.participantId)] });
74
+ case "PARTICIPANT_STREAM_ADDED":
75
+ return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
76
+ stream: action.payload.stream,
77
+ }) });
78
+ case "PARTICIPANT_VIDEO_ENABLED":
79
+ return Object.assign(Object.assign({}, state), { remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
80
+ isVideoEnabled: action.payload.isVideoEnabled,
81
+ }) });
82
+ case "PARTICIPANT_METADATA_CHANGED":
83
+ return Object.assign(Object.assign({}, state), { remoteParticipants: [
84
+ ...state.remoteParticipants.map((p) => p.id === action.payload.participantId ? Object.assign(Object.assign({}, p), { displayName: action.payload.displayName }) : p),
85
+ ] });
86
+ case "LOCAL_CLIENT_DISPLAY_NAME_CHANGED":
87
+ if (!state.localParticipant)
88
+ return state;
89
+ return Object.assign(Object.assign({}, state), { localParticipant: Object.assign(Object.assign({}, state.localParticipant), { displayName: action.payload.displayName }) });
90
+ case "SCREENSHARE_STARTED":
91
+ return Object.assign(Object.assign({}, state), { screenshares: addScreenshare(state.screenshares, {
92
+ participantId: action.payload.participantId,
93
+ id: action.payload.id,
94
+ hasAudioTrack: action.payload.hasAudioTrack,
95
+ stream: action.payload.stream,
96
+ isLocal: action.payload.isLocal,
97
+ }) });
98
+ case "SCREENSHARE_STOPPED":
99
+ return Object.assign(Object.assign({}, state), { screenshares: state.screenshares.filter((ss) => ss.id !== action.payload.id) });
100
+ case "LOCAL_SCREENSHARE_START_ERROR":
101
+ return Object.assign(Object.assign({}, state), { localScreenshareStatus: undefined });
102
+ case "LOCAL_SCREENSHARE_STARTING":
103
+ return Object.assign(Object.assign({}, state), { localScreenshareStatus: "starting" });
104
+ case "LOCAL_SCREENSHARE_STARTED":
105
+ return Object.assign(Object.assign({}, state), { localScreenshareStatus: "active" });
106
+ case "LOCAL_SCREENSHARE_STOPPED":
107
+ return Object.assign(Object.assign({}, state), { localScreenshareStatus: undefined, screenshares: state.screenshares.filter((ss) => !ss.isLocal) });
108
+ case "LOCAL_CAMERA_ENABLED":
109
+ if (!state.localParticipant)
110
+ return state;
111
+ return Object.assign(Object.assign({}, state), { localParticipant: Object.assign(Object.assign({}, state.localParticipant), { isVideoEnabled: action.payload }) });
112
+ case "LOCAL_MICROPHONE_ENABLED":
113
+ if (!state.localParticipant)
114
+ return state;
115
+ return Object.assign(Object.assign({}, state), { localParticipant: Object.assign(Object.assign({}, state.localParticipant), { isAudioEnabled: action.payload }) });
116
+ case "STREAMING_STARTED":
117
+ return Object.assign(Object.assign({}, state), { liveStream: {
118
+ status: action.payload.status,
119
+ startedAt: action.payload.startedAt,
120
+ } });
121
+ case "STREAMING_STOPPED":
122
+ delete state.liveStream;
123
+ return Object.assign({}, state);
124
+ case "WAITING_PARTICIPANT_JOINED":
125
+ return Object.assign(Object.assign({}, state), { waitingParticipants: [
126
+ ...state.waitingParticipants,
127
+ { id: action.payload.participantId, displayName: action.payload.displayName },
128
+ ] });
129
+ case "WAITING_PARTICIPANT_LEFT":
130
+ return Object.assign(Object.assign({}, state), { waitingParticipants: state.waitingParticipants.filter((wp) => wp.id !== action.payload.participantId) });
131
+ default:
132
+ throw state;
133
+ }
134
+ }
135
+ const defaultRoomConnectionOptions = {
136
+ localMediaOptions: {
137
+ audio: true,
138
+ video: true,
139
+ },
140
+ };
141
+ export function useRoomConnection(roomUrl, roomConnectionOptions = defaultRoomConnectionOptions) {
142
+ const [roomConnection] = useState(() => {
143
+ var _a;
144
+ 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 }));
145
+ });
146
+ const [state, dispatch] = useReducer(reducer, initialState);
147
+ function createEventListener(eventName, listener, options) {
148
+ return {
149
+ eventName,
150
+ listener,
151
+ options,
152
+ };
153
+ }
154
+ const eventListeners = React.useMemo(() => [
155
+ createEventListener("chat_message", (e) => {
156
+ dispatch({ type: "CHAT_MESSAGE", payload: e.detail });
157
+ }),
158
+ createEventListener("cloud_recording_request_started", () => {
159
+ dispatch({ type: "CLOUD_RECORDING_REQUEST_STARTED" });
160
+ }),
161
+ createEventListener("cloud_recording_started", (e) => {
162
+ const { status, startedAt } = e.detail;
163
+ dispatch({ type: "CLOUD_RECORDING_STARTED", payload: { status, startedAt } });
164
+ }),
165
+ createEventListener("cloud_recording_started_error", (e) => {
166
+ dispatch({ type: "CLOUD_RECORDING_STARTED_ERROR", payload: e.detail });
167
+ }),
168
+ createEventListener("cloud_recording_stopped", () => {
169
+ dispatch({ type: "CLOUD_RECORDING_STOPPED" });
170
+ }),
171
+ createEventListener("local_camera_enabled", (e) => {
172
+ const { enabled } = e.detail;
173
+ dispatch({ type: "LOCAL_CAMERA_ENABLED", payload: enabled });
174
+ }),
175
+ createEventListener("local_microphone_enabled", (e) => {
176
+ const { enabled } = e.detail;
177
+ dispatch({ type: "LOCAL_MICROPHONE_ENABLED", payload: enabled });
178
+ }),
179
+ createEventListener("participant_audio_enabled", (e) => {
180
+ const { participantId, isAudioEnabled } = e.detail;
181
+ dispatch({ type: "PARTICIPANT_AUDIO_ENABLED", payload: { participantId, isAudioEnabled } });
182
+ }),
183
+ createEventListener("participant_joined", (e) => {
184
+ const { remoteParticipant } = e.detail;
185
+ dispatch({
186
+ type: "PARTICIPANT_JOINED",
187
+ payload: {
188
+ paritipant: convertRemoteParticipantToRemoteParticipantState(remoteParticipant),
189
+ },
190
+ });
191
+ }),
192
+ createEventListener("participant_left", (e) => {
193
+ const { participantId } = e.detail;
194
+ dispatch({ type: "PARTICIPANT_LEFT", payload: { participantId } });
195
+ }),
196
+ createEventListener("participant_metadata_changed", (e) => {
197
+ const { participantId, displayName } = e.detail;
198
+ dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
199
+ }),
200
+ createEventListener("participant_stream_added", (e) => {
201
+ const { participantId, stream } = e.detail;
202
+ dispatch({ type: "PARTICIPANT_STREAM_ADDED", payload: { participantId, stream } });
203
+ }),
204
+ createEventListener("participant_video_enabled", (e) => {
205
+ const { participantId, isVideoEnabled } = e.detail;
206
+ dispatch({ type: "PARTICIPANT_VIDEO_ENABLED", payload: { participantId, isVideoEnabled } });
207
+ }),
208
+ createEventListener("connection_status_changed", (e) => {
209
+ const { connectionStatus } = e.detail;
210
+ dispatch({
211
+ type: "CONNECTION_STATUS_CHANGED",
212
+ payload: { connectionStatus },
213
+ });
214
+ }),
215
+ createEventListener("room_joined", (e) => {
216
+ const { localParticipant, remoteParticipants, waitingParticipants } = e.detail;
217
+ dispatch({
218
+ type: "ROOM_JOINED",
219
+ payload: {
220
+ localParticipant,
221
+ remoteParticipants: remoteParticipants.map(convertRemoteParticipantToRemoteParticipantState),
222
+ waitingParticipants,
223
+ },
224
+ });
225
+ }),
226
+ createEventListener("screenshare_started", (e) => {
227
+ const { participantId, stream, id, hasAudioTrack, isLocal } = e.detail;
228
+ dispatch({
229
+ type: "SCREENSHARE_STARTED",
230
+ payload: { participantId, stream, id, hasAudioTrack, isLocal },
231
+ });
232
+ }),
233
+ createEventListener("screenshare_stopped", (e) => {
234
+ var _a;
235
+ const { participantId, id } = e.detail;
236
+ dispatch({
237
+ type: "SCREENSHARE_STOPPED",
238
+ payload: { participantId, id },
239
+ });
240
+ if (participantId === ((_a = state.localParticipant) === null || _a === void 0 ? void 0 : _a.id)) {
241
+ dispatch({ type: "LOCAL_SCREENSHARE_STOPPED" });
242
+ }
243
+ }),
244
+ createEventListener("streaming_started", (e) => {
245
+ const { status, startedAt } = e.detail;
246
+ dispatch({ type: "STREAMING_STARTED", payload: { status, startedAt } });
247
+ }),
248
+ createEventListener("streaming_stopped", () => {
249
+ dispatch({ type: "STREAMING_STOPPED" });
250
+ }),
251
+ createEventListener("waiting_participant_joined", (e) => {
252
+ const { participantId, displayName } = e.detail;
253
+ dispatch({
254
+ type: "WAITING_PARTICIPANT_JOINED",
255
+ payload: { participantId, displayName },
256
+ });
257
+ }),
258
+ createEventListener("waiting_participant_left", (e) => {
259
+ const { participantId } = e.detail;
260
+ dispatch({
261
+ type: "WAITING_PARTICIPANT_LEFT",
262
+ payload: { participantId },
263
+ });
264
+ }),
265
+ ], []);
266
+ useEffect(() => {
267
+ eventListeners.forEach(({ eventName, listener }) => {
268
+ roomConnection.addEventListener(eventName, listener);
269
+ });
270
+ roomConnection.join();
271
+ return () => {
272
+ eventListeners.forEach(({ eventName, listener }) => {
273
+ roomConnection.removeEventListener(eventName, listener);
274
+ });
275
+ roomConnection.leave();
276
+ };
277
+ }, []);
278
+ return {
279
+ state,
280
+ actions: {
281
+ knock: () => {
282
+ roomConnection.knock();
283
+ },
284
+ sendChatMessage: (text) => {
285
+ roomConnection.sendChatMessage(text);
286
+ },
287
+ setDisplayName: (displayName) => {
288
+ roomConnection.setDisplayName(displayName);
289
+ dispatch({ type: "LOCAL_CLIENT_DISPLAY_NAME_CHANGED", payload: { displayName } });
290
+ },
291
+ toggleCamera: (enabled) => {
292
+ roomConnection.localMedia.toggleCameraEnabled(enabled);
293
+ },
294
+ toggleMicrophone: (enabled) => {
295
+ roomConnection.localMedia.toggleMichrophoneEnabled(enabled);
296
+ },
297
+ acceptWaitingParticipant: (participantId) => {
298
+ roomConnection.acceptWaitingParticipant(participantId);
299
+ },
300
+ rejectWaitingParticipant: (participantId) => {
301
+ roomConnection.rejectWaitingParticipant(participantId);
302
+ },
303
+ startCloudRecording: () => {
304
+ var _a;
305
+ if (state.cloudRecording && ["recording", "requested"].includes((_a = state.cloudRecording) === null || _a === void 0 ? void 0 : _a.status)) {
306
+ return;
307
+ }
308
+ roomConnection.startCloudRecording();
309
+ },
310
+ stopCloudRecording: () => {
311
+ roomConnection.stopCloudRecording();
312
+ },
313
+ startScreenshare: () => __awaiter(this, void 0, void 0, function* () {
314
+ dispatch({ type: "LOCAL_SCREENSHARE_STARTING" });
315
+ try {
316
+ yield roomConnection.startScreenshare();
317
+ dispatch({ type: "LOCAL_SCREENSHARE_STARTED" });
318
+ }
319
+ catch (error) {
320
+ dispatch({ type: "LOCAL_SCREENSHARE_START_ERROR", payload: error });
321
+ }
322
+ }),
323
+ stopScreenshare: () => {
324
+ roomConnection.stopScreenshare();
325
+ },
326
+ },
327
+ components: {
328
+ VideoView: (props) => React.createElement(VideoView, Object.assign({}, props, {
329
+ onResize: ({ stream, width, height, }) => {
330
+ roomConnection.updateStreamResolution({
331
+ streamId: stream.id,
332
+ width,
333
+ height,
334
+ });
335
+ },
336
+ })),
337
+ },
338
+ _ref: roomConnection,
339
+ };
340
+ }
@@ -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,18 @@
1
+ export default function debounce(fn, { delay = 500, edges } = {}) {
2
+ let timeout;
3
+ let nCalls = 0;
4
+ return (...args) => {
5
+ nCalls += 1;
6
+ if (edges && nCalls === 1) {
7
+ fn(...args);
8
+ }
9
+ clearTimeout(timeout);
10
+ timeout = setTimeout(() => {
11
+ if (!edges || nCalls > 1) {
12
+ fn(...args);
13
+ }
14
+ timeout = undefined;
15
+ nCalls = 0;
16
+ }, delay);
17
+ };
18
+ }
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export { default as fakeAudioStream } from "./fakeAudioStream";
2
+ export { default as fakeWebcamFrame } from "./fakeWebcamFrame";
@@ -0,0 +1,2 @@
1
+ export { default as fakeAudioStream } from "./fakeAudioStream";
2
+ export { default as fakeWebcamFrame } from "./fakeWebcamFrame";