This commit is contained in:
2026-07-13 08:12:04 +07:00
parent 2bb856f2fd
commit 719704f19e
4 changed files with 215 additions and 93 deletions

View File

@@ -32,6 +32,7 @@ export const ProctorStreamPanels: React.FC<ProctorStreamPanelsProps> = ({
useEffect(() => {
const wsUrl = getWsUrl('/ws?role=teacher');
let retryTimer: ReturnType<typeof setTimeout> | null = null;
intentionalClose.current = false;
hasOpened.current = false;
@@ -41,9 +42,6 @@ export const ProctorStreamPanels: React.FC<ProctorStreamPanelsProps> = ({
setScreenFrame(null);
setWebcamFrame(null);
const ws = new WebSocket(wsUrl);
wsRef.current = ws;
const markStreaming = () => {
if (!hasFrames.current) {
hasFrames.current = true;
@@ -52,48 +50,63 @@ export const ProctorStreamPanels: React.FC<ProctorStreamPanelsProps> = ({
}
};
ws.onopen = () => {
hasOpened.current = true;
setStreaming(true);
ws.send(JSON.stringify({ event: 'teacher:subscribe', data: { studentId } }));
};
ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data);
if (msg.event === 'teacher:screenshot-stream-frame' && msg.data.studentId === studentId) {
setScreenFrame(msg.data.imageBuffer);
markStreaming();
} else if (msg.event === 'teacher:webcam-stream-frame' && msg.data.studentId === studentId) {
setWebcamFrame(msg.data.imageBuffer);
markStreaming();
} else if (msg.event === 'teacher:stream-stopped' && msg.data.studentId === studentId) {
setScreenFrame(null);
setWebcamFrame(null);
setStreaming(false);
setErrorMessage('Sinh viên đã dừng stream');
}
} catch (err) {
console.error('Error parsing WS frame:', err);
}
};
ws.onerror = () => {};
ws.onclose = () => {
const connect = () => {
if (intentionalClose.current) return;
setStreaming(false);
if (!hasOpened.current && !hasFrames.current) {
setErrorMessage('Không thể kết nối máy chủ giám sát');
}
const ws = new WebSocket(wsUrl);
wsRef.current = ws;
ws.onopen = () => {
hasOpened.current = true;
setStreaming(true);
setErrorMessage(null);
ws.send(JSON.stringify({ event: 'teacher:subscribe', data: { studentId } }));
};
ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data);
if (msg.event === 'teacher:screenshot-stream-frame' && msg.data.studentId === studentId) {
setScreenFrame(msg.data.imageBuffer);
markStreaming();
} else if (msg.event === 'teacher:webcam-stream-frame' && msg.data.studentId === studentId) {
setWebcamFrame(msg.data.imageBuffer);
markStreaming();
} else if (msg.event === 'teacher:stream-stopped' && msg.data.studentId === studentId) {
setScreenFrame(null);
setWebcamFrame(null);
setStreaming(false);
setErrorMessage('Sinh viên đã dừng stream');
}
} catch (err) {
console.error('Error parsing WS frame:', err);
}
};
ws.onerror = () => {};
ws.onclose = () => {
if (intentionalClose.current) return;
setStreaming(false);
if (!hasOpened.current && !hasFrames.current) {
setErrorMessage('Không thể kết nối máy chủ giám sát');
} else {
setErrorMessage('Mất kết nối giám sát — đang thử lại...');
}
retryTimer = setTimeout(connect, 3000);
};
};
connect();
return () => {
intentionalClose.current = true;
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ event: 'teacher:unsubscribe', data: { studentId } }));
if (retryTimer) clearTimeout(retryTimer);
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify({ event: 'teacher:unsubscribe', data: { studentId } }));
}
ws.close();
wsRef.current?.close();
wsRef.current = null;
};
}, [studentId]);