Files
dark-lord/app/chatter/page.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

143 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import Link from "next/link";
import { useMemo } from "react";
import { useSearchParams } from "next/navigation";
import { formatRingLabel } from "@/lib/forumRings";
import { SHOP_CHATTER_COUNT, getChatterSlice } from "@/lib/shopChatterThreads";
const PAGE_SIZE = 12;
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`;
}
export default function HubChatterArchivePage() {
const sp = useSearchParams();
const page = Math.max(1, parseInt(sp.get("page") ?? "1", 10) || 1);
const { items, totalPages, total } = useMemo(
() => getChatterSlice(page, PAGE_SIZE),
[page],
);
const pages = useMemo(() => {
const window = 2;
const lo = Math.max(1, page - window);
const hi = Math.min(totalPages, page + window);
const out: number[] = [];
for (let i = lo; i <= hi; i++) out.push(i);
return out;
}, [page, totalPages]);
return (
<div className="min-h-screen bg-[#050403] text-[#d4c4b0]">
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.08]"
style={{
backgroundImage:
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,180,120,0.12) 3px)",
}}
/>
<header className="relative z-10 border-b border-[#3d3028] bg-[#0c0906]/95 px-4 py-8">
<div className="mx-auto max-w-3xl">
<p className="font-mono text-[10px] uppercase tracking-[0.4em] text-[#c45c26]/90">archive</p>
<h1 className="mt-2 font-mono text-2xl font-black text-[#ffcb8a] md:text-3xl">Hub chatter index</h1>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-[#9a8d7c]">
Scraped-style threads about the <strong className="text-[#ffcb8a]">CyberLux main storefront</strong> mirror
checks, checkout banter, UI roasts, class-lab meta. Each row opens in Void Aggregate for full comments.
</p>
<nav className="mt-6 flex flex-wrap gap-3 font-mono text-xs">
<Link
href="/forum"
className="rounded border border-[#c45c26] bg-[#c45c26]/15 px-4 py-2 font-bold text-[#ffcb8a] hover:bg-[#c45c26]/25"
>
Void Aggregate forum
</Link>
<Link href="/" className="rounded border border-[#3d3028] px-4 py-2 text-[#b8a995] hover:border-[#c45c26]/50">
Main hub
</Link>
</nav>
</div>
</header>
<main className="relative z-10 mx-auto max-w-3xl px-4 pb-24 pt-8">
<p className="mb-6 font-mono text-xs text-[#6b6358]">
Showing {(page - 1) * PAGE_SIZE + 1}{Math.min(page * PAGE_SIZE, total)} of {total} indexed posts ·{" "}
{PAGE_SIZE} per page
</p>
<ul className="space-y-4">
{items.map((row) => (
<li key={row.id} className="rounded-lg border border-[#2a2218] bg-[#0a0705]/95 p-4">
<div className="flex flex-wrap items-baseline justify-between gap-2 font-mono text-[10px] uppercase tracking-wider text-[#7a6e62]">
<span className="text-[#ff9a3c]/90">{formatRingLabel(row.topicSlug)}</span>
<span>
{row.author} · {formatAgo(row.ts)}
</span>
</div>
<Link
href={`/forum/post/${row.id}`}
className="mt-2 block font-mono text-base font-bold leading-snug text-[#ffcb8a] hover:underline"
>
{row.title}
</Link>
<p className="mt-2 line-clamp-3 text-sm leading-relaxed text-[#b0a090]">{row.body}</p>
<div className="mt-3 flex flex-wrap gap-3 font-mono text-[11px] text-[#6b6358]">
<span>{row.replies.length} comments</span>
<span>score ~{row.baseScore ?? 1}</span>
<Link href={`/forum/post/${row.id}`} className="text-[#ff9a3c] hover:underline">
open thread
</Link>
</div>
</li>
))}
</ul>
<nav className="mt-12 flex flex-wrap items-center justify-center gap-2 font-mono text-xs">
{page > 1 ? (
<Link
href={`/chatter?page=${page - 1}`}
className="rounded border border-[#3d3028] px-3 py-2 text-[#ffcb8a] hover:border-[#c45c26]"
>
Newer
</Link>
) : (
<span className="rounded border border-transparent px-3 py-2 text-[#4a433c]"> Newer</span>
)}
{pages.map((n) => (
<Link
key={n}
href={`/chatter?page=${n}`}
className={`rounded border px-3 py-2 ${
n === page ? "border-[#c45c26] bg-[#c45c26]/20 text-[#ffcb8a]" : "border-[#3d3028] text-[#b8a995] hover:border-[#c45c26]/40"
}`}
>
{n}
</Link>
))}
{page < totalPages ? (
<Link
href={`/chatter?page=${page + 1}`}
className="rounded border border-[#3d3028] px-3 py-2 text-[#ffcb8a] hover:border-[#c45c26]"
>
Older
</Link>
) : (
<span className="rounded border border-transparent px-3 py-2 text-[#4a433c]">Older </span>
)}
</nav>
<p className="mt-10 text-center font-mono text-[10px] text-[#4a4338]">Total catalogued: {SHOP_CHATTER_COUNT} threads</p>
</main>
</div>
);
}