feat: user preferences sync — server-side storage, whiteboard defaults, dashboard widget visibility
- New table `user_preferences` (user_id PK, JSON blob, updated_at) - GET/PATCH/DELETE /api/preferences with deep-merge UPSERT - LS.prefs singleton in api.js: dot-notation get/set, debounced flush (1.5s), server sync - classroom.html: load wb.color/width/lineStyle/theme from prefs on init; save on change - dashboard.html: widget configurator panel (gear button) — toggle visibility per-user, persisted server-side Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
const db = require('../db/db');
|
||||
|
||||
// Recursive deep merge: values from `patch` override `base`, objects are merged
|
||||
function deepMerge(base, patch) {
|
||||
const result = Object.assign({}, base);
|
||||
for (const key of Object.keys(patch)) {
|
||||
if (
|
||||
patch[key] !== null &&
|
||||
typeof patch[key] === 'object' &&
|
||||
!Array.isArray(patch[key]) &&
|
||||
typeof result[key] === 'object' &&
|
||||
result[key] !== null &&
|
||||
!Array.isArray(result[key])
|
||||
) {
|
||||
result[key] = deepMerge(result[key], patch[key]);
|
||||
} else {
|
||||
result[key] = patch[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ── GET /api/preferences ────────────────────────────────────────────────── */
|
||||
function getPreferences(req, res) {
|
||||
const row = db.prepare('SELECT data FROM user_preferences WHERE user_id = ?').get(req.user.id);
|
||||
res.json(JSON.parse(row?.data || '{}'));
|
||||
}
|
||||
|
||||
/* ── PATCH /api/preferences ──────────────────────────────────────────────── */
|
||||
function patchPreferences(req, res) {
|
||||
if (!req.body || typeof req.body !== 'object' || Array.isArray(req.body)) {
|
||||
return res.status(400).json({ error: 'Body must be a JSON object' });
|
||||
}
|
||||
const current = JSON.parse(
|
||||
db.prepare('SELECT data FROM user_preferences WHERE user_id = ?').get(req.user.id)?.data || '{}'
|
||||
);
|
||||
const merged = deepMerge(current, req.body);
|
||||
db.prepare(`
|
||||
INSERT INTO user_preferences (user_id, data, updated_at)
|
||||
VALUES (?, ?, datetime('now'))
|
||||
ON CONFLICT(user_id) DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at
|
||||
`).run(req.user.id, JSON.stringify(merged));
|
||||
res.json(merged);
|
||||
}
|
||||
|
||||
/* ── DELETE /api/preferences ─────────────────────────────────────────────── */
|
||||
function resetPreferences(req, res) {
|
||||
db.prepare('DELETE FROM user_preferences WHERE user_id = ?').run(req.user.id);
|
||||
res.json({});
|
||||
}
|
||||
|
||||
module.exports = { getPreferences, patchPreferences, resetPreferences };
|
||||
Reference in New Issue
Block a user