fix: тема доски синхронизируется между учителем и учениками в реальном времени
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ const GUEST_EVENTS = new Set([
|
||||
'classroom_stroke_updated', 'classroom_page_added', 'classroom_page_changed',
|
||||
'classroom_template_changed', 'classroom_page_cleared', 'classroom_page_renamed',
|
||||
'classroom_page_duplicated', 'classroom_page_deleted', 'classroom_ended',
|
||||
'classroom_board_theme',
|
||||
]);
|
||||
|
||||
/* ── Helper: broadcast to all session participants ─────────────────────── */
|
||||
@@ -490,6 +491,21 @@ function updatePageTemplate(req, res) {
|
||||
res.json({ ok: true, template });
|
||||
}
|
||||
|
||||
/* PATCH /api/classroom/:id/board-theme — change board theme and broadcast to all */
|
||||
function updateBoardTheme(req, res) {
|
||||
const sessionId = Number(req.params.id);
|
||||
const VALID_THEMES = new Set(['chalkboard', 'blackboard', 'whiteboard', 'dark', 'grid', 'dots']);
|
||||
const { theme } = req.body;
|
||||
if (!theme || !VALID_THEMES.has(theme)) return res.status(400).json({ error: 'invalid theme' });
|
||||
const session = db.prepare(`SELECT * FROM classroom_sessions WHERE id=? AND status='active'`).get(sessionId);
|
||||
if (!session) return res.status(404).json({ error: 'Сессия не активна' });
|
||||
if (session.teacher_id !== req.user.id && req.user.role !== 'admin')
|
||||
return res.status(403).json({ error: 'Нет доступа' });
|
||||
db.prepare('UPDATE classroom_sessions SET board_theme=? WHERE id=?').run(theme, sessionId);
|
||||
emitToSession(sessionId, { type: 'classroom_board_theme', sessionId, theme });
|
||||
res.json({ ok: true, theme });
|
||||
}
|
||||
|
||||
/* POST /api/classroom/:id/hand — raise hand */
|
||||
function raiseHand(req, res) {
|
||||
const sessionId = Number(req.params.id);
|
||||
@@ -1521,6 +1537,7 @@ module.exports = {
|
||||
addPage,
|
||||
changePage,
|
||||
updatePageTemplate,
|
||||
updateBoardTheme,
|
||||
getPages,
|
||||
renamePage,
|
||||
duplicatePage,
|
||||
|
||||
@@ -2858,6 +2858,8 @@ db.exec(`
|
||||
|
||||
// Guest token for classroom sessions (public read-only whiteboard access)
|
||||
try { db.exec("ALTER TABLE classroom_sessions ADD COLUMN guest_token TEXT UNIQUE"); } catch {}
|
||||
// Board theme per session (synced to all participants)
|
||||
try { db.exec("ALTER TABLE classroom_sessions ADD COLUMN board_theme TEXT NOT NULL DEFAULT 'chalkboard'"); } catch {}
|
||||
|
||||
// Persistent draw permissions (survives server restart)
|
||||
db.exec(`
|
||||
|
||||
@@ -76,6 +76,7 @@ router.get('/:id/pages', ...auth, c.getPages);
|
||||
router.post('/:id/pages', ...teacher, c.addPage);
|
||||
router.put('/:id/page', ...teacher, c.changePage);
|
||||
router.patch('/:id/page-template', ...teacher, c.updatePageTemplate);
|
||||
router.patch('/:id/board-theme', ...teacher, c.updateBoardTheme);
|
||||
router.patch('/:id/pages/:pageNum/name', ...teacher, c.renamePage);
|
||||
router.post('/:id/pages/:pageNum/duplicate', ...teacher, c.duplicatePage);
|
||||
router.delete('/:id/pages/:pageNum', ...teacher, c.deletePage);
|
||||
|
||||
+19
-3
@@ -3160,6 +3160,7 @@
|
||||
let _me = null;
|
||||
let _session = null;
|
||||
let _sessionId = null;
|
||||
let _sessionBoardTheme = null; // pending theme from session data, applied in initWhiteboard
|
||||
let _sseHandle = null;
|
||||
let _participants = {}; // userId -> {name, micMuted}
|
||||
let _raisedHands = {}; // userId -> userName
|
||||
@@ -3554,6 +3555,12 @@
|
||||
if (sel) sel.value = data.template;
|
||||
wbUpdateThumbnail(_wbCurrentPage);
|
||||
}
|
||||
} else if (data.type === 'classroom_board_theme') {
|
||||
if (_sessionId == data.sessionId && _wb) {
|
||||
_wb.setBoardTheme(data.theme);
|
||||
const sel = document.getElementById('wb-theme-select');
|
||||
if (sel) sel.value = data.theme;
|
||||
}
|
||||
} else if (data.type === 'classroom_hand_raised') {
|
||||
if (_sessionId == data.sessionId) onHandRaised(data.userId, data.userName);
|
||||
} else if (data.type === 'classroom_hand_lowered') {
|
||||
@@ -3981,6 +3988,8 @@
|
||||
// page state
|
||||
_wbCurrentPage = session.current_page || 1;
|
||||
_totalPages = session.pageCount || 1;
|
||||
// Remember board theme from session — will be applied in initWhiteboard
|
||||
if (session.board_theme) _sessionBoardTheme = session.board_theme;
|
||||
_raisedHands = {};
|
||||
_handRaised = false;
|
||||
_followTeacher = true;
|
||||
@@ -4069,8 +4078,12 @@
|
||||
stylusMultiplier: _prefs.stylusMultiplier,
|
||||
});
|
||||
|
||||
// Sync board theme selector to whiteboard default
|
||||
{ const sel = document.getElementById('wb-theme-select'); if (sel) sel.value = 'chalkboard'; }
|
||||
// Apply session board theme (set by teacher, synced for all participants)
|
||||
if (_sessionBoardTheme && _sessionBoardTheme !== 'chalkboard') {
|
||||
_wb.setBoardTheme(_sessionBoardTheme);
|
||||
}
|
||||
{ const sel = document.getElementById('wb-theme-select'); if (sel) sel.value = _sessionBoardTheme || 'chalkboard'; }
|
||||
_sessionBoardTheme = null; // consumed
|
||||
|
||||
// Apply saved whiteboard defaults from user preferences
|
||||
if (canEdit && LS.prefs) {
|
||||
@@ -5508,10 +5521,13 @@
|
||||
function wbSetBoardTheme(theme) {
|
||||
if (!_wb) return;
|
||||
_wb.setBoardTheme(theme);
|
||||
// Update selector if called programmatically
|
||||
const sel = document.getElementById('wb-theme-select');
|
||||
if (sel) sel.value = theme;
|
||||
if (LS.prefs) LS.prefs.set('wb.theme', theme);
|
||||
// Broadcast to students if in active session
|
||||
if (_sessionId) {
|
||||
LS.patch(`/api/classroom/${_sessionId}/board-theme`, { theme }).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Page context menu ── */
|
||||
|
||||
Reference in New Issue
Block a user