200 lines
5.3 KiB
TypeScript
Raw Normal View History

2022-08-29 22:05:56 -05:00
import React, { useContext, useEffect, useState } from 'react'
2022-08-19 09:42:14 -05:00
import { v4 as uuid } from 'uuid'
2022-08-27 21:48:22 -05:00
import Box from '@mui/material/Box'
2022-08-17 09:28:22 -05:00
import FormControl from '@mui/material/FormControl'
2022-08-27 19:06:54 -05:00
import Stack from '@mui/material/Stack'
2022-08-17 09:28:22 -05:00
import TextField from '@mui/material/TextField'
2022-08-27 19:06:54 -05:00
import Fab from '@mui/material/Fab'
import ArrowUpward from '@mui/icons-material/ArrowUpward'
2022-08-09 22:14:59 -05:00
2022-08-29 22:05:56 -05:00
import { ShellContext } from 'ShellContext'
2022-08-18 09:14:13 -05:00
import { usePeerRoom, usePeerRoomAction } from 'hooks/usePeerRoom'
import { PeerActions } from 'models/network'
import { UnsentMessage, ReceivedMessage } from 'models/chat'
2022-08-20 22:23:43 -05:00
import { ChatTranscript } from 'components/ChatTranscript'
2022-08-13 12:11:59 -05:00
2022-08-18 21:10:16 -05:00
export interface RoomProps {
appId?: string
2022-08-19 09:42:14 -05:00
getUuid?: typeof uuid
2022-08-23 21:15:58 -05:00
roomId: string
2022-08-20 14:20:51 -05:00
userId: string
2022-08-18 21:10:16 -05:00
}
export function Room({
appId = `${encodeURI(window.location.origin)}_${process.env.REACT_APP_NAME}`,
2022-08-19 09:42:14 -05:00
getUuid = uuid,
2022-08-23 21:15:58 -05:00
roomId,
userId,
2022-08-18 21:10:16 -05:00
}: RoomProps) {
const [numberOfPeers, setNumberOfPeers] = useState(1) // Includes this peer
2022-08-29 22:05:56 -05:00
const shellContext = useContext(ShellContext)
const [isMessageSending, setIsMessageSending] = useState(false)
2022-08-18 09:14:13 -05:00
const [textMessage, setTextMessage] = useState('')
2022-08-20 22:23:43 -05:00
const [messageLog, setMessageLog] = useState<
Array<ReceivedMessage | UnsentMessage>
>([])
2022-08-17 09:28:22 -05:00
const peerRoom = usePeerRoom(
{
appId,
trackerUrls: process.env.REACT_APP_TRACKER_URL
? [process.env.REACT_APP_TRACKER_URL]
: undefined,
2022-08-30 09:38:14 -05:00
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
)
2022-08-13 12:11:59 -05:00
2022-08-29 22:05:56 -05:00
useEffect(() => {
2022-09-01 21:28:45 -05:00
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)
2022-08-29 22:05:56 -05:00
})
2022-09-01 21:28:45 -05:00
return () => {
shellContext.setDoShowPeers(false)
}
}, [numberOfPeers, peerRoom, shellContext])
2022-08-29 22:05:56 -05:00
2022-08-18 09:14:13 -05:00
const [sendMessage, receiveMessage] = usePeerRoomAction<UnsentMessage>(
2022-08-16 09:40:01 -05:00
peerRoom,
PeerActions.MESSAGE
2022-08-14 21:26:50 -05:00
)
2022-08-17 09:28:22 -05:00
const handleMessageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target
2022-08-18 09:14:13 -05:00
setTextMessage(value)
2022-08-17 09:28:22 -05:00
}
const canMessageBeSent = () => {
return textMessage.trim().length > 0 && !isMessageSending
}
const performMessageSend = async () => {
if (!canMessageBeSent()) return
2022-08-20 22:23:43 -05:00
const unsentMessage: UnsentMessage = {
2022-08-20 14:20:51 -05:00
authorId: userId,
text: textMessage,
timeSent: Date.now(),
id: getUuid(),
2022-08-20 22:23:43 -05:00
}
2022-08-18 09:14:13 -05:00
setTextMessage('')
setIsMessageSending(true)
2022-08-20 22:23:43 -05:00
setMessageLog([...messageLog, unsentMessage])
await sendMessage(unsentMessage)
setMessageLog([
...messageLog,
{ ...unsentMessage, timeReceived: Date.now() },
])
setIsMessageSending(false)
2022-08-17 09:28:22 -05:00
}
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()
}
2022-08-14 21:26:50 -05:00
receiveMessage(message => {
2022-08-20 22:23:43 -05:00
setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }])
2022-08-14 21:26:50 -05:00
})
return (
2022-08-31 19:26:38 -05:00
<Box
className="Room"
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
}}
>
2022-08-26 09:39:01 -05:00
<ChatTranscript
messageLog={messageLog}
userId={userId}
className="grow overflow-auto"
/>
2022-08-31 19:26:38 -05:00
<form onSubmit={handleMessageSubmit} className="mt-2">
2022-08-27 19:06:54 -05:00
<Stack direction="row" spacing={2}>
<FormControl fullWidth>
<TextField
variant="outlined"
value={textMessage}
onChange={handleMessageChange}
onKeyPress={handleMessageKeyPress}
2022-08-27 19:06:54 -05:00
size="medium"
placeholder="Your message"
multiline
2022-08-27 19:06:54 -05:00
/>
</FormControl>
<Fab
sx={{
flexShrink: 0,
// The !important is needed to override a Stack style
marginTop: 'auto!important',
}}
2022-08-27 19:06:54 -05:00
aria-label="Send"
type="submit"
disabled={!canMessageBeSent()}
2022-08-27 19:06:54 -05:00
color="primary"
>
<ArrowUpward />
</Fab>
</Stack>
2022-08-17 09:28:22 -05:00
</form>
2022-08-27 21:48:22 -05:00
</Box>
2022-08-14 21:26:50 -05:00
)
2022-08-09 22:14:59 -05:00
}