@@ -1241,41 +1175,7 @@
-
-
-
-
-
-
-
-
diff --git a/resources/views/interview/candidate_room.blade.php b/resources/views/interview/candidate_room.blade.php
index d173812..3b552b4 100644
--- a/resources/views/interview/candidate_room.blade.php
+++ b/resources/views/interview/candidate_room.blade.php
@@ -368,7 +368,7 @@
No messages yet. Send a message below.
-
+
@@ -458,13 +458,25 @@
}
});
- // Real-Time Code Sync to Interviewer (Before Submission)
+ // Real-Time Instant BroadcastChannel + Backend Code Sync
+ const liveSyncChannel = new BroadcastChannel('live_sync_' + '{{ $interview->id }}');
+ liveSyncChannel.onmessage = function(event) {
+ if (event.data && event.data.type === 'chat_message' && typeof pollCandidateChat === 'function') {
+ pollCandidateChat();
+ }
+ };
let codeSyncTimeout = null;
editor.onDidChangeModelContent(function () {
+ const code = editor.getValue();
+ const lang = document.getElementById('language-select').value;
+
+ // Broadcast instant local event (0ms latency)
+ if (typeof liveSyncChannel !== 'undefined' && liveSyncChannel) {
+ liveSyncChannel.postMessage({ type: 'code_sync', code: code, language: lang });
+ }
+
clearTimeout(codeSyncTimeout);
codeSyncTimeout = setTimeout(function () {
- const code = editor.getValue();
- const lang = document.getElementById('language-select').value;
fetch(`{{ route("interview.candidate.sync-code", $interview->id) }}`, {
method: 'POST',
headers: {
@@ -473,7 +485,7 @@
},
body: JSON.stringify({ code: code, language: lang })
});
- }, 600);
+ }, 200);
});
});
@@ -1176,6 +1188,10 @@ function showCandidateJoinOption() {
if (joinBtn) joinBtn.style.display = 'none';
+ if (!localMediaStream) {
+ await startCandidateCameraStream();
+ }
+
isCandidateCallConnected = true;
const badge = document.getElementById('call-status-badge');
@@ -1380,6 +1396,12 @@ function initCandidatePeer() {
peerInstance.on('call', call => {
activeCallInstance = call;
+ latestCallStatus = 'active';
+
+ if (!isCandidateCallConnected && !candidateDeclinedOrLeftCall) {
+ triggerCandidateRing();
+ }
+
const sendStream = localMediaStream || new MediaStream();
call.answer(sendStream);
@@ -1388,15 +1410,17 @@ function initCandidatePeer() {
const placeholder = document.getElementById('interviewer-video-placeholder');
if (remoteVideo) {
remoteVideo.srcObject = remoteStream;
- remoteVideo.play().catch(e => console.log(e));
+ remoteVideo.play().catch(e => console.log('Candidate remote video play error:', e));
}
if (placeholder) placeholder.style.display = 'none';
- const badge = document.getElementById('call-status-badge');
- if (badge) {
- badge.innerText = '🟢 CALL CONNECTED (LIVE)';
- badge.style.background = 'rgba(16,185,129,0.3)';
- badge.style.color = '#34d399';
+ if (isCandidateCallConnected) {
+ const badge = document.getElementById('call-status-badge');
+ if (badge) {
+ badge.innerText = '🟢 CALL CONNECTED (LIVE)';
+ badge.style.background = 'rgba(16,185,129,0.3)';
+ badge.style.color = '#34d399';
+ }
}
});
@@ -1805,6 +1829,10 @@ function sendCandidateMessage() {
if (!msg) return;
input.value = '';
+ if (typeof liveSyncChannel !== 'undefined' && liveSyncChannel) {
+ liveSyncChannel.postMessage({ type: 'chat_message', message: msg, sender: 'candidate' });
+ }
+
fetch(`{{ route('interview.candidate.send-message', $interview->id) }}`, {
method: 'POST',
headers: {
@@ -1815,9 +1843,7 @@ function sendCandidateMessage() {
})
.then(r => r.json())
.then(res => {
- if (res.success) {
- pollCandidateChat();
- }
+ pollCandidateChat();
});
}
@@ -1830,9 +1856,13 @@ function toggleNotepadModal() {
}
function saveNotepadContent() {
+ const notes = document.getElementById('notepad-textarea').value;
+ if (typeof liveSyncChannel !== 'undefined' && liveSyncChannel) {
+ liveSyncChannel.postMessage({ type: 'notes_sync', notes: notes });
+ }
+
clearTimeout(notesTimeout);
notesTimeout = setTimeout(() => {
- const notes = document.getElementById('notepad-textarea').value;
fetch(`{{ route('interview.candidate.notes', $interview->id) }}`, {
method: 'POST',
headers: {
@@ -1841,13 +1871,14 @@ function saveNotepadContent() {
},
body: JSON.stringify({ notes: notes })
});
- }, 300);
+ }, 150);
}
// HTML5 Canvas Drawing Logic
let isDrawing = false;
let canvas, ctx;
let drawingSaveTimeout = null;
+ let lastDrawingSaveTime = 0;
function toggleDrawingModal() {
const modal = document.getElementById('drawing-modal');
@@ -1889,29 +1920,38 @@ function draw(e) {
ctx.strokeStyle = document.getElementById('draw-color').value;
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
- saveCanvasDrawing();
+ saveCanvasDrawing(false);
}
function stopDraw() {
if (isDrawing) {
isDrawing = false;
ctx.closePath();
- saveCanvasDrawing();
+ saveCanvasDrawing(true);
}
}
function clearCanvasDraw() {
if (ctx && canvas) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
- saveCanvasDrawing();
+ saveCanvasDrawing(true);
}
}
- function saveCanvasDrawing() {
- clearTimeout(drawingSaveTimeout);
- drawingSaveTimeout = setTimeout(() => {
- if (!canvas) return;
- const drawingData = canvas.toDataURL();
+ function saveCanvasDrawing(force = false) {
+ if (!canvas) return;
+ const now = Date.now();
+ const drawingData = canvas.toDataURL();
+
+ // Broadcast instant local event (0ms latency)
+ if (typeof liveSyncChannel !== 'undefined' && liveSyncChannel) {
+ liveSyncChannel.postMessage({ type: 'drawing_sync', drawing: drawingData });
+ }
+
+ // Throttled HTTP POST save to backend (fires every 250ms while drawing & on mouseup)
+ if (force || (now - lastDrawingSaveTime >= 250)) {
+ clearTimeout(drawingSaveTimeout);
+ lastDrawingSaveTime = now;
fetch(`{{ route('interview.candidate.drawing', $interview->id) }}`, {
method: 'POST',
headers: {
@@ -1920,7 +1960,12 @@ function saveCanvasDrawing() {
},
body: JSON.stringify({ drawing: drawingData })
});
- }, 300);
+ } else {
+ clearTimeout(drawingSaveTimeout);
+ drawingSaveTimeout = setTimeout(() => {
+ saveCanvasDrawing(true);
+ }, 250);
+ }
}
setInterval(() => {
@@ -1944,18 +1989,13 @@ function saveCanvasDrawing() {
}, 1200);
}
if (!isCandidateCallConnected && !candidateDeclinedOrLeftCall) {
- const elapsed = data.call_started_at ? (Date.now() - new Date(data.call_started_at).getTime()) : 999999;
- if (elapsed <= 12000 && lastRungCallStartedAt !== data.call_started_at) {
- lastRungCallStartedAt = data.call_started_at;
- triggerCandidateRing(null, data.call_started_at);
- } else if (elapsed > 12000) {
- showCandidateJoinOption();
- }
+ 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;
@@ -1978,7 +2018,7 @@ function saveCanvasDrawing() {
}
})
.catch(() => {});
- }, 2200);
+ }, 1000);