All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 37s
- Beide verzoeken hangen alleen af van dezelfde sessie-cookie, niet van elkaars antwoord - ze werden tot nu toe na elkaar aangeroepen (hydrateFromServer() startte pas na een voltooide /auth/me), wat een hele round-trip extra kost bij elke opstart voor staf/thuisgebruikers. - app.js vraagt ze nu tegelijk op via Promise.allSettled; hydrateFromServer() krijgt het /me/data-resultaat optioneel meegegeven i.p.v. het zelf (opnieuw) op te halen. Voor een leerling/ouder wordt dat resultaat genegeerd (zij krijgen sowieso geen whiteboard-data) - nooit verspild werk, soms ongebruikt, wat de server niet meer kost dan de bestaande losse aanroep die er toch al was. - De twee andere hydrateFromServer()-aanroepen (na inloggen, na een save-conflict) geven bewust niets mee en halen gewoon vers op, zoals voorheen - alleen de allereerste opstart kan dit parallelliseren. - Geverifieerd met de volledige testsuite (112/112) en drie losse boot-scenario's in de sandbox (gast/staf/leerling): in alle drie komen /auth/me en /me/data binnen ~1ms van elkaar aan (voorheen serieel), en blijft het bestaande gedrag (gast blijft gast, staf krijgt het bord, leerling krijgt de afgeschermde weergave) exact hetzelfde. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
102 lines
No EOL
4.8 KiB
JavaScript
102 lines
No EOL
4.8 KiB
JavaScript
/* teach - start: beeldgrootte, contrast, sessie-herstel */
|
|
/* =========================================================
|
|
init
|
|
==========================================================*/
|
|
/* display size: scale the whole app up for pupils who need it (device setting) */
|
|
let ZOOM = 100;
|
|
const btnZoomIn = document.getElementById("btnZoomIn");
|
|
const btnZoomOut = document.getElementById("btnZoomOut");
|
|
const zoomPct = document.getElementById("zoomPct");
|
|
function setZoom(z){
|
|
ZOOM = Math.max(100, Math.min(200, Math.round(z/10)*10));
|
|
document.body.style.zoom = ZOOM/100;
|
|
zoomPct.textContent = ZOOM+"%";
|
|
zoomPct.classList.toggle("on", ZOOM!==100);
|
|
btnZoomIn.title = btnZoomOut.title = zoomPct.title = T("zoomUI");
|
|
btnZoomIn.disabled = ZOOM>=200;
|
|
btnZoomOut.disabled = ZOOM<=100;
|
|
try{ localStorage.setItem("teach.zoom", ZOOM); }catch(e){}
|
|
}
|
|
btnZoomIn.addEventListener("click", ()=>setZoom(ZOOM+10));
|
|
btnZoomOut.addEventListener("click", ()=>setZoom(ZOOM-10));
|
|
zoomPct.addEventListener("click", ()=>setZoom(100)); /* tap the % to reset */
|
|
document.addEventListener("langchange", ()=>{ btnZoomIn.title = btnZoomOut.title = zoomPct.title = T("zoomUI"); });
|
|
try{ const z = +localStorage.getItem("teach.zoom"); if(z>100) setZoom(z); else setZoom(100); }catch(e){ setZoom(100); }
|
|
|
|
/* weergave-instellingen (Instellingen → Weergave): apparaat-gebonden voorkeuren.
|
|
Elke seg-groep: knopjes met data-v, keuze in localStorage, body-class toepassen. */
|
|
function initSeg(id, key, def, apply){
|
|
const seg = document.getElementById(id);
|
|
let val = def;
|
|
try{ val = localStorage.getItem(key) || def; }catch(e){}
|
|
if(![...seg.querySelectorAll("button")].some(b=>b.dataset.v===val)) val = def;
|
|
const set = v=>{
|
|
seg.querySelectorAll("button").forEach(b=>b.classList.toggle("active", b.dataset.v===v));
|
|
apply(v);
|
|
try{ localStorage.setItem(key, v); }catch(e){}
|
|
};
|
|
seg.querySelectorAll("button").forEach(b=>b.addEventListener("click", ()=>set(b.dataset.v)));
|
|
set(val);
|
|
}
|
|
/* thema: licht / donker / automatisch (volgt het OS) */
|
|
const darkMq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
const applyTheme = v=>{
|
|
document.body.classList.toggle("dark", v==="dark" || (v==="auto" && darkMq.matches));
|
|
};
|
|
initSeg("segTheme", "teach.theme", "light", applyTheme);
|
|
darkMq.addEventListener("change", ()=>{
|
|
try{ if((localStorage.getItem("teach.theme")||"light")==="auto") applyTheme("auto"); }catch(e){}
|
|
});
|
|
/* hoog contrast (voorheen de 👁-knop in de menubalk; zelfde localStorage-sleutel) */
|
|
(function(){
|
|
let init = "off";
|
|
try{ if(localStorage.getItem("teach.hc")==="1") init = "on"; }catch(e){}
|
|
const seg = document.getElementById("segHC");
|
|
const set = v=>{
|
|
seg.querySelectorAll("button").forEach(b=>b.classList.toggle("active", b.dataset.v===v));
|
|
document.body.classList.toggle("hc", v==="on");
|
|
try{ localStorage.setItem("teach.hc", v==="on" ? "1" : ""); }catch(e){}
|
|
};
|
|
seg.querySelectorAll("button").forEach(b=>b.addEventListener("click", ()=>set(b.dataset.v)));
|
|
set(init);
|
|
})();
|
|
/* menubalk-positie: boven / links / rechts */
|
|
initSeg("segTbar", "teach.tbar", "top", v=>{
|
|
document.body.classList.toggle("tb-left", v==="left");
|
|
document.body.classList.toggle("tb-right", v==="right");
|
|
});
|
|
/* minder beweging: standaard uit als het OS erom vraagt */
|
|
const motionDef = window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "off" : "on";
|
|
initSeg("segMotion", "teach.motion", motionDef, v=>document.body.classList.toggle("no-motion", v==="off"));
|
|
/* glas-effect (transparantie/blur van widgets en balken) */
|
|
initSeg("segGlass", "teach.glass", "on", v=>document.body.classList.toggle("no-glass", v==="off"));
|
|
|
|
/* taalkeuze onthouden (de schakelaar staat nu onder Weergave) */
|
|
try{
|
|
const l = localStorage.getItem("teach.lang");
|
|
if(l && I18N[l]) LANG = l;
|
|
}catch(e){}
|
|
|
|
applyI18n();
|
|
updateEmptyHint();
|
|
/* sessie herstellen: de HttpOnly session-cookie (niet leesbaar door JS, dus
|
|
geen client-side token-check meer mogelijk of nodig) gaat vanzelf mee met
|
|
dit verzoek - een geldige sessie levert de gebruiker op, anders 401 (gast) */
|
|
(async function(){
|
|
updateBoardsUI();
|
|
try{
|
|
/* /auth/me en /me/data hangen allebei alleen af van dezelfde sessie-cookie,
|
|
niet van elkaars antwoord - serieel wachten (eerst wie-ben-ik, dan pas
|
|
mijn-data ophalen) kost een hele round-trip voor niets. Parallel
|
|
opvragen en het tweede antwoord meegeven aan hydrateFromServer()
|
|
(die het gewoon negeert voor een leerling/ouder, zie daar) scheelt die
|
|
round-trip in het veelvoorkomende geval (staf/thuisgebruiker). */
|
|
const [meResult, dataResult] = await Promise.allSettled([api("/auth/me"), api("/me/data")]);
|
|
if(meResult.status !== "fulfilled") throw meResult.reason;
|
|
currentUser = meResult.value.user;
|
|
updateUserUI();
|
|
await hydrateFromServer(dataResult.status === "fulfilled" ? dataResult.value : null);
|
|
}catch(e){
|
|
resetToGuest();
|
|
}
|
|
})(); |