diff --git a/CHANGELOG.md b/CHANGELOG.md index 341a2d7f..57e8e7c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ## [Unreleased] +## [v0.51.593] — 2026-06-22 — Release UZ (gateway-poll visibility-listener cleanup) + +### Fixed + +- **The gateway-poll fallback's tab-refocus catch-up listener is now removed when polling stops and correctly re-attaches when it restarts.** Follow-up to #4730: the `visibilitychange` catch-up listener was added once via a sticky flag but never removed in `stopGatewayPollFallback()` — so after the poll stopped (e.g. when live SSE reconnects) the listener lingered, and on a later poll restart the sticky flag prevented re-attaching it, leaving the refocus catch-up silently disabled. The handler is now tracked in a module variable, removed on stop, and re-added on the next start. Thanks @akrhin. (#4730) + ## [v0.51.592] — 2026-06-22 — Release UY (skip gateway poll when hidden/streaming + smd re-init) ### Fixed diff --git a/static/sessions.js b/static/sessions.js index 1867768e..7f72e516 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -4098,6 +4098,8 @@ function ensureSessionEventsSSE(){ if(typeof window!=='undefined') window.refreshSessionList = refreshSessionList; +let _gatewayPollVisibilityHandler = null; // saved so stopGatewayPollFallback can remove it + function startGatewayPollFallback(ms){ const intervalMs = Math.max(5000, Number(ms) || _gatewayFallbackPollMs); if(_gatewayPollTimer) clearInterval(_gatewayPollTimer); @@ -4110,13 +4112,14 @@ function startGatewayPollFallback(ms){ }, intervalMs); // Visibility catch-up: refresh immediately when tab re-gains focus, // so no gateway updates are dropped during hidden-skip periods. - if(typeof document !== 'undefined' && !document._hermesGatewayPollVisibilityHook){ - document.addEventListener('visibilitychange', () => { + // Save the handler so stopGatewayPollFallback can removeEventListener it (#4730 review). + if(typeof document !== 'undefined' && !_gatewayPollVisibilityHandler){ + _gatewayPollVisibilityHandler = () => { if(!document.hidden && typeof renderSessionList === 'function'){ void renderSessionList({deferWhileInteracting:false}); } - }); - document._hermesGatewayPollVisibilityHook = true; + }; + document.addEventListener('visibilitychange', _gatewayPollVisibilityHandler); } } @@ -4125,6 +4128,10 @@ function stopGatewayPollFallback(){ clearInterval(_gatewayPollTimer); _gatewayPollTimer = null; } + if(_gatewayPollVisibilityHandler && typeof document !== 'undefined'){ + document.removeEventListener('visibilitychange', _gatewayPollVisibilityHandler); + _gatewayPollVisibilityHandler = null; + } } function _gatewaySessionSnapshotKey(sessions){