Frontend caching for live status to prevent reload on back button #9

Closed
opened 2026-04-26 11:47:26 +02:00 by wandabastyle · 0 comments
wandabastyle commented 2026-04-26 11:47:26 +02:00 (Migrated from github.com)

Summary

When the user navigates away from the front page (e.g., to watch a stream) and then hits the browser back button to return, the live status currently re-fetches from the server even if cached data exists. The server already has a prewarm worker that caches live status every 60 seconds with a 30 second TTL, but the frontend always fetches fresh on page mount.

Expected Behavior

  • Show cached live status immediately when returning to the front page
  • Fetch fresh data in the background
  • Only show loading/empty state on initial load (no prior cache)

Selected Solution: Frontend Caching

Implementation

web/src/lib/api.ts - Add caching layer to getLiveStatus()

const LIVE_STATUS_CACHE_KEY = 'twitchRelay.liveStatus';
const CACHE_MAX_AGE_MS = 60000; // 60 seconds

function getLiveStatusFromCache(): LiveStatusResponse | null {
  try {
    const cached = sessionStorage.getItem(LIVE_STATUS_CACHE_KEY);
    if (!cached) return null;
    const { data, timestamp } = JSON.parse(cached);
    if (Date.now() - timestamp > CACHE_MAX_AGE_MS) return null;
    return data;
  } catch {
    return null;
  }
}

function setLiveStatusCache(data: LiveStatusResponse): void {
  try {
    sessionStorage.setItem(LIVE_STATUS_CACHE_KEY, JSON.stringify({
      data,
      timestamp: Date.now()
    }));
  } catch {}
}

export async function getLiveStatus(): Promise<LiveStatusResponse> {
  const cached = getLiveStatusFromCache();
  if (cached) {
    fetch('/api/live-status')
      .then(res => res.ok ? res.json() : null)
      .then(data => { if (data) setLiveStatusCache(data); })
      .catch(() => {});
    return cached;
  }
  
  const response = await request('/api/live-status');
  if (!response.ok) {
    const payload = await safeJson(response);
    throw new Error(readError(payload));
  }
  const payload = await safeJson(response);
  if (!isObject(payload) || !isObject(payload.channels)) {
    throw new Error('live status payload is invalid');
  }
  setLiveStatusCache(payload);
  return { channels: payload.channels as Record<string, ChannelStatus> };
}

web/src/routes/+page.svelte - Update loadLiveStatus()

  • Remove initial call from initialize() - let API handle caching
  • Keep background polling (every 60s)

Why This Approach

  • Immediate display of cached data on back navigation
  • No empty state / loading flicker
  • Works regardless of server cache state
  • Background refresh keeps data fresh with minimal latency

Priority: Low (UX improvement)
Labels: enhancement

## Summary When the user navigates away from the front page (e.g., to watch a stream) and then hits the browser back button to return, the live status currently re-fetches from the server even if cached data exists. The server already has a prewarm worker that caches live status every 60 seconds with a 30 second TTL, but the frontend always fetches fresh on page mount. ## Expected Behavior - Show cached live status immediately when returning to the front page - Fetch fresh data in the background - Only show loading/empty state on initial load (no prior cache) ## Selected Solution: Frontend Caching ### Implementation **`web/src/lib/api.ts`** - Add caching layer to `getLiveStatus()` ```typescript const LIVE_STATUS_CACHE_KEY = 'twitchRelay.liveStatus'; const CACHE_MAX_AGE_MS = 60000; // 60 seconds function getLiveStatusFromCache(): LiveStatusResponse | null { try { const cached = sessionStorage.getItem(LIVE_STATUS_CACHE_KEY); if (!cached) return null; const { data, timestamp } = JSON.parse(cached); if (Date.now() - timestamp > CACHE_MAX_AGE_MS) return null; return data; } catch { return null; } } function setLiveStatusCache(data: LiveStatusResponse): void { try { sessionStorage.setItem(LIVE_STATUS_CACHE_KEY, JSON.stringify({ data, timestamp: Date.now() })); } catch {} } export async function getLiveStatus(): Promise<LiveStatusResponse> { const cached = getLiveStatusFromCache(); if (cached) { fetch('/api/live-status') .then(res => res.ok ? res.json() : null) .then(data => { if (data) setLiveStatusCache(data); }) .catch(() => {}); return cached; } const response = await request('/api/live-status'); if (!response.ok) { const payload = await safeJson(response); throw new Error(readError(payload)); } const payload = await safeJson(response); if (!isObject(payload) || !isObject(payload.channels)) { throw new Error('live status payload is invalid'); } setLiveStatusCache(payload); return { channels: payload.channels as Record<string, ChannelStatus> }; } ``` **`web/src/routes/+page.svelte`** - Update `loadLiveStatus()` - Remove initial call from `initialize()` - let API handle caching - Keep background polling (every 60s) ## Why This Approach - Immediate display of cached data on back navigation - No empty state / loading flicker - Works regardless of server cache state - Background refresh keeps data fresh with minimal latency Priority: Low (UX improvement) Labels: enhancement
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
wandabastyle/twitch_relay#9
No description provided.