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 { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Skeleton } from '@/components/ui/skeleton';
import { ZapButton } from '@/components/ZapButton';
import { ZapCrewDialog } from '@/components/ZapCrewDialog';
import { ClinkPayButton } from '@/components/ClinkPayButton';
import { CollectiveMembers } from '@/components/CollectiveMembers';
import { CrewHero } from '@/components/crew/CrewHero';
import { CrewLeadership } from '@/components/crew/CrewLeadership';
import { useCrewsActivity, crewCoordinate } from '@/hooks/useCrewActivity';
import { CrewChat } from '@/components/CrewChat';
import { CrewAgentPanel } from '@/components/CrewAgentPanel';
import { CrewTreasuryCard } from '@/components/CrewTreasuryCard';
import { CrewLookingForCard } from '@/components/CrewLookingFor';
import { CrewRequestToJoin } from '@/components/CrewRequestToJoin';
import { useCrewEvents } from '@/hooks/useCrewEvents';
import { EventCard } from '@/components/EventCard';
import { ConfirmMembershipCard } from '@/components/ConfirmMembershipCard';
import { MemberVisibilityControl } from '@/components/MemberVisibilityControl';
import { LinkedLabCard } from '@/components/CrossLinkCards';
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { useCollectiveMembers } from '@/hooks/useCollectiveMembers';
import { useCrewRole } from '@/hooks/useCrewRole';
import { useCrewModeration, useHideCrewPost } from '@/hooks/useCrewModeration';
import { CrewInviteDialog } from '@/components/CrewInviteDialog';
import { DeleteEntityDialog } from '@/components/DeleteEntityDialog';
import { useToast } from '@/hooks/useToast';
import { NoteContent } from '@/components/NoteContent';
import { useCrew } from '@/hooks/useCrews';
import { useCrewFeed } from '@/hooks/useCrewFeed';
import { useAuthor } from '@/hooks/useAuthor';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { genUserName } from '@/lib/genUserName';
import { SKATE_CREW_KIND, SKATE_SPOT_KIND, SHOW_CREW_TREASURY } from '@/lib/types';
import { sanitizeUrl, urlDomain } from '@/lib/sanitizeUrl';
import { cn } from '@/lib/utils';
import NotFound from './NotFound';
import { Users, MapPin, ArrowLeft, Calendar, Globe, Instagram, Edit, MessageSquare, Zap, ShieldCheck, EyeOff, Music } from 'lucide-react';
import type { NostrEvent } from '@nostrify/nostrify';
function FeedItem({
event,
canModerate,
onHide,
isHiding,
}: {
event: NostrEvent;
canModerate?: boolean;
onHide?: (event: NostrEvent) => void;
isHiding?: boolean;
}) {
const author = useAuthor(event.pubkey);
const metadata = author.data?.metadata;
const name = metadata?.name ?? genUserName(event.pubkey);
const npub = nip19.npubEncode(event.pubkey);
return (
{name.slice(0, 2).toUpperCase()}
{name}
{new Date(event.created_at * 1000).toLocaleDateString()}
{canModerate && onHide && (
Hide this post?
As a crew moderator you can hide this post from the crew feed for everyone.
The original note stays on Nostr — this only removes it from this crew's feed.
Cancel onHide(event)}
className="bg-red-600 hover:bg-red-700"
>
Hide post
)}
{/* Confirm membership prompt (listed pending members only) */}
{hasMembers && (
)}
{/* Immersive team-page hero */}
{canManage && }
{isFounder && (
<>
>
)}
{isAdmin && !isFounder && (
You're a captain
)}
{role === 'member' && (
Member
)}
{role === 'none' && (
)}
>
) : null
}
/>
{/* Open call — looking for filmers/editors/members (Lab pipeline) */}
{/* Crew treasury — hidden for now (kept in code, re-enable by flipping SHOW_CREW_TREASURY) */}
{SHOW_CREW_TREASURY && crew.goalRef && }
{/* Crew Agent — AI guide, shown when the crew enabled it */}
{crew.agentEnabled && (
)}
{/* Activity — one clean tabbed surface instead of three stacked cards */}
Events
{crewEvents && crewEvents.length > 0 && (
{crewEvents.length}
)}
Chat
{/* Events */}
{canManage && (
)}
{crewEvents && crewEvents.length > 0 ? (
crewEvents.slice(0, 8).map((ev) => )
) : (
No crew events yet. Host a jam, comp or meetup as the crew — it connects your
crew to the events calendar and lets people zap it.
)}
{/* Chat */}
{/* Sidebar — ordered: leadership → who → support → band → connections → founder */}
{/* Leadership — team leader (founder) + captains (admins) */}
{/* Members (with per-member visibility respected) */}
{hasMembers && (
)}
{/* A confirmed member can change their own visibility here */}
{hasMembers && (
)}
{/* Support the crew */}
Support
{/* Zap the whole crew — split across founder + confirmed members */}
{crewZapRecipients.length > 1 && (
)}
{crew.noffer && (
)}
{/* Crew Band — many crews double as a local band. Shown as its own card,
with a "Book for a gig" action that deep-links to create a Concert
event hosted by this crew (connects bands → the Events calendar). */}
{crew.bandName && (
Crew Band
🎸
{crew.bandName}
Live music · skate & sound
{crew.bandUrl && sanitizeUrl(crew.bandUrl) && (
Listen to the tunes
)}
Set up a gig
{user && user.pubkey !== crew.pubkey && (
Message about a gig
)}
Set up a gig adds a music event to the
calendar hosted by this crew. Message opens
an encrypted DM to the crew to sort dates & details.