2022-09-01 21:28:45 -05:00

200 lines
5.3 KiB
TypeScript

import React, { useContext, useEffect, useState } from 'react'
import { v4 as uuid } from 'uuid'
import Box from '@mui/material/Box'
import FormControl from '@mui/material/FormControl'
import Stack from '@mui/material/Stack'
import TextField from '@mui/material/TextField'
import Fab from '@mui/material/Fab'
import ArrowUpward from '@mui/icons-material/ArrowUpward'
import { ShellContext } from 'ShellContext'
import { usePeerRoom, usePeerRoomAction } from 'hooks/usePeerRoom'
import { PeerActions } from 'models/network'
import { UnsentMessage, ReceivedMessage } from 'models/chat'
import { ChatTranscript } from 'components/ChatTranscript'
export interface RoomProps {
appId?: string
getUuid?: typeof uuid
roomId: string
userId: string
}
export function Room({
appId = `${encodeURI(window.location.origin)}_${process.env.REACT_APP_NAME}`,
getUuid = uuid,
roomId,
userId,
}: RoomProps) {
const [numberOfPeers, setNumberOfPeers] = useState(1) // Includes this peer
const shellContext = useContext(ShellContext)
const [isMessageSending, setIsMessageSending] = useState(false)
const [textMessage, setTextMessage] = useState('')
const [messageLog, setMessageLog] = useState<
Array<ReceivedMessage | UnsentMessage>
>([])
const peerRoom = usePeerRoom(
{
appId,
trackerUrls: process.env.REACT_APP_TRACKER_URL
? [process.env.REACT_APP_TRACKER_URL]
: undefined,
rtcConfig: {
iceServers: [
{
urls: 'stun:openrelay.metered.ca:80',
},
{
urls: 'turn:openrelay.metered.ca:80',
username: 'openrelayproject',
credential: 'openrelayproject',
},
{
urls: 'turn:openrelay.metered.ca:443',
username: 'openrelayproject',
credential: 'openrelayproject',
},
{
urls: 'turn:openrelay.metered.ca:443?transport=tcp',
username: 'openrelayproject',
credential: 'openrelayproject',
},
],
},
},
roomId
)
useEffect(() => {
shellContext.setDoShowPeers(true)
peerRoom.onPeerJoin(() => {
shellContext.showAlert(`Someone has joined the room`, {
severity: 'success',
})
const newNumberOfPeers = numberOfPeers + 1
setNumberOfPeers(newNumberOfPeers)
shellContext.setNumberOfPeers(newNumberOfPeers)
})
peerRoom.onPeerLeave(() => {
shellContext.showAlert(`Someone has left the room`, {
severity: 'warning',
})
const newNumberOfPeers = numberOfPeers - 1
setNumberOfPeers(newNumberOfPeers)
shellContext.setNumberOfPeers(newNumberOfPeers)
})
return () => {
shellContext.setDoShowPeers(false)
}
}, [numberOfPeers, peerRoom, shellContext])
const [sendMessage, receiveMessage] = usePeerRoomAction<UnsentMessage>(
peerRoom,
PeerActions.MESSAGE
)
const handleMessageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target
setTextMessage(value)
}
const canMessageBeSent = () => {
return textMessage.trim().length > 0 && !isMessageSending
}
const performMessageSend = async () => {
if (!canMessageBeSent()) return
const unsentMessage: UnsentMessage = {
authorId: userId,
text: textMessage,
timeSent: Date.now(),
id: getUuid(),
}
setTextMessage('')
setIsMessageSending(true)
setMessageLog([...messageLog, unsentMessage])
await sendMessage(unsentMessage)
setMessageLog([
...messageLog,
{ ...unsentMessage, timeReceived: Date.now() },
])
setIsMessageSending(false)
}
const handleMessageKeyPress = async (
event: React.KeyboardEvent<HTMLInputElement>
) => {
const { key, shiftKey } = event
if (key === 'Enter' && shiftKey === false) {
event.preventDefault()
await performMessageSend()
}
}
const handleMessageSubmit = async (
event: React.SyntheticEvent<HTMLFormElement>
) => {
event.preventDefault()
await performMessageSend()
}
receiveMessage(message => {
setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }])
})
return (
<Box
className="Room"
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
}}
>
<ChatTranscript
messageLog={messageLog}
userId={userId}
className="grow overflow-auto"
/>
<form onSubmit={handleMessageSubmit} className="mt-2">
<Stack direction="row" spacing={2}>
<FormControl fullWidth>
<TextField
variant="outlined"
value={textMessage}
onChange={handleMessageChange}
onKeyPress={handleMessageKeyPress}
size="medium"
placeholder="Your message"
multiline
/>
</FormControl>
<Fab
sx={{
flexShrink: 0,
// The !important is needed to override a Stack style
marginTop: 'auto!important',
}}
aria-label="Send"
type="submit"
disabled={!canMessageBeSent()}
color="primary"
>
<ArrowUpward />
</Fab>
</Stack>
</form>
</Box>
)
}