feat: Video focusing (#71)

* feat: focus video on click
This commit is contained in:
Jeremy Kahn 2022-11-14 22:05:25 -06:00 committed by GitHub
parent 69da8fed2f
commit f20f32f6a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 152 additions and 43 deletions

View File

@ -2,12 +2,22 @@ import { useEffect, useRef } from 'react'
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import { PeerNameDisplay } from 'components/PeerNameDisplay' import { PeerNameDisplay } from 'components/PeerNameDisplay'
import { VideoStreamType } from 'models/chat'
import { SelectedPeerStream } from './RoomVideoDisplay'
interface PeerVideoProps { interface PeerVideoProps {
isSelfVideo?: boolean isSelfVideo?: boolean
numberOfVideos: number numberOfVideos: number
onVideoClick?: (
userId: string,
videoStreamType: VideoStreamType,
videoStream: MediaStream
) => void
selectedPeerStream: SelectedPeerStream | null
userId: string userId: string
videoStream: MediaStream videoStream: MediaStream
videoStreamType: VideoStreamType
} }
// Adapted from https://www.geeksforgeeks.org/find-the-next-perfect-square-greater-than-a-given-number/ // Adapted from https://www.geeksforgeeks.org/find-the-next-perfect-square-greater-than-a-given-number/
@ -20,8 +30,11 @@ const nextPerfectSquare = (base: number) => {
export const PeerVideo = ({ export const PeerVideo = ({
isSelfVideo, isSelfVideo,
numberOfVideos, numberOfVideos,
onVideoClick,
userId, userId,
selectedPeerStream,
videoStream, videoStream,
videoStreamType,
}: PeerVideoProps) => { }: PeerVideoProps) => {
const videoRef = useRef<HTMLVideoElement>(null) const videoRef = useRef<HTMLVideoElement>(null)
@ -35,6 +48,10 @@ export const PeerVideo = ({
const sizePercent = 100 / Math.sqrt(nextPerfectSquare(numberOfVideos - 1)) const sizePercent = 100 / Math.sqrt(nextPerfectSquare(numberOfVideos - 1))
const handleVideoClick = () => {
onVideoClick?.(userId, videoStreamType, videoStream)
}
return ( return (
<Paper <Paper
sx={{ sx={{
@ -42,19 +59,27 @@ export const PeerVideo = ({
flexDirection: 'column', flexDirection: 'column',
flexShrink: 1, flexShrink: 1,
justifyContent: 'center', justifyContent: 'center',
margin: '0.5em', margin: '0 0.5em 0.5em 0.5em',
overflow: 'auto', overflow: 'auto',
py: 2, py: 2,
width: `calc(${sizePercent}% - 1em)`, ...(selectedPeerStream
height: `calc(${sizePercent}% - 1em)`, ? {
height: 'calc(100% - 0.5em)',
}
: {
width: `calc(${sizePercent}% - 1em)`,
height: `calc(${sizePercent}% - 1em)`,
}),
}} }}
elevation={10} elevation={10}
> >
<video <video
playsInline playsInline
ref={videoRef} ref={videoRef}
onClick={handleVideoClick}
style={{ style={{
borderRadius: '.25em', borderRadius: '.25em',
cursor: 'pointer',
overflow: 'auto', overflow: 'auto',
marginLeft: 'auto', marginLeft: 'auto',
marginRight: 'auto', marginRight: 'auto',

View File

@ -1,18 +1,25 @@
import { Fragment, useContext } from 'react' import { Fragment, useContext, useEffect, useState } from 'react'
import Box from '@mui/material/Box'
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import { RoomContext } from 'contexts/RoomContext' import { RoomContext } from 'contexts/RoomContext'
import { ShellContext } from 'contexts/ShellContext' import { ShellContext } from 'contexts/ShellContext'
import { Peer } from 'models/chat' import { Peer, VideoStreamType } from 'models/chat'
import { PeerVideo } from './PeerVideo' import { PeerVideo } from './PeerVideo'
type PeerWithVideo = { interface PeerWithVideo {
peer: Peer peer: Peer
videoStream?: MediaStream videoStream?: MediaStream
screenStream?: MediaStream screenStream?: MediaStream
} }
export interface SelectedPeerStream {
peerId: string
videoStreamType: VideoStreamType
videoStream: MediaStream
}
export interface RoomVideoDisplayProps { export interface RoomVideoDisplayProps {
userId: string userId: string
} }
@ -20,6 +27,8 @@ 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 roomContext = useContext(RoomContext)
const [selectedPeerStream, setSelectedPeerStream] =
useState<SelectedPeerStream | null>(null)
const { peerList } = shellContext const { peerList } = shellContext
const { const {
@ -29,6 +38,31 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
selfScreenStream, selfScreenStream,
} = roomContext } = roomContext
useEffect(() => {
if (!selectedPeerStream) return
const allMediaStreams = [
...Object.values(peerVideoStreams),
...Object.values(peerScreenStreams),
selfVideoStream,
selfScreenStream,
]
for (const mediaStream of allMediaStreams) {
if (mediaStream?.id === selectedPeerStream.videoStream.id) {
return
}
}
setSelectedPeerStream(null)
}, [
peerScreenStreams,
peerVideoStreams,
selectedPeerStream,
selfScreenStream,
selfVideoStream,
])
const peersWithVideo: PeerWithVideo[] = peerList.reduce( const peersWithVideo: PeerWithVideo[] = peerList.reduce(
(acc: PeerWithVideo[], peer: Peer) => { (acc: PeerWithVideo[], peer: Peer) => {
const videoStream = peerVideoStreams[peer.peerId] const videoStream = peerVideoStreams[peer.peerId]
@ -57,6 +91,18 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
return sum return sum
}, 0) }, 0)
const handleVideoClick = (
peerId: string,
videoStreamType: VideoStreamType,
videoStream: MediaStream
) => {
if (selectedPeerStream?.videoStream === videoStream) {
setSelectedPeerStream(null)
} else {
setSelectedPeerStream({ peerId, videoStreamType, videoStream })
}
}
return ( return (
<Paper <Paper
className="RoomVideoDisplay" className="RoomVideoDisplay"
@ -66,47 +112,85 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
alignContent: 'center', alignContent: 'center',
alignItems: 'center', alignItems: 'center',
display: 'flex', display: 'flex',
flexDirection: numberOfVideos === 1 ? 'column' : 'row', flexDirection: 'column',
flexGrow: 1,
flexWrap: 'wrap',
justifyContent: 'center',
overflow: 'auto', overflow: 'auto',
width: '75%', width: '85%',
}} }}
> >
{selfVideoStream && ( {selectedPeerStream && (
<PeerVideo <Box sx={{ height: 'calc(85% - 1em)', mb: 'auto' }}>
isSelfVideo <PeerVideo
numberOfVideos={numberOfVideos} numberOfVideos={numberOfVideos}
userId={userId} onVideoClick={handleVideoClick}
videoStream={selfVideoStream} userId={userId}
/> selectedPeerStream={selectedPeerStream}
videoStream={selectedPeerStream.videoStream}
videoStreamType={selectedPeerStream.videoStreamType}
/>
</Box>
)} )}
{selfScreenStream && ( <Box
<PeerVideo sx={{
numberOfVideos={numberOfVideos} alignContent: 'center',
userId={userId} alignItems: 'center',
videoStream={selfScreenStream} display: 'flex',
/> flexDirection: 'row',
)} flexGrow: 1,
{peersWithVideo.map(peerWithVideo => ( flexWrap: selectedPeerStream ? 'nowrap' : 'wrap',
<Fragment key={peerWithVideo.peer.peerId}> overflow: 'auto',
{peerWithVideo.videoStream && ( width: '100%',
<PeerVideo ...(selectedPeerStream && {
numberOfVideos={numberOfVideos} height: '15%',
userId={peerWithVideo.peer.userId} maxHeight: '15%',
videoStream={peerWithVideo.videoStream} }),
/> }}
)} >
{peerWithVideo.screenStream && ( {selfVideoStream && (
<PeerVideo <PeerVideo
numberOfVideos={numberOfVideos} isSelfVideo
userId={peerWithVideo.peer.userId} numberOfVideos={numberOfVideos}
videoStream={peerWithVideo.screenStream} onVideoClick={handleVideoClick}
/> userId={userId}
)} selectedPeerStream={selectedPeerStream}
</Fragment> videoStream={selfVideoStream}
))} videoStreamType={VideoStreamType.WEBCAM}
/>
)}
{selfScreenStream && (
<PeerVideo
numberOfVideos={numberOfVideos}
onVideoClick={handleVideoClick}
userId={userId}
selectedPeerStream={selectedPeerStream}
videoStream={selfScreenStream}
videoStreamType={VideoStreamType.SCREEN_SHARE}
/>
)}
{peersWithVideo.map(peerWithVideo => (
<Fragment key={peerWithVideo.peer.peerId}>
{peerWithVideo.videoStream && (
<PeerVideo
numberOfVideos={numberOfVideos}
onVideoClick={handleVideoClick}
userId={peerWithVideo.peer.userId}
selectedPeerStream={selectedPeerStream}
videoStream={peerWithVideo.videoStream}
videoStreamType={VideoStreamType.WEBCAM}
/>
)}
{peerWithVideo.screenStream && (
<PeerVideo
numberOfVideos={numberOfVideos}
onVideoClick={handleVideoClick}
userId={peerWithVideo.peer.userId}
selectedPeerStream={selectedPeerStream}
videoStream={peerWithVideo.screenStream}
videoStreamType={VideoStreamType.SCREEN_SHARE}
/>
)}
</Fragment>
))}
</Box>
</Paper> </Paper>
) )
} }