SkateSpots/src/AppRouter.tsx
shakespeare.diy b42c234709 Add Skater profiles (kind 35000) + /skaters directory
- New app-native Skater Profile kind 35000 with `skateskater` marker tag,
  the anti-spam boundary that keeps generic kind-0 Nostr profiles out of the
  directory (queried only from the community relay).
- Skater type, stance/style/level constants, and parser in types.ts.
- useSkaters + useUserSkaterProfile hooks.
- /skaters directory page: immersive hero, live count, search, style filter
  chips, ranked skater grid with NIP-05 verification badges and lazy author
  metadata.
- /become-skater opt-in flow (bio, photo, styles, stance, level, years, home
  spot, location w/ geohash, socials).
- Wire "Skaters" into the mobile nav and a cross-link banner in The Lab so
  plain skaters finally have a home.
- Document kind 35000 in NIP.md.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-02 20:58:51 +07:00

169 lines
9.8 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 the map — it's now the homepage
import MapPage from "./pages/MapPage";
// Lazy load everything else — each page becomes its own JS chunk
const Index = lazy(() => import("./pages/Index"));
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 BecomePhotographerPage = lazy(() => import("./pages/BecomePhotographerPage"));
const BecomeBuilderPage = lazy(() => import("./pages/BecomeBuilderPage"));
const BecomeArtistPage = lazy(() => import("./pages/BecomeArtistPage"));
const BecomeGraffitiPage = lazy(() => import("./pages/BecomeGraffitiPage"));
const BecomeTattooPage = lazy(() => import("./pages/BecomeTattooPage"));
const BecomeTrainerPage = lazy(() => import("./pages/BecomeTrainerPage"));
const BecomeMusicianPage = lazy(() => import("./pages/BecomeMusicianPage"));
const BecomeJudgePage = lazy(() => import("./pages/BecomeJudgePage"));
const BecomeSkaterPage = lazy(() => import("./pages/BecomeSkaterPage"));
const SkatersPage = lazy(() => import("./pages/SkatersPage"));
const CrewsPage = lazy(() => import("./pages/CrewsPage"));
const CreateCrewPage = lazy(() => import("./pages/CreateCrewPage"));
const EditCrewPage = lazy(() => import("./pages/EditCrewPage"));
const CrewDetailPage = lazy(() => import("./pages/CrewDetailPage"));
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 ContactsPage = lazy(() => import("./pages/ContactsPage"));
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 PrivacyPage = lazy(() => import("./pages/PrivacyPage"));
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 GlossaryPage = lazy(() => import("./pages/GlossaryPage"));
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>
{/* Map is the homepage */}
<Route path="/" element={<MapPage />} />
{/* /map redirects to / */}
<Route path="/map" element={<MapPage />} />
{/* Legacy home page still accessible */}
<Route path="/home" element={<Index />} />
<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="/become-photographer" element={<BecomePhotographerPage />} />
<Route path="/become-builder" element={<BecomeBuilderPage />} />
<Route path="/become-artist" element={<BecomeArtistPage />} />
<Route path="/become-graffiti" element={<BecomeGraffitiPage />} />
<Route path="/become-tattoo" element={<BecomeTattooPage />} />
<Route path="/become-trainer" element={<BecomeTrainerPage />} />
<Route path="/become-musician" element={<BecomeMusicianPage />} />
<Route path="/become-judge" element={<BecomeJudgePage />} />
<Route path="/skaters" element={<SkatersPage />} />
<Route path="/become-skater" element={<BecomeSkaterPage />} />
<Route path="/crews" element={<CrewsPage />} />
<Route path="/create-crew" element={<CreateCrewPage />} />
<Route path="/edit-crew/:pubkey/:identifier" element={<EditCrewPage />} />
<Route path="/crew/:pubkey/:identifier" element={<CrewDetailPage />} />
<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 />} />
<Route path="/contacts" element={<ContactsPage />} />
{/* 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="/privacy" element={<PrivacyPage />} />
<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 />} />
{/* Trick Glossary */}
<Route path="/glossary" element={<GlossaryPage />} />
{/* 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;