优化评论区展示:弱化用户名样式、调整回复布局与编辑时限。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-06 00:33:06 +08:00
parent e8b28a37e4
commit 322ac14055
7 changed files with 107 additions and 41 deletions

View File

@@ -25,20 +25,21 @@ import {
} from '../utils/comment';
import { isTimeDiffSignificant } from '../utils/content';
import { useForumLimits } from '../hooks/useForumLimits';
import { Tooltip } from './ui/Tooltip';
import UserLink from './UserLink';
function isCommentAuthor(c: Comment, user?: User | null): boolean {
return !!user && c.user_id > 0 && c.user_id === user.id;
}
function canEditComment(c: Comment, user: User | null | undefined, windowHours: number): boolean {
function canEditComment(c: Comment, user: User | null | undefined, windowMinutes: number): boolean {
if (!user) return false;
if (user.role === 'admin') return true;
if (!isCommentAuthor(c, user)) return false;
if (windowHours <= 0) return true;
if (windowMinutes <= 0) return true;
const created = new Date(c.created_at).getTime();
if (Number.isNaN(created)) return false;
return Date.now() - created <= windowHours * 3600_000;
return Date.now() - created <= windowMinutes * 60_000;
}
interface ItemProps {
@@ -84,7 +85,21 @@ function CommentItem({
const isReplying = replyToId === c.id;
const isEditing = editingId === c.id;
const isAdmin = currentUser?.role === 'admin';
const canEdit = canEditComment(c, currentUser, limits.comment_edit_window_hours ?? 24);
const editWindowMinutes = limits.comment_edit_window_minutes ?? 3;
// 到期后强制重渲染,使「编辑」按钮自动消失
const [, setEditExpireTick] = useState(0);
useEffect(() => {
if (!currentUser || currentUser.role === 'admin') return;
if (!isCommentAuthor(c, currentUser)) return;
if (editWindowMinutes <= 0) return;
const created = new Date(c.created_at).getTime();
if (Number.isNaN(created)) return;
const remaining = created + editWindowMinutes * 60_000 - Date.now();
if (remaining <= 0) return;
const timer = window.setTimeout(() => setEditExpireTick(n => n + 1), remaining + 30);
return () => window.clearTimeout(timer);
}, [c.created_at, c.id, c.user_id, currentUser, editWindowMinutes]);
const canEdit = canEditComment(c, currentUser, editWindowMinutes);
const canDelete = isAdmin;
const canApprove = isAdmin
&& (c.status === 'pending' || c.status === 'rejected')
@@ -153,6 +168,11 @@ function CommentItem({
) : (
<span className="waline-comment-author">{nick}</span>
)}
{!c.reply_to && (
<span className="waline-comment-floor" aria-label={`${c.floor}`}>
#{c.floor}
</span>
)}
</div>
{hidden ? (
@@ -184,9 +204,6 @@ function CommentItem({
</div>
) : (
<div className="waline-comment-bubble">
{c.reply_target && (
<span className="waline-reply-at">@{commentNick(c.reply_target)}</span>
)}
<CommentContent content={c.content} />
</div>
)}
@@ -195,10 +212,13 @@ function CommentItem({
<span className="waline-comment-date">
<Clock size={14} />
{formatCommentDate(c.created_at)}
{showEdited && <span className="waline-comment-edited"> · </span>}
{isAdmin && showEdited && <span className="waline-comment-edited"> · </span>}
{c.status === 'pending' && <span className="waline-comment-status waline-comment-status--pending"> · </span>}
{c.status === 'rejected' && <span className="waline-comment-status waline-comment-status--rejected"> · </span>}
</span>
{c.reply_target && (
<span className="waline-reply-at">@{commentNick(c.reply_target)}</span>
)}
{!hidden && !isEditing && canApprove && (
<button
type="button"
@@ -224,10 +244,12 @@ function CommentItem({
</button>
) : (
<button type="button" className="waline-comment-reply-btn" onClick={() => onReply(c)}>
<MessageSquare size={14} />
</button>
<Tooltip content={`回复给 ${nick}`} side="top">
<button type="button" className="waline-comment-reply-btn" onClick={() => onReply(c)}>
<MessageSquare size={14} />
</button>
</Tooltip>
)
)}
{!hidden && !isEditing && canEdit && (