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 { CrewNip05Badge } from '@/components/CrewNip05Badge';
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 { CrewRepBadge } from '@/components/CrewBadge';
import { useCrewEvents } from '@/hooks/useCrewEvents';
import { EventCard } from '@/components/EventCard';
import { ConfirmMembershipCard } from '@/components/ConfirmMembershipCard';
import { LinkedLabCard } from '@/components/CrossLinkCards';
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 { genUserName } from '@/lib/genUserName';
import { SKATE_CREW_KIND, SKATE_SPOT_KIND, CREW_VIBE_LABELS } 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 } 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
)}
))
) : (() => {
// Moderators still see hidden posts (greyed via the badge on hide),
// but for everyone the hidden set is filtered out.
const visible = (feed ?? []).filter(
(ev) => canManage || !hiddenIds?.has(ev.id),
);
if (visible.length === 0) {
return (
No recent posts from the crew yet.
);
}
return visible.map((ev) => (
{hiddenIds?.has(ev.id) && (
Hidden from feed
)}
));
})()}
{/* Crew Agent — AI guide, shown when the crew enabled it */}
{crew.agentEnabled && (
)}
{/* Crew Chat — private E2E-encrypted group chat for members */}