diff --git a/App/assets/js/live-client.js b/App/assets/js/live-client.js index a95c2cf..8da535b 100644 --- a/App/assets/js/live-client.js +++ b/App/assets/js/live-client.js @@ -55,7 +55,8 @@ // Großzügig (5 min): Lesen und Nachdenken sollen NICHT als „weg" gelten. // Scrollen/Mausbewegung zählen ohnehin als Interaktion (siehe EVENTS), und // echtes Weg-Sein (anderer Tab) wird über die Tab-Sichtbarkeit ausgeblendet. - var IDLE_THRESHOLD_MS = 300000; // 5 min ohne jede Regung → idle + var IDLE_THRESHOLD_MS = 300000; // 5 min ohne jede Regung → idle (Backstop) + var IDLE_PAUSE_MS = 180000; // 3 min → sichtbare Pause mit „Weiter"-Button var MAX_SESSION_MS = 7200000; // 120 min — Plattform-Hardcap var TICK_MS = 1000; // Auflösung der activeMs-Akkumulation var sessionStartedAt = Date.now(); @@ -64,6 +65,7 @@ var lastTickAt = Date.now(); var sessionCapped = false; var paused = false; // Sim-Pause: friert activeMs ein (opt-in) + var idleActive = false; // sichtbare Idle-Pause aktiv (Weiter-Overlay) function onInteraction() { lastInteractionAt = Date.now(); @@ -81,9 +83,9 @@ var now = Date.now(); var delta = now - lastTickAt; lastTickAt = now; - // Spiel pausiert (Sim hat setPaused(true) gerufen) → Zeit steht, nicht zählen. + // Pausiert (Sim-setPaused ODER sichtbare Idle-Pause) → Zeit steht, nicht zählen. // lastTickAt ist schon aktualisiert, daher springt activeMs beim Fortsetzen nicht. - if (paused) return; + if (paused || idleActive) return; // Tab nicht sichtbar (anderer Tab, minimiert) → echtes Weg-Sein, nicht zählen. // Denkpausen bei SICHTBAREM Tab bleiben über das großzügige Idle-Fenster erhalten. if (typeof document !== 'undefined' && document.hidden) return; @@ -91,6 +93,14 @@ // anrechnen, der Rest war definitiv nicht aktive Spielzeit. if (delta > TICK_MS * 3) delta = 0; var idleFor = now - lastInteractionAt; + // Nach 3 min Untätigkeit: sichtbare Pause einblenden (friert die Zeit ein, + // damit Idle nicht als Arbeitszeit zählt). Nicht im Spectator-Modus und + // nicht, während die Lehrperson ohnehin pausiert hat. + if (idleFor >= IDLE_PAUSE_MS && !isSpectator() + && !(pauseOverlay && pauseOverlay.style.display !== 'none')) { + showIdlePause(); + return; + } if (idleFor < IDLE_THRESHOLD_MS) { activeMs += delta; if (activeMs >= MAX_SESSION_MS && !sessionCapped) { @@ -138,7 +148,8 @@ state.__lastInteractionAt = lastInteractionAt; state.__sessionStartedAt = sessionStartedAt; state.__capped = sessionCapped; - state.__paused = paused; + state.__paused = paused || idleActive; + state.__idlePaused = idleActive; fetch(apiBase + '/live.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -175,6 +186,38 @@ .catch(function () {}); } + // --- Idle-Pause (plattformweit, für JEDE Sim) --- + // Nach längerer Untätigkeit blenden wir eine freundliche Pause mit „Weiter"- + // Button ein. Solange sie sichtbar ist, steht die Aktivzeit (idleActive) — + // die Pause zählt also NICHT als Arbeitszeit. Der Lernende kann jederzeit + // per Klick weitermachen. Kein Sim-Code nötig (dieses Asset lädt überall). + var idleOverlay = null; + function ensureIdleOverlay() { + if (idleOverlay) return idleOverlay; + var el = document.createElement('div'); + el.id = 'ggs-idle-overlay'; + el.style.cssText = 'position:fixed;inset:0;background:rgba(31,75,55,.82);color:#fff;z-index:99999;display:none;flex-direction:column;align-items:center;justify-content:center;font-family:Inter,-apple-system,system-ui,sans-serif;text-align:center;padding:2rem;backdrop-filter:blur(3px);cursor:pointer'; + el.innerHTML = '
😴
' + + '
Kurze Pause
' + + '
Du warst eine Weile ganz ruhig — ich habe die Zeit angehalten, damit die Pause nicht als Arbeitszeit zählt. Klick auf „Weiter", wenn du weitermachen möchtest.
' + + ''; + document.body.appendChild(el); + el.addEventListener('click', resumeFromIdle); + idleOverlay = el; + return el; + } + function showIdlePause() { + if (idleActive) return; + idleActive = true; + ensureIdleOverlay().style.display = 'flex'; + } + function resumeFromIdle() { + idleActive = false; + lastInteractionAt = Date.now(); + lastTickAt = Date.now(); // kein Zeitsprung beim Fortsetzen + if (idleOverlay) idleOverlay.style.display = 'none'; + } + // --- Spectator-Mode für Lehrkräfte --- // ?view=teacher schaltet Input ab und blendet ein dezentes Banner ein. function isSpectator() {