import { useState, type ReactNode } from 'react'; import { useNavigate } from 'react-router-dom'; import { Trash2, Loader2, AlertTriangle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { useDeleteEntity } from '@/hooks/useDeleteEntity'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useToast } from '@/hooks/useToast'; import { isAdminPubkey } from '@/lib/admins'; import { cn } from '@/lib/utils'; import type { NostrEvent } from '@nostrify/nostrify'; interface DeleteEntityDialogProps { /** The addressable event to delete (crew, Lab profile, spot, …). */ event: NostrEvent; /** Human label for the thing being deleted, e.g. "crew" or "coach profile". */ entityLabel: string; /** The name/title shown in the confirmation copy. */ entityName: string; /** Where to send the user after a successful delete (e.g. "/crews"). */ redirectTo?: string; /** Verb shown on the trigger button (default "Delete"). */ triggerText?: string; /** Optional custom trigger. If omitted a default outline button is rendered. */ trigger?: ReactNode; /** Extra classes for the default trigger button. */ className?: string; } /** * Reusable "delete / dissolve" control for any addressable event the current * user owns. Publishes a NIP-09 deletion request (see useDeleteEntity) after an * explicit confirmation, and is only rendered for the owner. */ export function DeleteEntityDialog({ event, entityLabel, entityName, redirectTo, triggerText = 'Delete', trigger, className, }: DeleteEntityDialogProps) { const { user } = useCurrentUser(); const { mutateAsync: deleteEntity, isPending } = useDeleteEntity(); const { toast } = useToast(); const navigate = useNavigate(); const [open, setOpen] = useState(false); if (!user) return null; const isOwner = user.pubkey === event.pubkey; const isAdmin = isAdminPubkey(user.pubkey); // The owner can delete their own event; a SkateSpots admin can take down any. if (!isOwner && !isAdmin) return null; // An admin acting on someone else's content is a moderation takedown. const adminTakedown = isAdmin && !isOwner; const handleDelete = async () => { try { await deleteEntity({ event, reason: adminTakedown ? `SkateSpots admin removed ${entityLabel} "${entityName}"` : `Deleted ${entityLabel} "${entityName}"`, }); toast({ title: adminTakedown ? 'Content removed' : 'Deletion requested', description: adminTakedown ? `This ${entityLabel} has been taken down and will be hidden across SkateSpots.` : `Your ${entityLabel} has been removed. Relays that honor deletions will stop serving it.`, }); setOpen(false); if (redirectTo) navigate(redirectTo); } catch (error) { toast({ title: 'Delete failed', description: error instanceof Error ? error.message : 'Something went wrong', variant: 'destructive', }); } }; return ( <> {trigger ? ( setOpen(true)}>{trigger} ) : ( )} Delete "{entityName}"?
{adminTakedown ? (

As a SkateSpots admin you’re taking down this {entityLabel}. It will be hidden across the app for everyone and a deletion request will be sent to relays. The original author can’t restore it from here.

) : (

This asks relays to remove your {entityLabel}. This can’t be undone from here — you’d have to create a new one.

)}

Nostr is decentralized, so deletion is best-effort: relays that honor NIP-09 will drop it, but a relay that doesn’t may keep a copy. SkateSpots hides admin-removed content in the app regardless.

); }