import { useSeoMeta } from '@unhead/react'; import { Link, useSearchParams } from 'react-router-dom'; import { Layout } from '@/components/Layout'; import { EditProfileForm } from '@/components/EditProfileForm'; import { SkaterDirectoryCard } from '@/components/SkaterDirectoryCard'; import { RelayListManager } from '@/components/RelayListManager'; import { WalletModal } from '@/components/WalletModal'; import { SecuritySettings } from '@/components/SecuritySettings'; import { RelatrSettings } from '@/components/RelatrSettings'; import { CVMHealthCheck } from '@/components/CVMHealthCheck'; import { LanguageSelector } from '@/components/LanguageToggle'; import { DesktopNavManager } from '@/components/DesktopNavManager'; import { MyDataTab } from '@/components/MyDataTab'; import { useQueryClient } from '@tanstack/react-query'; import { useNostr } from '@nostrify/react'; import { NSchema as n } from '@nostrify/nostrify'; import { nip19 } from 'nostr-tools'; import { useState, useEffect, useRef, useCallback } from 'react'; import { LoginArea } from '@/components/auth/LoginArea'; import { Button } from '@/components/ui/button'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useAppContext } from '@/hooks/useAppContext'; import { useCitrine } from '@/hooks/useCitrine'; import { toast } from '@/hooks/useToast'; import { cn } from '@/lib/utils'; import { NIP05_PROVIDERS } from '@/lib/nip05Providers'; import QRCode from 'qrcode'; import { User, Radio, Wallet, Wrench, RotateCcw, Shield, ExternalLink, Languages, RefreshCw, AlertCircle, Database, Package, ChevronRight, ChevronLeft, Network, Copy, Check, Smartphone, Download, Globe, Zap, WifiOff } from 'lucide-react'; // ─── Types ──────────────────────────────────────────────────────────────────── type Section = | null | 'profile' | 'follows' | 'nip05' | 'security' | 'mydata' | 'language' | 'appearance' | 'relays' | 'crewnetwork' | 'wallet' | 'stash' | 'advanced'; // ─── Back header ────────────────────────────────────────────────────────────── function BackHeader({ title, onBack }: { title: string; onBack: () => void }) { return (
{label}
{sublabel &&{sublabel}
}{title}
Log in to edit your profile
Profile not loaded from relays.
Get a verified Nostr address (like name@domain.com) to build trust. Required for creating events, coaching profiles, and shops.
{p.name}
{p.desc}
Local Relay Citrine
{state === 'off' && 'Faster reads & offline backup on this device'} {state === 'searching' && 'Searching for Citrine on this device…'} {state === 'live' && 'Connected — reading & backing up locally'} {state === 'waiting' && 'Enabled — start Citrine to connect'}
Can't reach Citrine.{' '} Make sure the free Citrine app is installed and its relay is started, then tap Recheck. It runs a private Nostr relay on your phone for instant reads and an offline backup.
Your Stash and messages stay end-to-end encrypted here, exactly like everywhere else. The local relay only speeds things up and keeps a copy on your device — it never replaces encryption.
Community Relay
{useSkateRelay ? 'Syncing with global skate community' : 'Using only personal relays'}
Language
Dark Mode Only
SkateSpots runs a dark neon aesthetic inspired by skate culture. Black background, neon pink, lime, and orange.
Navigation
🦧 Sasquatch Guide
Reset the onboarding guide to expanded view
Log in to manage your wallet
Open Wallet
Balance, send, receive
Private Encrypted Mesh
nostr-vpn turns your Nostr keys into a private WireGuard mesh for your crew. No accounts, no central server — your key is your identity. Your crew, your network.
Your Join Request
Generating…
QR failed to render
Show this QR to an admin in the nostr-vpn app. They scan → approve → you're on the encrypted mesh.
{/* URI display — improved contrast, selectable */}Join URI
{joinUri.length > 130 ? joinUri.slice(0, 130) + '…' : joinUri}
"Open app" requires nostr-vpn installed. If not installed, you'll be redirected to the download page.
Login required
Log in with your Nostr keys to generate a personal join request QR for your crew's encrypted mesh.
How It Works
{text}
Get nostr-vpn
{label}
{sublabel}
Why Skaters Use It
{label}
nostr-vpn on GitHub
Source code, docs, protocol spec
Account and app preferences