2021-07-27 19:27:59 +00:00
|
|
|
/*
|
|
|
|
Copyright 2021 New Vector Ltd
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-09-01 23:42:01 +00:00
|
|
|
import React, {
|
|
|
|
useEffect,
|
|
|
|
useMemo,
|
|
|
|
useState,
|
|
|
|
useRef,
|
|
|
|
useCallback,
|
|
|
|
} from "react";
|
2021-07-27 19:27:59 +00:00
|
|
|
import styles from "./Room.module.css";
|
2021-09-03 22:45:07 +00:00
|
|
|
import { useParams, useLocation, useHistory, Link } from "react-router-dom";
|
2021-09-10 19:20:17 +00:00
|
|
|
import { useGroupCall } from "./ConferenceCallManagerHooks";
|
2021-07-28 23:14:38 +00:00
|
|
|
import { DevTools } from "./DevTools";
|
2021-08-17 23:57:25 +00:00
|
|
|
import { VideoGrid } from "./VideoGrid";
|
2021-08-19 19:11:12 +00:00
|
|
|
import {
|
|
|
|
HangupButton,
|
|
|
|
SettingsButton,
|
|
|
|
MicButton,
|
|
|
|
VideoButton,
|
2021-09-01 23:42:01 +00:00
|
|
|
LayoutToggleButton,
|
2021-08-19 19:11:12 +00:00
|
|
|
} from "./RoomButton";
|
2021-08-20 16:28:11 +00:00
|
|
|
import { Header, LeftNav, RightNav, CenterNav } from "./Header";
|
2021-09-03 22:45:07 +00:00
|
|
|
import { Button, FieldRow, InputField, ErrorMessage } from "./Input";
|
|
|
|
import { Center, Content, Info, Modal } from "./Layout";
|
2021-07-28 23:14:38 +00:00
|
|
|
|
|
|
|
function useQuery() {
|
|
|
|
const location = useLocation();
|
|
|
|
return useMemo(() => new URLSearchParams(location.search), [location.search]);
|
|
|
|
}
|
2021-07-27 19:27:59 +00:00
|
|
|
|
2021-09-10 19:20:17 +00:00
|
|
|
function useDebugMode() {
|
2021-07-28 23:14:38 +00:00
|
|
|
const query = useQuery();
|
2021-07-29 18:24:34 +00:00
|
|
|
const debugStr = query.get("debug");
|
2021-09-16 22:01:14 +00:00
|
|
|
const debugEnabled = query.has("debug");
|
2021-09-10 19:20:17 +00:00
|
|
|
const [debugMode, setDebugMode] = useState(
|
|
|
|
debugStr === "" || debugStr === "true"
|
|
|
|
);
|
|
|
|
|
|
|
|
const toggleDebugMode = useCallback(() => {
|
|
|
|
setDebugMode((prevDebugMode) => !prevDebugMode);
|
|
|
|
}, []);
|
2021-07-28 23:14:38 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
function onKeyDown(event) {
|
|
|
|
if (
|
|
|
|
document.activeElement.tagName !== "input" &&
|
|
|
|
event.code === "Backquote"
|
|
|
|
) {
|
2021-09-10 19:20:17 +00:00
|
|
|
toggleDebugMode();
|
2021-07-28 23:14:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener("keydown", onKeyDown);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
window.removeEventListener("keydown", onKeyDown);
|
|
|
|
};
|
|
|
|
}, []);
|
2021-07-27 19:27:59 +00:00
|
|
|
|
2021-09-16 22:01:14 +00:00
|
|
|
return { debugEnabled, debugMode, toggleDebugMode };
|
2021-09-10 19:20:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function useRoomLayout() {
|
2021-09-01 23:42:01 +00:00
|
|
|
const [layout, setLayout] = useState("gallery");
|
|
|
|
|
|
|
|
const toggleLayout = useCallback(() => {
|
|
|
|
setLayout(layout === "spotlight" ? "gallery" : "spotlight");
|
|
|
|
}, [layout]);
|
|
|
|
|
2021-09-10 19:20:17 +00:00
|
|
|
return [layout, toggleLayout];
|
|
|
|
}
|
|
|
|
|
|
|
|
export function Room({ client }) {
|
2021-09-16 22:01:14 +00:00
|
|
|
const { debugEnabled, debugMode, toggleDebugMode } = useDebugMode();
|
2021-09-10 19:20:17 +00:00
|
|
|
const { roomId } = useParams();
|
|
|
|
const {
|
|
|
|
loading,
|
|
|
|
entered,
|
|
|
|
entering,
|
|
|
|
roomName,
|
|
|
|
participants,
|
|
|
|
groupCall,
|
|
|
|
microphoneMuted,
|
|
|
|
localVideoMuted,
|
|
|
|
error,
|
|
|
|
initLocalParticipant,
|
|
|
|
enter,
|
|
|
|
leave,
|
|
|
|
toggleLocalVideoMuted,
|
|
|
|
toggleMicrophoneMuted,
|
2021-09-16 22:01:14 +00:00
|
|
|
callDebugger,
|
|
|
|
} = useGroupCall(client, roomId, debugEnabled);
|
2021-09-10 19:20:17 +00:00
|
|
|
|
|
|
|
const content = () => {
|
|
|
|
if (error) {
|
|
|
|
return <LoadingErrorView error={error} />;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
return <LoadingRoomView />;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entering) {
|
|
|
|
return <EnteringRoomView />;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!entered) {
|
|
|
|
return (
|
|
|
|
<RoomSetupView
|
|
|
|
roomName={roomName}
|
|
|
|
onInitLocalParticipant={initLocalParticipant}
|
|
|
|
onEnter={enter}
|
|
|
|
microphoneMuted={microphoneMuted}
|
|
|
|
localVideoMuted={localVideoMuted}
|
|
|
|
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
|
|
|
toggleMicrophoneMuted={toggleMicrophoneMuted}
|
2021-08-21 00:02:47 +00:00
|
|
|
/>
|
2021-09-10 19:20:17 +00:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return (
|
|
|
|
<InRoomView
|
|
|
|
roomName={roomName}
|
|
|
|
microphoneMuted={microphoneMuted}
|
|
|
|
localVideoMuted={localVideoMuted}
|
|
|
|
toggleLocalVideoMuted={toggleLocalVideoMuted}
|
|
|
|
toggleMicrophoneMuted={toggleMicrophoneMuted}
|
|
|
|
participants={participants}
|
|
|
|
onLeave={leave}
|
|
|
|
groupCall={groupCall}
|
2021-09-16 22:01:14 +00:00
|
|
|
debugEnabled={debugEnabled}
|
|
|
|
debugMode={debugMode}
|
|
|
|
toggleDebugMode={toggleDebugMode}
|
|
|
|
callDebugger={callDebugger}
|
2021-09-10 19:20:17 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return <div className={styles.room}>{content()}</div>;
|
2021-07-27 19:27:59 +00:00
|
|
|
}
|
2021-08-21 00:02:47 +00:00
|
|
|
|
2021-09-10 19:20:17 +00:00
|
|
|
export function LoadingRoomView() {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div className={styles.centerMessage}>
|
|
|
|
<p>Loading room...</p>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
2021-09-03 22:45:07 +00:00
|
|
|
|
2021-09-10 19:20:17 +00:00
|
|
|
export function EnteringRoomView() {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div className={styles.centerMessage}>
|
|
|
|
<p>Entering room...</p>
|
|
|
|
</div>
|
|
|
|
</>
|
2021-09-03 22:45:07 +00:00
|
|
|
);
|
2021-09-10 19:20:17 +00:00
|
|
|
}
|
2021-09-03 22:45:07 +00:00
|
|
|
|
2021-09-10 19:20:17 +00:00
|
|
|
export function LoadingErrorView({ error }) {
|
2021-09-10 23:05:57 +00:00
|
|
|
useEffect(() => {
|
|
|
|
console.error(error);
|
|
|
|
}, [error]);
|
|
|
|
|
2021-09-03 22:45:07 +00:00
|
|
|
return (
|
|
|
|
<>
|
2021-09-10 19:20:17 +00:00
|
|
|
<div className={styles.centerMessage}>
|
|
|
|
<ErrorMessage>{error.message}</ErrorMessage>
|
|
|
|
</div>
|
2021-09-03 22:45:07 +00:00
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-09-10 19:20:17 +00:00
|
|
|
const PermissionState = {
|
|
|
|
Waiting: "waiting",
|
|
|
|
Granted: "granted",
|
|
|
|
Denied: "denied",
|
|
|
|
};
|
|
|
|
|
|
|
|
function RoomSetupView({
|
|
|
|
roomName,
|
|
|
|
onInitLocalParticipant,
|
|
|
|
onEnter,
|
|
|
|
microphoneMuted,
|
|
|
|
localVideoMuted,
|
|
|
|
toggleLocalVideoMuted,
|
|
|
|
toggleMicrophoneMuted,
|
2021-08-21 00:02:47 +00:00
|
|
|
}) {
|
|
|
|
const videoRef = useRef();
|
2021-09-10 19:20:17 +00:00
|
|
|
const [permissionState, setPermissionState] = useState(
|
|
|
|
PermissionState.Waiting
|
|
|
|
);
|
2021-08-21 00:02:47 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
2021-09-10 19:20:17 +00:00
|
|
|
onInitLocalParticipant()
|
|
|
|
.then((localParticipant) => {
|
2021-08-21 00:02:47 +00:00
|
|
|
if (videoRef.current) {
|
2021-09-10 19:20:17 +00:00
|
|
|
videoRef.current.srcObject = localParticipant.usermediaStream;
|
2021-08-21 00:02:47 +00:00
|
|
|
videoRef.current.play();
|
2021-09-10 19:20:17 +00:00
|
|
|
setPermissionState(PermissionState.Granted);
|
2021-08-21 00:02:47 +00:00
|
|
|
}
|
|
|
|
})
|
2021-09-10 23:05:57 +00:00
|
|
|
.catch((error) => {
|
|
|
|
console.error(error);
|
|
|
|
|
2021-08-21 00:02:47 +00:00
|
|
|
if (videoRef.current) {
|
2021-09-10 19:20:17 +00:00
|
|
|
setPermissionState(PermissionState.Denied);
|
2021-08-21 00:02:47 +00:00
|
|
|
}
|
|
|
|
});
|
2021-09-10 19:20:17 +00:00
|
|
|
}, [onInitLocalParticipant]);
|
2021-08-21 00:02:47 +00:00
|
|
|
|
|
|
|
return (
|
2021-09-10 19:20:17 +00:00
|
|
|
<>
|
|
|
|
<Header>
|
|
|
|
<LeftNav />
|
|
|
|
<CenterNav>
|
|
|
|
<h3>{roomName}</h3>
|
|
|
|
</CenterNav>
|
|
|
|
</Header>
|
|
|
|
<div className={styles.joinRoom}>
|
|
|
|
<div className={styles.preview}>
|
|
|
|
{permissionState === PermissionState.Denied && (
|
|
|
|
<p className={styles.webcamPermissions}>
|
|
|
|
Webcam permissions needed to join the call.
|
|
|
|
</p>
|
|
|
|
)}
|
|
|
|
<video ref={videoRef} muted playsInline disablePictureInPicture />
|
|
|
|
</div>
|
|
|
|
{permissionState === PermissionState.Granted && (
|
|
|
|
<div className={styles.previewButtons}>
|
|
|
|
<MicButton
|
|
|
|
muted={microphoneMuted}
|
|
|
|
onClick={toggleMicrophoneMuted}
|
|
|
|
/>
|
|
|
|
<VideoButton
|
|
|
|
enabled={localVideoMuted}
|
|
|
|
onClick={toggleLocalVideoMuted}
|
|
|
|
/>
|
|
|
|
</div>
|
2021-08-21 00:02:47 +00:00
|
|
|
)}
|
2021-09-10 19:20:17 +00:00
|
|
|
<Button
|
|
|
|
disabled={permissionState !== PermissionState.Granted}
|
|
|
|
onClick={onEnter}
|
|
|
|
>
|
|
|
|
Enter Call
|
|
|
|
</Button>
|
2021-08-21 00:02:47 +00:00
|
|
|
</div>
|
2021-09-10 19:20:17 +00:00
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function InRoomView({
|
|
|
|
roomName,
|
|
|
|
microphoneMuted,
|
|
|
|
localVideoMuted,
|
|
|
|
toggleLocalVideoMuted,
|
|
|
|
toggleMicrophoneMuted,
|
|
|
|
participants,
|
|
|
|
onLeave,
|
2021-09-16 22:01:14 +00:00
|
|
|
debugEnabled,
|
|
|
|
debugMode,
|
|
|
|
toggleDebugMode,
|
|
|
|
callDebugger,
|
2021-09-10 19:20:17 +00:00
|
|
|
}) {
|
|
|
|
const [roomLayout, toggleRoomLayout] = useRoomLayout();
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Header>
|
|
|
|
<LeftNav />
|
|
|
|
<CenterNav>
|
|
|
|
<h3>{roomName}</h3>
|
|
|
|
</CenterNav>
|
|
|
|
<RightNav>
|
|
|
|
<LayoutToggleButton
|
|
|
|
title={roomLayout === "spotlight" ? "Spotlight" : "Gallery"}
|
|
|
|
layout={roomLayout}
|
|
|
|
onClick={toggleRoomLayout}
|
|
|
|
/>
|
2021-09-16 22:01:14 +00:00
|
|
|
{debugEnabled && (
|
|
|
|
<SettingsButton
|
|
|
|
title={debugMode ? "Disable DevTools" : "Enable DevTools"}
|
|
|
|
onClick={toggleDebugMode}
|
|
|
|
/>
|
|
|
|
)}
|
2021-09-10 19:20:17 +00:00
|
|
|
</RightNav>
|
|
|
|
</Header>
|
|
|
|
{participants.length === 0 ? (
|
|
|
|
<div className={styles.centerMessage}>
|
|
|
|
<p>Waiting for other participants...</p>
|
2021-08-21 00:02:47 +00:00
|
|
|
</div>
|
2021-09-10 19:20:17 +00:00
|
|
|
) : (
|
|
|
|
<VideoGrid participants={participants} layout={roomLayout} />
|
2021-08-21 00:02:47 +00:00
|
|
|
)}
|
2021-09-10 19:20:17 +00:00
|
|
|
<div className={styles.footer}>
|
|
|
|
<MicButton muted={microphoneMuted} onClick={toggleMicrophoneMuted} />
|
|
|
|
<VideoButton
|
|
|
|
enabled={localVideoMuted}
|
|
|
|
onClick={toggleLocalVideoMuted}
|
|
|
|
/>
|
|
|
|
<HangupButton onClick={onLeave} />
|
|
|
|
</div>
|
2021-09-16 22:01:14 +00:00
|
|
|
{debugEnabled && debugMode && callDebugger && (
|
|
|
|
<DevTools callDebugger={callDebugger} />
|
|
|
|
)}
|
2021-09-10 19:20:17 +00:00
|
|
|
</>
|
2021-08-21 00:02:47 +00:00
|
|
|
);
|
|
|
|
}
|