import React from 'react'; import { Link } from 'react-router-dom'; import { ArrowUp, ArrowDown, MessageSquare } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; import type { Post } from '@opchan/core'; import { RelevanceIndicator } from '@/components/ui/relevance-indicator'; import { AuthorDisplay } from '@/components/ui/author-display'; import { BookmarkButton } from '@/components/ui/bookmark-button'; import { LinkRenderer } from '@/components/ui/link-renderer'; import { useAuth, useContent, usePermissions } from '@/hooks'; import { ShareButton } from '@/components/ui/ShareButton'; interface PostCardProps { post: Post; } const PostCard: React.FC = ({ post }) => { const { bookmarks, pending, vote, togglePostBookmark, cells, commentsByPost, } = useContent(); const permissions = usePermissions(); const { currentUser } = useAuth(); const cellName = cells.find(c => c.id === post.cellId)?.name || 'unknown'; const commentCount = commentsByPost[post.id]?.length || 0; const isPending = pending.isPending(post.id); const isBookmarked = bookmarks.some( b => b.targetId === post.id && b.type === 'post' ); const [bookmarkLoading, setBookmarkLoading] = React.useState(false); const score = post.upvotes.length - post.downvotes.length; const userUpvoted = Boolean( post.upvotes.some(v => v.author === currentUser?.address) ); const userDownvoted = Boolean( post.downvotes.some(v => v.author === currentUser?.address) ); const contentText = typeof post.content === 'string' ? post.content : String(post.content ?? ''); const contentPreview = contentText.length > 200 ? contentText.substring(0, 200) + '...' : contentText; const handleVote = async (e: React.MouseEvent, isUpvote: boolean) => { e.preventDefault(); await vote({ targetId: post.id, isUpvote }); }; const handleBookmark = async (e?: React.MouseEvent) => { if (e) { e.preventDefault(); e.stopPropagation(); } setBookmarkLoading(true); try { await togglePostBookmark(post, post.cellId); } finally { setBookmarkLoading(false); } }; return (
{/* Vote column - compact */}
0 ? 'text-primary' : score < 0 ? 'text-red-400' : 'text-muted-foreground'}`}> {score}
{/* Content */}
{/* Title */}

{post.title}

{/* Metadata line */}
{ if (!cellName) e.preventDefault(); }} > r/{cellName} · · {formatDistanceToNow(new Date(post.timestamp), { addSuffix: true, })} {isPending && ( <> · syncing )}
{/* Content preview */} {contentPreview && (

)} {/* Actions */}
{commentCount} {commentCount === 1 ? 'reply' : 'replies'}
); }; export default PostCard;