import { Label, useTheme, IconButton, TooltipHost, Icon, Persona, PersonaSize, mergeStyleSets, mergeStyles, PersonaInitialsColor, DefaultButton, } from '@fluentui/react' import { useCallback, useEffect, useRef, useState } from 'react' import type { FC, VideoHTMLAttributes } from 'react' import { fadeIn } from '../utils/theme/common-styles' import { useIsSpeaking } from '../hooks/use-is-speaking' import { MAX_MEDIA_HEIGHT, MAX_MEDIA_WIDTH, Stream } from '../state' import { ConnectionMenu } from './connection-menu' import { useIsPaused } from '../hooks/use-is-paused' const classes = mergeStyleSets({ video: { display: 'block', width: '100%', backgroundColor: 'transparent', }, videoContainer: { position: 'relative', width: '100%', height: '100%', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', }, placeholder: { height: '100%', width: '100%', }, fadeIn: { animation: `${fadeIn} .5s ease-in`, }, container: { width: '100%', height: '100%', display: 'flex', position: 'relative', maxWidth: MAX_MEDIA_WIDTH, maxHeight: MAX_MEDIA_HEIGHT, }, label: { padding: '.25em .5em', }, bottomRow: { position: 'absolute', bottom: 0, left: 0, display: 'flex', flexDirection: 'row', width: '100%', justifyContent: 'space-between', alignItems: 'flex-end', }, centerOverlay: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', }, playButton: { height: 30, borderRadius: 1000, }, }) export interface VideoBoxProps extends VideoHTMLAttributes { stream: Stream label?: string flip?: boolean personaText?: string noContextualMenu?: boolean info?: string } const VideoBox: FC = ({ stream, label, personaText, noContextualMenu, info, flip = true, ...props }) => { const theme = useTheme() const videoElem = useRef(null) const containerRef = useRef(null) const iconRef = useRef(null) const [showPersona, setShowPersona] = useState(false) const updateVideo = useCallback(() => { const video = videoElem.current if (!video) { return } if (stream.empty) { video.srcObject = null } else { video.srcObject = stream } if (stream.noVideo) { setShowPersona(true) } else { setShowPersona(false) } video.classList.add(classes.fadeIn) setTimeout(() => { video.classList.remove(classes.fadeIn) }, 500) if (!video.oncanplay) { video.oncanplay = () => video.play() } }, [stream]) useEffect(() => { updateVideo() }, [updateVideo]) useEffect(() => { const onAddRemoveTrack = () => { updateVideo() } stream.addEventListener('addtrack', onAddRemoveTrack) stream.addEventListener('removetrack', onAddRemoveTrack) return () => { stream.removeEventListener('addtrack', onAddRemoveTrack) stream.removeEventListener('removetrack', onAddRemoveTrack) } }, [stream, updateVideo]) const { isSpeaking } = useIsSpeaking(stream) const borderColor = isSpeaking ? theme.semanticColors.errorText : theme.palette.neutralLighter const { isPaused } = useIsPaused(videoElem.current) return (
{isPaused && (
videoElem.current?.play()} className={classes.playButton} > Play
)} {showPersona && (
)}
{label && ( <> {info && ( )} )} {!noContextualMenu && ( )}
{!noContextualMenu && ( )}
) } export default VideoBox