Auto-refresh sidebar data so a stale tab picks up backend polls

Verified the PoE2 24h-change computation itself is correct (simulated
24+ hours of real hourly polling against the actual recordRate/
rateAtOrBefore/markPolled functions — change24h starts populating
right on schedule at hour 24). The real problem: weather/stocks/poe2/
bookmarks are only fetched once when the layout first loads.
SvelteKit never re-runs a load() on its own — only on navigation or
explicit invalidation — so a browser tab left open never sees any of
those widgets update no matter how long the backend has been polling.

Named the layout's load() dependency ('app:sidebar') and invalidate
just that on a 5-minute timer, rather than invalidateAll() — which
would also re-run page-level loads and could reset things like the
home feed's own pagination state on every refresh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014c1L8ghNBFjfiH64UMViP8
This commit is contained in:
Claude
2026-07-26 00:00:11 +00:00
parent 0ff8da8363
commit d0bf6d18bc
2 changed files with 18 additions and 2 deletions
+13 -1
View File
@@ -1,7 +1,8 @@
<script lang="ts">
import '../lib/styles/app.css';
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { invalidateAll } from '$app/navigation';
import { invalidateAll, invalidate } from '$app/navigation';
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
import PrivateAccessModal from '$lib/components/PrivateAccessModal.svelte';
import Sidebar from '$lib/components/sidebar/Sidebar.svelte';
@@ -13,6 +14,17 @@
let showLoginModal = $state(false);
// Weather/Stocks/PoE2/Bookmarks are all backend-polled on their own cadence (15m-1h) —
// without this, a tab left open never sees those updates, since SvelteKit only re-runs
// a load() on navigation or explicit invalidation, never on a timer by itself. Scoped
// to this layout's own 'app:sidebar' dependency (see +layout.ts) rather than
// invalidateAll(), so it doesn't also re-run page-level loads — e.g. the home feed's
// own pagination state would otherwise reset every refresh.
onMount(() => {
const interval = setInterval(() => invalidate('app:sidebar'), 5 * 60_000);
return () => clearInterval(interval);
});
// Admin pages already use full page width for their own tab UI — the sidebar's utility
// widgets don't belong there, unlike every reader-facing route (home, category,
// article, event, more, weather).
+5 -1
View File
@@ -2,7 +2,11 @@ import type { LayoutLoad } from './$types';
import { getCategories, getEvents, getWeather, getStocks, getBookmarks, getPoe2 } from '$lib/api';
import { getPrivateAccessStatus } from '$lib/privateAccess';
export const load: LayoutLoad = async ({ fetch, data }) => {
// Named so the layout can be re-fetched on its own (see +layout.svelte's periodic
// invalidate('app:sidebar')) without disturbing page-level loads — e.g. the home feed's
// own pagination state, which a blanket invalidateAll() would reset every refresh.
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
depends('app:sidebar');
const [categories, events, privateAccess, weather, stocks, bookmarks, poe2] = await Promise.all([
getCategories(fetch),
getEvents(fetch),