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,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import * as api from '$lib/api';
|
||||
import type { RedeployTrigger } from '$lib/api';
|
||||
import { triggersCache } from '$lib/stores/caches';
|
||||
import { IconPlus, IconRefresh } from '$lib/components/icons';
|
||||
import ForgeHero from '$lib/components/ForgeHero.svelte';
|
||||
import { t } from '$lib/i18n';
|
||||
@@ -13,9 +13,11 @@
|
||||
const KNOWN_KINDS = ['registry', 'git', 'manual', 'schedule', 'webhook', 'logscan'] as const;
|
||||
type KnownKind = (typeof KNOWN_KINDS)[number];
|
||||
|
||||
let triggers = $state<RedeployTrigger[]>([]);
|
||||
let loading = $state(true);
|
||||
let error = $state('');
|
||||
// Cache-backed (stale-while-revalidate) so revisiting this tab renders the
|
||||
// list immediately instead of flashing the cold skeleton. See caches.ts.
|
||||
const triggers = $derived<RedeployTrigger[]>($triggersCache.value);
|
||||
const loading = $derived($triggersCache.loading);
|
||||
const error = $derived($triggersCache.error);
|
||||
let kindFilter = $state<'all' | KnownKind | string>('all');
|
||||
|
||||
const filtered = $derived(
|
||||
@@ -69,17 +71,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function load(): Promise<void> {
|
||||
loading = true;
|
||||
error = '';
|
||||
try {
|
||||
triggers = await api.listTriggers();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Failed to load triggers';
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
const load = () => triggersCache.refresh();
|
||||
|
||||
onMount(load);
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user