import React from 'react'; import { Link } from 'react-router-dom'; import { formatDistanceToNow } from 'date-fns'; import type { Post } from '@opchan/core'; import { useAuth, useContent, usePermissions } from '@/hooks'; interface PostCardProps { post: Post; } const PostCard: React.FC = ({ post }) => { const { bookmarks, pending, vote, togglePostBookmark, toggleFollow, isFollowing, 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 [followLoading, setFollowLoading] = React.useState(false); const isOwnPost = currentUser?.address === post.author; const isFollowingAuthor = isFollowing(post.author); 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); } }; const handleFollow = async (e?: React.MouseEvent) => { if (e) { e.preventDefault(); e.stopPropagation(); } setFollowLoading(true); try { await toggleFollow(post.author); } finally { setFollowLoading(false); } }; return (
{/* Inline vote display */} 0 ? 'text-primary' : score < 0 ? 'text-red-400' : 'text-muted-foreground'}`}> {score} {/* Content - all inline */}
{ if (!cellName) e.preventDefault(); }} > r/{cellName} · {post.title} by {post.author.slice(0, 6)}...{post.author.slice(-4)} · {formatDistanceToNow(new Date(post.timestamp), { addSuffix: true, })} · {commentCount} {commentCount === 1 ? 'reply' : 'replies'} · {currentUser && !isOwnPost && ( <> · )} {isPending && ( <> · syncing )}
); }; export default PostCard;