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
This commit is contained in:
64
components/forum/ThreadVoteColumn.tsx
Normal file
64
components/forum/ThreadVoteColumn.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import type { ForumThread } from "@/lib/forumState";
|
||||
import {
|
||||
displayScore,
|
||||
getVoteForThread,
|
||||
setVoteForThread,
|
||||
type VoteChoice,
|
||||
} from "@/lib/forumState";
|
||||
|
||||
export function ThreadVoteColumn({
|
||||
thread,
|
||||
dense,
|
||||
onVoteChange,
|
||||
}: {
|
||||
thread: ForumThread;
|
||||
dense?: boolean;
|
||||
onVoteChange?: () => void;
|
||||
}) {
|
||||
const [tick, setTick] = useState(0);
|
||||
const choice = getVoteForThread(thread.id);
|
||||
const score = displayScore(thread);
|
||||
|
||||
const apply = useCallback(
|
||||
(dir: 1 | -1) => {
|
||||
const cur = getVoteForThread(thread.id);
|
||||
let next: VoteChoice = dir;
|
||||
if (cur === dir) next = 0;
|
||||
setVoteForThread(thread.id, next);
|
||||
setTick((t) => t + 1);
|
||||
onVoteChange?.();
|
||||
},
|
||||
[thread.id, onVoteChange],
|
||||
);
|
||||
|
||||
void tick;
|
||||
|
||||
const btn = dense ? "px-1 py-0.5 text-[10px]" : "px-1.5 py-1 text-xs";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col items-center rounded border border-[#3d3228] bg-[#0d0a07] font-mono ${dense ? "min-w-[44px] py-1" : "min-w-[52px] py-2"}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Upvote"
|
||||
onClick={() => apply(1)}
|
||||
className={`${btn} leading-none text-[#8a7d6b] hover:text-[#ff9a3c] ${choice === 1 ? "text-[#ff9a3c]" : ""}`}
|
||||
>
|
||||
▲
|
||||
</button>
|
||||
<span className="text-[11px] font-bold tabular-nums text-[#ffcb8a]">{score}</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Downvote"
|
||||
onClick={() => apply(-1)}
|
||||
className={`${btn} leading-none text-[#8a7d6b] hover:text-[#6b9fff] ${choice === -1 ? "text-[#6b9fff]" : ""}`}
|
||||
>
|
||||
▼
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user