import { useSeoMeta } from '@unhead/react'; import { Layout } from '@/components/Layout'; import { EditProfileForm } from '@/components/EditProfileForm'; import { RelayListManager } from '@/components/RelayListManager'; import { FollowListManager } from '@/components/FollowListManager'; import { WalletModal } from '@/components/WalletModal'; import { SecuritySettings } from '@/components/SecuritySettings'; import { RelatrSettings } from '@/components/RelatrSettings'; import { LanguageSelector } from '@/components/LanguageToggle'; import { MyDataTab } from '@/components/MyDataTab'; import { useTranslation } from '@/contexts/LanguageContext'; import { useQueryClient } from '@tanstack/react-query'; import { useNostr } from '@nostrify/react'; import { NSchema as n } from '@nostrify/nostrify'; import { useState } from 'react'; import { LoginArea } from '@/components/auth/LoginArea'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useAppContext } from '@/hooks/useAppContext'; import { toast } from '@/hooks/useToast'; import { User, Palette, Radio, Wallet, Settings, Wrench, RotateCcw, Shield, ExternalLink, Languages, RefreshCw, AlertCircle, Database, } from 'lucide-react'; // Fallback relays for fetching profile data const PROFILE_FALLBACK_RELAYS = [ 'wss://purplepag.es', 'wss://relay.damus.io', 'wss://nos.lol', ]; function ProfileTab() { const { user, metadata } = useCurrentUser(); const { nostr } = useNostr(); const queryClient = useQueryClient(); const [isRefreshing, setIsRefreshing] = useState(false); const handleRefreshProfile = async () => { if (!user) return; setIsRefreshing(true); console.log('[ProfileTab] Manual refresh for pubkey:', user.pubkey.slice(0, 8) + '...'); try { // First invalidate the cache queryClient.invalidateQueries({ queryKey: ['author', user.pubkey] }); // Try main relays let events = await nostr.query( [{ kinds: [0], authors: [user.pubkey], limit: 1 }], { signal: AbortSignal.timeout(8000) } ); console.log('[ProfileTab] Main relays returned', events.length, 'events'); // If not found, try fallback relays if (events.length === 0) { console.log('[ProfileTab] Trying fallback relays...'); const fallbackGroup = nostr.group(PROFILE_FALLBACK_RELAYS); events = await fallbackGroup.query( [{ kinds: [0], authors: [user.pubkey], limit: 1 }], { signal: AbortSignal.timeout(8000) } ); console.log('[ProfileTab] Fallback relays returned', events.length, 'events'); } if (events.length > 0) { const event = events[0]; try { const profileMetadata = n.json().pipe(n.metadata()).parse(event.content); queryClient.setQueryData(['author', user.pubkey], { metadata: profileMetadata, event }); toast({ title: 'Profile refreshed!', description: `Found profile: ${profileMetadata.name || 'unnamed'}`, }); } catch { toast({ title: 'Profile found', description: 'But metadata could not be parsed.', variant: 'destructive', }); } } else { toast({ title: 'Profile not found', description: 'Your profile (kind 0 event) was not found on any relay. You may need to publish it.', variant: 'destructive', }); } } catch (error) { console.error('[ProfileTab] Refresh failed:', error); toast({ title: 'Refresh failed', description: 'Could not connect to relays. Check your internet connection.', variant: 'destructive', }); } finally { setIsRefreshing(false); } }; if (!user) { return (

Login Required

