xhs_get_comments → xhs_get_sub_comments:针对单条评论加载子评论
getFeedDetail 已返回首屏一级评论(含 1-2 条子评论预览),不再需要独立的 评论加载工具。新增 xhs_get_sub_comments 针对指定一级评论加载完整子评论, 支持 max_count 参数(默认 20)控制加载量,避免超时和上下文溢出。 后端: - schemas: GetFeedCommentsSchema → GetSubCommentsSchema (feed_id, xsec_token, comment_id, max_count) - types: 删除 CommentsResult - feed-detail: 删除 getFeedComments/scrapeComments/CommentSort/parseCommentElement, 新增 getSubComments(导航→store就绪→定位评论→点击展开→读store) - selectors: 删除 commentSort* 选择器 - index/routes: 注册新工具和路由,超时改用 feed_detail(60s) 前端: - types/endpoints: 删除 CommentsResult,新增 getSubComments API - FeedDetail: 删除独立评论加载逻辑,评论随详情显示,新增 handleLoadSubComments - CommentTree: "还有 X 条回复" 改为可点击按钮,带加载状态
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import type { FeedDetail as FeedDetailType, Comment } from '@/api/types';
|
||||
import { getFeedDetail, getFeedComments, toggleLike, toggleFavorite, postComment, replyComment } from '@/api/endpoints';
|
||||
import { getFeedDetail, getSubComments, toggleLike, toggleFavorite, postComment, replyComment } from '@/api/endpoints';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Spinner } from '@/components/ui/Spinner';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
@@ -24,10 +24,7 @@ export function FeedDetail({ feedId, xsecToken, onClose, onUserClick }: Props) {
|
||||
const [currentImage, setCurrentImage] = useState(0);
|
||||
|
||||
const [comments, setComments] = useState<Comment[]>([]);
|
||||
const [commentsLoading, setCommentsLoading] = useState(false);
|
||||
const [commentsHasMore, setCommentsHasMore] = useState(false);
|
||||
const [commentsTotalCount, setCommentsTotalCount] = useState(0);
|
||||
const [commentsMaxCount, setCommentsMaxCount] = useState(20);
|
||||
const [subCommentsLoading, setSubCommentsLoading] = useState<string | null>(null);
|
||||
|
||||
const [liked, setLiked] = useState(false);
|
||||
const [favorited, setFavorited] = useState(false);
|
||||
@@ -49,8 +46,6 @@ export function FeedDetail({ feedId, xsecToken, onClose, onUserClick }: Props) {
|
||||
// Use comments from __INITIAL_STATE__ (first page, ~10-20).
|
||||
if (res.data.comments.length > 0) {
|
||||
setComments(res.data.comments);
|
||||
setCommentsTotalCount(res.data.commentCount);
|
||||
setCommentsHasMore(res.data.commentCount > res.data.comments.length);
|
||||
}
|
||||
} else {
|
||||
setError(res.error?.message || 'Failed to load detail');
|
||||
@@ -61,28 +56,26 @@ export function FeedDetail({ feedId, xsecToken, onClose, onUserClick }: Props) {
|
||||
|
||||
}, [feedId, xsecToken]);
|
||||
|
||||
const handleLoadComments = async (maxCount = commentsMaxCount) => {
|
||||
setCommentsLoading(true);
|
||||
const handleLoadSubComments = async (commentId: string) => {
|
||||
setSubCommentsLoading(commentId);
|
||||
try {
|
||||
const res = await getFeedComments(feedId, xsecToken, 'default', maxCount);
|
||||
const res = await getSubComments(feedId, xsecToken, commentId);
|
||||
if (res.success && res.data) {
|
||||
setComments(res.data.comments);
|
||||
setCommentsHasMore(res.data.hasMore);
|
||||
setCommentsTotalCount(res.data.totalCount);
|
||||
setComments((prev) =>
|
||||
prev.map((c) =>
|
||||
c.id === commentId
|
||||
? { ...c, subComments: res.data!, subCommentCount: res.data!.length }
|
||||
: c,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
toast('error', '加载评论失败');
|
||||
toast('error', '加载子评论失败');
|
||||
} finally {
|
||||
setCommentsLoading(false);
|
||||
setSubCommentsLoading(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
const nextCount = commentsMaxCount * 2;
|
||||
setCommentsMaxCount(nextCount);
|
||||
void handleLoadComments(nextCount);
|
||||
};
|
||||
|
||||
const handleToggleLike = async () => {
|
||||
setActionLoading('like');
|
||||
try {
|
||||
@@ -323,25 +316,10 @@ export function FeedDetail({ feedId, xsecToken, onClose, onUserClick }: Props) {
|
||||
</div>
|
||||
|
||||
{/* Comments */}
|
||||
{comments.length === 0 && !commentsLoading && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => void handleLoadComments()}
|
||||
>
|
||||
加载评论
|
||||
</Button>
|
||||
)}
|
||||
{commentsLoading && (
|
||||
<div className="flex items-center gap-2 py-4 text-dark-muted text-sm">
|
||||
<Spinner size="sm" />
|
||||
<span>加载评论中...</span>
|
||||
</div>
|
||||
)}
|
||||
{comments.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-dark-muted uppercase tracking-wider mb-3">
|
||||
评论 ({commentsTotalCount > 0 ? commentsTotalCount : comments.length})
|
||||
评论 ({detail.commentCount > 0 ? detail.commentCount : comments.length})
|
||||
</h3>
|
||||
<CommentTree
|
||||
comments={comments}
|
||||
@@ -349,17 +327,9 @@ export function FeedDetail({ feedId, xsecToken, onClose, onUserClick }: Props) {
|
||||
setReplyTarget({ commentId, userId, nickname });
|
||||
setReplyText('');
|
||||
}}
|
||||
onLoadSubComments={(commentId) => void handleLoadSubComments(commentId)}
|
||||
subCommentsLoadingId={subCommentsLoading}
|
||||
/>
|
||||
{commentsHasMore && !commentsLoading && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={handleLoadMore}
|
||||
className="mt-3 w-full"
|
||||
>
|
||||
加载更多评论
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user