forked from Shiloh/remnantchat
parent
69da8fed2f
commit
f20f32f6a6
@ -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',
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user