feat: 全站评论开关,并统一后台表单控件
关闭评论后前台隐藏评论能力;外观/设置/公告改用 AdminField 与 Stepper。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -16,6 +16,7 @@ export default function UserProfileContent({
|
||||
commentData,
|
||||
commentError,
|
||||
page,
|
||||
allowComments = true,
|
||||
}: {
|
||||
userId: string;
|
||||
profile: UserProfile;
|
||||
@@ -23,6 +24,7 @@ export default function UserProfileContent({
|
||||
commentData: UserCommentsResponse | null;
|
||||
commentError: boolean;
|
||||
page: number;
|
||||
allowComments?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
@@ -50,9 +52,11 @@ export default function UserProfileContent({
|
||||
</h2>
|
||||
<div className="flex items-center gap-4 meta text-[12px]">
|
||||
<span className="tabular-nums">{new Date(post.created_at).toLocaleDateString("zh-CN")}</span>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<IconComment size={12} /> {post.comment_count}
|
||||
</span>
|
||||
{allowComments ? (
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<IconComment size={12} /> {post.comment_count}
|
||||
</span>
|
||||
) : null}
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<IconEye size={12} /> {post.view_count}
|
||||
</span>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { fetchUserProfile, fetchUserComments, type UserProfile, type UserCommentsResponse } from "@/lib/api";
|
||||
import { authCookieHeader } from "@/lib/cookies";
|
||||
import { getMeCached } from "@/lib/serverData";
|
||||
import { getMeCached, getPublicSettingsCached } from "@/lib/serverData";
|
||||
import UserProfileContent from "./UserProfileContent";
|
||||
import ProfileForm from "./ProfileForm";
|
||||
import PasswordForm from "./PasswordForm";
|
||||
@@ -28,9 +28,12 @@ export async function generateMetadata({ params }: UserPageProps): Promise<Metad
|
||||
const { id } = await params;
|
||||
try {
|
||||
const data = await fetchUserProfile(id, 1);
|
||||
const allowComments = (await getPublicSettingsCached()).allow_comments !== false;
|
||||
return {
|
||||
title: data.user.nickname || data.user.username,
|
||||
description: `查看 ${data.user.nickname} 的资料、发帖与评论`,
|
||||
description: allowComments
|
||||
? `查看 ${data.user.nickname} 的资料、发帖与评论`
|
||||
: `查看 ${data.user.nickname} 的资料与发帖`,
|
||||
};
|
||||
} catch {
|
||||
return { title: "用户不存在" };
|
||||
@@ -53,6 +56,7 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
|
||||
const cookie = authCookieHeader(await cookies());
|
||||
const me = await getMeCached(cookie || undefined);
|
||||
const allowComments = (await getPublicSettingsCached()).allow_comments !== false;
|
||||
|
||||
let data: UserProfile;
|
||||
try {
|
||||
@@ -75,13 +79,14 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
// 判断是否本人(决定能否看到 settings / security tab)
|
||||
const isOwner = me.user?.id === data.user.id;
|
||||
const currentUser = me.user;
|
||||
const effectiveTab: UserTab =
|
||||
let effectiveTab: UserTab =
|
||||
(tab === "settings" || tab === "security") && !isOwner ? "posts" : tab;
|
||||
if (!allowComments && effectiveTab === "comments") effectiveTab = "posts";
|
||||
|
||||
// 评论 tab:SSR 直出对应分页
|
||||
let commentData: UserCommentsResponse | null = null;
|
||||
let commentError = false;
|
||||
if (effectiveTab === "comments") {
|
||||
if (allowComments && effectiveTab === "comments") {
|
||||
try {
|
||||
commentData = await fetchUserComments(id, page);
|
||||
} catch {
|
||||
@@ -96,7 +101,9 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
// 导航项(桌面左栏 / 移动 chips 共用定义)
|
||||
const navItems: { key: UserTab; label: string; count?: number; icon: React.ReactNode; ownerOnly?: boolean }[] = [
|
||||
{ key: "posts", label: "发帖", count: data.stats.post_count, icon: <FileText size={15} /> },
|
||||
{ key: "comments", label: "评论", count: data.stats.comment_count, icon: <MessageCircle size={15} /> },
|
||||
...(allowComments
|
||||
? [{ key: "comments" as const, label: "评论", count: data.stats.comment_count, icon: <MessageCircle size={15} /> }]
|
||||
: []),
|
||||
{ key: "settings", label: "资料设置", icon: <Settings size={15} />, ownerOnly: true },
|
||||
{ key: "security", label: "安全设置", icon: <KeyRound size={15} />, ownerOnly: true },
|
||||
];
|
||||
@@ -106,7 +113,7 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
// 统计条数据
|
||||
const heroStats = [
|
||||
{ label: "发帖", value: data.stats.post_count },
|
||||
{ label: "评论", value: data.stats.comment_count },
|
||||
...(allowComments ? [{ label: "评论", value: data.stats.comment_count }] : []),
|
||||
{ label: "积分", value: data.stats.points, foot: data.stats.streak > 0 ? `连签 ${data.stats.streak} 天` : undefined },
|
||||
];
|
||||
|
||||
@@ -227,7 +234,10 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
</div>
|
||||
|
||||
{/* 统计条 */}
|
||||
<div className="relative z-10 grid grid-cols-3 mt-4 border-t" style={{ borderColor: "var(--line)" }}>
|
||||
<div
|
||||
className={`relative z-10 grid mt-4 border-t ${heroStats.length === 3 ? "grid-cols-3" : "grid-cols-2"}`}
|
||||
style={{ borderColor: "var(--line)" }}
|
||||
>
|
||||
{heroStats.map((s, i) => (
|
||||
<div
|
||||
key={s.label}
|
||||
@@ -315,6 +325,7 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
commentData={commentData}
|
||||
commentError={commentError}
|
||||
page={page}
|
||||
allowComments={allowComments}
|
||||
/>
|
||||
) : effectiveTab === "settings" && isOwner && currentUser ? (
|
||||
<div>
|
||||
@@ -423,7 +434,11 @@ export default async function UserPage({ params, searchParams }: UserPageProps)
|
||||
</li>
|
||||
<li className="flex gap-2">
|
||||
<span className="shrink-0 mt-px" style={{ color: "var(--accent)" }}>·</span>
|
||||
<span>昵称和头像会公开展示在帖子、评论和个人主页。</span>
|
||||
<span>
|
||||
{allowComments
|
||||
? "昵称和头像会公开展示在帖子、评论和个人主页。"
|
||||
: "昵称和头像会公开展示在帖子和个人主页。"}
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex gap-2">
|
||||
<span className="shrink-0 mt-px" style={{ color: "var(--accent)" }}>·</span>
|
||||
|
||||
Reference in New Issue
Block a user