import { useState, useEffect, useCallback } from "react"; import { useParams, useNavigate, Link, useLocation } from "react-router-dom"; import { ArrowLeft, Edit2, Trash2, Plus } from "lucide-react"; import { getCollections, getCollectionItems, removeItemFromCollection, deleteCollection, resolveHandle, } from "../api/client"; import { useAuth } from "../context/AuthContext"; import CollectionModal from "../components/CollectionModal"; import CollectionIcon from "../components/CollectionIcon"; import AnnotationCard, { HighlightCard } from "../components/AnnotationCard"; import BookmarkCard from "../components/BookmarkCard"; import ShareMenu from "../components/ShareMenu"; export default function CollectionDetail() { const { rkey, handle, "*": wildcardPath } = useParams(); const location = useLocation(); const navigate = useNavigate(); const { user } = useAuth(); const [collection, setCollection] = useState(null); const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isEditModalOpen, setIsEditModalOpen] = useState(false); const searchParams = new URLSearchParams(location.search); const paramAuthorDid = searchParams.get("author"); const isOwner = user?.did && (collection?.creator?.did === user.did || paramAuthorDid === user.did); const fetchContext = useCallback(async () => { try { setLoading(true); let targetUri = null; let targetDid = paramAuthorDid || user?.did; if (handle && rkey) { try { targetDid = await resolveHandle(handle); targetUri = `at://${targetDid}/at.margin.collection/${rkey}`; } catch (e) { console.error("Failed to resolve handle", e); } } else if (wildcardPath) { targetUri = decodeURIComponent(wildcardPath); } else if (rkey && targetDid) { targetUri = `at://${targetDid}/at.margin.collection/${rkey}`; } if (!targetUri) { if (!user && !handle && !paramAuthorDid) { setError("Please log in to view your collections"); return; } setError("Invalid collection URL"); return; } if (!targetDid && targetUri.startsWith("at://")) { const parts = targetUri.split("/"); if (parts.length > 2) targetDid = parts[2]; } if (!targetDid) { setError("Could not determine collection owner"); return; } const [cols, itemsData] = await Promise.all([ getCollections(targetDid), getCollectionItems(targetUri), ]); const found = cols.items?.find((c) => c.uri === targetUri) || cols.items?.find( (c) => targetUri && c.uri.endsWith(targetUri.split("/").pop()), ); if (!found) { setError("Collection not found"); return; } setCollection(found); setItems(itemsData || []); } catch (err) { console.error(err); setError("Failed to load collection"); } finally { setLoading(false); } }, [paramAuthorDid, user, handle, rkey, wildcardPath]); useEffect(() => { fetchContext(); }, [fetchContext]); const handleEditSuccess = () => { fetchContext(); setIsEditModalOpen(false); }; const handleDeleteItem = async (itemUri) => { if (!confirm("Remove this item from the collection?")) return; try { await removeItemFromCollection(itemUri); setItems((prev) => prev.filter((i) => i.uri !== itemUri)); } catch (err) { console.error(err); alert("Failed to remove item"); } }; if (loading) { return (
); } if (error || !collection) { return (
⚠️

{error || "Collection not found"}

); } return (
Collections

{collection.name}

{collection.description && (

{collection.description}

)}
{items.length} {items.length === 1 ? "item" : "items"} · Created {new Date(collection.createdAt).toLocaleDateString()}
{isOwner && ( <> )}
{items.length === 0 ? (

Collection is empty

{isOwner ? 'Add items to this collection from your feed or bookmarks using the "Collect" button.' : "This collection has no items yet."}

) : ( items.map((item) => (
{isOwner && ( )} {item.annotation ? ( ) : item.highlight ? ( ) : item.bookmark ? ( ) : (

Item could not be loaded

)}
)) )}
{isOwner && ( setIsEditModalOpen(false)} onSuccess={handleEditSuccess} collectionToEdit={collection} /> )}
); }