feat(web): stale-while-revalidate caches to eliminate tab-switch flicker
Build / build (push) Failing after 4m51s
Build / build (push) Failing after 4m51s
Sidebar tabs, Settings, and drill-in detail pages re-fetched on every
visit (loading=true + onMount), flashing an empty skeleton frame on each
navigation. Add an SWR cache layer so revisiting a view renders cached
data instantly while refreshing in the background.
- resourceCache.ts: single-value + keyed (per-id) SWR cache factories
- caches.ts: per-resource cache instances; resetAllCaches() on logout
- eventsSnapshot.ts: warm-seed snapshot for the SSE/paginated events page
- List/sidebar pages read $cache.value via $derived, refresh() on mount;
mutations refresh the cache
- Settings forms seed once from settingsCache (edit-safe) and refetch
after save (PUT /api/settings returns {status}, not the Settings object)
- Detail [id] pages warm-seed per id; apps/[id] seeds {workload,containers},
resets non-seeded panels on warm nav, clears workload on 404, and
invalidates its cache entry on delete
Deferred (still cold-fetch): triggers/[id] (webhook secret + multi-fetch
body gate), apps/new (create wizard).
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { listProxyRoutes } from '$lib/api';
|
||||
import { get } from 'svelte/store';
|
||||
import type { ProxyRoute, ProxyRouteSource } from '$lib/types';
|
||||
import { proxyRoutesCache } from '$lib/stores/caches';
|
||||
import { toasts } from '$lib/stores/toast';
|
||||
import { t } from '$lib/i18n';
|
||||
import Skeleton from '$lib/components/Skeleton.svelte';
|
||||
@@ -10,8 +11,10 @@
|
||||
|
||||
type SourceFilter = 'all' | ProxyRouteSource;
|
||||
|
||||
let routes = $state<ProxyRoute[]>([]);
|
||||
let loading = $state(true);
|
||||
// Cache-backed (stale-while-revalidate) so revisiting this tab renders the
|
||||
// routes immediately instead of flashing the cold skeleton. See caches.ts.
|
||||
const routes = $derived<ProxyRoute[]>($proxyRoutesCache.value);
|
||||
const loading = $derived($proxyRoutesCache.loading);
|
||||
let search = $state('');
|
||||
let sourceFilter = $state<SourceFilter>('all');
|
||||
|
||||
@@ -66,14 +69,11 @@
|
||||
}
|
||||
|
||||
async function loadRoutes() {
|
||||
loading = true;
|
||||
try {
|
||||
routes = await listProxyRoutes();
|
||||
} catch (err) {
|
||||
toasts.error(err instanceof Error ? err.message : $t('proxies.loadFailed'));
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
await proxyRoutesCache.refresh();
|
||||
// Cache stores the error rather than throwing; surface it as a toast to
|
||||
// preserve the page's prior failure UX.
|
||||
const { error } = get(proxyRoutesCache);
|
||||
if (error) toasts.error(error || $t('proxies.loadFailed'));
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
|
||||
Reference in New Issue
Block a user