refactor: move video stream data into new room context

This commit is contained in:
Jeremy Kahn 2022-11-06 20:49:48 -06:00
parent 828e3c12b9
commit 19d0faaccc
8 changed files with 105 additions and 87 deletions

View File

@ -8,6 +8,7 @@ import { v4 as uuid } from 'uuid'
import { rtcConfig } from 'config/rtcConfig' import { rtcConfig } from 'config/rtcConfig'
import { trackerUrls } from 'config/trackerUrls' import { trackerUrls } from 'config/trackerUrls'
import { RoomContext } from 'contexts/RoomContext'
import { MessageForm } from 'components/MessageForm' import { MessageForm } from 'components/MessageForm'
import { ChatTranscript } from 'components/ChatTranscript' import { ChatTranscript } from 'components/ChatTranscript'
@ -35,6 +36,7 @@ export function Room({
isMessageSending, isMessageSending,
messageLog, messageLog,
peerRoom, peerRoom,
roomContextValue,
sendMessage, sendMessage,
showVideoDisplay, showVideoDisplay,
} = useRoom( } = useRoom(
@ -56,54 +58,56 @@ export function Room({
} }
return ( return (
<Box <RoomContext.Provider value={roomContextValue}>
className="Room"
sx={{
height: '100%',
display: 'flex',
flexGrow: '1',
overflow: 'auto',
}}
>
{showVideoDisplay && <RoomVideoDisplay userId={userId} />}
<Box <Box
className="Room"
sx={{ sx={{
height: '100%',
display: 'flex', display: 'flex',
flexDirection: 'column',
flexGrow: '1', flexGrow: '1',
overflow: 'auto', overflow: 'auto',
}} }}
> >
<Accordion> {showVideoDisplay && <RoomVideoDisplay userId={userId} />}
<AccordionSummary <Box
expandIcon={<ExpandMoreIcon />} sx={{
aria-controls="panel1a-content" display: 'flex',
id="panel1a-header" flexDirection: 'column',
></AccordionSummary> flexGrow: '1',
<AccordionDetails> overflow: 'auto',
<Box }}
sx={{ >
alignItems: 'flex-start', <Accordion>
display: 'flex', <AccordionSummary
justifyContent: 'center', expandIcon={<ExpandMoreIcon />}
}} aria-controls="panel1a-content"
> id="panel1a-header"
<RoomAudioControls peerRoom={peerRoom} /> ></AccordionSummary>
<RoomVideoControls peerRoom={peerRoom} /> <AccordionDetails>
</Box> <Box
</AccordionDetails> sx={{
</Accordion> alignItems: 'flex-start',
<ChatTranscript display: 'flex',
messageLog={messageLog} justifyContent: 'center',
userId={userId} }}
className="grow overflow-auto px-4" >
/> <RoomAudioControls peerRoom={peerRoom} />
<Divider /> <RoomVideoControls peerRoom={peerRoom} />
<MessageForm </Box>
onMessageSubmit={handleMessageSubmit} </AccordionDetails>
isMessageSending={isMessageSending} </Accordion>
/> <ChatTranscript
messageLog={messageLog}
userId={userId}
className="grow overflow-auto px-4"
/>
<Divider />
<MessageForm
onMessageSubmit={handleMessageSubmit}
isMessageSending={isMessageSending}
/>
</Box>
</Box> </Box>
</Box> </RoomContext.Provider>
) )
} }

View File

