From 0d765961afd2bd2072389bc73c21c1e506dcd2e2 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 23 Aug 2026 12:50:30 +0200 Subject: [PATCH] Idle-Pause fuer Heli funktionsfaehig + iframe-aware Interaktionserfassung - Interaktionen aus gleich-origin iFrames (Heli-Start/-Flug/-Landung) zaehlen jetzt (periodisches _hookFrame), plus postMessage {type:'ggs-activity'} + __GGS_LIVE__.ping(). Aktives Steuern gilt nicht mehr faelschlich als idle. - EVENTS erweitert (keyup/touchmove/pointermove). Heli in IDLE_PAUSE_SIMS aufgenommen. Co-Authored-By: Claude Opus 4.8 --- App/assets/js/live-client.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/App/assets/js/live-client.js b/App/assets/js/live-client.js index 85f7a6d..2ca703b 100644 --- a/App/assets/js/live-client.js +++ b/App/assets/js/live-client.js @@ -60,7 +60,7 @@ // Sichtbare Idle-Pause vorerst NUR dort, wo sie getestet ist. Sonst stört sie // passive Phasen (z. B. die Heli-Flug-Animation, wo man minutenlang nur zuschaut). // Freischalten pro App: Sim hier eintragen ODER window.GGS_IDLE_PAUSE=true setzen. - var IDLE_PAUSE_SIMS = ['klima', 'klima-3d']; + var IDLE_PAUSE_SIMS = ['klima', 'klima-3d', 'heli']; var IDLE_PAUSE_ENABLED = IDLE_PAUSE_SIMS.indexOf(simId) !== -1 || window.GGS_IDLE_PAUSE === true; var MAX_SESSION_MS = 7200000; // 120 min — Plattform-Hardcap var TICK_MS = 1000; // Auflösung der activeMs-Akkumulation @@ -76,11 +76,34 @@ lastInteractionAt = Date.now(); } // Passive Listener, damit Scrollen/Touch nicht ausgebremst wird. - var EVENTS = ['mousemove', 'mousedown', 'keydown', 'touchstart', 'wheel', 'click', 'pointerdown']; + var EVENTS = ['mousemove', 'mousedown', 'keydown', 'keyup', 'touchstart', 'touchmove', 'wheel', 'click', 'pointerdown', 'pointermove']; EVENTS.forEach(function (ev) { window.addEventListener(ev, onInteraction, { passive: true, capture: true }); }); + // Interaktionen auch aus (gleich-origin) iFrames erfassen — sonst gilt aktives + // Steuern in einem Sub-iFrame (z. B. Heli-Start/-Flug/-Landung) fälschlich als + // idle, obwohl die Schüler:in gerade lenkt. iFrames werden dynamisch geladen/ + // getauscht → periodisch nachhängen. Cross-origin wirft → still ignorieren. + function _hookFrame(doc) { + if (!doc || doc.__ggsActivityHooked) return; + doc.__ggsActivityHooked = true; + EVENTS.forEach(function (ev) { + try { doc.addEventListener(ev, onInteraction, { passive: true, capture: true }); } catch (_) {} + }); + } + function _scanFrames() { + var frames = document.getElementsByTagName('iframe'); + for (var i = 0; i < frames.length; i++) { + try { _hookFrame(frames[i].contentDocument); } catch (_) { /* cross-origin */ } + } + } + setInterval(_scanFrames, 2500); + // Fallback: Sims/iFrames können aktives Arbeiten explizit signalisieren. + window.addEventListener('message', function (e) { + if (e && e.data && e.data.type === 'ggs-activity') onInteraction(); + }); + // Tick-Loop für activeMs-Akkumulation. Läuft auch wenn Sim nichts tut, // hält die Buchhaltung sauber. Inkrementiert in Echtzeit-Schritten, deckt // damit auch große Lücken (Tab-Sleep) korrekt ab. @@ -129,6 +152,8 @@ get paused() { return paused; }, // Sim ruft dies bei Pause/Fortsetzen — friert die Aktivzeit-Buchhaltung ein. setPaused: function (v) { paused = !!v; }, + // Sim/iFrame kann aktives Arbeiten signalisieren (setzt den Idle-Zähler zurück). + ping: function () { onInteraction(); }, MAX_SESSION_MS: MAX_SESSION_MS, IDLE_THRESHOLD_MS: IDLE_THRESHOLD_MS, });