aboutsummaryrefslogtreecommitdiff
path: root/src/lib/auto-refresh.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/auto-refresh.ts')
-rw-r--r--src/lib/auto-refresh.ts59
1 files changed, 59 insertions, 0 deletions
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);
+ };
+}