@ -1,8 +1,9 @@
import { useContext } from 'react' import { useContext } from 'react'
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import { Peer } from 'models/chat' import { RoomContext } from 'contexts/RoomContext'
import { ShellContext } from 'contexts/ShellContext' import { ShellContext } from 'contexts/ShellContext'
import { Peer } from 'models/chat'
import { PeerVideo } from './PeerVideo' import { PeerVideo } from './PeerVideo'
@ -14,10 +15,14 @@ export interface RoomVideoDisplayProps {
export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => { export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
const shellContext = useContext(ShellContext) const shellContext = useContext(ShellContext)
const roomContext = useContext(RoomContext)
const peersWithVideo: PeerWithVideo[] = shellContext.peerList.reduce( const { peerList } = shellContext
const { peerVideoStreams, selfVideoStream } = roomContext
const peersWithVideo: PeerWithVideo[] = peerList.reduce(
(acc: PeerWithVideo[], peer: Peer) => { (acc: PeerWithVideo[], peer: Peer) => {
const videoStream = shellContext.peerVideoStreams[peer.peerId] const videoStream = peerVideoStreams[peer.peerId]
if (videoStream) { if (videoStream) {
acc.push({ acc.push({
peer, peer,
@ -45,12 +50,8 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
width: '75%', width: '75%',
}} }}
> >
{shellContext.selfVideoStream && ( {selfVideoStream && (
<PeerVideo <PeerVideo isSelf userId={userId} videoStream={selfVideoStream} />
isSelf
userId={userId}
videoStream={shellContext.selfVideoStream}
/>
)} )}
{peersWithVideo.map(peerWithVideo => ( {peersWithVideo.map(peerWithVideo => (
<PeerVideo <PeerVideo

View File

@ -1,7 +1,8 @@
import { useContext, useEffect, useState } from 'react' import { useContext, useEffect, useMemo, useState } from 'react'
import { BaseRoomConfig } from 'trystero' import { BaseRoomConfig } from 'trystero'
import { TorrentRoomConfig } from 'trystero/torrent' import { TorrentRoomConfig } from 'trystero/torrent'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import { funAnimalName } from 'fun-animal-names'
import { ShellContext } from 'contexts/ShellContext' import { ShellContext } from 'contexts/ShellContext'
import { SettingsContext } from 'contexts/SettingsContext' import { SettingsContext } from 'contexts/SettingsContext'
@ -14,7 +15,6 @@ import {
VideoState, VideoState,
isMessageReceived, isMessageReceived,
} from 'models/chat' } from 'models/chat'
import { funAnimalName } from 'fun-animal-names'
import { getPeerName } from 'components/PeerNameDisplay' import { getPeerName } from 'components/PeerNameDisplay'
import { NotificationService } from 'services/Notification' import { NotificationService } from 'services/Notification'
import { Audio as AudioService } from 'services/Audio' import { Audio as AudioService } from 'services/Audio'
@ -55,6 +55,23 @@ export function useRoom(
_setMessageLog(messages.slice(-messageTranscriptSizeLimit)) _setMessageLog(messages.slice(-messageTranscriptSizeLimit))
} }
const [selfVideoStream, setSelfVideoStream] = useState<MediaStream | null>(
null
)
const [peerVideoStreams, setPeerVideoStreams] = useState<
Record<string, MediaStream>
>({})
const roomContextValue = useMemo(
() => ({
selfVideoStream,
setSelfVideoStream,
peerVideoStreams,
setPeerVideoStreams,
}),
[selfVideoStream, setSelfVideoStream, peerVideoStreams, setPeerVideoStreams]
)
useEffect(() => { useEffect(() => {
return () => { return () => {
peerRoom.leaveRoom() peerRoom.leaveRoom()
@ -200,13 +217,13 @@ export function useRoom(
}) })
const showVideoDisplay = const showVideoDisplay =
shellContext.selfVideoStream || selfVideoStream || Object.values(peerVideoStreams).length > 0
Object.values(shellContext.peerVideoStreams).length > 0
return { return {
isMessageSending, isMessageSending,
messageLog, messageLog,
peerRoom, peerRoom,
roomContextValue,
sendMessage, sendMessage,
showVideoDisplay, showVideoDisplay,
} }

View File

@ -24,6 +24,8 @@ export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
string | null string | null
>(null) >(null)
const { peerList, setPeerList, setAudioState } = shellContext
useEffect(() => { useEffect(() => {
;(async () => { ;(async () => {
if (!audioStream) return if (!audioStream) return
@ -40,7 +42,7 @@ export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
) )
receiveAudioChange((audioState, peerId) => { receiveAudioChange((audioState, peerId) => {
const newPeerList = shellContext.peerList.map(peer => { const newPeerList = peerList.map(peer => {
const newPeer: Peer = { ...peer } const newPeer: Peer = { ...peer }
if (peer.peerId === peerId) { if (peer.peerId === peerId) {
@ -54,7 +56,7 @@ export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
return newPeer return newPeer
}) })
shellContext.setPeerList(newPeerList) setPeerList(newPeerList)
}) })
peerRoom.onPeerStream(PeerStreamType.AUDIO, (stream, peerId) => { peerRoom.onPeerStream(PeerStreamType.AUDIO, (stream, peerId) => {
@ -91,7 +93,7 @@ export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
peerRoom.addStream(newSelfStream) peerRoom.addStream(newSelfStream)
sendAudioChange(AudioState.PLAYING) sendAudioChange(AudioState.PLAYING)
shellContext.setAudioState(AudioState.PLAYING) setAudioState(AudioState.PLAYING)
setAudioStream(newSelfStream) setAudioStream(newSelfStream)
} }
} else { } else {
@ -100,20 +102,20 @@ export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
peerRoom.removeStream(audioStream, peerRoom.getPeers()) peerRoom.removeStream(audioStream, peerRoom.getPeers())
sendAudioChange(AudioState.STOPPED) sendAudioChange(AudioState.STOPPED)
shellContext.setAudioState(AudioState.STOPPED) setAudioState(AudioState.STOPPED)
setAudioStream(null) setAudioStream(null)
} }
} }
})() })()
}, [ }, [
audioStream,
cleanupAudio,
isSpeakingToRoom, isSpeakingToRoom,
peerAudios, peerAudios,
peerRoom, peerRoom,
audioStream,
selectedAudioDeviceId, selectedAudioDeviceId,
sendAudioChange, sendAudioChange,
shellContext, setAudioState,
cleanupAudio,
]) ])
useEffect(() => { useEffect(() => {

View File

@ -1,5 +1,6 @@
import { useContext, useEffect, useCallback, useState } from 'react' import { useContext, useEffect, useCallback, useState } from 'react'
import { RoomContext } from 'contexts/RoomContext'
import { ShellContext } from 'contexts/ShellContext' import { ShellContext } from 'contexts/ShellContext'
import { PeerActions } from 'models/network' import { PeerActions } from 'models/network'
import { VideoState, Peer } from 'models/chat' import { VideoState, Peer } from 'models/chat'
@ -13,21 +14,21 @@ interface UseRoomVideoConfig {
export function useRoomVideo({ peerRoom }: UseRoomVideoConfig) { export function useRoomVideo({ peerRoom }: UseRoomVideoConfig) {
const shellContext = useContext(ShellContext) const shellContext = useContext(ShellContext)
const roomContext = useContext(RoomContext)
const [isCameraEnabled, setIsCameraEnabled] = useState(false) const [isCameraEnabled, setIsCameraEnabled] = useState(false)
const [videoDevices, setVideoDevices] = useState<MediaDeviceInfo[]>([]) const [videoDevices, setVideoDevices] = useState<MediaDeviceInfo[]>([])
const [selectedVideoDeviceId, setSelectedVideoDeviceId] = useState< const [selectedVideoDeviceId, setSelectedVideoDeviceId] = useState<
string | null string | null
>(null) >(null)
const { peerList, setPeerList, setVideoState } = shellContext
const { const {
peerList,
peerVideoStreams, peerVideoStreams,
selfVideoStream, selfVideoStream,
setPeerList,
setPeerVideoStreams, setPeerVideoStreams,
setSelfVideoStream, setSelfVideoStream,
setVideoState, } = roomContext
} = shellContext
useEffect(() => { useEffect(() => {
;(async () => { ;(async () => {

View File

@ -46,12 +46,6 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
const [tabHasFocus, setTabHasFocus] = useState(true) const [tabHasFocus, setTabHasFocus] = useState(true)
const [audioState, setAudioState] = useState<AudioState>(AudioState.STOPPED) const [audioState, setAudioState] = useState<AudioState>(AudioState.STOPPED)
const [videoState, setVideoState] = useState<VideoState>(VideoState.STOPPED) const [videoState, setVideoState] = useState<VideoState>(VideoState.STOPPED)
const [selfVideoStream, setSelfVideoStream] = useState<MediaStream | null>(
null
)
const [peerVideoStreams, setPeerVideoStreams] = useState<
Record<string, MediaStream>
>({})
const showAlert = useCallback< const showAlert = useCallback<
(message: string, options?: AlertOptions) => void (message: string, options?: AlertOptions) => void
@ -78,10 +72,6 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
setAudioState, setAudioState,
videoState, videoState,
setVideoState, setVideoState,
selfVideoStream,
setSelfVideoStream,
peerVideoStreams,
setPeerVideoStreams,
}), }),
[ [
isPeerListOpen, isPeerListOpen,
@ -97,10 +87,6 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
setAudioState, setAudioState,
videoState, videoState,
setVideoState, setVideoState,
selfVideoStream,
setSelfVideoStream,
peerVideoStreams,
setPeerVideoStreams,
] ]
) )

View File

@ -0,0 +1,15 @@
import { createContext, Dispatch, SetStateAction } from 'react'
interface RoomContextProps {
selfVideoStream: MediaStream | null
setSelfVideoStream: Dispatch<SetStateAction<MediaStream | null>>
peerVideoStreams: Record<string, MediaStream>
setPeerVideoStreams: Dispatch<SetStateAction<Record<string, MediaStream>>>
}
export const RoomContext = createContext<RoomContextProps>({
selfVideoStream: null,
setSelfVideoStream: () => {},
peerVideoStreams: {},
setPeerVideoStreams: () => {},
})

View File

@ -18,10 +18,6 @@ interface ShellContextProps {
setAudioState: Dispatch<SetStateAction<AudioState>> setAudioState: Dispatch<SetStateAction<AudioState>>
videoState: VideoState videoState: VideoState
setVideoState: Dispatch<SetStateAction<VideoState>> setVideoState: Dispatch<SetStateAction<VideoState>>
selfVideoStream: MediaStream | null
setSelfVideoStream: Dispatch<SetStateAction<MediaStream | null>>
peerVideoStreams: Record<string, MediaStream>
setPeerVideoStreams: Dispatch<SetStateAction<Record<string, MediaStream>>>
} }
export const ShellContext = createContext<ShellContextProps>({ export const ShellContext = createContext<ShellContextProps>({
@ -39,8 +35,4 @@ export const ShellContext = createContext<ShellContextProps>({
setAudioState: () => {}, setAudioState: () => {},
videoState: VideoState.STOPPED, videoState: VideoState.STOPPED,
setVideoState: () => {}, setVideoState: () => {},
selfVideoStream: null,
setSelfVideoStream: () => {},
peerVideoStreams: {},
setPeerVideoStreams: () => {},
}) })