diff --git a/src/components/Room/Room.tsx b/src/components/Room/Room.tsx index fff4871..94ae867 100644 --- a/src/components/Room/Room.tsx +++ b/src/components/Room/Room.tsx @@ -1,21 +1,13 @@ -import { useContext, useEffect, useState } from 'react' -import { v4 as uuid } from 'uuid' import Box from '@mui/material/Box' import Divider from '@mui/material/Divider' +import { v4 as uuid } from 'uuid' import { rtcConfig } from 'config/rtcConfig' import { trackerUrls } from 'config/trackerUrls' -import { ShellContext } from 'contexts/ShellContext' -import { SettingsContext } from 'contexts/SettingsContext' -import { usePeerRoom, usePeerRoomAction } from 'hooks/usePeerRoom' -import { PeerActions } from 'models/network' -import { Peer, ReceivedMessage, UnsentMessage } from 'models/chat' import { MessageForm } from 'components/MessageForm' import { ChatTranscript } from 'components/ChatTranscript' -import { funAnimalName } from 'fun-animal-names' -import { getPeerName } from 'components/PeerNameDisplay' -import { NotificationService } from 'services/Notification' -import { Audio } from 'services/Audio' + +import { useRoom } from './useRoom' export interface RoomProps { appId?: string @@ -30,156 +22,21 @@ export function Room({ roomId, userId, }: RoomProps) { - const [numberOfPeers, setNumberOfPeers] = useState(1) // Includes this peer - const shellContext = useContext(ShellContext) - const settingsContext = useContext(SettingsContext) - const [isMessageSending, setIsMessageSending] = useState(false) - const [messageLog, setMessageLog] = useState< - Array - >([]) - const [newMessageAudio] = useState( - () => new Audio(process.env.PUBLIC_URL + '/sounds/new-message.aac') - ) - - const peerRoom = usePeerRoom( + const { messageLog, sendMessage, isMessageSending } = useRoom( { appId, trackerUrls, rtcConfig, }, - roomId + { + roomId, + userId, + getUuid, + } ) - const [sendPeerId, receivePeerId] = usePeerRoomAction( - peerRoom, - PeerActions.PEER_NAME - ) - - useEffect(() => { - shellContext.setDoShowPeers(true) - - peerRoom.onPeerJoin((peerId: string) => { - shellContext.showAlert(`Someone has joined the room`, { - severity: 'success', - }) - - const newNumberOfPeers = numberOfPeers + 1 - setNumberOfPeers(newNumberOfPeers) - shellContext.setNumberOfPeers(newNumberOfPeers) - ;(async () => { - try { - await sendPeerId(userId, peerId) - } catch (e) { - console.error(e) - } - })() - }) - - peerRoom.onPeerLeave((peerId: string) => { - const peerIndex = shellContext.peerList.findIndex( - peer => peer.peerId === peerId - ) - const peerExist = peerIndex !== -1 - shellContext.showAlert( - `${ - peerExist - ? funAnimalName(shellContext.peerList[peerIndex].userId) - : 'Someone' - } has left the room`, - { - severity: 'warning', - } - ) - - const newNumberOfPeers = numberOfPeers - 1 - setNumberOfPeers(newNumberOfPeers) - shellContext.setNumberOfPeers(newNumberOfPeers) - - if (peerExist) { - const peerListClone = [...shellContext.peerList] - peerListClone.splice(peerIndex, 1) - shellContext.setPeerList(peerListClone) - } - }) - - return () => { - shellContext.setDoShowPeers(false) - } - }, [ - numberOfPeers, - shellContext.peerList, - peerRoom, - sendPeerId, - shellContext, - userId, - ]) - - const [sendMessage, receiveMessage] = usePeerRoomAction( - peerRoom, - PeerActions.MESSAGE - ) - - const performMessageSend = async (message: string) => { - if (isMessageSending) return - - const unsentMessage: UnsentMessage = { - authorId: userId, - text: message, - timeSent: Date.now(), - id: getUuid(), - } - - setIsMessageSending(true) - setMessageLog([...messageLog, unsentMessage]) - await sendMessage(unsentMessage) - - setMessageLog([ - ...messageLog, - { ...unsentMessage, timeReceived: Date.now() }, - ]) - setIsMessageSending(false) - } - - const upsertToPeerList = (peerToAdd: Peer) => { - const peerIndex = shellContext.peerList.findIndex( - peer => peer.peerId === peerToAdd.peerId - ) - if (peerIndex === -1) { - shellContext.setPeerList([ - ...shellContext.peerList, - { peerId: peerToAdd.peerId, userId: peerToAdd.userId }, - ]) - } else { - const peerListClone = [...shellContext.peerList] - peerListClone[peerIndex].userId = peerToAdd.userId - shellContext.setPeerList(peerListClone) - } - } - - receivePeerId((userId: string, peerId: string) => { - upsertToPeerList({ peerId, userId }) - }) - - receiveMessage(message => { - const userSettings = settingsContext.getUserSettings() - - if (!shellContext.tabHasFocus) { - if (userSettings.playSoundOnNewMessage) { - newMessageAudio.play() - } - - if (userSettings.showNotificationOnNewMessage) { - NotificationService.showNotification( - `${getPeerName(message.authorId)}: ${message.text}` - ) - } - } - - setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }]) - }) - const handleMessageSubmit = async (message: string) => { - await performMessageSend(message) + await sendMessage(message) } return ( diff --git a/src/hooks/usePeerRoom/usePeerRoomAction.ts b/src/components/Room/usePeerRoomAction.ts similarity index 100% rename from src/hooks/usePeerRoom/usePeerRoomAction.ts rename to src/components/Room/usePeerRoomAction.ts diff --git a/src/components/Room/useRoom.ts b/src/components/Room/useRoom.ts new file mode 100644 index 0000000..ab6a0a5 --- /dev/null +++ b/src/components/Room/useRoom.ts @@ -0,0 +1,167 @@ +import { useContext, useEffect, useState } from 'react' +import { BaseRoomConfig } from 'trystero' +import { TorrentRoomConfig } from 'trystero/torrent' +import { v4 as uuid } from 'uuid' + +import { ShellContext } from 'contexts/ShellContext' +import { SettingsContext } from 'contexts/SettingsContext' +import { PeerActions } from 'models/network' +import { ReceivedMessage, UnsentMessage } from 'models/chat' +import { funAnimalName } from 'fun-animal-names' +import { getPeerName } from 'components/PeerNameDisplay' +import { NotificationService } from 'services/Notification' +import { Audio } from 'services/Audio' +import { PeerRoom } from 'services/PeerRoom' + +import { usePeerRoomAction } from './usePeerRoomAction' + +interface UseRoomConfig { + roomId: string + userId: string + getUuid?: typeof uuid +} + +export function useRoom( + roomConfig: BaseRoomConfig & TorrentRoomConfig, + { roomId, userId, getUuid = uuid }: UseRoomConfig +) { + const [peerRoom] = useState(() => new PeerRoom(roomConfig, roomId)) + const [numberOfPeers, setNumberOfPeers] = useState(1) // Includes this peer + const shellContext = useContext(ShellContext) + const settingsContext = useContext(SettingsContext) + const [isMessageSending, setIsMessageSending] = useState(false) + const [messageLog, setMessageLog] = useState< + Array + >([]) + const [newMessageAudio] = useState( + () => new Audio(process.env.PUBLIC_URL + '/sounds/new-message.aac') + ) + + useEffect(() => { + return () => { + peerRoom.leaveRoom() + } + }, [peerRoom]) + + useEffect(() => { + shellContext.setDoShowPeers(true) + + return () => { + shellContext.setDoShowPeers(false) + } + }, [shellContext]) + + const [sendPeerId, receivePeerId] = usePeerRoomAction( + peerRoom, + PeerActions.PEER_NAME + ) + + const [sendPeerMessage, receivePeerMessage] = + usePeerRoomAction(peerRoom, PeerActions.MESSAGE) + + const sendMessage = async (message: string) => { + if (isMessageSending) return + + const unsentMessage: UnsentMessage = { + authorId: userId, + text: message, + timeSent: Date.now(), + id: getUuid(), + } + + setIsMessageSending(true) + setMessageLog([...messageLog, unsentMessage]) + await sendPeerMessage(unsentMessage) + + setMessageLog([ + ...messageLog, + { ...unsentMessage, timeReceived: Date.now() }, + ]) + setIsMessageSending(false) + } + + receivePeerId((userId: string, peerId: string) => { + const peerIndex = shellContext.peerList.findIndex( + peer => peer.peerId === peerId + ) + if (peerIndex === -1) { + shellContext.setPeerList([ + ...shellContext.peerList, + { peerId: peerId, userId: userId }, + ]) + } else { + const peerListClone = [...shellContext.peerList] + peerListClone[peerIndex].userId = userId + shellContext.setPeerList(peerListClone) + } + }) + + receivePeerMessage(message => { + const userSettings = settingsContext.getUserSettings() + + if (!shellContext.tabHasFocus) { + if (userSettings.playSoundOnNewMessage) { + newMessageAudio.play() + } + + if (userSettings.showNotificationOnNewMessage) { + NotificationService.showNotification( + `${getPeerName(message.authorId)}: ${message.text}` + ) + } + } + + setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }]) + }) + + peerRoom.onPeerJoin((peerId: string) => { + shellContext.showAlert(`Someone has joined the room`, { + severity: 'success', + }) + + const newNumberOfPeers = numberOfPeers + 1 + setNumberOfPeers(newNumberOfPeers) + shellContext.setNumberOfPeers(newNumberOfPeers) + ;(async () => { + try { + await sendPeerId(userId, peerId) + } catch (e) { + console.error(e) + } + })() + }) + + peerRoom.onPeerLeave((peerId: string) => { + const peerIndex = shellContext.peerList.findIndex( + peer => peer.peerId === peerId + ) + const peerExist = peerIndex !== -1 + shellContext.showAlert( + `${ + peerExist + ? funAnimalName(shellContext.peerList[peerIndex].userId) + : 'Someone' + } has left the room`, + { + severity: 'warning', + } + ) + + const newNumberOfPeers = numberOfPeers - 1 + setNumberOfPeers(newNumberOfPeers) + shellContext.setNumberOfPeers(newNumberOfPeers) + + if (peerExist) { + const peerListClone = [...shellContext.peerList] + peerListClone.splice(peerIndex, 1) + shellContext.setPeerList(peerListClone) + } + }) + + return { + peerRoom, + messageLog, + sendMessage, + isMessageSending, + } +} diff --git a/src/hooks/usePeerRoom/index.ts b/src/hooks/usePeerRoom/index.ts deleted file mode 100644 index 9241b36..0000000 --- a/src/hooks/usePeerRoom/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './usePeerRoom' -export * from './usePeerRoomAction' diff --git a/src/hooks/usePeerRoom/usePeerRoom.ts b/src/hooks/usePeerRoom/usePeerRoom.ts deleted file mode 100644 index 2793e27..0000000 --- a/src/hooks/usePeerRoom/usePeerRoom.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useEffect, useState } from 'react' -import { BaseRoomConfig } from 'trystero' -import { TorrentRoomConfig } from 'trystero/torrent' - -import { PeerRoom } from 'services/PeerRoom' - -export function usePeerRoom( - roomConfig: BaseRoomConfig & TorrentRoomConfig, - roomId: string -) { - const [peerRoom] = useState(() => new PeerRoom(roomConfig, roomId)) - - useEffect(() => { - return () => { - peerRoom.leaveRoom() - } - }, [peerRoom]) - - return peerRoom -}