diff --git a/AGENTS.md b/AGENTS.md index 9610dff..ddd3483 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -150,13 +150,16 @@ session with **no other memory of this project**. So: MOTIS 2, open CORS) — note origin/destination go to that service, unlike road modes. **No external map-app handoff anywhere** (product decision) and no scooter/moped profile. -- **⚠️ `Layout` is NOT a router layout** — ~62 pages wrap themselves in - ``, so its chrome (`NotificationBell`, `SasquatchGuide`, nav) **remounts - on every navigation**. Consequence: any *decrypt-on-mount* hook in that chrome - re-opens Amber (NIP-55) on every page change. Encrypted hooks must be gated for - interactive signers: `staleTime/gcTime: Infinity`, `refetchOnMount: false`, - `refetchOnReconnect: false` (see `useSyncedAppData` / `useStash`). Hoisting - `Layout` into the router would fix the whole class — a known follow-up. +- **`Layout` is a router layout** — `AppRouter` wraps every route (except + `/amber-callback` and the `*` 404) in **one** `}>` + with an ``, so the chrome (header, nav, notifications, Sasquatch) + mounts **once** and survives navigation. Pages render **fragments** (`<>…`), + never ``. The map is full-bleed via a route check (`isFullBleed` in + `Layout`), not a per-page `noPadding` prop. **Don't reintroduce per-page + `` wrappers** — remounting the chrome re-runs its hooks (and with Amber + that means signer popups). Encrypted-on-mount hooks are also gated for + interactive signers (`staleTime/gcTime: Infinity`, `refetchOnMount: false`) — + see `useSyncedAppData` / `useStash`. - **Mints directory**: `/mints` page lists Cashu mints from the community **MintRadar** project. MintRadar has **no CORS**, so it's fetched same-origin at `/api/mints/*` — prod proxies via `public/_redirects` (`/api/mints/* → diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index 307eb20..9b43724 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -5,6 +5,7 @@ import { ScrollToTop } from "./components/ScrollToTop"; import { BECOME_ROLES } from "./lib/becomeRoles"; import { DeepLinkHandler } from "./components/DeepLinkHandler"; import { RouteErrorBoundary } from "./components/RouteErrorBoundary"; +import { Layout } from "./components/Layout"; // The map is the homepage, but lazy-loading it keeps its chunk (plus the // IndexedDB offline-cache layer) out of the initial entry bundle. @@ -93,6 +94,10 @@ export function AppRouter() { }> + {/* One persistent layout for the whole app: the chrome (header, nav, + notifications, Sasquatch, scroll position) mounts ONCE and survives + navigation. Pages no longer wrap themselves in . */} + }> {/* Map is the homepage */} } /> {/* /map redirects to / */} @@ -162,8 +167,6 @@ export function AppRouter() { } /> } /> } /> - {/* Amber NIP-55 callback — Amber redirects here after signing/login */} - } /> {/* Marketplace */} } /> {/* Newsletter */} @@ -172,6 +175,11 @@ export function AppRouter() { } /> {/* NIP-19 route for npub1, note1, naddr1, nevent1, nprofile1 */} } /> + + + {/* Outside the layout: Amber's callback + 404 (render no chrome). */} + {/* Amber NIP-55 callback — Amber redirects here after signing/login */} + } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> diff --git a/src/components/CraftProfileForm.tsx b/src/components/CraftProfileForm.tsx index b53ef26..96cec9e 100644 --- a/src/components/CraftProfileForm.tsx +++ b/src/components/CraftProfileForm.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -295,7 +294,7 @@ export function CraftProfileForm({ config }: { config: CraftFormConfig }) { if (!user) { return ( - + <>
@@ -306,7 +305,7 @@ export function CraftProfileForm({ config }: { config: CraftFormConfig }) {
-
+ ); } @@ -314,7 +313,7 @@ export function CraftProfileForm({ config }: { config: CraftFormConfig }) { const entityCoord = `${kind}:${user.pubkey}:${identifier}`; return ( - + <>
{/* Header */}
@@ -572,6 +571,6 @@ export function CraftProfileForm({ config }: { config: CraftFormConfig }) {
- + ); } diff --git a/src/components/ErrorState.tsx b/src/components/ErrorState.tsx index cd96d5a..c1c9b35 100644 --- a/src/components/ErrorState.tsx +++ b/src/components/ErrorState.tsx @@ -1,7 +1,6 @@ import { AlertTriangle, RefreshCw } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; -import { Layout } from '@/components/Layout'; import { cn } from '@/lib/utils'; /** @@ -46,10 +45,10 @@ export function PageError(props: { onRetry?: () => void; }) { return ( - + <>
-
+ ); } diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index b78aafe..242bb52 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,5 +1,5 @@ import { useState, useRef, lazy, Suspense } from 'react'; -import { Link, useLocation } from 'react-router-dom'; +import { Link, Outlet, useLocation } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; import { cn } from '@/lib/utils'; import { isNativePlatform } from '@/lib/platform'; @@ -100,9 +100,12 @@ function hasCashuWalletHint(): boolean { } interface LayoutProps { - children: React.ReactNode; + /** Page content. Optional: when rendered as a router layout (via ) + * there are no children. */ + children?: React.ReactNode; /** If true, removes the bottom padding that prevents bottom nav overlap. - * Use for full-bleed pages like the map that manage their own height. */ + * Use for full-bleed pages like the map that manage their own height. + * The map routes get this automatically (see `isFullBleed`). */ noPadding?: boolean; } @@ -491,6 +494,9 @@ function MobileNav({ export function Layout({ children, noPadding }: LayoutProps) { const location = useLocation(); + // The map is full-bleed (it manages its own height), so it gets no bottom + // padding even though it shares this one persistent layout with every route. + const isFullBleed = noPadding || location.pathname === '/' || location.pathname === '/map'; const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [showStatusDialog, setShowStatusDialog] = useState(false); const [showCustomizeNav, setShowCustomizeNav] = useState(false); @@ -1008,8 +1014,8 @@ export function Layout({ children, noPadding }: LayoutProps) { {/* Main Content — extra bottom padding on mobile so the fixed bottom nav never overlaps */} {/* noPadding: full-bleed pages (map) manage their own height */} -
- {children} +
+ {children ?? }
{/* Footer - Desktop */} diff --git a/src/components/NewsletterIssueView.tsx b/src/components/NewsletterIssueView.tsx index 18ba199..a1556bf 100644 --- a/src/components/NewsletterIssueView.tsx +++ b/src/components/NewsletterIssueView.tsx @@ -5,7 +5,6 @@ import { useSeoMeta } from '@unhead/react'; import { nip19 } from 'nostr-tools'; import { useQuery } from '@tanstack/react-query'; import { ArrowLeft, Radio, Calendar } from 'lucide-react'; -import { Layout } from '@/components/Layout'; import { Skeleton } from '@/components/ui/skeleton'; import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'; import { ZapButton } from '@/components/ZapButton'; @@ -126,7 +125,7 @@ export function NewsletterIssueView({ pubkey, identifier }: { pubkey: string; id if (isLoading) { return ( - + <>
@@ -137,7 +136,7 @@ export function NewsletterIssueView({ pubkey, identifier }: { pubkey: string; id
-
+ ); } @@ -153,7 +152,7 @@ export function NewsletterIssueView({ pubkey, identifier }: { pubkey: string; id }); return ( - + <>
-
+ ); } diff --git a/src/pages/AboutPage.tsx b/src/pages/AboutPage.tsx index f2ace54..4a88ac6 100644 --- a/src/pages/AboutPage.tsx +++ b/src/pages/AboutPage.tsx @@ -1,6 +1,5 @@ import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { useCurrentUser } from '@/hooks/useCurrentUser'; @@ -54,7 +53,7 @@ Version: v0.1.0`; const bugReportUrl = `/messages/npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx?message=${encodeURIComponent(bugReportTemplate)}`; return ( - + <>
{/* Hero */}
@@ -321,6 +320,6 @@ Version: v0.1.0`;
-
+ ); } diff --git a/src/pages/BecomeBuilderPage.tsx b/src/pages/BecomeBuilderPage.tsx index 518f286..874992c 100644 --- a/src/pages/BecomeBuilderPage.tsx +++ b/src/pages/BecomeBuilderPage.tsx @@ -3,7 +3,6 @@ import { useSeoMeta } from '@unhead/react'; import { useNavigate, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -312,7 +311,7 @@ export default function BecomeBuilderPage() { if (!user) { return ( - + <>
@@ -323,14 +322,14 @@ export default function BecomeBuilderPage() {
-
+ ); } const hasNip05 = metadata?.nip05; return ( - + <>
@@ -624,6 +623,6 @@ export default function BecomeBuilderPage() {
- + ); } diff --git a/src/pages/BecomeCoachPage.tsx b/src/pages/BecomeCoachPage.tsx index d792089..dc1de32 100644 --- a/src/pages/BecomeCoachPage.tsx +++ b/src/pages/BecomeCoachPage.tsx @@ -3,7 +3,6 @@ import { useSeoMeta } from '@unhead/react'; import { useNavigate, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -250,7 +249,7 @@ export default function BecomeCoachPage() { if (!user) { return ( - + <>
@@ -261,14 +260,14 @@ export default function BecomeCoachPage() {
-
+ ); } const hasNip05 = metadata?.nip05; return ( - + <>
@@ -535,6 +534,6 @@ export default function BecomeCoachPage() {
- + ); } diff --git a/src/pages/BecomeEditorPage.tsx b/src/pages/BecomeEditorPage.tsx index f103a24..dfb9723 100644 --- a/src/pages/BecomeEditorPage.tsx +++ b/src/pages/BecomeEditorPage.tsx @@ -3,7 +3,6 @@ import { useSeoMeta } from '@unhead/react'; import { useNavigate, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -245,7 +244,7 @@ export default function BecomeEditorPage() { if (!user) { return ( - + <>
@@ -256,14 +255,14 @@ export default function BecomeEditorPage() {
-
+ ); } const hasNip05 = !!author.data?.metadata?.nip05; return ( - + <>
@@ -530,6 +529,6 @@ export default function BecomeEditorPage() {
-
+ ); } diff --git a/src/pages/BecomeFilmerPage.tsx b/src/pages/BecomeFilmerPage.tsx index 042688b..ed315a7 100644 --- a/src/pages/BecomeFilmerPage.tsx +++ b/src/pages/BecomeFilmerPage.tsx @@ -3,7 +3,6 @@ import { useSeoMeta } from '@unhead/react'; import { useNavigate, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -274,7 +273,7 @@ export default function BecomeFilmerPage() { if (!user) { return ( - + <>
@@ -285,14 +284,14 @@ export default function BecomeFilmerPage() {
-
+ ); } const hasNip05 = metadata?.nip05; return ( - + <>
@@ -601,6 +600,6 @@ export default function BecomeFilmerPage() {
- + ); } diff --git a/src/pages/BecomePhotographerPage.tsx b/src/pages/BecomePhotographerPage.tsx index 5cb52ee..ec717cf 100644 --- a/src/pages/BecomePhotographerPage.tsx +++ b/src/pages/BecomePhotographerPage.tsx @@ -3,7 +3,6 @@ import { useSeoMeta } from '@unhead/react'; import { useNavigate, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -333,7 +332,7 @@ export default function BecomePhotographerPage() { if (!user) { return ( - + <>
@@ -344,14 +343,14 @@ export default function BecomePhotographerPage() {
-
+ ); } const hasNip05 = metadata?.nip05; return ( - + <>
@@ -650,6 +649,6 @@ export default function BecomePhotographerPage() {
- + ); } diff --git a/src/pages/BecomeSkaterPage.tsx b/src/pages/BecomeSkaterPage.tsx index 516eb26..f524d54 100644 --- a/src/pages/BecomeSkaterPage.tsx +++ b/src/pages/BecomeSkaterPage.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -210,7 +209,7 @@ export default function BecomeSkaterPage() { if (!user) { return ( - + <>
@@ -223,7 +222,7 @@ export default function BecomeSkaterPage() {
-
+ ); } @@ -232,7 +231,7 @@ export default function BecomeSkaterPage() { const levelEntries = Object.entries(SKATER_LEVELS) as [SkaterLevel, { label: string; emoji: string; color: string }][]; return ( - + <>
@@ -666,6 +665,6 @@ export default function BecomeSkaterPage() { - + ); } diff --git a/src/pages/BountyLeaderboardPage.tsx b/src/pages/BountyLeaderboardPage.tsx index 7f276c5..0a111dc 100644 --- a/src/pages/BountyLeaderboardPage.tsx +++ b/src/pages/BountyLeaderboardPage.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { ErrorState } from '@/components/ErrorState'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -190,16 +189,16 @@ export default function BountyLeaderboardPage() { if (shopsError || spotsError) { return ( - + <>
{ refetchShops(); refetchSpots(); }} />
-
+ ); } return ( - + <>
{/* Header */}
@@ -474,6 +473,6 @@ export default function BountyLeaderboardPage() {
- + ); } diff --git a/src/pages/CoachDetailPage.tsx b/src/pages/CoachDetailPage.tsx index 2346872..558c2f1 100644 --- a/src/pages/CoachDetailPage.tsx +++ b/src/pages/CoachDetailPage.tsx @@ -1,6 +1,5 @@ import { useSeoMeta } from '@unhead/react'; import { useParams, Link, useNavigate } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { PageError } from '@/components/ErrorState'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; @@ -55,7 +54,7 @@ import { nip19 } from 'nostr-tools'; function CoachDetailSkeleton() { return ( - + <>
@@ -78,7 +77,7 @@ function CoachDetailSkeleton() {
-
+ ); } @@ -230,7 +229,7 @@ export default function CoachDetailPage() { const skillsTitle = SKILLS_TITLES[profile.profileType] ?? 'Services & Skills'; return ( - + <>
-
+ ); } diff --git a/src/pages/CollectionDetailPage.tsx b/src/pages/CollectionDetailPage.tsx index b0551a7..a6a31fe 100644 --- a/src/pages/CollectionDetailPage.tsx +++ b/src/pages/CollectionDetailPage.tsx @@ -3,7 +3,6 @@ import { useParams, Link, useNavigate } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { useNostr } from '@nostrify/react'; import { useQuery } from '@tanstack/react-query'; -import { Layout } from '@/components/Layout'; import { PageError } from '@/components/ErrorState'; import { SpotCard, SpotCardSkeleton } from '@/components/SpotCard'; import { SpotMap } from '@/components/SpotMap'; @@ -24,7 +23,7 @@ import { useState } from 'react'; function CollectionDetailSkeleton() { return ( - + <>
@@ -37,7 +36,7 @@ function CollectionDetailSkeleton() {
-
+ ); } @@ -206,7 +205,7 @@ export default function CollectionDetailPage() { } return ( - + <>
{/* Back link */} - + ); } diff --git a/src/pages/CommunityStatsPage.tsx b/src/pages/CommunityStatsPage.tsx index eb1ca02..3331741 100644 --- a/src/pages/CommunityStatsPage.tsx +++ b/src/pages/CommunityStatsPage.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { useAuthor } from '@/hooks/useAuthor'; @@ -194,7 +193,7 @@ export default function CommunityStatsPage() { const labTotal = useMemo(() => (stats ? labCraftTotal(stats) : 0), [stats]); return ( - + <>
{/* ── Header ── */} @@ -512,6 +511,6 @@ export default function CommunityStatsPage() {
-
+ ); } diff --git a/src/pages/ContactsPage.tsx b/src/pages/ContactsPage.tsx index 2f447f9..065c04f 100644 --- a/src/pages/ContactsPage.tsx +++ b/src/pages/ContactsPage.tsx @@ -2,7 +2,6 @@ import { useMemo, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { PageHeader } from '@/components/PageHeader'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -169,7 +168,7 @@ export default function ContactsPage() { if (!user) { return ( - + <>
@@ -185,7 +184,7 @@ export default function ContactsPage() {
-
+ ); } @@ -219,7 +218,7 @@ export default function ContactsPage() { ) : undefined; return ( - + <>
{/* Header */}
-
+ ); } diff --git a/src/pages/ConversationPage.tsx b/src/pages/ConversationPage.tsx index a7243a0..de40a80 100644 --- a/src/pages/ConversationPage.tsx +++ b/src/pages/ConversationPage.tsx @@ -2,7 +2,6 @@ import { useState, useRef, useEffect } from 'react'; import { useParams, Link, useSearchParams } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { LoginArea } from '@/components/auth/LoginArea'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -243,7 +242,7 @@ export default function ConversationPage() { // Not logged in if (!user) { return ( - + <>
@@ -255,7 +254,7 @@ export default function ConversationPage() {
-
+ ); } @@ -265,7 +264,7 @@ export default function ConversationPage() { const isMobile = isAndroid || /iPhone|iPad|iPod/i.test(navigator.userAgent); return ( - + <>
@@ -287,12 +286,12 @@ export default function ConversationPage() {
-
+ ); } return ( - + <>
{/* Header */}
@@ -473,6 +472,6 @@ export default function ConversationPage() {
-
+ ); } diff --git a/src/pages/CreateCrewPage.tsx b/src/pages/CreateCrewPage.tsx index 2643843..c3d58b4 100644 --- a/src/pages/CreateCrewPage.tsx +++ b/src/pages/CreateCrewPage.tsx @@ -1,6 +1,5 @@ import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { Card, CardContent } from '@/components/ui/card'; import { LoginArea } from '@/components/auth/LoginArea'; import { CrewForm } from '@/components/CrewForm'; @@ -17,7 +16,7 @@ export default function CreateCrewPage() { if (!user) { return ( - + <>
@@ -28,12 +27,12 @@ export default function CreateCrewPage() {
-
+ ); } return ( - + <>
@@ -47,6 +46,6 @@ export default function CreateCrewPage() {
-
+ ); } diff --git a/src/pages/CreateEventPage.tsx b/src/pages/CreateEventPage.tsx index ae9fd8c..808ded2 100644 --- a/src/pages/CreateEventPage.tsx +++ b/src/pages/CreateEventPage.tsx @@ -2,7 +2,6 @@ import { useState, useEffect, useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -434,7 +433,7 @@ export default function CreateEventPage() { if (!user) { return ( - + <>
@@ -447,14 +446,14 @@ export default function CreateEventPage() {
-
+ ); } const hasNip05 = metadata?.nip05; return ( - + <>
@@ -1144,6 +1143,6 @@ export default function CreateEventPage() {
- + ); } diff --git a/src/pages/CreateListingPage.tsx b/src/pages/CreateListingPage.tsx index 332c703..1b28f0e 100644 --- a/src/pages/CreateListingPage.tsx +++ b/src/pages/CreateListingPage.tsx @@ -1,7 +1,6 @@ import { useState, useEffect, useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, Link, useSearchParams } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { PageHeader } from '@/components/PageHeader'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -336,7 +335,7 @@ export default function CreateListingPage() { if (!user) { return ( - + <>
@@ -347,12 +346,12 @@ export default function CreateListingPage() {
-
+ ); } return ( - + <>
- + ); } diff --git a/src/pages/CreateShopPage.tsx b/src/pages/CreateShopPage.tsx index 4893b55..31e1168 100644 --- a/src/pages/CreateShopPage.tsx +++ b/src/pages/CreateShopPage.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -245,7 +244,7 @@ export default function CreateShopPage() { if (!user) { return ( - + <>
@@ -258,12 +257,12 @@ export default function CreateShopPage() {
-
+ ); } return ( - + <>
@@ -507,6 +506,6 @@ export default function CreateShopPage() {
- + ); } diff --git a/src/pages/CreateSpotPage.tsx b/src/pages/CreateSpotPage.tsx index eead418..6082468 100644 --- a/src/pages/CreateSpotPage.tsx +++ b/src/pages/CreateSpotPage.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -380,7 +379,7 @@ export default function CreateSpotPage() { if (!user) { return ( - + <>
@@ -393,12 +392,12 @@ export default function CreateSpotPage() {
-
+ ); } return ( - + <>
@@ -773,6 +772,6 @@ export default function CreateSpotPage() {
- + ); } diff --git a/src/pages/CreateTourPage.tsx b/src/pages/CreateTourPage.tsx index 578ac86..67460e3 100644 --- a/src/pages/CreateTourPage.tsx +++ b/src/pages/CreateTourPage.tsx @@ -2,7 +2,6 @@ import { useState, useEffect, useRef } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -855,7 +854,7 @@ export default function CreateTourPage({ existing }: { existing?: SkateTour } = if (!user) { return ( - + <>
@@ -868,7 +867,7 @@ export default function CreateTourPage({ existing }: { existing?: SkateTour } =
-
+ ); } @@ -879,7 +878,7 @@ export default function CreateTourPage({ existing }: { existing?: SkateTour } = // Show loading state while profile is being fetched if (isLoadingProfile) { return ( - + <>
@@ -891,23 +890,23 @@ export default function CreateTourPage({ existing }: { existing?: SkateTour } =
-
+ ); } // Require NIP-05 for tour creation if (!hasNip05) { return ( - + <>
-
+ ); } return ( - + <>
{/* Immersive header — layered, category-tinted, skate-punk */}
@@ -2508,6 +2507,6 @@ export default function CreateTourPage({ existing }: { existing?: SkateTour } =
- + ); } diff --git a/src/pages/CrewDetailPage.tsx b/src/pages/CrewDetailPage.tsx index cdc5646..73e1e51 100644 --- a/src/pages/CrewDetailPage.tsx +++ b/src/pages/CrewDetailPage.tsx @@ -2,7 +2,6 @@ import { useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { PageError } from '@/components/ErrorState'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -135,7 +134,7 @@ export default function CrewDetailPage() { if (isLoading) { return ( - + <>
@@ -146,7 +145,7 @@ export default function CrewDetailPage() {
-
+ ); } @@ -159,7 +158,7 @@ export default function CrewDetailPage() { const hasMembers = crew.members.length > 0; return ( - + <>
@@ -567,6 +566,6 @@ export default function CrewDetailPage() { }} /> )} - + ); } diff --git a/src/pages/CrewsPage.tsx b/src/pages/CrewsPage.tsx index 1178a1e..7aa24a7 100644 --- a/src/pages/CrewsPage.tsx +++ b/src/pages/CrewsPage.tsx @@ -1,7 +1,6 @@ import { useState, useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { LazyMount } from '@/components/LazyMount'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -212,7 +211,7 @@ export default function CrewsPage() { }; return ( - + <>
{/* Hero header band */}
@@ -381,7 +380,7 @@ export default function CrewsPage() {
)}
-
+ ); } diff --git a/src/pages/DirectMessagesPage.tsx b/src/pages/DirectMessagesPage.tsx index dd12322..bf153a3 100644 --- a/src/pages/DirectMessagesPage.tsx +++ b/src/pages/DirectMessagesPage.tsx @@ -2,7 +2,6 @@ import { useState } from 'react'; import { Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { nip19 } from 'nostr-tools'; -import { Layout } from '@/components/Layout'; import { PageHeader } from '@/components/PageHeader'; import { LoginArea } from '@/components/auth/LoginArea'; import { Card, CardContent } from '@/components/ui/card'; @@ -229,7 +228,7 @@ export default function DirectMessagesPage() { // Not logged in if (!user) { return ( - + <>
@@ -244,7 +243,7 @@ export default function DirectMessagesPage() {
-
+ ); } @@ -255,7 +254,7 @@ export default function DirectMessagesPage() { const isMobile = isAndroid || isIOS; return ( - + <>
@@ -297,12 +296,12 @@ export default function DirectMessagesPage() {
-
+ ); } return ( - + <>
{/* Phishing warning — shown once, dismissible */} @@ -394,6 +393,6 @@ export default function DirectMessagesPage() { {/* New Message — searchable people picker */} - + ); } diff --git a/src/pages/EditCrewPage.tsx b/src/pages/EditCrewPage.tsx index e842951..7be93c9 100644 --- a/src/pages/EditCrewPage.tsx +++ b/src/pages/EditCrewPage.tsx @@ -1,6 +1,5 @@ import { useSeoMeta } from '@unhead/react'; import { Link, useParams } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { Card, CardContent } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { LoginArea } from '@/components/auth/LoginArea'; @@ -22,7 +21,7 @@ export default function EditCrewPage() { if (!user) { return ( - + <>
@@ -32,18 +31,18 @@ export default function EditCrewPage() {
-
+ ); } if (isLoading) { return ( - + <>
-
+ ); } @@ -52,7 +51,7 @@ export default function EditCrewPage() { // Only the crew's founder can edit it if (crew.pubkey !== user.pubkey) { return ( - + <>
@@ -62,12 +61,12 @@ export default function EditCrewPage() {
-
+ ); } return ( - + <>
@@ -80,6 +79,6 @@ export default function EditCrewPage() {
-
+ ); } diff --git a/src/pages/EditEventPage.tsx b/src/pages/EditEventPage.tsx index 7e4016d..583ec97 100644 --- a/src/pages/EditEventPage.tsx +++ b/src/pages/EditEventPage.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useParams } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -46,7 +45,7 @@ function formatTimeForInput(timestamp: number): string { function EditEventSkeleton() { return ( - + <>
@@ -67,7 +66,7 @@ function EditEventSkeleton() {
-
+ ); } @@ -377,7 +376,7 @@ export default function EditEventPage() { // Check if user is logged in if (!user) { return ( - + <>
@@ -390,14 +389,14 @@ export default function EditEventPage() {
-
+ ); } // Check if user is the owner if (user.pubkey !== event.pubkey) { return ( - + <>
@@ -412,12 +411,12 @@ export default function EditEventPage() {
-
+ ); } return ( - + <>
- + ); } diff --git a/src/pages/EditShopPage.tsx b/src/pages/EditShopPage.tsx index d72e465..76a7cf4 100644 --- a/src/pages/EditShopPage.tsx +++ b/src/pages/EditShopPage.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useParams } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -31,7 +30,7 @@ import NotFound from './NotFound'; function EditShopSkeleton() { return ( - + <>
@@ -53,7 +52,7 @@ function EditShopSkeleton() {
-
+ ); } @@ -315,7 +314,7 @@ export default function EditShopPage() { // Check if user is the owner if (!user) { return ( - + <>
@@ -328,13 +327,13 @@ export default function EditShopPage() {
-
+ ); } if (user.pubkey !== shop.pubkey) { return ( - + <>
@@ -349,12 +348,12 @@ export default function EditShopPage() {
-
+ ); } return ( - + <>
- + ); } diff --git a/src/pages/EditSpotPage.tsx b/src/pages/EditSpotPage.tsx index 131199f..f1d053b 100644 --- a/src/pages/EditSpotPage.tsx +++ b/src/pages/EditSpotPage.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useParams } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -33,7 +32,7 @@ import NotFound from './NotFound'; function EditSpotSkeleton() { return ( - + <>
@@ -55,7 +54,7 @@ function EditSpotSkeleton() {
-
+ ); } @@ -308,7 +307,7 @@ export default function EditSpotPage() { // Check if user is the owner if (!user) { return ( - + <>
@@ -321,13 +320,13 @@ export default function EditSpotPage() {
-
+ ); } if (user.pubkey !== spot.pubkey) { return ( - + <>
@@ -342,12 +341,12 @@ export default function EditSpotPage() {
-
+ ); } return ( - + <>
- + ); } diff --git a/src/pages/EditTourPage.tsx b/src/pages/EditTourPage.tsx index 0978b70..8930735 100644 --- a/src/pages/EditTourPage.tsx +++ b/src/pages/EditTourPage.tsx @@ -1,6 +1,5 @@ import { useParams } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; -import { Layout } from '@/components/Layout'; import { Card, CardContent } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { LoginArea } from '@/components/auth/LoginArea'; @@ -28,14 +27,14 @@ export default function EditTourPage() { if (isLoading) { return ( - + <>
-
+ ); } @@ -45,7 +44,7 @@ export default function EditTourPage() { if (!user) { return ( - + <>
@@ -56,13 +55,13 @@ export default function EditTourPage() {
-
+ ); } if (user.pubkey !== tour.pubkey) { return ( - + <>
@@ -72,7 +71,7 @@ export default function EditTourPage() {
-
+ ); } diff --git a/src/pages/EventDetailPage.tsx b/src/pages/EventDetailPage.tsx index 5a73311..fca68f4 100644 --- a/src/pages/EventDetailPage.tsx +++ b/src/pages/EventDetailPage.tsx @@ -2,7 +2,6 @@ import { useMemo, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; -import { Layout } from '@/components/Layout'; import { PageError } from '@/components/ErrorState'; import { SpotMap } from '@/components/SpotMap'; import { RouteDialog } from '@/components/RouteDialog'; @@ -43,7 +42,7 @@ function formatTime(timestamp: number): string { function EventDetailSkeleton() { return ( - + <>
@@ -64,7 +63,7 @@ function EventDetailSkeleton() {
-
+ ); } @@ -266,7 +265,7 @@ export default function EventDetailPage() { } return ( - + <>
{/* Back link + owner/admin controls */}
@@ -629,6 +628,6 @@ export default function EventDetailPage() { destination={coordinates} destinationLabel={event?.title ?? 'Event'} /> - + ); } diff --git a/src/pages/EventsPage.tsx b/src/pages/EventsPage.tsx index 32ef59b..c53ba54 100644 --- a/src/pages/EventsPage.tsx +++ b/src/pages/EventsPage.tsx @@ -1,7 +1,6 @@ import { useState, useMemo, useRef, useEffect, useCallback } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { LazyMount } from '@/components/LazyMount'; import { EventCard, EventCardSkeleton } from '@/components/EventCard'; import { TourCard, TourCardSkeleton } from '@/components/TourCard'; @@ -1057,7 +1056,7 @@ export default function EventsPage() { const hasTourFilters = !!(tourSearchQuery || tourLocationQuery || tourCategory !== 'all' || tourSkillLevel !== 'all' || tourTrustFilter !== 'all' || tourSortBy !== 'recent'); return ( - + <>
{/* ── Page header ── */} @@ -1320,6 +1319,6 @@ export default function EventsPage() { onClearAll={handleClearAll} activeCount={activeFilterCount} /> - + ); } diff --git a/src/pages/FAQPage.tsx b/src/pages/FAQPage.tsx index f3db8a5..71bf4a2 100644 --- a/src/pages/FAQPage.tsx +++ b/src/pages/FAQPage.tsx @@ -6,7 +6,6 @@ import { useEffect } from 'react'; import { useSeoMeta } from '@unhead/react'; -import { Layout } from '@/components/Layout'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { HelpCircle, MessageCircle, MapPin, Zap, Shield, Package } from 'lucide-react'; @@ -87,7 +86,7 @@ export default function FAQPage() { }; return ( - + <>
{/* Hero */} @@ -187,6 +186,6 @@ export default function FAQPage() { The Sasquatch uses live Nostr data from the SkateSpots relay + AI to answer your questions. 🛹

-
+ ); } diff --git a/src/pages/FoodPage.tsx b/src/pages/FoodPage.tsx index 458a4ef..f494bd4 100644 --- a/src/pages/FoodPage.tsx +++ b/src/pages/FoodPage.tsx @@ -1,6 +1,5 @@ import { useState, useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; -import { Layout } from '@/components/Layout'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { RouteDialog } from '@/components/RouteDialog'; @@ -186,7 +185,7 @@ export default function FoodPage() { } = useFoodSpots(selectedSpot?.geohash, 1); return ( - + <>
{/* Header */}
@@ -280,6 +279,6 @@ export default function FoodPage() { )}
- + ); } diff --git a/src/pages/GearSwapPage.tsx b/src/pages/GearSwapPage.tsx index 7e0af3c..145a51c 100644 --- a/src/pages/GearSwapPage.tsx +++ b/src/pages/GearSwapPage.tsx @@ -1,7 +1,6 @@ import { useState, useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { PageHeader } from '@/components/PageHeader'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -161,7 +160,7 @@ export default function GearSwapPage() { ]; return ( - + <>
)}
-
+ ); } diff --git a/src/pages/GlossaryPage.tsx b/src/pages/GlossaryPage.tsx index 2a5f61e..0feb298 100644 --- a/src/pages/GlossaryPage.tsx +++ b/src/pages/GlossaryPage.tsx @@ -1,6 +1,5 @@ import { useState, useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; -import { Layout } from '@/components/Layout'; import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; import { @@ -47,7 +46,7 @@ export default function GlossaryPage() { ); return ( - + <>
{/* Hero */}
@@ -195,6 +194,6 @@ export default function GlossaryPage() {
-
+ ); } diff --git a/src/pages/GroupConversationPage.tsx b/src/pages/GroupConversationPage.tsx index 7e757e0..a3bbca4 100644 --- a/src/pages/GroupConversationPage.tsx +++ b/src/pages/GroupConversationPage.tsx @@ -1,7 +1,6 @@ import { useState, useRef, useEffect, useMemo } from 'react'; import { useParams, Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; -import { Layout } from '@/components/Layout'; import { LoginArea } from '@/components/auth/LoginArea'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; @@ -60,17 +59,17 @@ export default function GroupConversationPage() { if (!participantsParam || otherPubkeys.length === 0) { return ( - + <>
This group chat link is invalid.
-
+ ); } if (!user) { return ( - + <>
@@ -82,13 +81,13 @@ export default function GroupConversationPage() {
-
+ ); } if (!checkNip44(user, logins)) { return ( - + <>
@@ -98,7 +97,7 @@ export default function GroupConversationPage() {
-
+ ); } @@ -120,7 +119,7 @@ export default function GroupConversationPage() { }; return ( - + <>
{/* Header */}
@@ -222,6 +221,6 @@ export default function GroupConversationPage() {
-
+ ); } diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 2ca2587..a8e6811 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; -import { Layout } from '@/components/Layout'; import { SpotCard, SpotCardSkeleton } from '@/components/SpotCard'; import { EventCard, EventCardSkeleton } from '@/components/EventCard'; import { Button } from '@/components/ui/button'; @@ -630,7 +629,7 @@ export default function Index() { const platformSponsors = undefined; return ( - + <> @@ -645,6 +644,6 @@ export default function Index() { title="Platform Partners" />