/** * useNavContext * * Provides prev/next artwork IDs scoped to the same author via API. */ import { useCallback } from 'react'; // Module-level cache for API calls const fallbackCache = new Map(); function normalizeNavigation(data) { if (!data || typeof data !== 'object') return null; const hasPrev = data.prev_id == null || (data.prev_url && data.prev_slug); const hasNext = data.next_id == null || (data.next_url && data.next_slug); if (!hasPrev || !hasNext) return null; return { prevId: data.prev_id ?? null, nextId: data.next_id ?? null, prevUrl: data.prev_url ?? null, nextUrl: data.next_url ?? null, prevPreview: data.prev_preview ?? null, nextPreview: data.next_preview ?? null, }; } async function fetchFallback(artworkId) { const key = String(artworkId); if (fallbackCache.has(key)) return fallbackCache.get(key); try { const res = await fetch(`/api/artworks/navigation/${artworkId}`, { headers: { Accept: 'application/json' }, }); if (!res.ok) return { prevId: null, nextId: null, prevUrl: null, nextUrl: null, prevPreview: null, nextPreview: null }; const data = await res.json(); const result = normalizeNavigation(data) || { prevId: null, nextId: null, prevUrl: null, nextUrl: null, prevPreview: null, nextPreview: null }; fallbackCache.set(key, result); return result; } catch { return { prevId: null, nextId: null, prevUrl: null, nextUrl: null, prevPreview: null, nextPreview: null }; } } export function useNavContext(currentArtworkId, embeddedNavigation = null) { /** * Always resolve via API to guarantee same-user navigation. */ const getNeighbors = useCallback(async () => { const embedded = normalizeNavigation(embeddedNavigation); if (embedded) return embedded; return fetchFallback(currentArtworkId); }, [currentArtworkId, embeddedNavigation]); return { getNeighbors }; }