"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import {
apiVotePoll,
apiClosePoll,
apiRefundBounty,
apiCloseLottery,
apiDrawLottery,
apiSolveQuestion,
apiReopenQuestion,
type PollState,
type BountyState,
type LotteryState,
type QuestionState,
} from "@/lib/api";
import { toast } from "@/lib/toast";
import Avatar from "@/components/Avatar";
export default function PostInteractPanels({
postId,
question,
poll,
bounty,
lottery,
loggedIn,
}: {
postId: number;
question?: QuestionState | null;
poll?: PollState | null;
bounty?: BountyState | null;
lottery?: LotteryState | null;
loggedIn: boolean;
}) {
if (!question && !poll && !bounty && !lottery) return null;
return (
{question &&
}
{poll &&
}
{bounty &&
}
{lottery &&
}
);
}
function formatEndsAt(iso?: string) {
if (!iso) return "";
try {
return new Date(iso).toLocaleString("zh-CN");
} catch {
return iso;
}
}
function QuestionPanel({ postId, state }: { postId: number; state: QuestionState }) {
const router = useRouter();
const [busy, setBusy] = useState(false);
const act = async (fn: () => Promise<{ post?: unknown; error?: string }>, ok: string) => {
setBusy(true);
try {
const res = await fn();
if (res.post) {
toast(ok, "ok");
router.refresh();
} else toast(res.error || "操作失败", "error");
} finally {
setBusy(false);
}
};
return (
问答 · {state.solved ? "已解决" : "未解决"}
{state.solved
? state.accepted_comment_id
? (
<>
已采纳{" "}
#{state.accepted_floor || "?"}
>
)
: "作者已标记为已解决"
: "可在评论旁采纳答案,或手动标记已解决"}
{state.can_solve && (
)}
{state.can_reopen && (
)}
);
}
function PollPanel({
postId,
state,
loggedIn,
}: {
postId: number;
state: PollState;
loggedIn: boolean;
}) {
const router = useRouter();
const [sel, setSel] = useState(state.my_options || []);
const [busy, setBusy] = useState(false);
const showResults = state.closed || (state.my_options && state.my_options.length > 0);
const toggle = (idx: number) => {
if (!state.can_vote) return;
if (state.multi) {
setSel((prev) =>
prev.includes(idx) ? prev.filter((x) => x !== idx) : [...prev, idx]
);
} else {
setSel([idx]);
}
};
const vote = async () => {
if (sel.length === 0) {
toast("请先选择选项", "error");
return;
}
setBusy(true);
try {
const res = await apiVotePoll(String(postId), sel);
if (res.post) {
toast("投票成功", "ok");
router.refresh();
} else toast(res.error || "投票失败", "error");
} catch {
toast("投票失败", "error");
} finally {
setBusy(false);
}
};
const close = async () => {
setBusy(true);
try {
const res = await apiClosePoll(String(postId));
if (res.post) {
toast("已结束投票", "ok");
router.refresh();
} else toast(res.error || "操作失败", "error");
} finally {
setBusy(false);
}
};
return (
投票{state.multi ? "(多选)" : "(单选)"}
{state.anonymous ? " · 匿名" : " · 实名"}
{state.voters} 人参与 · {state.total_votes} 票
{state.closed ? " · 已结束" : ""}
{state.options.map((o) => {
const checked = sel.includes(o.index);
const namedVoters = !state.anonymous && showResults ? o.voters || [] : [];
return (
-
{namedVoters.length > 0 && (
{namedVoters.map((v) => (
-
{v.nickname || v.username}
))}
)}
);
})}
{state.can_vote && (
)}
{!loggedIn && !state.closed && (
登录后投票
)}
{state.can_close && (
)}
);
}
function BountyPanel({ postId, state }: { postId: number; state: BountyState }) {
const router = useRouter();
const [busy, setBusy] = useState(false);
const [confirming, setConfirming] = useState(false);
const refund = async () => {
if (!confirming) {
setConfirming(true);
window.setTimeout(() => setConfirming(false), 3000);
return;
}
setBusy(true);
try {
const res = await apiRefundBounty(String(postId));
if (res.post) {
toast("悬赏已退回", "ok");
router.refresh();
} else toast(res.error || "退回失败", "error");
} finally {
setBusy(false);
setConfirming(false);
}
};
const statusText = state.expired
? "已过期,积分已自动退回"
: state.refunded
? "已退回给发帖人"
: state.accepted_comment_id
? null
: state.escrowed
? "积分已托管,采纳优质回答后发放"
: "悬赏未托管";
return (
悬赏 {state.points} 积分
{state.accepted_comment_id ? (
<>
已采纳{" "}
#{state.accepted_floor || "?"}
>
) : (
statusText
)}
{state.ends_at && !state.settled ? ` · 截止 ${formatEndsAt(state.ends_at)}` : ""}
{state.can_refund && (
)}
{state.can_accept && (
在评论旁点击「采纳」发放积分
)}
);
}
function LotteryPanel({
postId,
state,
}: {
postId: number;
state: LotteryState;
}) {
const router = useRouter();
const [busy, setBusy] = useState(false);
const act = async (fn: () => Promise<{ post?: unknown; error?: string }>, ok: string) => {
setBusy(true);
try {
const res = await fn();
if (res.post) {
toast(ok, "ok");
router.refresh();
} else toast(res.error || "操作失败", "error");
} finally {
setBusy(false);
}
};
return (
抽奖 · {state.slots} 个名额
已有 {state.entry_count} 名回帖用户参与
{state.drawn ? " · 已开奖" : state.closed ? " · 已截止" : ""}
{state.eligible && !state.drawn ? " · 你已在奖池中" : ""}
{state.ends_at && !state.drawn ? ` · 截止 ${formatEndsAt(state.ends_at)}` : ""}
{!state.drawn && (
对本帖发表评论即可参与(楼主除外)
)}
{state.can_close && (
)}
{state.can_draw && (
)}
{state.drawn && (
{state.winners.length > 0 ? "中奖名单" : "无人回帖,开奖结果为空"}
{state.winners.length > 0 && (
{state.winners.map((w) => (
-
{w.nickname || w.username}
))}
)}
)}
);
}