Files
dark-lord/components/forum/ForumPostCard.tsx
drjones 78a071ba02 Harden onion boot flow and deepen site surfaces
Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation.

Made-with: Cursor
2026-04-07 21:35:52 -07:00

75 lines
2.9 KiB
TypeScript

"use client";
import Link from "next/link";
import { formatRingLabel } from "@/lib/forumRings";
import { displayScore, isPinnedThread, isReadOnlyThread, type ForumThread } from "@/lib/forumState";
import { ThreadVoteColumn } from "@/components/forum/ThreadVoteColumn";
export function ForumPostCard({
thread,
onVoteChange,
}: {
thread: ForumThread;
/** Fire when vote changes so list meta (score) re-renders from localStorage */
onVoteChange?: () => void;
}) {
const pinned = isPinnedThread(thread);
const locked = isReadOnlyThread(thread.id);
const score = displayScore(thread);
return (
<div
className={`flex gap-3 rounded-lg border bg-[#120e0a]/90 p-3 transition-colors ${
pinned
? "border-[#c45c26]/65 shadow-[0_0_0_1px_rgba(196,92,38,0.12)] hover:border-[#ff9a3c]/55"
: "border-[#3d3228] hover:border-[#c45c26]/45"
}`}
>
<div onClick={(e) => e.stopPropagation()} className="shrink-0">
<ThreadVoteColumn thread={thread} dense onVoteChange={onVoteChange} />
</div>
<Link href={`/forum/post/${thread.id}`} className="min-w-0 flex-1 outline-none">
<div className="flex flex-wrap items-center gap-2">
{pinned ? (
<span className="rounded bg-[#c45c26]/35 px-2 py-0.5 font-mono text-[9px] font-black uppercase tracking-widest text-[#ffcb8a]">
Pinned
</span>
) : null}
{locked ? (
<span className="rounded border border-[#5c5044] px-2 py-0.5 font-mono text-[9px] uppercase tracking-wider text-[#8a7d6b]">
Staff
</span>
) : null}
</div>
<p className="mt-1 font-mono text-[10px] uppercase tracking-wider text-[#8a7d6b]">
<span className="text-[#ff9a3c]/90">{formatRingLabel(thread.topicSlug)}</span>
<span className="mx-2 text-[#3d3228]">·</span>
<span>{thread.author || "Anonymous"}</span>
<span className="mx-2 text-[#3d3228]">·</span>
<span>{formatAgo(thread.ts)}</span>
<span className="mx-2 text-[#3d3228]">·</span>
<span className="tabular-nums text-[#ffcb8a]/80">{score} pts</span>
</p>
<h3 className="mt-1 font-mono text-base font-bold leading-snug text-[#ffcb8a] hover:underline md:text-lg">
{thread.title}
</h3>
<p className="mt-2 line-clamp-2 text-sm leading-relaxed text-[#b8a995]">{thread.body}</p>
<div className="mt-3 flex flex-wrap gap-3 font-mono text-[11px] text-[#8a7d6b]">
<span className="rounded border border-[#3d3228] px-2 py-0.5">{thread.replies.length} comments</span>
</div>
</Link>
</div>
);
}
function formatAgo(ts: number): string {
const s = Math.floor((Date.now() - ts) / 1000);
if (s < 60) return `${s}s ago`;
const m = Math.floor(s / 60);
if (m < 60) return `${m}m ago`;
const h = Math.floor(m / 60);
if (h < 48) return `${h}h ago`;
const d = Math.floor(h / 24);
return `${d}d ago`;
}