From 031f99e626f52ea7baac9155b0cbe6e4fcfbfb31 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Sun, 18 Oct 2026 21:26:24 +0200 Subject: feat: poll server data so shared pages stay near-live Refetch the current page every 15s while the tab is visible, gated by a pure refresh policy: edit pages, hidden or offline tabs and in-flight navigations are left alone. Replaces refresh-on-refocus with auto-refresh. --- src/lib/auto-refresh.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/lib/auto-refresh.ts (limited to 'src/lib/auto-refresh.ts') diff --git a/src/lib/auto-refresh.ts b/src/lib/auto-refresh.ts new file mode 100644 index 0000000..72bcdb9 --- /dev/null +++ b/src/lib/auto-refresh.ts @@ -0,0 +1,59 @@ +import { invalidateAll } from '$app/navigation'; +import { navigating, page } from '$app/stores'; +import { get } from 'svelte/store'; +import { shouldAutoRefresh } from '$lib/shared/refresh-policy'; + +/** Away for at least this long before refocus re-runs the page load. */ +const MIN_AWAY_MS = 10_000; + +/** Poll cadence: near-live for a shared list, cheap for one open tab. */ +const POLL_INTERVAL_MS = 15_000; + +/** + * Keeps the current page near-live: polls the server while the tab is visible, + * and refetches on refocus after an absence. Call from `onMount`; the returned + * function tears the listeners and timer down. + */ +export function startAutoRefresh(): () => void { + // When the tab stopped being the active one; null while it is active. + let deactivatedAt: number | null = null; + + const conditions = () => ({ + routeId: get(page).route.id, + visible: document.visibilityState === 'visible', + online: navigator.onLine, + navigating: get(navigating) !== null, + }); + + const deactivate = () => { + deactivatedAt ??= Date.now(); + }; + + const activate = () => { + if (deactivatedAt === null) return; + const awayMs = Date.now() - deactivatedAt; + deactivatedAt = null; + if (awayMs >= MIN_AWAY_MS && shouldAutoRefresh(conditions())) + void invalidateAll(); + }; + + const onVisibilityChange = () => { + if (document.visibilityState === 'hidden') deactivate(); + else activate(); + }; + + const timer = setInterval(() => { + if (shouldAutoRefresh(conditions())) void invalidateAll(); + }, POLL_INTERVAL_MS); + + document.addEventListener('visibilitychange', onVisibilityChange); + window.addEventListener('blur', deactivate); + window.addEventListener('focus', activate); + + return () => { + clearInterval(timer); + document.removeEventListener('visibilitychange', onVisibilityChange); + window.removeEventListener('blur', deactivate); + window.removeEventListener('focus', activate); + }; +} -- cgit v1.3