fix issue
This commit is contained in:
parent
e3e1e8aaf5
commit
3b82f4c453
@ -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 = '<div style="color: #94a3b8; font-style: italic; text-align: center; padding-top: 40px;">No messages yet. Send a message below.</div>';
|
||||
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 += `<div style="margin-bottom:8px; background:${bgColor}; padding:8px 10px; border-radius:8px; border-left:3px solid ${badgeColor};">
|
||||
html += `<div style="margin-bottom:8px; background:${bgColor}; padding:8px 10px; border-radius:8px; border-left:3px solid ${badgeColor}; text-align: left;">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:4px;">
|
||||
<strong style="color:${badgeColor}; font-size:0.75rem;">${senderName}</strong>
|
||||
<span style="font-size:0.65rem; color:#94a3b8;">${new Date(w.created_at || Date.now()).toLocaleTimeString()}</span>
|
||||
</div>
|
||||
<div style="color: #ffffff !important; font-size:0.85rem; font-weight:500; line-height:1.4;">${w.message}</div>
|
||||
<div style="color: #ffffff !important; font-size:0.85rem; font-weight:500; line-height:1.4; word-break: break-word;">${w.message}</div>
|
||||
</div>`;
|
||||
});
|
||||
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);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user