|
import Stack from "@mui/material/Stack"; |
|
import TextField from "@mui/material/TextField"; |
|
import { isValidRoomID, isValidPartialRoomID } from "./generateNewRoomID"; |
|
import { useCallback, useEffect, useState } from "react"; |
|
import Button from "@mui/material/Button"; |
|
import { useSocket } from "./useSocket"; |
|
import FormGroup from "@mui/material/FormGroup"; |
|
import FormControlLabel from "@mui/material/FormControlLabel"; |
|
import Checkbox from "@mui/material/Checkbox"; |
|
import { RoomState } from "./types/RoomState"; |
|
import setURLParam from "./setURLParam"; |
|
import { getURLParams } from "./URLParams"; |
|
import { |
|
JoinRoomConfig, |
|
Roles, |
|
ServerState, |
|
StreamingStatus, |
|
} from "./types/StreamingTypes"; |
|
import Alert from "@mui/material/Alert"; |
|
import { Paper, Typography } from "@mui/material"; |
|
|
|
function capitalize(str: string): string { |
|
return str.charAt(0).toUpperCase() + str.slice(1); |
|
} |
|
|
|
type Props = { |
|
roomState: RoomState | null; |
|
serverState: ServerState | null; |
|
onJoinRoomOrUpdateRoles?: () => void; |
|
streamingStatus: StreamingStatus; |
|
}; |
|
|
|
export default function RoomConfig({ |
|
roomState, |
|
serverState, |
|
onJoinRoomOrUpdateRoles, |
|
streamingStatus, |
|
}: Props) { |
|
const { socket, clientID } = useSocket(); |
|
|
|
const urlParams = getURLParams(); |
|
const roomIDParam = urlParams.roomID; |
|
const autoJoinRoom = urlParams.autoJoin; |
|
|
|
const [roomID, setRoomID] = useState<string>( |
|
(roomIDParam ?? "").toUpperCase() |
|
); |
|
const [roomIDError, setRoomIDError] = useState<boolean>(false); |
|
const [roles, setRoles] = useState<{ speaker: boolean; listener: boolean }>({ |
|
speaker: false, |
|
listener: false, |
|
}); |
|
const [lockServer, setLockServer] = useState<boolean>(false); |
|
const [lockServerName, setLockServerName] = useState<string>(""); |
|
|
|
const [joinInProgress, setJoinInProgress] = useState<boolean>(false); |
|
const [didAttemptAutoJoin, setDidAttemptAutoJoin] = useState<boolean>(false); |
|
|
|
const isValidServerLock = |
|
lockServer === false || |
|
(lockServerName != null && lockServerName.length > 0); |
|
const isValidRoles = Object.values(roles).filter(Boolean).length > 0; |
|
const isValidAllInputs = |
|
isValidRoomID(roomID) && isValidRoles && isValidServerLock; |
|
const roomIDFromServer = roomState?.room_id ?? null; |
|
|
|
const onJoinRoom = useCallback( |
|
(createNewRoom: boolean) => { |
|
if (socket == null) { |
|
console.error("Socket is null, cannot join room"); |
|
return; |
|
} |
|
console.debug(`Attempting to join roomID ${roomID}...`); |
|
|
|
const lockServerValidated: string | null = |
|
lockServer && roles["speaker"] ? lockServerName : null; |
|
|
|
setJoinInProgress(true); |
|
|
|
const configObject: JoinRoomConfig = { |
|
roles: (Object.keys(roles) as Array<Roles>).filter( |
|
(role) => roles[role] === true |
|
), |
|
lockServerName: lockServerValidated, |
|
}; |
|
|
|
socket.emit( |
|
"join_room", |
|
clientID, |
|
createNewRoom ? null : roomID, |
|
configObject, |
|
(result) => { |
|
console.log("join_room result:", result); |
|
if (createNewRoom) { |
|
setRoomID(result.roomID); |
|
} |
|
if (onJoinRoomOrUpdateRoles != null) { |
|
onJoinRoomOrUpdateRoles(); |
|
} |
|
setURLParam("roomID", result.roomID); |
|
setJoinInProgress(false); |
|
} |
|
); |
|
}, |
|
[ |
|
clientID, |
|
lockServer, |
|
lockServerName, |
|
onJoinRoomOrUpdateRoles, |
|
roles, |
|
roomID, |
|
socket, |
|
] |
|
); |
|
|
|
useEffect(() => { |
|
if ( |
|
autoJoinRoom === true && |
|
didAttemptAutoJoin === false && |
|
socket != null |
|
) { |
|
|
|
|
|
setDidAttemptAutoJoin(true); |
|
if ( |
|
isValidAllInputs && |
|
joinInProgress === false && |
|
roomIDFromServer == null |
|
) { |
|
console.debug("Attempting to auto-join room..."); |
|
|
|
onJoinRoom(false); |
|
} else { |
|
console.debug("Unable to auto-join room", { |
|
isValidAllInputs, |
|
joinInProgress, |
|
roomIDFromServer, |
|
}); |
|
} |
|
} |
|
}, [ |
|
autoJoinRoom, |
|
didAttemptAutoJoin, |
|
isValidAllInputs, |
|
joinInProgress, |
|
onJoinRoom, |
|
roomIDFromServer, |
|
socket, |
|
]); |
|
|
|
return ( |
|
<Stack direction="column" spacing="12px" sx={{ width: "80%" }}> |
|
<Stack spacing={2}> |
|
<Paper |
|
elevation={3} |
|
style={{ |
|
padding: "1em", |
|
borderRadius: "15px", |
|
border: "1px solid #ccc", |
|
}} |
|
> |
|
<Stack |
|
direction="column" |
|
spacing={1} |
|
sx={{ alignItems: "center", width: "100%" }} |
|
> |
|
<Typography fontWeight={600}>Join an Existing Room</Typography> |
|
<Typography variant="body2"> |
|
Join an existing room by entering the room code provided by the |
|
host. |
|
</Typography> |
|
<TextField |
|
size="small" |
|
label="Room Code" |
|
placeholder="Enter Room Code" |
|
variant="outlined" |
|
disabled={roomState?.room_id != null} |
|
value={roomID} |
|
error={roomIDError} |
|
onChange={(e) => { |
|
const id = e.target.value.toUpperCase(); |
|
if (isValidPartialRoomID(id)) { |
|
setRoomIDError(false); |
|
setRoomID(id); |
|
} else { |
|
setRoomIDError(true); |
|
} |
|
}} |
|
sx={{ width: "100%" }} |
|
/> |
|
<div> |
|
<Button |
|
sx={{borderRadius: "20px"}} |
|
variant="contained" |
|
disabled={ |
|
isValidAllInputs === false || |
|
joinInProgress || |
|
streamingStatus !== "stopped" |
|
} |
|
onClick={() => onJoinRoom(false)} |
|
> |
|
{roomState?.room_id != null ? "Update Roles" : "Join Room"} |
|
</Button> |
|
</div> |
|
</Stack> |
|
</Paper> |
|
|
|
<Paper |
|
elevation={3} |
|
style={{ |
|
padding: "1em", |
|
borderRadius: "15px", |
|
border: "1px solid #ccc", |
|
}} |
|
> |
|
<Stack |
|
direction="column" |
|
spacing={1} |
|
sx={{ alignItems: "center", width: "100%" }} |
|
> |
|
<Typography fontWeight={600}>Create a New Room</Typography> |
|
<Typography variant="body2"> |
|
Start a new room and invite others to join you in collaborative |
|
sessions or discussions. |
|
</Typography> |
|
|
|
{roomState?.room_id == null && ( |
|
<div> |
|
<Button |
|
sx={{borderRadius: "20px"}} |
|
variant="contained" |
|
disabled={ |
|
roomState?.room_id != null || |
|
joinInProgress || |
|
streamingStatus !== "stopped" || !roles["speaker"] |
|
} |
|
onClick={() => onJoinRoom(true)} |
|
> |
|
{"Create New Room"} |
|
</Button> |
|
</div> |
|
)} |
|
</Stack> |
|
</Paper> |
|
</Stack> |
|
|
|
<FormGroup> |
|
<Stack direction="row" justifyContent="space-between"> |
|
{Object.keys(roles).map((role) => { |
|
return ( |
|
<FormControlLabel |
|
disabled={streamingStatus !== "stopped"} |
|
key={role} |
|
control={ |
|
<Checkbox |
|
checked={roles[role]} |
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { |
|
setRoles((prevRoles) => ({ |
|
...prevRoles, |
|
[role]: event.target.checked, |
|
})); |
|
}} |
|
/> |
|
} |
|
label={capitalize(role)} |
|
/> |
|
); |
|
})} |
|
</Stack> |
|
|
|
{urlParams.enableServerLock && roles["speaker"] === true && ( |
|
<> |
|
<FormControlLabel |
|
disabled={streamingStatus !== "stopped"} |
|
control={ |
|
<Checkbox |
|
checked={lockServer} |
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { |
|
setLockServer(event.target.checked); |
|
}} |
|
/> |
|
} |
|
label="Lock Server (prevent other users from streaming)" |
|
/> |
|
</> |
|
)} |
|
</FormGroup> |
|
|
|
{urlParams.enableServerLock && |
|
roles["speaker"] === true && |
|
lockServer && ( |
|
<TextField |
|
disabled={streamingStatus !== "stopped"} |
|
label="Enter Your Name + Expected Lock End Time" |
|
variant="outlined" |
|
value={lockServerName} |
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { |
|
setLockServerName(event.target.value); |
|
}} |
|
helperText="Locking the server will prevent anyone else from using it until you close the page, in order to maximize server performance. Please only use this for live demos." |
|
/> |
|
)} |
|
|
|
{serverState?.serverLock != null && |
|
serverState.serverLock.clientID === clientID && ( |
|
<Alert severity="success">{`The server is now locked for your use (${serverState?.serverLock?.name}). Close this window to release the lock so that others may use the server.`}</Alert> |
|
)} |
|
</Stack> |
|
); |
|
} |
|
|