import { useState, useEffect } from "react"; import { useAuth } from "../context/AuthContext"; import { Link } from "react-router-dom"; import { normalizeAnnotation, normalizeBookmark, likeAnnotation, unlikeAnnotation, getLikeCount, deleteBookmark, } from "../api/client"; import { HeartIcon, TrashIcon, BookmarkIcon } from "./Icons"; import { Folder } from "lucide-react"; import ShareMenu from "./ShareMenu"; export default function BookmarkCard({ bookmark, onAddToCollection, onDelete, }) { const { user, login } = useAuth(); const raw = bookmark; const data = raw.type === "Bookmark" ? normalizeBookmark(raw) : normalizeAnnotation(raw); const [likeCount, setLikeCount] = useState(0); const [isLiked, setIsLiked] = useState(false); const [deleting, setDeleting] = useState(false); const isOwner = user?.did && data.author?.did === user.did; useEffect(() => { let mounted = true; async function fetchData() { try { const likeRes = await getLikeCount(data.uri); if (mounted) { if (likeRes.count !== undefined) setLikeCount(likeRes.count); if (likeRes.liked !== undefined) setIsLiked(likeRes.liked); } } catch { /* ignore */ } } if (data.uri) fetchData(); return () => { mounted = false; }; }, [data.uri]); const handleLike = async () => { if (!user) { login(); return; } try { if (isLiked) { setIsLiked(false); setLikeCount((prev) => Math.max(0, prev - 1)); await unlikeAnnotation(data.uri); } else { setIsLiked(true); setLikeCount((prev) => prev + 1); const cid = data.cid || ""; if (data.uri && cid) await likeAnnotation(data.uri, cid); } } catch { setIsLiked(!isLiked); setLikeCount((prev) => (isLiked ? prev + 1 : prev - 1)); } }; const handleDelete = async () => { if (onDelete) { onDelete(data.uri); return; } if (!confirm("Delete this bookmark?")) return; try { setDeleting(true); const parts = data.uri.split("/"); const rkey = parts[parts.length - 1]; await deleteBookmark(rkey); window.location.reload(); } catch (err) { alert("Failed to delete: " + err.message); } finally { setDeleting(false); } }; const formatDate = (dateString) => { if (!dateString) return ""; const date = new Date(dateString); const now = new Date(); const diff = now - date; const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (minutes < 1) return "just now"; if (minutes < 60) return `${minutes}m`; if (hours < 24) return `${hours}h`; if (days < 7) return `${days}d`; return date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); }; let domain = ""; try { if (data.url) domain = new URL(data.url).hostname.replace("www.", ""); } catch { /* ignore */ } const authorDisplayName = data.author?.displayName || data.author?.handle; const authorHandle = data.author?.handle; const authorAvatar = data.author?.avatar; const authorDid = data.author?.did; const marginProfileUrl = authorDid ? `/profile/${authorDid}` : null; return (
{authorAvatar ? ( {authorDisplayName} ) : ( {(authorDisplayName || authorHandle || "??") ?.substring(0, 2) .toUpperCase()} )}
{authorDisplayName} {authorHandle && ( @{authorHandle} )}
{formatDate(data.createdAt)}
{(isOwner || onDelete) && ( )}
{domain}

{data.title || data.url}

{data.description && (

{data.description}

)}
{data.tags?.length > 0 && (
{data.tags.map((tag, i) => ( #{tag} ))}
)}
); }