{authorName}
)} {authorHandle ? (@{authorHandle}
) : null}“{post.caption}”
{post.images && post.images.length > 0 ? ("use client";
/**
* PostCard — renders one post in the public feed and the /u/me feed.
*
* • Author header — avatar + display name + @handle on the left,
* three-dot overflow menu on the right. Menu contents vary by
* viewer (see PostCardMenu): author sees Delete; signed-in
* non-author sees Block + Report; anonymous sees Report.
* • Caption in serif (locked, can't be edited)
* • Image grid (max 5; collapses to one column on the cheap)
* • "Source" link back to the page the quote came from
* • Optional onDelete callback — passed through to PostCardMenu so
* Delete actually does something on owner views (parent confirms +
* calls the API; same pattern as before the menu refactor).
*
* The source link always lands the user on the original passage with `?hl=`
* highlighting. If the stored source_url already carries `?hl=` we use it
* verbatim; otherwise we synthesise one from the post's caption so older
* posts (or imported data) still scroll to + highlight the text on click.
*/
import Image from "next/image";
import Link from "next/link";
import { ExternalLink } from "lucide-react";
import type { Post } from "@/lib/social";
import { displaySourceLabel } from "@/lib/source-reference";
import { appendHighlight, hasHighlight } from "@/lib/text-highlight";
import { PostActionsFooter } from "./post-actions-footer";
import { PostCardMenu } from "./post-card-menu";
export function PostCard({
post,
onDelete,
}: {
post: Post;
onDelete?: (id: string) => void;
}) {
const sourceHref =
hasHighlight(post.source_url) || !post.caption
? post.source_url
: appendHighlight(post.source_url, post.caption);
const authorName = post.author_name || "Unknown";
const authorHandle = post.author_handle ?? null;
return (
{authorName}
@{authorHandle}
“{post.caption}”