import { useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link } from 'react-router-dom'; import ngeohash from 'ngeohash'; import { Layout } from '@/components/Layout'; import { SpotMap } from '@/components/SpotMap'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Skeleton } from '@/components/ui/skeleton'; import { Button } from '@/components/ui/button'; import { ZapButton } from '@/components/ZapButton'; import { CommentsSection } from '@/components/comments/CommentsSection'; import { useSkateEvent } from '@/hooks/useSkateEvents'; import { useAuthor } from '@/hooks/useAuthor'; import { genUserName } from '@/lib/genUserName'; import { EVENT_TYPE_LABELS } from '@/lib/types'; import NotFound from './NotFound'; import { Calendar, Clock, MapPin, ArrowLeft, Share2, Navigation, CalendarPlus, Pencil, } from 'lucide-react'; import { useCurrentUser } from '@/hooks/useCurrentUser'; function formatDate(timestamp: number): string { const date = new Date(timestamp * 1000); return date.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', }); } function formatTime(timestamp: number): string { const date = new Date(timestamp * 1000); return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', }); } function EventDetailSkeleton() { return (
); } export default function EventDetailPage() { const { pubkey, identifier } = useParams<{ pubkey: string; identifier: string }>(); const { data: event, isLoading, error } = useSkateEvent(identifier || '', pubkey); const author = useAuthor(event?.pubkey); const { user } = useCurrentUser(); const isOwner = user && event && user.pubkey === event.pubkey; useSeoMeta({ title: event ? `${event.title} - SkateSpots` : 'Skate Event - SkateSpots', description: event?.description || 'View this skateboarding event on SkateSpots.', }); const metadata = author.data?.metadata; const displayName = metadata?.name ?? genUserName(event?.pubkey || ''); const coordinates = useMemo(() => { if (!event?.geohash) return null; const { latitude, longitude } = ngeohash.decode(event.geohash); return { lat: latitude, lng: longitude }; }, [event?.geohash]); const isUpcoming = event ? event.start > Date.now() / 1000 : false; const isPast = event ? event.start < Date.now() / 1000 : false; const handleShare = async () => { if (navigator.share) { try { await navigator.share({ title: event?.title, text: event?.description || `Check out this skate event: ${event?.title}`, url: window.location.href, }); } catch { // User cancelled or share failed } } else { navigator.clipboard.writeText(window.location.href); } }; const handleDirections = () => { if (coordinates) { window.open( `https://www.google.com/maps/dir/?api=1&destination=${coordinates.lat},${coordinates.lng}`, '_blank' ); } }; const handleAddToCalendar = () => { if (!event) return; const startDate = new Date(event.start * 1000); const endDate = event.end ? new Date(event.end * 1000) : new Date(event.start * 1000 + 3600000); // Default 1 hour const formatICSDate = (date: Date) => { return date.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'; }; const icsContent = [ 'BEGIN:VCALENDAR', 'VERSION:2.0', 'BEGIN:VEVENT', `DTSTART:${formatICSDate(startDate)}`, `DTEND:${formatICSDate(endDate)}`, `SUMMARY:${event.title}`, `DESCRIPTION:${event.description || ''}`, `LOCATION:${event.location || ''}`, 'END:VEVENT', 'END:VCALENDAR', ].join('\n'); const blob = new Blob([icsContent], { type: 'text/calendar' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${event.title.replace(/[^a-z0-9]/gi, '-')}.ics`; link.click(); URL.revokeObjectURL(url); }; if (isLoading) { return ; } if (error || !event) { return ; } return (
{/* Back link */} Back to Events
{/* Main Content */}
{/* Cover Image */} {event.image ? (
{event.title}
) : (
)} {/* Event Info */}
{isUpcoming && ( Upcoming )} {isPast && ( Past Event )}

{event.title}

{isOwner && ( )} {isUpcoming && ( )}
{/* Date & Time */}
{formatDate(event.start)}
{formatTime(event.start)} {event.end && ` - ${formatTime(event.end)}`}
{/* Location */} {event.location && (

{event.location}

)} {/* Types */} {event.types.length > 0 && (
{event.types.map((type) => ( {EVENT_TYPE_LABELS[type] || type} ))}
)} {/* Description */} {event.description && (

{event.description}

)} {/* Comments Section */} {event.event && }
{/* Sidebar */}
{/* Date Card */}
{new Date(event.start * 1000).toLocaleDateString('en-US', { month: 'long' })}
{new Date(event.start * 1000).getDate()}
{new Date(event.start * 1000).toLocaleDateString('en-US', { weekday: 'long' })}
{formatTime(event.start)}
{isUpcoming && ( )}
{/* Map (if has location) */} {coordinates && ( Location
)} {/* Organizer Card */} Organizer
{displayName.slice(0, 2).toUpperCase()}

{displayName}

Event Organizer

); }