diff --git a/src/hooks/useClassMonitor.ts b/src/hooks/useClassMonitor.ts index d512630..34b2b63 100644 --- a/src/hooks/useClassMonitor.ts +++ b/src/hooks/useClassMonitor.ts @@ -1,12 +1,25 @@ import { $XXL } from '@/utils' +import { userStore } from '@/stores' import { useTips } from './useTips' let monitorSessionId = '' let monitorState: 0 | 1 = 0 let closeMonitorTip: (() => void) | null = null +/** 旧版课堂监控 WS 类型 */ const MONITOR_MSG_TYPE = 'monitorStart' const MONITOR_STOP_MSG_TYPE = 'monitorStop' +/** 新版学生投屏 WS 类型 */ +const STUDENT_SCREEN_SHARE_START = 'studentScreenShareStart' +const STUDENT_SCREEN_SHARE_STOP = 'studentScreenShareStop' + +const MONITOR_WS_TYPES = new Set([ + MONITOR_MSG_TYPE, + MONITOR_STOP_MSG_TYPE, + STUDENT_SCREEN_SHARE_START, + STUDENT_SCREEN_SHARE_STOP, +]) + const MONITOR_LOG_PREFIX = '[class-monitor]' const monitorLog = (...args: unknown[]) => console.log(MONITOR_LOG_PREFIX, ...args) @@ -20,22 +33,74 @@ const showMonitorTip = (tips: string) => { }) } -const startStudentShareScreen = () => { - monitorLog('call native startStudentShareScreen') - $XXL.startStudentShareScreen() +const mergeMonitorPayload = (data: anyObj): anyObj => { + const msg = data?.msg && typeof data.msg === 'object' && !Array.isArray(data.msg) ? data.msg : {} + return { + ...(data && typeof data === 'object' ? data : {}), + ...msg, + } } -const stopStudentShareScreen = () => { - monitorLog('call native stopStudentShareScreen') - $XXL.stopStudentShareScreen() +const getMonitorSessionKey = (msg: anyObj) => { + const key = + msg?.sessionId ?? + msg?.startedAt ?? + msg?.classId ?? + msg?.monitorSessionId ?? + msg?.classMonitorSessionId + + return key !== undefined && key !== null ? String(key).trim() : '' } -/** 与老师讲解 WS 等场景兼容:外层 type 可能是 push/classMessage,监控类型在 msg.type */ +const applyStudentShareStart = (msg: anyObj): boolean => { + const host = msg.ip !== undefined && msg.ip !== null ? String(msg.ip) : '' + const port = Number(msg.port) + if (!host || !Number.isFinite(port)) { + monitorWarn('startStudentShareScreen missing ip/port', msg) + return false + } + + const u = userStore().userInfo + const config = { + host, + port, + name: String(u?.name || u?.nickName || ''), + id: String(u?.userId ?? ''), + } + monitorLog('startStudentShareScreen config', config) + $XXL.startStudentShareScreen(config) + return true +} + +const startStudentShareScreenLive = (msg: anyObj) => { + const sessionKey = getMonitorSessionKey(msg) + monitorLog('start student screen share', msg) + if (monitorState === 1 && sessionKey && monitorSessionId === sessionKey) { + monitorLog('start ignored, session already running', sessionKey) + return + } + + stopClassMonitor() + monitorSessionId = sessionKey + monitorState = 1 + showMonitorTip('已进入课堂监控状态') + if (!applyStudentShareStart(msg)) { + monitorState = 0 + monitorSessionId = '' + } +} + +/** 与老师讲解 WS 等场景兼容:外层 type 可能是 push/classMessage,监控类型在 msg.type 或 data.type */ export const resolveMonitorWsType = (data: anyObj): string | undefined => { - const nested = data?.msg?.type - if (nested === MONITOR_STOP_MSG_TYPE || nested === MONITOR_MSG_TYPE) return nested - const top = data?.type - if (top === MONITOR_STOP_MSG_TYPE || top === MONITOR_MSG_TYPE) return top + const candidates = [data?.msg?.type, data?.type] + const inner = (data as Record).data + if (inner && typeof inner === 'object') { + candidates.push((inner as anyObj).type) + } + + for (const t of candidates) { + if (typeof t === 'string' && MONITOR_WS_TYPES.has(t)) return t + } return undefined } @@ -43,7 +108,7 @@ export const stopClassMonitor = (showTip = false) => { const isMonitoring = !!monitorSessionId || monitorState === 1 monitorLog('stop monitor', { showTip, isMonitoring, monitorSessionId, monitorState }) if (monitorState === 1) { - stopStudentShareScreen() + $XXL.stopStudentShareScreen() } if (showTip && isMonitoring) { showMonitorTip('已结束课堂监控') @@ -59,16 +124,19 @@ const startClassMonitor = (msg: anyObj) => { monitorWarn('start monitor ignored because sessionId is empty') return } - if (monitorState === 1 && monitorSessionId === sessionId) { + if (monitorState === 1 && monitorSessionId === String(sessionId)) { monitorLog('start monitor ignored because session is already running', sessionId) return } stopClassMonitor() - monitorSessionId = sessionId + monitorSessionId = String(sessionId) monitorState = 1 showMonitorTip('已进入课堂监控状态') - startStudentShareScreen() + if (!applyStudentShareStart(msg)) { + monitorState = 0 + monitorSessionId = '' + } } const getMonitorSessionId = (data: anyObj) => { @@ -87,16 +155,22 @@ const getMonitorSessionId = (data: anyObj) => { export const handleClassMonitorMessage = (data: anyObj) => { const monitorType = resolveMonitorWsType(data) monitorLog('handle monitor ws message', { monitorType, data }) - if (monitorType === MONITOR_STOP_MSG_TYPE) { + if (!monitorType) return false + + const msg = mergeMonitorPayload(data) + + if (monitorType === STUDENT_SCREEN_SHARE_STOP || monitorType === MONITOR_STOP_MSG_TYPE) { stopClassMonitor(true) return true } + + if (monitorType === STUDENT_SCREEN_SHARE_START) { + startStudentShareScreenLive(msg) + return true + } + if (monitorType !== MONITOR_MSG_TYPE) return false - const msg: anyObj = { - ...(data && typeof data === 'object' ? data : {}), - ...(data?.msg && typeof data.msg === 'object' && !Array.isArray(data.msg) ? data.msg : {}), - } const monitoring = msg?.monitoring if (monitoring === true || monitoring === 'true') { startClassMonitor(msg) @@ -122,9 +196,8 @@ export const syncClassMonitorState = (state: unknown) => { const sessionId = getMonitorSessionId(stateInfo) if (!sessionId) { monitorWarn('sync monitor state ignored because sessionId is empty', stateInfo) - if (monitorState !== 1) { + if (monitorState !== 1 && applyStudentShareStart(stateInfo)) { monitorState = 1 - startStudentShareScreen() } return } diff --git a/src/hooks/useSocket.ts b/src/hooks/useSocket.ts index 9c465e3..c02ba53 100644 --- a/src/hooks/useSocket.ts +++ b/src/hooks/useSocket.ts @@ -25,6 +25,8 @@ enum SOCKET_MSG_TYPE { messageReceive = 'messageReceive', monitorStart = 'monitorStart', monitorStop = 'monitorStop', + studentScreenShareStart = 'studentScreenShareStart', + studentScreenShareStop = 'studentScreenShareStop', } const inspectorEnd = ref() @@ -152,6 +154,8 @@ export const useSocket = () => { break case SOCKET_MSG_TYPE.monitorStart: case SOCKET_MSG_TYPE.monitorStop: + case SOCKET_MSG_TYPE.studentScreenShareStart: + case SOCKET_MSG_TYPE.studentScreenShareStop: handleClassMonitorMessage(data) break case SOCKET_MSG_TYPE.inspectorStart: diff --git a/src/utils/android.ts b/src/utils/android.ts index be63c14..51c8981 100644 --- a/src/utils/android.ts +++ b/src/utils/android.ts @@ -109,8 +109,9 @@ export const $XXL = { packSync('startTeacherShareScreen', JSON.stringify(config)), /** 老师投屏结束 */ stopTeacherShareScreen: () => packSync('stopTeacherShareScreen'), - /** 学生课堂监控投屏开始 */ - startStudentShareScreen: () => packSync('startStudentShareScreen'), + /** 学生课堂监控投屏开始:参数与 `startTeacherShareScreen` 一致 */ + startStudentShareScreen: (config: { host: string; port: number; name: string; id: string }) => + packSync('startStudentShareScreen', JSON.stringify(config)), /** 学生课堂监控投屏结束 */ stopStudentShareScreen: () => packSync('stopStudentShareScreen'), // openApp: (config: anyObj) => pack('openApp', config),