teach/public/js/pupil.js
Ramon 99e807595a
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 51s
feat: alle widgets mobielvriendelijk maken (v0.3.64-beta)
2026-07-16 08:02:20 +02:00

96 lines
3.7 KiB
JavaScript

/* teach - leerling-omgeving: toont enkel de widgets die de leerkracht heeft
klaargezet (via een toewijzing aan de klas of de leerling zelf), in
speelmodus (readonly) en zonder de sleep/vergroot/sluit-chrome van het bord.
Geen momentopname: /my/assignment zoekt live in het bord van de leerkracht,
dus deze pagina pollt periodiek zodat nieuwe woorden vanzelf doorkomen.
Modus per toewijzing: "werken" (default, leerling werkt in de widget en
voortgang wordt gelogd) of "kijken" (alleen meekijken: alle interactie
geblokkeerd via een overlay, snellere poll zodat wijzigingen van de
leerkracht vlot doorkomen). */
const PUPIL_WIDGET_CATS = ["taal", "rekenen"];
const PUPIL_POLL_MS = { kijken: 5000, werken: 20000 };
const pupilView = document.getElementById("pupilView");
let pupilPollTimer = null;
let pupilLastSig = null;
let pupilMode = "werken";
function pupilAllowedDefs(){
return REGISTRY.filter(d => PUPIL_WIDGET_CATS.includes(d.cat));
}
function renderPupilWidgets(widgets, mode){
pupilView.innerHTML = "";
const defs = pupilAllowedDefs();
const watch = mode === "kijken";
const usable = widgets
.map(w => ({ w, def: defs.find(d => d.id === w.id) }))
.filter(x => x.def);
if(!usable.length){
const hint = document.createElement("div");
hint.className = "pupil-empty";
hint.textContent = T("pupilEmpty");
pupilView.appendChild(hint);
return;
}
usable.forEach(({ w, def }, idx)=>{
const card = document.createElement("div");
card.className = "widget pupil-card" + (watch ? " watch" : "");
card.dataset.widget = def.id;
card.style.width = `min(100%, ${def.w}px)`;
card.style.minWidth = "0";
card.style.minHeight = def.h + "px";
const body = document.createElement("div");
body.className = "widget-body";
card.appendChild(body);
if(watch){
const badge = document.createElement("div");
badge.className = "pupil-watch-badge";
badge.textContent = "👁 " + T("pupilWatching");
card.appendChild(badge);
}
pupilView.appendChild(card);
/* borden die vóór de invoering van stabiele widget-ids zijn opgeslagen
hebben nog geen wid - dan is positie in de lijst de terugval-identiteit,
stabiel zolang de leerkracht de widgets op het bord niet herschikt */
const widgetId = w.wid || (def.id + ":" + idx);
/* in kijken-modus geen voortgang: de leerling doet zelf niets */
const opts = watch ? { readonly:true } : { readonly:true, onProgress: (p)=>{
api("/my/progress", { method:"POST", body: { widgetId, widgetType: def.id, ...p } }).catch(()=>{});
}};
def.mount(body, w.state || null, opts);
});
}
async function pupilPoll(){
try{
const res = await api("/my/assignment");
const mode = res.mode === "kijken" ? "kijken" : "werken";
/* modus in de signature zodat een moduswissel ook zonder widget-wijziging
een her-render (en dus de juiste overlay) oplevert */
const sig = mode + "|" + JSON.stringify(res.widgets || []);
if(sig !== pupilLastSig){
pupilLastSig = sig;
renderPupilWidgets(res.widgets || [], mode);
}
if(mode !== pupilMode){
pupilMode = mode;
clearInterval(pupilPollTimer);
pupilPollTimer = setInterval(pupilPoll, PUPIL_POLL_MS[mode]);
}
}catch(e){ /* stille mislukking - volgende poll probeert opnieuw */ }
}
function startPupilView(){
document.body.classList.add("pupil-mode");
pupilLastSig = null;
pupilMode = "werken";
pupilPoll();
clearInterval(pupilPollTimer);
pupilPollTimer = setInterval(pupilPoll, PUPIL_POLL_MS[pupilMode]);
}
function stopPupilView(){
document.body.classList.remove("pupil-mode");
clearInterval(pupilPollTimer);
pupilPollTimer = null;
pupilView.innerHTML = "";
}