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 { PeerNameDisplay } from 'components/PeerNameDisplay'
import { VideoStreamType } from 'models/chat'
import { SelectedPeerStream } from './RoomVideoDisplay'
interface PeerVideoProps {
isSelfVideo?: boolean
numberOfVideos: number
onVideoClick?: (
userId: string,
videoStreamType: VideoStreamType,
videoStream: MediaStream
) => void
selectedPeerStream: SelectedPeerStream | null
userId: string
videoStream: MediaStream
videoStreamType: VideoStreamType
}
// 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 = ({
isSelfVideo,
numberOfVideos,
onVideoClick,
userId,
selectedPeerStream,
videoStream,
videoStreamType,
}: PeerVideoProps) => {
const videoRef = useRef<HTMLVideoElement>(null)
@ -35,6 +48,10 @@ export const PeerVideo = ({
const sizePercent = 100 / Math.sqrt(nextPerfectSquare(numberOfVideos - 1))
const handleVideoClick = () => {
onVideoClick?.(userId, videoStreamType, videoStream)
}
return (
<Paper
sx={{
@ -42,19 +59,27 @@ export const PeerVideo = ({
flexDirection: 'column',
flexShrink: 1,
justifyContent: 'center',
margin: '0.5em',
margin: '0 0.5em 0.5em 0.5em',
overflow: 'auto',
py: 2,
width: `calc(${sizePercent}% - 1em)`,
height: `calc(${sizePercent}% - 1em)`,
...(selectedPeerStream
? {
height: 'calc(100% - 0.5em)',
}
: {
width: `calc(${sizePercent}% - 1em)`,
height: `calc(${sizePercent}% - 1em)`,
}),
}}
elevation={10}
>
<video
playsInline
ref={videoRef}
onClick={handleVideoClick}
style={{
borderRadius: '.25em',
cursor: 'pointer',
overflow: 'auto',
marginLeft: '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 { RoomContext } from 'contexts/RoomContext'
import { ShellContext } from 'contexts/ShellContext'
import { Peer } from 'models/chat'
import { Peer, VideoStreamType } from 'models/chat'
import { PeerVideo } from './PeerVideo'
type PeerWithVideo = {
interface PeerWithVideo {
peer: Peer
videoStream?: MediaStream
screenStream?: MediaStream
}
export interface SelectedPeerStream {
peerId: string
videoStreamType: VideoStreamType
videoStream: MediaStream
}
export interface RoomVideoDisplayProps {
userId: string
}
@ -20,6 +27,8 @@ export interface RoomVideoDisplayProps {
export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
const shellContext = useContext(ShellContext)
const roomContext = useContext(RoomContext)
const [selectedPeerStream, setSelectedPeerStream] =
useState<SelectedPeerStream | null>(null)
const { peerList } = shellContext
const {
@ -29,6 +38,31 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
selfScreenStream,
} = 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(
(acc: PeerWithVideo[], peer: Peer) => {
const videoStream = peerVideoStreams[peer.peerId]
@ -57,6 +91,18 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
return sum
}, 0)
const handleVideoClick = (
peerId: string,
videoStreamType: VideoStreamType,
videoStream: MediaStream
) => {
if (selectedPeerStream?.videoStream === videoStream) {
setSelectedPeerStream(null)
} else {
setSelectedPeerStream({ peerId, videoStreamType, videoStream })
}
}
return (
<Paper
className="RoomVideoDisplay"
@ -66,47 +112,85 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => {
alignContent: 'center',
alignItems: 'center',
display: 'flex',
flexDirection: numberOfVideos === 1 ? 'column' : 'row',
flexGrow: 1,
flexWrap: 'wrap',
justifyContent: 'center',
flexDirection: 'column',
overflow: 'auto',
width: '75%',
width: '85%',
}}
>
{selfVideoStream && (
<PeerVideo
isSelfVideo
numberOfVideos={numberOfVideos}
userId={userId}
videoStream={selfVideoStream}
/>
{selectedPeerStream && (
<Box sx={{ height: 'calc(85% - 1em)', mb: 'auto' }}>
<PeerVideo
numberOfVideos={numberOfVideos}
onVideoClick={handleVideoClick}
userId={userId}
selectedPeerStream={selectedPeerStream}
videoStream={selectedPeerStream.videoStream}
videoStreamType={selectedPeerStream.videoStreamType}
/>
</Box>
)}
{selfScreenStream && (
<PeerVideo
numberOfVideos={numberOfVideos}
userId={userId}
videoStream={selfScreenStream}
/>
)}
{peersWithVideo.map(peerWithVideo => (
<Fragment key={peerWithVideo.peer.peerId}>
{peerWithVideo.videoStream && (
<PeerVideo
numberOfVideos={numberOfVideos}
userId={peerWithVideo.peer.userId}
videoStream={peerWithVideo.videoStream}
/>
)}
{peerWithVideo.screenStream && (
<PeerVideo
numberOfVideos={numberOfVideos}
userId={peerWithVideo.peer.userId}
videoStream={peerWithVideo.screenStream}
/>
)}
</Fragment>
))}
<Box
sx={{
alignContent: 'center',
alignItems: 'center',
display: 'flex',
flexDirection: 'row',
flexGrow: 1,
flexWrap: selectedPeerStream ? 'nowrap' : 'wrap',
overflow: 'auto',
width: '100%',
...(selectedPeerStream && {
height: '15%',
maxHeight: '15%',
}),
}}
>
{selfVideoStream && (
<PeerVideo
isSelfVideo
numberOfVideos={numberOfVideos}
onVideoClick={handleVideoClick}
userId={userId}
selectedPeerStream={selectedPeerStream}
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>
)
}