From c2fc1624a402e27100323b415c66539586a1aa23 Mon Sep 17 00:00:00 2001 From: inmake Date: Fri, 27 Feb 2026 18:34:51 +0500 Subject: [PATCH] Enhance Video and StreamPage components with improved media stream handling. Added audio and video track management, refined microphone and camera status updates, and optimized rendering based on available media streams. Improved user experience with conditional rendering and state management for audio and video devices. --- src/components/Video.tsx | 93 ++++++--- src/components/modals/stream/SetNameModal.tsx | 52 ++--- src/pages/StreamPage.tsx | 183 +++++++++++------- 3 files changed, 216 insertions(+), 112 deletions(-) diff --git a/src/components/Video.tsx b/src/components/Video.tsx index 6eee6ce..9d6bb6d 100644 --- a/src/components/Video.tsx +++ b/src/components/Video.tsx @@ -13,51 +13,94 @@ interface Props { user?: IUser; } +const SPEAKING_HIDE_DELAY_MS = 400; + function Video({ mediaStream, muted, user }: Props) { const remoteVideoRef = useRef(null); + const remoteAudioRef = useRef(null); const isSpeaking = useIsAudioActive({ source: mediaStream }); + const [showSpeakingBorder, setShowSpeakingBorder] = useState(false); + const hideTimeoutRef = useRef | null>(null); const [_muted, setMuted] = useState(muted); const [isLoading, setIsLoading] = useState(true); const [minimized, setMinimized] = useState(user?.isAdmin ? false : true); + const hasVideo = (mediaStream?.getVideoTracks().length ?? 0) > 0; + + useEffect(() => { + if (user && user.micEnabled === false) { + if (hideTimeoutRef.current) { + clearTimeout(hideTimeoutRef.current); + hideTimeoutRef.current = null; + } + setShowSpeakingBorder(false); + return; + } + if (isSpeaking) { + if (hideTimeoutRef.current) { + clearTimeout(hideTimeoutRef.current); + hideTimeoutRef.current = null; + } + setShowSpeakingBorder(true); + } else { + hideTimeoutRef.current = setTimeout(() => { + setShowSpeakingBorder(false); + hideTimeoutRef.current = null; + }, SPEAKING_HIDE_DELAY_MS); + } + return () => { + if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current); + }; + }, [isSpeaking, user?.micEnabled]); + function toggleSound() { - if (!remoteVideoRef.current) return; - // remoteVideoRef.current.muted = !remoteVideoRef.current.muted; setMuted((prev) => !prev); } useEffect(() => { - if (!remoteVideoRef.current) return; + if (!mediaStream) return; - remoteVideoRef.current.srcObject = mediaStream; - remoteVideoRef.current.onloadedmetadata = () => { - remoteVideoRef.current?.play(); - }; - - remoteVideoRef.current.onplay = () => { + if (hasVideo && remoteVideoRef.current) { + remoteVideoRef.current.srcObject = mediaStream; + remoteVideoRef.current.onloadedmetadata = () => { + remoteVideoRef.current?.play(); + }; + remoteVideoRef.current.onplay = () => setIsLoading(false); + } else if (!hasVideo && remoteAudioRef.current) { + remoteAudioRef.current.srcObject = mediaStream; + remoteAudioRef.current.onloadedmetadata = () => { + remoteAudioRef.current?.play(); + }; + remoteAudioRef.current.onplay = () => setIsLoading(false); setIsLoading(false); - }; - - console.log("mediaStream", mediaStream?.getTracks()); - }, [mediaStream]); - - useEffect(() => { - console.log("remoteVideoRef.current", remoteVideoRef); - }, [remoteVideoRef.current]); + } + }, [mediaStream, hasVideo]); return (
- + {hasVideo ? ( + + ) : ( + <> +