@whereby.com/browser-sdk 2.0.0-alpha → 2.0.0-alpha1
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/dist/lib.cjs.js +5467 -0
- package/dist/lib.esm.js +5451 -0
- package/dist/types.d.ts +104 -0
- package/dist/v2-alpha1.js +105 -0
- package/package.json +2 -1
- package/.eslintrc +0 -23
- package/.github/actions/build/action.yml +0 -17
- package/.github/workflows/deploy.yml +0 -102
- package/.github/workflows/test.yml +0 -24
- package/.prettierignore +0 -7
- package/.prettierrc +0 -4
- package/.storybook/main.cjs +0 -16
- package/.storybook/preview.js +0 -9
- package/jest.config.js +0 -6
- package/rollup.config.js +0 -70
- package/src/lib/RoomConnection.ts +0 -516
- package/src/lib/RoomParticipant.ts +0 -77
- package/src/lib/__tests__/embed.unit.ts +0 -77
- package/src/lib/api/ApiClient.ts +0 -111
- package/src/lib/api/Credentials.ts +0 -45
- package/src/lib/api/HttpClient.ts +0 -95
- package/src/lib/api/MultipartHttpClient.ts +0 -53
- package/src/lib/api/OrganizationApiClient.ts +0 -64
- package/src/lib/api/Response.ts +0 -34
- package/src/lib/api/credentialsService/index.ts +0 -159
- package/src/lib/api/credentialsService/test/index.spec.ts +0 -181
- package/src/lib/api/deviceService/index.ts +0 -42
- package/src/lib/api/deviceService/tests/index.spec.ts +0 -74
- package/src/lib/api/extractUtils.ts +0 -160
- package/src/lib/api/index.ts +0 -8
- package/src/lib/api/localStorageWrapper/index.ts +0 -15
- package/src/lib/api/models/Account.ts +0 -48
- package/src/lib/api/models/Meeting.ts +0 -42
- package/src/lib/api/models/Organization.ts +0 -186
- package/src/lib/api/models/Room.ts +0 -44
- package/src/lib/api/models/account/EmbeddedFreeTierStatus.ts +0 -34
- package/src/lib/api/models/tests/Account.spec.ts +0 -128
- package/src/lib/api/models/tests/Organization.spec.ts +0 -161
- package/src/lib/api/models/tests/Room.spec.ts +0 -74
- package/src/lib/api/modules/AbstractStore.ts +0 -18
- package/src/lib/api/modules/ChromeStorageStore.ts +0 -44
- package/src/lib/api/modules/LocalStorageStore.ts +0 -57
- package/src/lib/api/modules/tests/ChromeStorageStore.spec.ts +0 -67
- package/src/lib/api/modules/tests/LocalStorageStore.spec.ts +0 -79
- package/src/lib/api/modules/tests/__mocks__/storage.ts +0 -24
- package/src/lib/api/organizationService/index.ts +0 -284
- package/src/lib/api/organizationService/tests/index.spec.ts +0 -781
- package/src/lib/api/organizationServiceCache/index.ts +0 -28
- package/src/lib/api/organizationServiceCache/tests/index.spec.ts +0 -101
- package/src/lib/api/parameterAssertUtils.ts +0 -166
- package/src/lib/api/roomService/index.ts +0 -310
- package/src/lib/api/roomService/tests/index.spec.ts +0 -668
- package/src/lib/api/test/ApiClient.spec.ts +0 -139
- package/src/lib/api/test/HttpClient.spec.ts +0 -120
- package/src/lib/api/test/MultipartHttpClient.spec.ts +0 -145
- package/src/lib/api/test/OrganizationApiClient.spec.ts +0 -132
- package/src/lib/api/test/extractUtils.spec.ts +0 -357
- package/src/lib/api/test/helpers.ts +0 -41
- package/src/lib/api/test/parameterAssertUtils.spec.ts +0 -265
- package/src/lib/api/types.ts +0 -6
- package/src/lib/embed.ts +0 -172
- package/src/lib/index.ts +0 -3
- package/src/lib/react/VideoElement.tsx +0 -16
- package/src/lib/react/index.ts +0 -3
- package/src/lib/react/useLocalMedia.ts +0 -25
- package/src/lib/react/useRoomConnection.ts +0 -92
- package/src/lib/reducer.ts +0 -142
- package/src/stories/custom-ui.stories.tsx +0 -133
- package/src/stories/prebuilt-ui.stories.tsx +0 -131
- package/src/stories/styles.css +0 -74
- package/src/types.d.ts +0 -175
- package/tsconfig.json +0 -30
package/src/lib/embed.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { define, ref } from "heresy";
|
|
2
|
-
|
|
3
|
-
interface WherebyEmbedAttributes {
|
|
4
|
-
audio: string;
|
|
5
|
-
avatarUrl: string;
|
|
6
|
-
background: string;
|
|
7
|
-
cameraAccess: string;
|
|
8
|
-
chat: string;
|
|
9
|
-
displayName: string;
|
|
10
|
-
emptyRoomInvitation: string;
|
|
11
|
-
floatSelf: string;
|
|
12
|
-
help: string;
|
|
13
|
-
leaveButton: string;
|
|
14
|
-
logo: string;
|
|
15
|
-
people: string;
|
|
16
|
-
precallReview: string;
|
|
17
|
-
recording: string;
|
|
18
|
-
screenshare: string;
|
|
19
|
-
video: string;
|
|
20
|
-
virtualBackgroundUrl: string;
|
|
21
|
-
room: string;
|
|
22
|
-
style: { [key: string]: string };
|
|
23
|
-
}
|
|
24
|
-
declare global {
|
|
25
|
-
namespace JSX {
|
|
26
|
-
interface IntrinsicElements {
|
|
27
|
-
["whereby-embed"]: Partial<WherebyEmbedAttributes>;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const boolAttrs = [
|
|
33
|
-
"audio",
|
|
34
|
-
"background",
|
|
35
|
-
"cameraaccess",
|
|
36
|
-
"chat",
|
|
37
|
-
"people",
|
|
38
|
-
"embed",
|
|
39
|
-
"emptyRoomInvitation",
|
|
40
|
-
"help",
|
|
41
|
-
"leaveButton",
|
|
42
|
-
"precallReview",
|
|
43
|
-
"screenshare",
|
|
44
|
-
"video",
|
|
45
|
-
"floatSelf",
|
|
46
|
-
"recording",
|
|
47
|
-
"logo",
|
|
48
|
-
"locking",
|
|
49
|
-
"participantCount",
|
|
50
|
-
"settingsButton",
|
|
51
|
-
"pipButton",
|
|
52
|
-
"moreButton",
|
|
53
|
-
"personality",
|
|
54
|
-
"subgridLabels",
|
|
55
|
-
"lowData",
|
|
56
|
-
"breakout",
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
define("WherebyEmbed", {
|
|
60
|
-
oninit() {
|
|
61
|
-
this.iframe = ref();
|
|
62
|
-
},
|
|
63
|
-
onconnected() {
|
|
64
|
-
window.addEventListener("message", this.onmessage);
|
|
65
|
-
},
|
|
66
|
-
ondisconnected() {
|
|
67
|
-
window.removeEventListener("message", this.onmessage);
|
|
68
|
-
},
|
|
69
|
-
observedAttributes: [
|
|
70
|
-
"displayName",
|
|
71
|
-
"minimal",
|
|
72
|
-
"room",
|
|
73
|
-
"subdomain",
|
|
74
|
-
"lang",
|
|
75
|
-
"metadata",
|
|
76
|
-
"groups",
|
|
77
|
-
"virtualBackgroundUrl",
|
|
78
|
-
"avatarUrl",
|
|
79
|
-
...boolAttrs,
|
|
80
|
-
].map((a) => a.toLowerCase()),
|
|
81
|
-
onattributechanged({ attributeName, oldValue }: { attributeName: string; oldValue: string | boolean }) {
|
|
82
|
-
if (["room", "subdomain"].includes(attributeName) && oldValue == null) return;
|
|
83
|
-
this.render();
|
|
84
|
-
},
|
|
85
|
-
style(self: string) {
|
|
86
|
-
return `
|
|
87
|
-
${self} {
|
|
88
|
-
display: block;
|
|
89
|
-
}
|
|
90
|
-
${self} iframe {
|
|
91
|
-
border: none;
|
|
92
|
-
height: 100%;
|
|
93
|
-
width: 100%;
|
|
94
|
-
}
|
|
95
|
-
`;
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
// Commands
|
|
99
|
-
_postCommand(command: string, args = []) {
|
|
100
|
-
if (this.iframe.current) {
|
|
101
|
-
const url = new URL(this.room, `https://${this.subdomain}.whereby.com`);
|
|
102
|
-
this.iframe.current.contentWindow.postMessage({ command, args }, url.origin);
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
startRecording() {
|
|
106
|
-
this._postCommand("start_recording");
|
|
107
|
-
},
|
|
108
|
-
stopRecording() {
|
|
109
|
-
this._postCommand("stop_recording");
|
|
110
|
-
},
|
|
111
|
-
toggleCamera(enabled: boolean) {
|
|
112
|
-
this._postCommand("toggle_camera", [enabled]);
|
|
113
|
-
},
|
|
114
|
-
toggleMicrophone(enabled: boolean) {
|
|
115
|
-
this._postCommand("toggle_microphone", [enabled]);
|
|
116
|
-
},
|
|
117
|
-
toggleScreenshare(enabled: boolean) {
|
|
118
|
-
this._postCommand("toggle_screenshare", [enabled]);
|
|
119
|
-
},
|
|
120
|
-
onmessage({ origin, data }: { origin: string; data: { type: string; payload: string } }) {
|
|
121
|
-
const url = new URL(this.room, `https://${this.subdomain}.whereby.com`);
|
|
122
|
-
if (origin !== url.origin) return;
|
|
123
|
-
const { type, payload: detail } = data;
|
|
124
|
-
this.dispatchEvent(new CustomEvent(type, { detail }));
|
|
125
|
-
},
|
|
126
|
-
render() {
|
|
127
|
-
const {
|
|
128
|
-
avatarurl: avatarUrl,
|
|
129
|
-
displayname: displayName,
|
|
130
|
-
lang,
|
|
131
|
-
metadata,
|
|
132
|
-
minimal,
|
|
133
|
-
room,
|
|
134
|
-
groups,
|
|
135
|
-
virtualbackgroundurl: virtualBackgroundUrl,
|
|
136
|
-
} = this;
|
|
137
|
-
if (!room) return this.html`Whereby: Missing room attribute.`;
|
|
138
|
-
// Get subdomain from room URL, or use it specified
|
|
139
|
-
const m = /https:\/\/([^.]+)\.whereby.com\/.+/.exec(room);
|
|
140
|
-
const subdomain = (m && m[1]) || this.subdomain;
|
|
141
|
-
if (!subdomain) return this.html`Whereby: Missing subdomain attr.`;
|
|
142
|
-
const url = new URL(room, `https://${subdomain}.whereby.com`);
|
|
143
|
-
Object.entries({
|
|
144
|
-
jsApi: true,
|
|
145
|
-
we: "__SDK_VERSION__",
|
|
146
|
-
iframeSource: subdomain,
|
|
147
|
-
...(displayName && { displayName }),
|
|
148
|
-
...(lang && { lang }),
|
|
149
|
-
...(metadata && { metadata }),
|
|
150
|
-
...(groups && { groups }),
|
|
151
|
-
...(virtualBackgroundUrl && { virtualBackgroundUrl }),
|
|
152
|
-
...(avatarUrl && { avatarUrl }),
|
|
153
|
-
// the original ?embed name was confusing, so we give minimal
|
|
154
|
-
...(minimal != null && { embed: minimal }),
|
|
155
|
-
...boolAttrs.reduce(
|
|
156
|
-
// add to URL if set in any way
|
|
157
|
-
(o, v) => (this[v.toLowerCase()] != null ? { ...o, [v]: this[v.toLowerCase()] } : o),
|
|
158
|
-
{}
|
|
159
|
-
),
|
|
160
|
-
}).forEach(([k, v]) => {
|
|
161
|
-
if (!url.searchParams.has(k) && typeof v === "string") {
|
|
162
|
-
url.searchParams.set(k, v);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
return this.html`
|
|
166
|
-
<iframe
|
|
167
|
-
ref=${this.iframe}
|
|
168
|
-
src=${url}
|
|
169
|
-
allow="autoplay; camera; microphone; fullscreen; speaker; display-capture" />
|
|
170
|
-
`;
|
|
171
|
-
},
|
|
172
|
-
});
|
package/src/lib/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import React, { useCallback } from "react";
|
|
2
|
-
|
|
3
|
-
interface VideoElProps {
|
|
4
|
-
stream: MediaStream;
|
|
5
|
-
style?: React.CSSProperties;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export default ({ stream, style }: VideoElProps) => {
|
|
9
|
-
const videoEl = useCallback<(node: HTMLVideoElement) => void>((node) => {
|
|
10
|
-
if (node !== null && node.srcObject !== stream) {
|
|
11
|
-
node.srcObject = stream;
|
|
12
|
-
}
|
|
13
|
-
}, []);
|
|
14
|
-
|
|
15
|
-
return <video ref={videoEl} autoPlay playsInline style={style} />;
|
|
16
|
-
};
|
package/src/lib/react/index.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
|
|
3
|
-
export default function useLocalMedia() {
|
|
4
|
-
const [localStream, setLocalStream] = useState<MediaStream | undefined>(undefined);
|
|
5
|
-
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
const getLocalStream = async () => {
|
|
8
|
-
if (!localStream) {
|
|
9
|
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
|
|
10
|
-
setLocalStream(stream);
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
getLocalStream();
|
|
15
|
-
|
|
16
|
-
// Stop tracks on unmount
|
|
17
|
-
return () => {
|
|
18
|
-
localStream?.getTracks().forEach((t) => {
|
|
19
|
-
t.stop();
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
}, [localStream]);
|
|
23
|
-
|
|
24
|
-
return [localStream];
|
|
25
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { useEffect, useReducer, useState } from "react";
|
|
2
|
-
import RoomConnection, { RoomConnectionOptions } from "../RoomConnection";
|
|
3
|
-
import reducer, { RoomState } from "../reducer";
|
|
4
|
-
import VideoElement from "./VideoElement";
|
|
5
|
-
|
|
6
|
-
interface RoomConnectionActions {
|
|
7
|
-
toggleCamera(enabled?: boolean): void;
|
|
8
|
-
toggleMicrophone(enabled?: boolean): void;
|
|
9
|
-
setDisplayName(displayName: string): void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface RoomConnectionComponents {
|
|
13
|
-
VideoView: typeof VideoElement;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default function useRoomConnection(
|
|
17
|
-
roomUrl: string,
|
|
18
|
-
roomConnectionOptions: RoomConnectionOptions
|
|
19
|
-
): [state: RoomState, actions: RoomConnectionActions, components: RoomConnectionComponents] {
|
|
20
|
-
const [roomConnection, setRoomConnection] = useState<RoomConnection | null>(null);
|
|
21
|
-
const [state, dispatch] = useReducer(reducer, { remoteParticipants: [] });
|
|
22
|
-
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
setRoomConnection(new RoomConnection(roomUrl, roomConnectionOptions));
|
|
25
|
-
}, [roomUrl]);
|
|
26
|
-
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
if (!roomConnection) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
roomConnection.addEventListener("participant_audio_enabled", (e) => {
|
|
33
|
-
const { participantId, isAudioEnabled } = e.detail;
|
|
34
|
-
dispatch({ type: "PARTICIPANT_AUDIO_ENABLED", payload: { participantId, isAudioEnabled } });
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
roomConnection.addEventListener("participant_joined", (e) => {
|
|
38
|
-
const { remoteParticipant } = e.detail;
|
|
39
|
-
dispatch({ type: "PARTICIPANT_JOINED", payload: { paritipant: remoteParticipant } });
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
roomConnection.addEventListener("participant_left", (e) => {
|
|
43
|
-
const { participantId } = e.detail;
|
|
44
|
-
dispatch({ type: "PARTICIPANT_LEFT", payload: { participantId } });
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
roomConnection.addEventListener("participant_stream_added", (e) => {
|
|
48
|
-
const { participantId, stream } = e.detail;
|
|
49
|
-
dispatch({ type: "PARTICIPANT_STREAM_ADDED", payload: { participantId, stream } });
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
roomConnection.addEventListener("room_joined", (e) => {
|
|
53
|
-
const { localParticipant, remoteParticipants } = e.detail;
|
|
54
|
-
dispatch({ type: "ROOM_JOINED", payload: { localParticipant, remoteParticipants } });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
roomConnection.addEventListener("participant_video_enabled", (e) => {
|
|
58
|
-
const { participantId, isVideoEnabled } = e.detail;
|
|
59
|
-
dispatch({ type: "PARTICIPANT_VIDEO_ENABLED", payload: { participantId, isVideoEnabled } });
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
roomConnection.addEventListener("participant_metadata_changed", (e) => {
|
|
63
|
-
const { participantId, displayName } = e.detail;
|
|
64
|
-
dispatch({ type: "PARTICIPANT_METADATA_CHANGED", payload: { participantId, displayName } });
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
roomConnection.join();
|
|
68
|
-
|
|
69
|
-
return () => {
|
|
70
|
-
roomConnection.leave();
|
|
71
|
-
};
|
|
72
|
-
}, [roomConnection]);
|
|
73
|
-
|
|
74
|
-
return [
|
|
75
|
-
state,
|
|
76
|
-
{
|
|
77
|
-
toggleCamera: (enabled) => {
|
|
78
|
-
roomConnection?.toggleCamera(enabled);
|
|
79
|
-
},
|
|
80
|
-
toggleMicrophone: (enabled) => {
|
|
81
|
-
roomConnection?.toggleMicrophone(enabled);
|
|
82
|
-
},
|
|
83
|
-
setDisplayName: (displayName) => {
|
|
84
|
-
roomConnection?.setDisplayName(displayName);
|
|
85
|
-
dispatch({ type: "LOCAL_CLIENT_DISPLAY_NAME_CHANGED", payload: { displayName } });
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
VideoView: VideoElement,
|
|
90
|
-
},
|
|
91
|
-
];
|
|
92
|
-
}
|
package/src/lib/reducer.ts
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { LocalParticipant, RemoteParticipant } from "./RoomParticipant";
|
|
2
|
-
|
|
3
|
-
type RemoteParticipantState = Omit<RemoteParticipant, "updateStreamState">;
|
|
4
|
-
|
|
5
|
-
export interface RoomState {
|
|
6
|
-
localParticipant?: LocalParticipant;
|
|
7
|
-
roomConnectionStatus?: "connecting" | "connected" | "disconnected";
|
|
8
|
-
remoteParticipants: RemoteParticipantState[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type Action =
|
|
12
|
-
| {
|
|
13
|
-
type: "ROOM_JOINED";
|
|
14
|
-
payload: {
|
|
15
|
-
localParticipant: LocalParticipant;
|
|
16
|
-
remoteParticipants: RemoteParticipantState[];
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
| {
|
|
20
|
-
type: "PARTICIPANT_AUDIO_ENABLED";
|
|
21
|
-
payload: {
|
|
22
|
-
participantId: string;
|
|
23
|
-
isAudioEnabled: boolean;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
| {
|
|
27
|
-
type: "PARTICIPANT_JOINED";
|
|
28
|
-
payload: {
|
|
29
|
-
paritipant: RemoteParticipantState;
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
| {
|
|
33
|
-
type: "PARTICIPANT_LEFT";
|
|
34
|
-
payload: {
|
|
35
|
-
participantId: string;
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
| {
|
|
39
|
-
type: "PARTICIPANT_STREAM_ADDED";
|
|
40
|
-
payload: {
|
|
41
|
-
participantId: string;
|
|
42
|
-
stream: MediaStream;
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
| {
|
|
46
|
-
type: "PARTICIPANT_VIDEO_ENABLED";
|
|
47
|
-
payload: {
|
|
48
|
-
participantId: string;
|
|
49
|
-
isVideoEnabled: boolean;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
| {
|
|
53
|
-
type: "PARTICIPANT_METADATA_CHANGED";
|
|
54
|
-
payload: {
|
|
55
|
-
participantId: string;
|
|
56
|
-
displayName: string;
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
| {
|
|
60
|
-
type: "LOCAL_CLIENT_DISPLAY_NAME_CHANGED";
|
|
61
|
-
payload: {
|
|
62
|
-
displayName: string;
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
function updateParticipant(
|
|
67
|
-
remoteParticipants: RemoteParticipantState[],
|
|
68
|
-
participantId: string,
|
|
69
|
-
updates: Partial<RemoteParticipantState>
|
|
70
|
-
): RemoteParticipantState[] {
|
|
71
|
-
const existingParticipant = remoteParticipants.find((p) => p.id === participantId);
|
|
72
|
-
if (!existingParticipant) {
|
|
73
|
-
return remoteParticipants;
|
|
74
|
-
}
|
|
75
|
-
const index = remoteParticipants.indexOf(existingParticipant);
|
|
76
|
-
|
|
77
|
-
return [
|
|
78
|
-
...remoteParticipants.slice(0, index),
|
|
79
|
-
{ ...existingParticipant, ...updates },
|
|
80
|
-
...remoteParticipants.slice(index + 1),
|
|
81
|
-
];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export default function reducer(state: RoomState, action: Action): RoomState {
|
|
85
|
-
switch (action.type) {
|
|
86
|
-
case "ROOM_JOINED":
|
|
87
|
-
return {
|
|
88
|
-
...state,
|
|
89
|
-
localParticipant: action.payload.localParticipant,
|
|
90
|
-
remoteParticipants: action.payload.remoteParticipants,
|
|
91
|
-
roomConnectionStatus: "connected",
|
|
92
|
-
};
|
|
93
|
-
case "PARTICIPANT_AUDIO_ENABLED":
|
|
94
|
-
return {
|
|
95
|
-
...state,
|
|
96
|
-
remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
|
|
97
|
-
isAudioEnabled: action.payload.isAudioEnabled,
|
|
98
|
-
}),
|
|
99
|
-
};
|
|
100
|
-
case "PARTICIPANT_JOINED":
|
|
101
|
-
return {
|
|
102
|
-
...state,
|
|
103
|
-
remoteParticipants: [...state.remoteParticipants, action.payload.paritipant],
|
|
104
|
-
};
|
|
105
|
-
case "PARTICIPANT_LEFT":
|
|
106
|
-
return {
|
|
107
|
-
...state,
|
|
108
|
-
remoteParticipants: [...state.remoteParticipants.filter((p) => p.id !== action.payload.participantId)],
|
|
109
|
-
};
|
|
110
|
-
case "PARTICIPANT_STREAM_ADDED":
|
|
111
|
-
return {
|
|
112
|
-
...state,
|
|
113
|
-
remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
|
|
114
|
-
stream: action.payload.stream,
|
|
115
|
-
}),
|
|
116
|
-
};
|
|
117
|
-
case "PARTICIPANT_VIDEO_ENABLED":
|
|
118
|
-
return {
|
|
119
|
-
...state,
|
|
120
|
-
remoteParticipants: updateParticipant(state.remoteParticipants, action.payload.participantId, {
|
|
121
|
-
isVideoEnabled: action.payload.isVideoEnabled,
|
|
122
|
-
}),
|
|
123
|
-
};
|
|
124
|
-
case "PARTICIPANT_METADATA_CHANGED":
|
|
125
|
-
return {
|
|
126
|
-
...state,
|
|
127
|
-
remoteParticipants: [
|
|
128
|
-
...state.remoteParticipants.map((p) =>
|
|
129
|
-
p.id === action.payload.participantId ? { ...p, displayName: action.payload.displayName } : p
|
|
130
|
-
),
|
|
131
|
-
],
|
|
132
|
-
};
|
|
133
|
-
case "LOCAL_CLIENT_DISPLAY_NAME_CHANGED":
|
|
134
|
-
if (!state.localParticipant) return state;
|
|
135
|
-
return {
|
|
136
|
-
...state,
|
|
137
|
-
localParticipant: { ...state.localParticipant, displayName: action.payload.displayName },
|
|
138
|
-
};
|
|
139
|
-
default:
|
|
140
|
-
throw state;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
|
-
import { useLocalMedia, useRoomConnection, VideoElement } from "../lib/react";
|
|
3
|
-
import "./styles.css";
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
title: "Examples/Custom UI",
|
|
7
|
-
argTypes: {
|
|
8
|
-
displayName: { control: "text", defaultValue: "SDK" },
|
|
9
|
-
roomUrl: { control: "text", defaultValue: process.env.STORYBOOK_ROOM, type: { required: true } },
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const DisplayNameForm = ({
|
|
14
|
-
initialDisplayName,
|
|
15
|
-
onSetDisplayName,
|
|
16
|
-
}: {
|
|
17
|
-
initialDisplayName?: string;
|
|
18
|
-
onSetDisplayName: (displayName: string) => void;
|
|
19
|
-
}) => {
|
|
20
|
-
const [displayName, setDisplayName] = useState(initialDisplayName || "");
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<div>
|
|
24
|
-
<label htmlFor="displayName">Display name: </label>
|
|
25
|
-
<input
|
|
26
|
-
type="text"
|
|
27
|
-
name="displayName"
|
|
28
|
-
id="displayName"
|
|
29
|
-
value={displayName}
|
|
30
|
-
onChange={(e) => setDisplayName(e.target.value)}
|
|
31
|
-
/>
|
|
32
|
-
<button onClick={() => onSetDisplayName(displayName || "")}>Save</button>
|
|
33
|
-
</div>
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const VideoExperience = ({
|
|
38
|
-
displayName,
|
|
39
|
-
roomName,
|
|
40
|
-
localStream,
|
|
41
|
-
}: {
|
|
42
|
-
displayName?: string;
|
|
43
|
-
roomName: string;
|
|
44
|
-
localStream?: MediaStream;
|
|
45
|
-
}) => {
|
|
46
|
-
const [state, actions, components] = useRoomConnection(roomName, {
|
|
47
|
-
displayName,
|
|
48
|
-
localMediaConstraints: {
|
|
49
|
-
audio: true,
|
|
50
|
-
video: true,
|
|
51
|
-
},
|
|
52
|
-
localStream,
|
|
53
|
-
logger: console,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const { localParticipant, remoteParticipants } = state;
|
|
57
|
-
const { setDisplayName, toggleCamera, toggleMicrophone } = actions;
|
|
58
|
-
const { VideoView } = components;
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<div>
|
|
62
|
-
<div className="container">
|
|
63
|
-
{[localParticipant, ...remoteParticipants].map((participant, i) => (
|
|
64
|
-
<div className="participantWrapper" key={participant?.id || i}>
|
|
65
|
-
{participant ? (
|
|
66
|
-
<>
|
|
67
|
-
<div
|
|
68
|
-
className="bouncingball"
|
|
69
|
-
style={{
|
|
70
|
-
animationDelay: `${Math.random() * 1000}ms`,
|
|
71
|
-
...(participant.isAudioEnabled
|
|
72
|
-
? {
|
|
73
|
-
border: "2px solid grey",
|
|
74
|
-
}
|
|
75
|
-
: null),
|
|
76
|
-
...(!participant.isVideoEnabled
|
|
77
|
-
? {
|
|
78
|
-
backgroundColor: "green",
|
|
79
|
-
}
|
|
80
|
-
: null),
|
|
81
|
-
}}
|
|
82
|
-
>
|
|
83
|
-
{participant.stream && participant.isVideoEnabled && (
|
|
84
|
-
<VideoView
|
|
85
|
-
stream={participant.stream}
|
|
86
|
-
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
|
87
|
-
/>
|
|
88
|
-
)}
|
|
89
|
-
</div>
|
|
90
|
-
<div className="displayName">{participant.displayName || "Guest"}</div>
|
|
91
|
-
</>
|
|
92
|
-
) : null}
|
|
93
|
-
</div>
|
|
94
|
-
))}
|
|
95
|
-
</div>
|
|
96
|
-
<div className="controls">
|
|
97
|
-
<button onClick={() => toggleCamera()}>Toggle camera</button>
|
|
98
|
-
<button onClick={() => toggleMicrophone()}>Toggle microphone</button>
|
|
99
|
-
<DisplayNameForm initialDisplayName={displayName} onSetDisplayName={setDisplayName} />
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const roomRegEx = new RegExp(/^https:\/\/.*\/.*/);
|
|
106
|
-
|
|
107
|
-
export const Simplistic = ({ roomUrl, displayName }: { roomUrl: string; displayName?: string }) => {
|
|
108
|
-
if (!roomUrl || !roomUrl.match(roomRegEx)) {
|
|
109
|
-
return <p>Set room url on the Controls panel</p>;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return <VideoExperience displayName={displayName} roomName={roomUrl} />;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
export const WithPreCall = ({ roomUrl, displayName }: { roomUrl: string; displayName?: string }) => {
|
|
116
|
-
const [localStream] = useLocalMedia();
|
|
117
|
-
const [shouldJoin, setShouldJoin] = useState(false);
|
|
118
|
-
|
|
119
|
-
if (!roomUrl || !roomUrl.match(roomRegEx)) {
|
|
120
|
-
return <p>Set room url on the Controls panel</p>;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return (
|
|
124
|
-
<div>
|
|
125
|
-
{shouldJoin ? (
|
|
126
|
-
<VideoExperience displayName={displayName} roomName={roomUrl} localStream={localStream} />
|
|
127
|
-
) : (
|
|
128
|
-
<div>{localStream && <VideoElement stream={localStream} />}</div>
|
|
129
|
-
)}
|
|
130
|
-
<button onClick={() => setShouldJoin(!shouldJoin)}>{shouldJoin ? "Leave room" : "Join room"}</button>
|
|
131
|
-
</div>
|
|
132
|
-
);
|
|
133
|
-
};
|