SkateSpots/src/AppRouter.tsx
shakespeare.diy c3ef0247fc fix: switch to HashRouter for Capacitor Android compatibility
The primary cause of the white screen in the APK was BrowserRouter.
Capacitor serves files via https://localhost/ and the HTML5 History API
routing (BrowserRouter) fails to resolve routes in this context,
resulting in a blank white page.

Changes:
- Switch BrowserRouter → HashRouter in AppRouter.tsx
- Add DeepLinkHandler component (handles appUrlOpen for Amber callbacks
  and other deep links in native context)
- Fix Amber callback URL builder to use org.skatespots.app:// custom
  scheme when running in Capacitor (so Android fires an intent back into
  the app instead of navigating a browser)
- Fix getSearchParams() in amber.ts to extract query params from hash
  fragment (HashRouter URLs look like /#/amber-callback?event=XXX)
- Fix captureAmberCallback in main.tsx to check hash fragment too
- Remove debug error overlay (no longer needed)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-18 21:56:23 +07:00

130 lines
7.3 KiB
TypeScript

import { lazy, Suspense } from "react";
import { HashRouter, Route, Routes } from "react-router-dom";
import { ScrollToTop } from "./components/ScrollToTop";
import { DeepLinkHandler } from "./components/DeepLinkHandler";
// Eagerly load only the most-visited pages
import Index from "./pages/Index";
// Lazy load everything else — each page becomes its own JS chunk
const MapPage = lazy(() => import("./pages/MapPage"));
const EventsPage = lazy(() => import("./pages/EventsPage"));
const LabPage = lazy(() => import("./pages/LabPage"));
const WalletPage = lazy(() => import("./pages/WalletPage"));
const SettingsPage = lazy(() => import("./pages/SettingsPage"));
const CreateSpotPage = lazy(() => import("./pages/CreateSpotPage"));
const CreateShopPage = lazy(() => import("./pages/CreateShopPage"));
const CreateEventPage = lazy(() => import("./pages/CreateEventPage"));
const CreateListingPage = lazy(() => import("./pages/CreateListingPage"));
const CreateTourPage = lazy(() => import("./pages/CreateTourPage"));
const EditSpotPage = lazy(() => import("./pages/EditSpotPage"));
const EditShopPage = lazy(() => import("./pages/EditShopPage"));
const EditEventPage = lazy(() => import("./pages/EditEventPage"));
const GearSwapPage = lazy(() => import("./pages/GearSwapPage"));
const SpotDetailPage = lazy(() => import("./pages/SpotDetailPage"));
const ShopDetailPage = lazy(() => import("./pages/ShopDetailPage"));
const EventDetailPage = lazy(() => import("./pages/EventDetailPage"));
const OnboardingPage = lazy(() => import("./pages/OnboardingPage"));
const PyramidPage = lazy(() => import("./pages/PyramidPage"));
const BecomeCoachPage = lazy(() => import("./pages/BecomeCoachPage"));
const BecomeFilmerPage = lazy(() => import("./pages/BecomeFilmerPage"));
const BecomeEditorPage = lazy(() => import("./pages/BecomeEditorPage"));
const CoachDetailPage = lazy(() => import("./pages/CoachDetailPage"));
const TourDetailPage = lazy(() => import("./pages/TourDetailPage"));
const AboutPage = lazy(() => import("./pages/AboutPage"));
const FAQPage = lazy(() => import("./pages/FAQPage"));
const FoodPage = lazy(() => import("./pages/FoodPage"));
const CommunityStatsPage = lazy(() => import("./pages/CommunityStatsPage"));
const SpotCollectionsPage = lazy(() => import("./pages/SpotCollectionsPage"));
const CollectionDetailPage = lazy(() => import("./pages/CollectionDetailPage"));
const DirectMessagesPage = lazy(() => import("./pages/DirectMessagesPage"));
const ConversationPage = lazy(() => import("./pages/ConversationPage"));
const BountyLeaderboardPage = lazy(() => import("./pages/BountyLeaderboardPage"));
const SignersPage = lazy(() => import("./pages/SignersPage"));
const StashPage = lazy(() => import("./pages/StashPage"));
const NIP19Page = lazy(() => import("./pages/NIP19Page").then(m => ({ default: m.NIP19Page })));
const LicensePage = lazy(() => import("./pages/LicensePage"));
const SupportersPage = lazy(() => import("./pages/SupportersPage"));
const RiderCardPage = lazy(() => import("./pages/RiderCardPage"));
const AmberCallbackPage = lazy(() => import("./pages/AmberCallbackPage"));
const MarketplacePage = lazy(() => import("./pages/MarketplacePage"));
const NewsletterPage = lazy(() => import("./pages/NewsletterPage"));
const NotFound = lazy(() => import("./pages/NotFound"));
// Minimal inline fallback — no extra component needed
function PageLoader() {
return (
<div className="min-h-screen bg-black flex items-center justify-center">
<div className="w-8 h-8 border-2 border-[#ff0040] border-t-transparent rounded-full animate-spin" />
</div>
);
}
export function AppRouter() {
return (
<HashRouter>
<DeepLinkHandler />
<ScrollToTop />
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Index />} />
<Route path="/map" element={<MapPage />} />
<Route path="/events" element={<EventsPage />} />
<Route path="/lab" element={<LabPage />} />
{/* Legacy route redirect */}
<Route path="/coaches" element={<LabPage />} />
<Route path="/become-coach" element={<BecomeCoachPage />} />
<Route path="/become-filmer" element={<BecomeFilmerPage />} />
<Route path="/become-editor" element={<BecomeEditorPage />} />
<Route path="/coach/:pubkey/:identifier" element={<CoachDetailPage />} />
<Route path="/onboarding" element={<OnboardingPage />} />
<Route path="/pyramid" element={<PyramidPage />} />
<Route path="/marketplace" element={<GearSwapPage />} />
<Route path="/stats" element={<CommunityStatsPage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/faq" element={<FAQPage />} />
<Route path="/food" element={<FoodPage />} />
<Route path="/wallet" element={<WalletPage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/create-spot" element={<CreateSpotPage />} />
<Route path="/create-shop" element={<CreateShopPage />} />
<Route path="/create-event" element={<CreateEventPage />} />
<Route path="/create-listing" element={<CreateListingPage />} />
<Route path="/create-tour" element={<CreateTourPage />} />
<Route path="/edit-spot/:pubkey/:identifier" element={<EditSpotPage />} />
<Route path="/edit-shop/:pubkey/:identifier" element={<EditShopPage />} />
<Route path="/edit-event/:pubkey/:identifier" element={<EditEventPage />} />
<Route path="/spot/:pubkey/:identifier" element={<SpotDetailPage />} />
<Route path="/shop/:pubkey/:identifier" element={<ShopDetailPage />} />
<Route path="/event/:pubkey/:identifier" element={<EventDetailPage />} />
<Route path="/tour/:pubkey/:identifier" element={<TourDetailPage />} />
<Route path="/collections" element={<SpotCollectionsPage />} />
<Route path="/collections/:pubkey/:identifier" element={<CollectionDetailPage />} />
<Route path="/messages" element={<DirectMessagesPage />} />
<Route path="/messages/:npub" element={<ConversationPage />} />
{/* Bounty System */}
<Route path="/bounties" element={<BountyLeaderboardPage />} />
{/* Security & Signers */}
<Route path="/signers" element={<SignersPage />} />
{/* Stash - Private Inventory */}
<Route path="/stash" element={<StashPage />} />
{/* Info pages */}
<Route path="/card" element={<RiderCardPage />} />
<Route path="/license" element={<LicensePage />} />
<Route path="/supporters" element={<SupportersPage />} />
{/* Amber NIP-55 callback — Amber redirects here after signing/login */}
<Route path="/amber-callback" element={<AmberCallbackPage />} />
{/* Marketplace */}
<Route path="/marketplace-listings" element={<MarketplacePage />} />
{/* Newsletter */}
<Route path="/newsletter" element={<NewsletterPage />} />
{/* NIP-19 route for npub1, note1, naddr1, nevent1, nprofile1 */}
<Route path="/:nip19" element={<NIP19Page />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</Suspense>
</HashRouter>
);
}
export default AppRouter;