From 8c7ca3b8690943c13a3c65a5a1676f20dcc8f966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D0=B8=D0=BC=D0=BE=D0=B2=20=D0=A0=D1=83?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC?= Date: Wed, 11 Mar 2026 17:15:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A42?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/components/CallModal.tsx | 31 ++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/CallModal.tsx b/apps/web/src/components/CallModal.tsx index 660076d..ec06e13 100644 --- a/apps/web/src/components/CallModal.tsx +++ b/apps/web/src/components/CallModal.tsx @@ -114,6 +114,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi const [isVideoOff, setIsVideoOff] = useState(false); const [isScreenSharing, setIsScreenSharing] = useState(false); const [hasRemoteVideo, setHasRemoteVideo] = useState(false); + const hasRemoteVideoRef = useRef(false); const [isFullscreen, setIsFullscreen] = useState(false); const [isMinimized, setIsMinimized] = useState(false); const [duration, setDuration] = useState(0); @@ -211,6 +212,23 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi scheduleClose(); }, [cleanup, scheduleClose]); + // Ensure remote stream is bound to video element when it becomes available + useEffect(() => { + if (hasRemoteVideo && remoteVideoRef.current && remoteStreamRef.current) { + const video = remoteVideoRef.current; + const stream = remoteStreamRef.current; + + if (video.srcObject !== stream) { + console.log('[WebRTC] Effect: Binding stream to video element'); + video.srcObject = stream; + video.muted = false; + video.play().catch(e => { + if (e.name === 'NotAllowedError') setNeedsInteraction(true); + }); + } + } + }, [hasRemoteVideo]); + // Setup common peer connection event handlers const setupPeerHandlers = useCallback((pc: RTCPeerConnection) => { pc.onicecandidate = (e) => { @@ -255,7 +273,11 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi `${t.label} state=${t.readyState} enabled=${t.enabled} muted=${t.muted}` ), '→ hasRemoteVideo:', hasVideo); - setHasRemoteVideo(hasVideo); + if (hasVideo !== hasRemoteVideoRef.current) { + hasRemoteVideoRef.current = hasVideo; + setHasRemoteVideo(hasVideo); + } + if (hasVideo && callType !== 'video') { setCallType('video'); } @@ -1597,7 +1619,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi ref={remoteVideoRef} autoPlay playsInline - className="w-full h-full object-contain" + className={`w-full h-full object-contain transition-opacity duration-300 ${hasRemoteVideo ? 'opacity-100' : 'opacity-0'}`} onContextMenu={(e) => { e.preventDefault(); setShowVolumeSlider(true); }} /> @@ -1616,7 +1638,10 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi {displayName} - Соединение... +
+
+ Ожидание потока... +
)}