From 3b82f4c4536883cb30e854d01241dfaf0c014c69 Mon Sep 17 00:00:00 2001 From: subhajit Date: Mon, 3 Aug 2026 13:46:39 +0530 Subject: [PATCH] fix issue --- .../views/interview/candidate_room.blade.php | 118 ++++++++++++------ 1 file changed, 77 insertions(+), 41 deletions(-) diff --git a/resources/views/interview/candidate_room.blade.php b/resources/views/interview/candidate_room.blade.php index 3b552b4..051bd1f 100644 --- a/resources/views/interview/candidate_room.blade.php +++ b/resources/views/interview/candidate_room.blade.php @@ -1069,14 +1069,25 @@ class CandRingtoneEngine { let candidateDeclinedOrLeftCall = false; let pendingOffer = null; let candRingTimer = null; + let latestCallStartedAt = null; + + function getCallSessionKey(timestamp) { + return 'handled_call_' + interviewId + '_' + (timestamp || latestCallStartedAt || 'active'); + } function triggerCandidateRing(offer = null, callStartedAt = null) { if (isCandidateCallConnected) return; if (latestCallStatus === 'ended' || latestCallStatus === 'idle') return; - if (candidateDeclinedOrLeftCall) { + if (callStartedAt) latestCallStartedAt = callStartedAt; + + const sessionKey = getCallSessionKey(callStartedAt); + const isHandled = sessionStorage.getItem(sessionKey) === 'handled'; + + if (isHandled || candidateDeclinedOrLeftCall) { showCandidateJoinOption(); return; } + if (offer) pendingOffer = offer; const badge = document.getElementById('call-status-badge'); @@ -1105,6 +1116,8 @@ function declineCandidateCall() { clearTimeout(candRingTimer); candRingTimer = null; } + const sessionKey = getCallSessionKey(); + sessionStorage.setItem(sessionKey, 'handled'); const modal = document.getElementById('candidate-incoming-modal'); if (modal) { modal.style.opacity = '0'; @@ -1120,6 +1133,8 @@ function stopCandidateRing() { clearTimeout(candRingTimer); candRingTimer = null; } + const sessionKey = getCallSessionKey(); + sessionStorage.setItem(sessionKey, 'handled'); const modal = document.getElementById('candidate-incoming-modal'); if (modal) { modal.style.opacity = '0'; @@ -1154,6 +1169,9 @@ function showCandidateJoinOption() { } async function acceptCandidateCall() { + const sessionKey = getCallSessionKey(); + sessionStorage.setItem(sessionKey, 'handled'); + candidateDeclinedOrLeftCall = false; candRingtone.stop(); if (candRingTimer) { @@ -1201,25 +1219,41 @@ function showCandidateJoinOption() { badge.style.color = '#34d399'; } - // Push candidate camera & microphone tracks to active WebRTC peer connection - if (localMediaStream && activeCallInstance && activeCallInstance.peerConnection) { - localMediaStream.getTracks().forEach(track => { - const senders = activeCallInstance.peerConnection.getSenders(); - const sender = senders.find(s => s.track && s.track.kind === track.kind); - if (sender) { - sender.replaceTrack(track).catch(e => {}); - } else { - try { activeCallInstance.peerConnection.addTrack(track, localMediaStream); } catch(e) {} - } - }); + // Dual 2-Way Connection: Answer incoming call AND establish connection to interviewer peer + const stream = localMediaStream || new MediaStream(); + + if (activeCallInstance) { + try { activeCallInstance.answer(stream); } catch(e) {} } + // Signal candidate ready via WebRTC BroadcastChannel + callSigChannel.postMessage({ type: 'candidate_ready', sender: 'candidate' }); + if (pendingOffer) { await handleInterviewerOffer(pendingOffer); pendingOffer = null; } - // Automatically capture candidate system screenshot on call start (if screenshot feature enabled) + // Call interviewer peer directly if interviewer registered + if (peerInstance) { + const interviewerPeerId = 'interviewer_' + interviewId; + try { + const outCall = peerInstance.call(interviewerPeerId, stream); + if (outCall) { + outCall.on('stream', remoteStream => { + const remoteVideo = document.getElementById('interviewer-video'); + const placeholder = document.getElementById('interviewer-video-placeholder'); + if (remoteVideo) { + remoteVideo.srcObject = remoteStream; + remoteVideo.play().catch(e => console.log('Remote video play error:', e)); + } + if (placeholder) placeholder.style.display = 'none'; + }); + } + } catch(e) {} + } + + // Automatically capture candidate system screenshot on call start (if enabled) if (!hasCapturedCallStartScreenshot && isTabSwitchScreenshotEnabled) { hasCapturedCallStartScreenshot = true; setTimeout(() => { @@ -1777,41 +1811,25 @@ function pollCandidateChat() { return; } - if (data.call_status === 'active') { - latestCallStatus = 'active'; - if (!isCandidateCallConnected) { - if (candidateDeclinedOrLeftCall) { - showCandidateJoinOption(); - } else { - triggerCandidateRing(null, data.call_started_at || null); - } + if (data.warnings && Array.isArray(data.warnings)) { + if (data.warnings.length === 0) { + chatHistory.innerHTML = '
No messages yet. Send a message below.
'; + return; } - } else if (data.call_status === 'ended' || data.call_status === 'idle') { - latestCallStatus = data.call_status; - candidateDeclinedOrLeftCall = false; - candidateLeaveCall(true); - const badge = document.getElementById('call-status-badge'); - if (badge) { - badge.innerText = 'CALL ENDED BY HOST'; - badge.style.background = 'rgba(239,68,68,0.2)'; - badge.style.color = '#fca5a5'; - } - } - if (data.warnings && data.warnings.length > 0) { let html = ''; data.warnings.forEach(w => { const isInterviewer = w.sent_by !== null; const senderName = isInterviewer ? (w.sender ? w.sender.name : 'Interviewer / Panelist') : 'You (Candidate)'; const badgeColor = isInterviewer ? '#6366f1' : '#38bdf8'; - const bgColor = isInterviewer ? 'rgba(99,102,241,0.1)' : 'rgba(56,189,248,0.1)'; + const bgColor = isInterviewer ? 'rgba(99,102,241,0.15)' : 'rgba(56,189,248,0.15)'; - html += `
+ html += `
${senderName} ${new Date(w.created_at || Date.now()).toLocaleTimeString()}
-
${w.message}
+
${w.message}
`; }); chatHistory.innerHTML = html; @@ -1820,7 +1838,7 @@ function pollCandidateChat() { }) .catch(() => {}); } - setInterval(pollCandidateChat, 1000); + setInterval(pollCandidateChat, 600); pollCandidateChat(); function sendCandidateMessage() { @@ -1982,23 +2000,41 @@ function saveCanvasDrawing(force = false) { if (data.call_status === 'active') { latestCallStatus = 'active'; + if (data.call_started_at) latestCallStartedAt = data.call_started_at; + if (!hasCapturedCallStartScreenshot && isTabSwitchScreenshotEnabled) { hasCapturedCallStartScreenshot = true; setTimeout(() => { captureAndUploadTabScreenshot('call_start'); }, 1200); } - if (!isCandidateCallConnected && !candidateDeclinedOrLeftCall) { - triggerCandidateRing(null, data.call_started_at); + + const sessionKey = getCallSessionKey(data.call_started_at); + const isHandledInSession = sessionStorage.getItem(sessionKey) === 'handled'; + + if (!isCandidateCallConnected) { + if (isHandledInSession || candidateDeclinedOrLeftCall) { + showCandidateJoinOption(); + } else { + triggerCandidateRing(null, data.call_started_at); + } } } else if (data.call_status === 'ended' || data.call_status === 'idle') { latestCallStatus = data.call_status; hasCapturedCallStartScreenshot = false; - lastRungCallStartedAt = null; candidateDeclinedOrLeftCall = false; candRingtone.stop(); if (candRingTimer) clearTimeout(candRingTimer); isCandidateCallConnected = false; + + // Clear session storage handled keys for ended/idle call sessions + for (let i = sessionStorage.length - 1; i >= 0; i--) { + const key = sessionStorage.key(i); + if (key && key.startsWith('handled_call_' + interviewId)) { + sessionStorage.removeItem(key); + } + } + const modal = document.getElementById('candidate-incoming-modal'); if (modal) { modal.style.opacity = '0'; @@ -2018,7 +2054,7 @@ function saveCanvasDrawing(force = false) { } }) .catch(() => {}); - }, 1000); + }, 800); -- 2.43.0