From c30b349a48b1721e77b1b4549ad559c1f566e715 Mon Sep 17 00:00:00 2001 From: Simon Morley Date: Fri, 24 Apr 2026 20:32:38 +0100 Subject: [PATCH] front: pass ?preview=all through to API for gated-chain bypass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New previewParam() helper in src/api/client.ts reads window.location.search live per request and returns 'all' iff the URL has ?preview=all. Not cached — SPA navigation to/from the preview URL flips visibility immediately. SSR-safe via typeof window check. fetchEvents, fetchNetworks, fetchStats now include ?preview=all in their outbound requests when the caller's URL carries it. Other endpoints (validator profile, leaderboard, insights) are reachable directly and don't list gated networks; leaving them unmodified keeps the PR tight. No new frontend state. No route changes. Users hitting the site without the query param see exactly what they saw before (five networks post-Celestia-gating). Internal users with ?preview=all see all enabled networks. --- src/api/client.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/api/client.ts b/src/api/client.ts index 5d4abba..66bee58 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -26,6 +26,22 @@ import { getMockEvents, getMockNetworks, getMockStats, getMockValidator, getMock const BASE_URL = import.meta.env.VITE_API_URL || ''; const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true'; +/** + * Preview-bypass for gated networks (api side: migration 052). + * Returns 'all' iff the current URL has ?preview=all, else null. + * + * Live-reads window.location each call so SPA navigation to/from the + * preview URL flips the behaviour immediately. Not cached. + * + * SSR-safe fallback (typeof window check) for future build-time + * prerendering. + */ +function previewParam(): string | null { + if (typeof window === 'undefined') return null; + const p = new URLSearchParams(window.location.search).get('preview'); + return p === 'all' ? 'all' : null; +} + export async function fetchEvents(params?: { network?: string; search?: string; @@ -39,6 +55,8 @@ export async function fetchEvents(params?: { if (params?.search) qs.set('search', params.search); if (params?.cursor) qs.set('cursor', params.cursor); if (params?.limit) qs.set('limit', String(params.limit)); + const pv = previewParam(); + if (pv) qs.set('preview', pv); const query = qs.toString(); const res = await fetch(`${BASE_URL}/v1/events${query ? `?${query}` : ''}`); if (!res.ok) throw new Error(`API error: ${res.status}`); @@ -48,7 +66,9 @@ export async function fetchEvents(params?: { export async function fetchNetworks(): Promise> { if (USE_MOCK) return getMockNetworks(); - const res = await fetch(`${BASE_URL}/v1/networks`); + const pv = previewParam(); + const suffix = pv ? `?preview=${pv}` : ''; + const res = await fetch(`${BASE_URL}/v1/networks${suffix}`); if (!res.ok) throw new Error(`API error: ${res.status}`); return res.json() as Promise>; } @@ -56,7 +76,9 @@ export async function fetchNetworks(): Promise> { export async function fetchStats(): Promise> { if (USE_MOCK) return getMockStats(); - const res = await fetch(`${BASE_URL}/v1/stats`); + const pv = previewParam(); + const suffix = pv ? `?preview=${pv}` : ''; + const res = await fetch(`${BASE_URL}/v1/stats${suffix}`); if (!res.ok) throw new Error(`API error: ${res.status}`); return res.json() as Promise>; }