import { useEffect } from 'react'; import { isNativePlatform } from '@/lib/platform'; /** * Hover / touch prefetch for the primary navigation destinations. * * The route chunks stay lazy (so first paint is not blocked), but once the user * shows intent — hovers a menu row, tabs into a link, or touches it — the page * module starts downloading. On mobile the touchstart head start is often enough * to avoid the page skeleton entirely. Only modules are fetched; no page hooks * mount, so this never triggers relay work or an Amber prompt. */ const loaders: Record Promise> = { '/lab': () => import('@/pages/LabPage'), '/events': () => import('@/pages/EventsPage'), '/crews': () => import('@/pages/CrewsPage'), '/community': () => import('@/pages/CommunityPage'), '/skaters': () => import('@/pages/SkatersPage'), '/messages': () => import('@/pages/DirectMessagesPage'), '/settings': () => import('@/pages/SettingsPage'), '/wallet': () => import('@/pages/WalletPage'), '/stash': () => import('@/pages/StashPage'), '/marketplace': () => import('@/pages/MarketplaceHubPage'), '/stats': () => import('@/pages/CommunityStatsPage'), '/collections': () => import('@/pages/SpotCollectionsPage'), '/glossary': () => import('@/pages/GlossaryPage'), '/about': () => import('@/pages/AboutPage'), '/card': () => import('@/pages/RiderCardPage'), '/supporters': () => import('@/pages/SupportersPage'), '/newsletter': () => import('@/pages/NewsletterPage'), }; const preloaded = new Set(); function routePath(anchor: HTMLAnchorElement): string | null { const href = anchor.getAttribute('href') ?? ''; if (!href || href.startsWith('http') || href.startsWith('mailto:') || href.startsWith('tel:')) { return null; } // HashRouter renders to="#/lab" as href="#/lab". if (href.startsWith('#/')) return href.slice(1).split('?')[0] || '/'; try { const url = new URL(anchor.href, window.location.href); if (url.hash.startsWith('#/')) return url.hash.slice(1).split('?')[0] || '/'; return url.pathname; } catch { return null; } } function preloadFromTarget(target: EventTarget | null): void { if (!(target instanceof Element)) return; const anchor = target.closest('a[href]'); if (!(anchor instanceof HTMLAnchorElement)) return; const path = routePath(anchor); if (!path || preloaded.has(path)) return; const loader = loaders[path]; if (!loader) return; preloaded.add(path); void loader().catch(() => { // A failed prefetch is harmless; lazyWithRetry will retry on navigation. preloaded.delete(path); }); } export function RoutePreloader() { useEffect(() => { if (isNativePlatform()) return; const onIntent = (event: Event) => preloadFromTarget(event.target); document.addEventListener('pointerover', onIntent, { capture: true, passive: true }); document.addEventListener('focusin', onIntent, { capture: true, passive: true }); document.addEventListener('touchstart', onIntent, { capture: true, passive: true }); return () => { document.removeEventListener('pointerover', onIntent, true); document.removeEventListener('focusin', onIntent, true); document.removeEventListener('touchstart', onIntent, true); }; }, []); return null; }