Log in to edit your profile.

); } return (
{/* Profile sync status - show if no metadata found */} {!metadata?.name && (

Profile not loaded. Your profile data wasn't found on the configured relays. Try refreshing or make sure you've published your profile.

)} {/* Following & Followers */} {/* NIP-05 Providers Info */} NIP-05 Verification Providers Get a verified Nostr address (like email@domain.com) to build trust. Required for creating events, coaching, and shops.

💡 Tip: After getting your NIP-05, add it to your profile in the "NIP-05" field above and save.

); } function AppearanceTab() { const { t } = useTranslation(); return (
{/* Language Settings */} {t('settings.language')} {t('settings.selectLanguage')} {/* Theme Settings */} {t('settings.appearance')} SkateSpots uses a dark neon aesthetic inspired by skate culture.
🌙

Dark Mode Only

SkateSpots is designed with a dark neon aesthetic inspired by Tony Hawk's Underground 2 and skate culture. The black background with neon pink, lime, and orange creates the signature skateboarding vibe.

💡 Embrace the darkness - that's where skaters thrive
); } function RelaysTab() { const { user } = useCurrentUser(); const { config, updateConfig } = useAppContext(); const useSkateRelay = config.useSkateRelay ?? true; const handleToggleCommunityRelay = () => { updateConfig((current) => ({ ...current, useSkateRelay: !useSkateRelay, })); toast({ title: useSkateRelay ? 'Community relay disabled' : 'Community relay enabled', description: useSkateRelay ? 'You will now only use your configured relays for skate content.' : 'The SkateSpots community relay will be included for skate content.', }); }; return (
{/* Community Relay Setting - Only shown when logged in */} {user && ( 🛹 Community Relay Control whether to include the SkateSpots community relay for skate-specific content.

Use Community Relay

{useSkateRelay ? 'Enabled - Your content syncs with the global skate community' : 'Disabled - Using only your personal relays'}

💡 What does this do?

  • Enabled (default): Your spots, events, and profiles sync with the global SkateSpots community
  • Disabled: Content only goes to your personal relays - you're fully decentralized but may not see all community content
{!useSkateRelay && (

⚠️ Note: With the community relay disabled, you'll only see skate content from relays you've configured below. Make sure you have at least one relay set up!

)}
)} Nostr Relays Manage which relays you connect to for sending and receiving data. {/* Default Relays Explanation */} Default Relays
{/* Community Relay */}
🛹

goskatespots.nostr1.com

wss://goskatespots.nostr1.com

The dedicated SkateSpots community relay. Custom policies for spots, events, coaches, filmers, and shops. Toggle it on/off above.

⚠️ Allowlisted: Your npub must be approved to publish here. DM the relay owner to request access.

{/* Auth relay */}
🔐

auth.nostr1.com

wss://auth.nostr1.com

Authentication relay for Nostr identity verification and NIP-42 auth flows.

{/* WoTR relay */}
🛡️

wotr.relatr.xyz

wss://wotr.relatr.xyz

Relatr's Web of Trust relay. Powers trust scores and broader Nostr network connectivity.

{/* Ditto relay */}
🌐

relay.ditto.pub

wss://relay.ditto.pub

General purpose relay with broad Nostr network coverage for maximum reach.

{/* Profile Aggregator */}
👤

purplepag.es

wss://purplepag.es

Read only

Profile aggregator that indexes Nostr profiles from across the network. Used for fast profile lookups and discovery.

Your content publishes to all write-enabled relays for maximum reliability. You can customise your relay list above using the relay manager.

💡 Want to run your own relay? Check out{' '} relay.tools {' '}to easily spin up and manage your own Nostr relay!

); } function WalletTab() { const { user } = useCurrentUser(); if (!user) { return (

Login Required

Log in to manage your wallet.

); } return (
{/* Redirect to Wallet Page */} Your Wallet Manage your Cashu wallet and Lightning connections from the dedicated wallet page. {/* NWC Quick Connect */} Connect External Wallet (NWC) Connect your existing Lightning wallet using Nostr Wallet Connect for one-click zaps.

What is NWC? Nostr Wallet Connect lets you link your Lightning wallet (like Alby, Minibits, Zeus, or Coinos) to send zaps without copy-pasting invoices.

); } function AdvancedTab() { const handleResetSasquatch = () => { // Clear minimized state so Sasquatch appears expanded localStorage.removeItem('sasquatch-minimized'); // Show confirmation and reload alert('The Sasquatch has been expanded! 🦧 Refresh the page to see him.'); window.location.reload(); }; return (
{/* Relatr Integration */} {/* Sasquatch Guide */} 🦧 The Sasquatch Guide The Sasquatch is your onboarding guide - always available on home and onboarding pages.

The Sasquatch appears as a minimizable button in the bottom-left corner. Click to expand or minimize at any time. If you want to reset it to expanded view, use the button below.

💡 The Sasquatch is always available - just look for the button in the bottom-left corner!
); } export default function SettingsPage() { useSeoMeta({ title: 'Settings - SkateSpots', description: 'Manage your SkateSpots account settings.', }); // Check URL params for tab selection const urlParams = new URLSearchParams(window.location.search); const initialTab = urlParams.get('tab') || 'profile'; return (

Settings

Manage your account and app preferences

{/* Grid tabs for mobile, horizontal for larger screens */} Profile My Data Security Theme Relays Wallet Advanced
); }