Plattformweit: Idle-Pause-Overlay nach 3 min Untaetigkeit mit "Weiter"-Button
live-client.js (laedt in JEDER Sim): nach 3 min ohne Interaktion sichtbare Pause (friert Aktivzeit ein -> Idle zaehlt nicht als Arbeitszeit), Weiterarbeiten per Klick. Heartbeat meldet __paused/__idlePaused. Kein Sim-Code noetig. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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 = '<div style="font-size:3.6rem;margin-bottom:.6rem">😴</div>'
|
||||
+ '<div style="font-size:1.55rem;font-weight:700;margin-bottom:.5rem">Kurze Pause</div>'
|
||||
+ '<div style="font-size:1rem;font-weight:500;opacity:.9;max-width:440px;line-height:1.45;margin-bottom:1.5rem">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.</div>'
|
||||
+ '<button id="ggs-idle-resume" style="font-size:1.05rem;font-weight:700;padding:.75rem 2.2rem;border:none;border-radius:10px;background:#fff;color:#1f4b37;cursor:pointer;box-shadow:0 3px 10px rgba(0,0,0,.25)">Weiter</button>';
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user