@whereby.com/browser-sdk 0.0.0-canary-20240326095648
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +180 -0
- package/dist/cdn/v0-embed-canary.js +16 -0
- package/dist/cdn/v0-react-canary.js +3 -0
- package/dist/embed/index.d.ts +110 -0
- package/dist/embed/index.esm.js +133 -0
- package/dist/react/index.d.ts +121 -0
- package/dist/react/index.esm.js +253 -0
- package/package.json +106 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ReactHTMLElement } from 'react';
|
|
2
|
+
|
|
3
|
+
interface WherebyEmbedElementAttributes extends ReactHTMLElement<HTMLElement> {
|
|
4
|
+
audio: string;
|
|
5
|
+
avatarUrl: string;
|
|
6
|
+
background: string;
|
|
7
|
+
breakout: string;
|
|
8
|
+
cameraAccess: string;
|
|
9
|
+
chat: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
emptyRoomInvitation: string;
|
|
12
|
+
externalId: string;
|
|
13
|
+
floatSelf: string;
|
|
14
|
+
groups: string;
|
|
15
|
+
help: string;
|
|
16
|
+
lang: string;
|
|
17
|
+
leaveButton: string;
|
|
18
|
+
locking: string;
|
|
19
|
+
logo: string;
|
|
20
|
+
lowData: string;
|
|
21
|
+
metadata: string;
|
|
22
|
+
minimal: string;
|
|
23
|
+
moreButton: string;
|
|
24
|
+
participantCount: string;
|
|
25
|
+
people: string;
|
|
26
|
+
pipButton: string;
|
|
27
|
+
precallReview: string;
|
|
28
|
+
recording: string;
|
|
29
|
+
room: string;
|
|
30
|
+
settingsButton: string;
|
|
31
|
+
screenshare: string;
|
|
32
|
+
style: {
|
|
33
|
+
[key: string]: string;
|
|
34
|
+
};
|
|
35
|
+
subgridLabels: string;
|
|
36
|
+
title: string;
|
|
37
|
+
video: string;
|
|
38
|
+
virtualBackgroundUrl: string;
|
|
39
|
+
toolbarDarkText: string;
|
|
40
|
+
}
|
|
41
|
+
interface WherebyEmbedElementEventMap {
|
|
42
|
+
ready: CustomEvent;
|
|
43
|
+
knock: CustomEvent;
|
|
44
|
+
participantupdate: CustomEvent<{
|
|
45
|
+
count: number;
|
|
46
|
+
}>;
|
|
47
|
+
join: CustomEvent;
|
|
48
|
+
leave: CustomEvent<{
|
|
49
|
+
removed: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
participant_join: CustomEvent<{
|
|
52
|
+
participant: {
|
|
53
|
+
metadata: string;
|
|
54
|
+
};
|
|
55
|
+
}>;
|
|
56
|
+
participant_leave: CustomEvent<{
|
|
57
|
+
participant: {
|
|
58
|
+
metadata: string;
|
|
59
|
+
};
|
|
60
|
+
}>;
|
|
61
|
+
microphone_toggle: CustomEvent<{
|
|
62
|
+
enabled: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
camera_toggle: CustomEvent<{
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
chat_toggle: CustomEvent<{
|
|
68
|
+
open: boolean;
|
|
69
|
+
}>;
|
|
70
|
+
pip_toggle: CustomEvent<{
|
|
71
|
+
open: boolean;
|
|
72
|
+
}>;
|
|
73
|
+
deny_device_permission: CustomEvent<{
|
|
74
|
+
denied: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
screenshare_toggle: CustomEvent<{
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
streaming_status_change: CustomEvent<{
|
|
80
|
+
status: string;
|
|
81
|
+
}>;
|
|
82
|
+
connection_status_change: CustomEvent<{
|
|
83
|
+
status: "stable" | "unstable";
|
|
84
|
+
}>;
|
|
85
|
+
}
|
|
86
|
+
interface WherebyEmbedElementCommands {
|
|
87
|
+
startRecording: () => void;
|
|
88
|
+
stopRecording: () => void;
|
|
89
|
+
startStreaming: () => void;
|
|
90
|
+
stopStreaming: () => void;
|
|
91
|
+
toggleCamera: (enabled?: boolean) => void;
|
|
92
|
+
toggleMicrophone: (enabled?: boolean) => void;
|
|
93
|
+
toggleScreenshare: (enabled?: boolean) => void;
|
|
94
|
+
toogleChat: (enabled?: boolean) => void;
|
|
95
|
+
}
|
|
96
|
+
interface WherebyEmbedElement extends HTMLIFrameElement, WherebyEmbedElementCommands {
|
|
97
|
+
addEventListener<K extends keyof (WherebyEmbedElementEventMap & HTMLElementEventMap)>(type: K, listener: (this: HTMLIFrameElement, ev: (WherebyEmbedElementEventMap & HTMLElementEventMap)[K]) => void, options?: boolean | AddEventListenerOptions | undefined): void;
|
|
98
|
+
}
|
|
99
|
+
declare global {
|
|
100
|
+
namespace JSX {
|
|
101
|
+
interface IntrinsicElements {
|
|
102
|
+
["whereby-embed"]: Partial<WherebyEmbedElementAttributes>;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
declare const _default: {
|
|
107
|
+
sdkVersion: string;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export { type WherebyEmbedElement, _default as default };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { define, ref } from 'heresy';
|
|
2
|
+
import { parseRoomUrlAndSubdomain } from '@whereby.com/core/utils';
|
|
3
|
+
|
|
4
|
+
const boolAttrs = [
|
|
5
|
+
"audio",
|
|
6
|
+
"background",
|
|
7
|
+
"cameraAccess",
|
|
8
|
+
"chat",
|
|
9
|
+
"people",
|
|
10
|
+
"embed",
|
|
11
|
+
"emptyRoomInvitation",
|
|
12
|
+
"help",
|
|
13
|
+
"leaveButton",
|
|
14
|
+
"precallReview",
|
|
15
|
+
"screenshare",
|
|
16
|
+
"video",
|
|
17
|
+
"floatSelf",
|
|
18
|
+
"recording",
|
|
19
|
+
"logo",
|
|
20
|
+
"locking",
|
|
21
|
+
"participantCount",
|
|
22
|
+
"settingsButton",
|
|
23
|
+
"pipButton",
|
|
24
|
+
"moreButton",
|
|
25
|
+
"personality",
|
|
26
|
+
"subgridLabels",
|
|
27
|
+
"lowData",
|
|
28
|
+
"breakout",
|
|
29
|
+
"toolbarDarkText",
|
|
30
|
+
];
|
|
31
|
+
define("WherebyEmbed", {
|
|
32
|
+
oninit() {
|
|
33
|
+
this.iframe = ref();
|
|
34
|
+
},
|
|
35
|
+
onconnected() {
|
|
36
|
+
window.addEventListener("message", this.onmessage.bind(this));
|
|
37
|
+
},
|
|
38
|
+
ondisconnected() {
|
|
39
|
+
window.removeEventListener("message", this.onmessage.bind(this));
|
|
40
|
+
},
|
|
41
|
+
observedAttributes: [
|
|
42
|
+
"displayName",
|
|
43
|
+
"minimal",
|
|
44
|
+
"room",
|
|
45
|
+
"subdomain",
|
|
46
|
+
"lang",
|
|
47
|
+
"metadata",
|
|
48
|
+
"groups",
|
|
49
|
+
"virtualBackgroundUrl",
|
|
50
|
+
"avatarUrl",
|
|
51
|
+
"externalId",
|
|
52
|
+
"title",
|
|
53
|
+
...boolAttrs,
|
|
54
|
+
].map((a) => a.toLowerCase()),
|
|
55
|
+
onattributechanged({ attributeName, oldValue }) {
|
|
56
|
+
if (["room", "subdomain"].includes(attributeName) && oldValue == null)
|
|
57
|
+
return;
|
|
58
|
+
this.render();
|
|
59
|
+
},
|
|
60
|
+
style(self) {
|
|
61
|
+
return `
|
|
62
|
+
${self} {
|
|
63
|
+
display: block;
|
|
64
|
+
}
|
|
65
|
+
${self} iframe {
|
|
66
|
+
border: none;
|
|
67
|
+
height: 100%;
|
|
68
|
+
width: 100%;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
},
|
|
72
|
+
_postCommand(command, args = []) {
|
|
73
|
+
if (this.iframe.current) {
|
|
74
|
+
this.iframe.current.contentWindow.postMessage({ command, args }, this.roomUrl.origin);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
startRecording() {
|
|
78
|
+
this._postCommand("start_recording");
|
|
79
|
+
},
|
|
80
|
+
stopRecording() {
|
|
81
|
+
this._postCommand("stop_recording");
|
|
82
|
+
},
|
|
83
|
+
startStreaming() {
|
|
84
|
+
this._postCommand("start_streaming");
|
|
85
|
+
},
|
|
86
|
+
stopStreaming() {
|
|
87
|
+
this._postCommand("stop_streaming");
|
|
88
|
+
},
|
|
89
|
+
toggleCamera(enabled) {
|
|
90
|
+
this._postCommand("toggle_camera", [enabled]);
|
|
91
|
+
},
|
|
92
|
+
toggleMicrophone(enabled) {
|
|
93
|
+
this._postCommand("toggle_microphone", [enabled]);
|
|
94
|
+
},
|
|
95
|
+
toggleScreenshare(enabled) {
|
|
96
|
+
this._postCommand("toggle_screenshare", [enabled]);
|
|
97
|
+
},
|
|
98
|
+
toggleChat(enabled) {
|
|
99
|
+
this._postCommand("toggle_chat", [enabled]);
|
|
100
|
+
},
|
|
101
|
+
onmessage({ origin, data, }) {
|
|
102
|
+
if (!this.roomUrl || origin !== this.roomUrl.origin)
|
|
103
|
+
return;
|
|
104
|
+
const { type, payload: detail } = data;
|
|
105
|
+
this.dispatchEvent(new CustomEvent(type, { detail }));
|
|
106
|
+
},
|
|
107
|
+
render() {
|
|
108
|
+
const { avatarurl: avatarUrl, displayname: displayName, lang, metadata, externalid: externalId, minimal, room, groups, virtualbackgroundurl: virtualBackgroundUrl, title, } = this;
|
|
109
|
+
let roomUrl, subdomain;
|
|
110
|
+
try {
|
|
111
|
+
({ roomUrl, subdomain } = parseRoomUrlAndSubdomain(room, this.subdomain));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return this.html `Whereby: ${error instanceof Error ? error.message : "unknown error"}`;
|
|
115
|
+
}
|
|
116
|
+
this.roomUrl = roomUrl;
|
|
117
|
+
Object.entries(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ jsApi: true, we: "1", iframeSource: subdomain }, (displayName && { displayName })), (lang && { lang: lang })), (metadata && { metadata: metadata })), (externalId && { externalId })), (groups && { groups: groups })), (virtualBackgroundUrl && { virtualBackgroundUrl: virtualBackgroundUrl })), (avatarUrl && { avatarUrl: avatarUrl })), (minimal != null && { embed: minimal })), boolAttrs.reduce((o, v) => (this[v.toLowerCase()] != null ? Object.assign(Object.assign({}, o), { [v]: this[v.toLowerCase()] }) : o), {}))).forEach(([k, v]) => {
|
|
118
|
+
if (!this.roomUrl.searchParams.has(k)) {
|
|
119
|
+
this.roomUrl.searchParams.set(k, v);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
this.html `
|
|
123
|
+
<iframe
|
|
124
|
+
title=${title || "Video calling component"}
|
|
125
|
+
ref=${this.iframe}
|
|
126
|
+
src=${this.roomUrl}
|
|
127
|
+
allow="autoplay; camera; microphone; fullscreen; speaker; display-capture; media-capture" />
|
|
128
|
+
`;
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
var index = { sdkVersion: "1" };
|
|
132
|
+
|
|
133
|
+
export { index as default };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Store, LocalMediaOptions, RemoteParticipant, LocalParticipant, Screenshare, ChatMessage as ChatMessage$1 } from '@whereby.com/core';
|
|
3
|
+
|
|
4
|
+
interface VideoViewSelfProps {
|
|
5
|
+
stream: MediaStream;
|
|
6
|
+
muted?: boolean;
|
|
7
|
+
mirror?: boolean;
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
onResize?: ({ width, height, stream }: {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
stream: MediaStream;
|
|
13
|
+
}) => void;
|
|
14
|
+
onSetAspectRatio?: ({ aspectRatio }: {
|
|
15
|
+
aspectRatio: number;
|
|
16
|
+
}) => void;
|
|
17
|
+
}
|
|
18
|
+
type VideoViewProps = VideoViewSelfProps & React.DetailedHTMLProps<React.VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement>;
|
|
19
|
+
declare const _default: ({ muted, mirror, stream, onResize, onSetAspectRatio, ...rest }: VideoViewProps) => React.JSX.Element;
|
|
20
|
+
|
|
21
|
+
interface LocalMediaState {
|
|
22
|
+
currentCameraDeviceId?: string;
|
|
23
|
+
currentMicrophoneDeviceId?: string;
|
|
24
|
+
cameraDeviceError: unknown;
|
|
25
|
+
cameraDevices: MediaDeviceInfo[];
|
|
26
|
+
isSettingCameraDevice: boolean;
|
|
27
|
+
isSettingMicrophoneDevice: boolean;
|
|
28
|
+
isStarting: boolean;
|
|
29
|
+
localStream?: MediaStream;
|
|
30
|
+
microphoneDeviceError: unknown;
|
|
31
|
+
microphoneDevices: MediaDeviceInfo[];
|
|
32
|
+
speakerDevices: MediaDeviceInfo[];
|
|
33
|
+
startError: unknown;
|
|
34
|
+
}
|
|
35
|
+
interface LocalMediaActions {
|
|
36
|
+
setCameraDevice: (deviceId: string) => void;
|
|
37
|
+
setMicrophoneDevice: (deviceId: string) => void;
|
|
38
|
+
toggleCameraEnabled: (enabled?: boolean) => void;
|
|
39
|
+
toggleMicrophoneEnabled: (enabled?: boolean) => void;
|
|
40
|
+
}
|
|
41
|
+
type UseLocalMediaResult = {
|
|
42
|
+
state: LocalMediaState;
|
|
43
|
+
actions: LocalMediaActions;
|
|
44
|
+
store: Store;
|
|
45
|
+
};
|
|
46
|
+
type UseLocalMediaOptions = LocalMediaOptions;
|
|
47
|
+
|
|
48
|
+
type RemoteParticipantState = Omit<RemoteParticipant, "newJoiner" | "streams">;
|
|
49
|
+
type LocalParticipantState = LocalParticipant;
|
|
50
|
+
interface WaitingParticipantState {
|
|
51
|
+
id: string;
|
|
52
|
+
displayName: string | null;
|
|
53
|
+
}
|
|
54
|
+
interface ChatMessageState {
|
|
55
|
+
senderId: string;
|
|
56
|
+
timestamp: string;
|
|
57
|
+
text: string;
|
|
58
|
+
}
|
|
59
|
+
type ScreenshareState = Screenshare;
|
|
60
|
+
type LocalScreenshareStatus = "starting" | "active";
|
|
61
|
+
type ChatMessage = Pick<ChatMessage$1, "senderId" | "timestamp" | "text">;
|
|
62
|
+
type ConnectionStatus = "initializing" | "connecting" | "connected" | "reconnect" | "room_locked" | "knocking" | "disconnecting" | "disconnected" | "knock_rejected";
|
|
63
|
+
type CloudRecordingState = {
|
|
64
|
+
error?: string;
|
|
65
|
+
status: "recording" | "requested" | "error";
|
|
66
|
+
startedAt?: number;
|
|
67
|
+
};
|
|
68
|
+
type LiveStreamState = {
|
|
69
|
+
status: "streaming";
|
|
70
|
+
startedAt?: number;
|
|
71
|
+
};
|
|
72
|
+
interface RoomConnectionState {
|
|
73
|
+
chatMessages: ChatMessage[];
|
|
74
|
+
cloudRecording?: CloudRecordingState;
|
|
75
|
+
localScreenshareStatus?: LocalScreenshareStatus;
|
|
76
|
+
localParticipant?: LocalParticipantState;
|
|
77
|
+
remoteParticipants: RemoteParticipantState[];
|
|
78
|
+
screenshares: Screenshare[];
|
|
79
|
+
connectionStatus: ConnectionStatus;
|
|
80
|
+
liveStream?: LiveStreamState;
|
|
81
|
+
waitingParticipants: WaitingParticipantState[];
|
|
82
|
+
}
|
|
83
|
+
interface RoomConnectionOptions {
|
|
84
|
+
displayName?: string;
|
|
85
|
+
localMediaOptions?: LocalMediaOptions;
|
|
86
|
+
roomKey?: string;
|
|
87
|
+
localMedia?: UseLocalMediaResult;
|
|
88
|
+
externalId?: string;
|
|
89
|
+
}
|
|
90
|
+
interface UseRoomConnectionOptions extends Omit<RoomConnectionOptions, "localMedia"> {
|
|
91
|
+
localMedia?: UseLocalMediaResult;
|
|
92
|
+
}
|
|
93
|
+
interface RoomConnectionActions {
|
|
94
|
+
sendChatMessage(text: string): void;
|
|
95
|
+
knock(): void;
|
|
96
|
+
setDisplayName(displayName: string): void;
|
|
97
|
+
toggleCamera(enabled?: boolean): void;
|
|
98
|
+
toggleMicrophone(enabled?: boolean): void;
|
|
99
|
+
acceptWaitingParticipant(participantId: string): void;
|
|
100
|
+
rejectWaitingParticipant(participantId: string): void;
|
|
101
|
+
startCloudRecording(): void;
|
|
102
|
+
startScreenshare(): void;
|
|
103
|
+
stopCloudRecording(): void;
|
|
104
|
+
stopScreenshare(): void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
type VideoViewComponentProps = Omit<React.ComponentProps<typeof _default>, "onResize">;
|
|
108
|
+
interface RoomConnectionComponents {
|
|
109
|
+
VideoView: (props: VideoViewComponentProps) => ReturnType<typeof _default>;
|
|
110
|
+
}
|
|
111
|
+
type RoomConnectionRef = {
|
|
112
|
+
state: RoomConnectionState;
|
|
113
|
+
actions: RoomConnectionActions;
|
|
114
|
+
components: RoomConnectionComponents;
|
|
115
|
+
_ref: Store;
|
|
116
|
+
};
|
|
117
|
+
declare function useRoomConnection(roomUrl: string, roomConnectionOptions?: UseRoomConnectionOptions): RoomConnectionRef;
|
|
118
|
+
|
|
119
|
+
declare function useLocalMedia(optionsOrStream?: UseLocalMediaOptions | MediaStream): UseLocalMediaResult;
|
|
120
|
+
|
|
121
|
+
export { type ChatMessageState as ChatMessage, type LocalParticipantState as LocalParticipant, type RemoteParticipantState as RemoteParticipant, type RoomConnectionState as RoomConnection, type ScreenshareState as Screenshare, type UseLocalMediaResult, _default as VideoView, type WaitingParticipantState as WaitingParticipant, useLocalMedia, useRoomConnection };
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
3
|
+
import { debounce } from '@whereby.com/core/utils';
|
|
4
|
+
import { selectChatMessages, selectCloudRecordingRaw, selectLocalParticipantRaw, selectLocalMediaStream, selectRemoteParticipants, selectScreenshares, selectRoomConnectionStatus, selectStreamingRaw, selectWaitingParticipants, createServices, createStore, observeStore, doAppJoin, sdkVersion, appLeft, doRtcReportStreamResolution, doSendChatMessage, doKnockRoom, doSetDisplayName, toggleCameraEnabled, toggleMicrophoneEnabled, doAcceptWaitingParticipant, doRejectWaitingParticipant, doStartCloudRecording, doStartScreenshare, doStopCloudRecording, doStopScreenshare, selectCameraDeviceError, selectCameraDevices, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsLocalMediaStarting, selectMicrophoneDeviceError, selectMicrophoneDevices, selectSpeakerDevices, selectLocalMediaStartError, doStartLocalMedia, doStopLocalMedia, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId } from '@whereby.com/core';
|
|
5
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
6
|
+
|
|
7
|
+
/******************************************************************************
|
|
8
|
+
Copyright (c) Microsoft Corporation.
|
|
9
|
+
|
|
10
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
11
|
+
purpose with or without fee is hereby granted.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
14
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
15
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
16
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
17
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
18
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
19
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
20
|
+
***************************************************************************** */
|
|
21
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
function __rest(s, e) {
|
|
25
|
+
var t = {};
|
|
26
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
27
|
+
t[p] = s[p];
|
|
28
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
29
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
30
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
31
|
+
t[p[i]] = s[p[i]];
|
|
32
|
+
}
|
|
33
|
+
return t;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
37
|
+
var e = new Error(message);
|
|
38
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
var VideoView = (_a) => {
|
|
42
|
+
var { muted, mirror = false, stream, onResize, onSetAspectRatio } = _a, rest = __rest(_a, ["muted", "mirror", "stream", "onResize", "onSetAspectRatio"]);
|
|
43
|
+
const videoEl = React.useRef(null);
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
if (!videoEl.current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const resizeObserver = new ResizeObserver(debounce(() => {
|
|
49
|
+
if (videoEl.current && (stream === null || stream === void 0 ? void 0 : stream.id)) {
|
|
50
|
+
if (onResize) {
|
|
51
|
+
onResize({
|
|
52
|
+
width: videoEl.current.clientWidth,
|
|
53
|
+
height: videoEl.current.clientHeight,
|
|
54
|
+
stream,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const h = videoEl.current.videoHeight;
|
|
58
|
+
const w = videoEl.current.videoWidth;
|
|
59
|
+
if (onSetAspectRatio && w && h && w + h > 20) {
|
|
60
|
+
onSetAspectRatio({ aspectRatio: w / h });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}, { delay: 1000, edges: true }));
|
|
64
|
+
resizeObserver.observe(videoEl.current);
|
|
65
|
+
return () => {
|
|
66
|
+
resizeObserver.disconnect();
|
|
67
|
+
};
|
|
68
|
+
}, [stream]);
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
if (!videoEl.current) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (videoEl.current.srcObject !== stream) {
|
|
74
|
+
videoEl.current.srcObject = stream;
|
|
75
|
+
}
|
|
76
|
+
if (videoEl.current.muted !== muted) {
|
|
77
|
+
videoEl.current.muted = Boolean(muted);
|
|
78
|
+
}
|
|
79
|
+
}, [muted, stream, videoEl]);
|
|
80
|
+
return (React.createElement("video", Object.assign({ ref: videoEl, autoPlay: true, playsInline: true }, rest, { style: Object.assign({ transform: mirror ? "scaleX(-1)" : "none", width: "100%", height: "100%" }, rest.style) })));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const selectRoomConnectionState = createSelector(selectChatMessages, selectCloudRecordingRaw, selectLocalParticipantRaw, selectLocalMediaStream, selectRemoteParticipants, selectScreenshares, selectRoomConnectionStatus, selectStreamingRaw, selectWaitingParticipants, (chatMessages, cloudRecording, localParticipant, localMediaStream, remoteParticipants, screenshares, connectionStatus, streaming, waitingParticipants) => {
|
|
84
|
+
const state = {
|
|
85
|
+
chatMessages,
|
|
86
|
+
cloudRecording: cloudRecording.isRecording ? { status: "recording" } : undefined,
|
|
87
|
+
localScreenshareStatus: localParticipant.isScreenSharing ? "active" : undefined,
|
|
88
|
+
localParticipant: Object.assign(Object.assign({}, localParticipant), { stream: localMediaStream }),
|
|
89
|
+
remoteParticipants,
|
|
90
|
+
screenshares,
|
|
91
|
+
connectionStatus,
|
|
92
|
+
liveStream: streaming.isStreaming
|
|
93
|
+
? {
|
|
94
|
+
status: "streaming",
|
|
95
|
+
startedAt: streaming.startedAt,
|
|
96
|
+
}
|
|
97
|
+
: undefined,
|
|
98
|
+
waitingParticipants,
|
|
99
|
+
};
|
|
100
|
+
return state;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const initialState$1 = {
|
|
104
|
+
chatMessages: [],
|
|
105
|
+
remoteParticipants: [],
|
|
106
|
+
connectionStatus: "initializing",
|
|
107
|
+
screenshares: [],
|
|
108
|
+
waitingParticipants: [],
|
|
109
|
+
};
|
|
110
|
+
const defaultRoomConnectionOptions = {
|
|
111
|
+
localMediaOptions: {
|
|
112
|
+
audio: true,
|
|
113
|
+
video: true,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
function useRoomConnection(roomUrl, roomConnectionOptions = defaultRoomConnectionOptions) {
|
|
117
|
+
const [store] = React.useState(() => {
|
|
118
|
+
if (roomConnectionOptions.localMedia) {
|
|
119
|
+
return roomConnectionOptions.localMedia.store;
|
|
120
|
+
}
|
|
121
|
+
const services = createServices();
|
|
122
|
+
return createStore({ injectServices: services });
|
|
123
|
+
});
|
|
124
|
+
const [boundVideoView, setBoundVideoView] = React.useState();
|
|
125
|
+
const [roomConnectionState, setRoomConnectionState] = React.useState(initialState$1);
|
|
126
|
+
React.useEffect(() => {
|
|
127
|
+
const unsubscribe = observeStore(store, selectRoomConnectionState, setRoomConnectionState);
|
|
128
|
+
const url = new URL(roomUrl);
|
|
129
|
+
const searchParams = new URLSearchParams(url.search);
|
|
130
|
+
const roomKey = searchParams.get("roomKey");
|
|
131
|
+
store.dispatch(doAppJoin({
|
|
132
|
+
displayName: roomConnectionOptions.displayName || "Guest",
|
|
133
|
+
localMediaOptions: roomConnectionOptions.localMedia
|
|
134
|
+
? undefined
|
|
135
|
+
: roomConnectionOptions.localMediaOptions,
|
|
136
|
+
roomKey,
|
|
137
|
+
roomUrl,
|
|
138
|
+
sdkVersion: sdkVersion,
|
|
139
|
+
externalId: roomConnectionOptions.externalId || null,
|
|
140
|
+
}));
|
|
141
|
+
return () => {
|
|
142
|
+
unsubscribe();
|
|
143
|
+
store.dispatch(appLeft());
|
|
144
|
+
};
|
|
145
|
+
}, []);
|
|
146
|
+
React.useEffect(() => {
|
|
147
|
+
if (store && !boundVideoView) {
|
|
148
|
+
setBoundVideoView(() => (props) => {
|
|
149
|
+
return React.createElement(VideoView, Object.assign({}, props, {
|
|
150
|
+
onResize: ({ stream, width, height, }) => {
|
|
151
|
+
store.dispatch(doRtcReportStreamResolution({
|
|
152
|
+
streamId: stream.id,
|
|
153
|
+
width,
|
|
154
|
+
height,
|
|
155
|
+
}));
|
|
156
|
+
},
|
|
157
|
+
}));
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}, [store, boundVideoView]);
|
|
161
|
+
const sendChatMessage = React.useCallback((text) => store.dispatch(doSendChatMessage({ text })), [store]);
|
|
162
|
+
const knock = React.useCallback(() => store.dispatch(doKnockRoom()), [store]);
|
|
163
|
+
const setDisplayName = React.useCallback((displayName) => store.dispatch(doSetDisplayName({ displayName })), [store]);
|
|
164
|
+
const toggleCamera = React.useCallback((enabled) => store.dispatch(toggleCameraEnabled({ enabled })), [store]);
|
|
165
|
+
const toggleMicrophone = React.useCallback((enabled) => store.dispatch(toggleMicrophoneEnabled({ enabled })), [store]);
|
|
166
|
+
const acceptWaitingParticipant = React.useCallback((participantId) => store.dispatch(doAcceptWaitingParticipant({ participantId })), [store]);
|
|
167
|
+
const rejectWaitingParticipant = React.useCallback((participantId) => store.dispatch(doRejectWaitingParticipant({ participantId })), [store]);
|
|
168
|
+
const startCloudRecording = React.useCallback(() => store.dispatch(doStartCloudRecording()), [store]);
|
|
169
|
+
const startScreenshare = React.useCallback(() => store.dispatch(doStartScreenshare()), [store]);
|
|
170
|
+
const stopCloudRecording = React.useCallback(() => store.dispatch(doStopCloudRecording()), [store]);
|
|
171
|
+
const stopScreenshare = React.useCallback(() => store.dispatch(doStopScreenshare()), [store]);
|
|
172
|
+
return {
|
|
173
|
+
state: roomConnectionState,
|
|
174
|
+
actions: {
|
|
175
|
+
sendChatMessage,
|
|
176
|
+
knock,
|
|
177
|
+
setDisplayName,
|
|
178
|
+
toggleCamera,
|
|
179
|
+
toggleMicrophone,
|
|
180
|
+
acceptWaitingParticipant,
|
|
181
|
+
rejectWaitingParticipant,
|
|
182
|
+
startCloudRecording,
|
|
183
|
+
startScreenshare,
|
|
184
|
+
stopCloudRecording,
|
|
185
|
+
stopScreenshare,
|
|
186
|
+
},
|
|
187
|
+
components: {
|
|
188
|
+
VideoView: boundVideoView || VideoView,
|
|
189
|
+
},
|
|
190
|
+
_ref: store,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const selectLocalMediaState = createSelector(selectCameraDeviceError, selectCameraDevices, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsLocalMediaStarting, selectLocalMediaStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectSpeakerDevices, selectLocalMediaStartError, (cameraDeviceError, cameraDevices, currentCameraDeviceId, currentMicrophoneDeviceId, isSettingCameraDevice, isSettingMicrophoneDevice, isStarting, localStream, microphoneDeviceError, microphoneDevices, speakerDevices, startError) => {
|
|
195
|
+
const state = {
|
|
196
|
+
cameraDeviceError,
|
|
197
|
+
cameraDevices,
|
|
198
|
+
currentCameraDeviceId,
|
|
199
|
+
currentMicrophoneDeviceId,
|
|
200
|
+
isSettingCameraDevice,
|
|
201
|
+
isSettingMicrophoneDevice,
|
|
202
|
+
isStarting,
|
|
203
|
+
localStream,
|
|
204
|
+
microphoneDeviceError,
|
|
205
|
+
microphoneDevices,
|
|
206
|
+
speakerDevices,
|
|
207
|
+
startError,
|
|
208
|
+
};
|
|
209
|
+
return state;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const initialState = {
|
|
213
|
+
cameraDeviceError: null,
|
|
214
|
+
cameraDevices: [],
|
|
215
|
+
isSettingCameraDevice: false,
|
|
216
|
+
isSettingMicrophoneDevice: false,
|
|
217
|
+
isStarting: false,
|
|
218
|
+
microphoneDeviceError: null,
|
|
219
|
+
microphoneDevices: [],
|
|
220
|
+
speakerDevices: [],
|
|
221
|
+
startError: null,
|
|
222
|
+
};
|
|
223
|
+
function useLocalMedia(optionsOrStream = { audio: true, video: true }) {
|
|
224
|
+
const [store] = useState(() => {
|
|
225
|
+
const services = createServices();
|
|
226
|
+
return createStore({ injectServices: services });
|
|
227
|
+
});
|
|
228
|
+
const [localMediaState, setLocalMediaState] = useState(initialState);
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
const unsubscribe = observeStore(store, selectLocalMediaState, setLocalMediaState);
|
|
231
|
+
store.dispatch(doStartLocalMedia(optionsOrStream));
|
|
232
|
+
return () => {
|
|
233
|
+
unsubscribe();
|
|
234
|
+
store.dispatch(doStopLocalMedia());
|
|
235
|
+
};
|
|
236
|
+
}, []);
|
|
237
|
+
const setCameraDevice = useCallback((deviceId) => store.dispatch(setCurrentCameraDeviceId({ deviceId })), [store]);
|
|
238
|
+
const setMicrophoneDevice = useCallback((deviceId) => store.dispatch(setCurrentMicrophoneDeviceId({ deviceId })), [store]);
|
|
239
|
+
const toggleCamera = useCallback((enabled) => store.dispatch(toggleCameraEnabled({ enabled })), [store]);
|
|
240
|
+
const toggleMicrophone = useCallback((enabled) => store.dispatch(toggleMicrophoneEnabled({ enabled })), [store]);
|
|
241
|
+
return {
|
|
242
|
+
state: localMediaState,
|
|
243
|
+
actions: {
|
|
244
|
+
setCameraDevice,
|
|
245
|
+
setMicrophoneDevice,
|
|
246
|
+
toggleCameraEnabled: toggleCamera,
|
|
247
|
+
toggleMicrophoneEnabled: toggleMicrophone,
|
|
248
|
+
},
|
|
249
|
+
store,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export { VideoView, useLocalMedia, useRoomConnection };
|