补齐论坛核心能力:讨论锁定、发帖本地草稿、标签精确筛选,以及私信与通知分流。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-07 06:32:06 +08:00
parent 0f83183620
commit 10185178e2
28 changed files with 1165 additions and 266 deletions

View File

@@ -32,18 +32,25 @@ export default function HomePage() {
const boardId = Number(params.get('board')) || ctx?.boardId || 0;
const keyword = params.get('keyword') || '';
const tag = params.get('tag') || '';
const sort = parseFeedSort(params.get('sort'));
const board = (ctx?.boards ?? []).find(b => b.id === boardId);
const isSiteHome = !boardId && !keyword;
const isSiteHome = !boardId && !keyword && !tag;
const siteIntro = siteMetaDescription(branding);
const feedTitle = keyword
? `搜索${keyword}`
: (boardId && board ? board.name : '');
const feedTitle = tag
? `标签${tag}`
: keyword
? `搜索:${keyword}`
: (boardId && board ? board.name : '');
usePageSEO({
title: feedTitle || undefined,
description: board?.description?.trim() || siteIntro,
keywords: joinSEOKeywords(board?.name, branding.keywords),
canonicalPath: boardId ? `/?board=${boardId}` : '/',
keywords: joinSEOKeywords(board?.name, tag, branding.keywords),
canonicalPath: tag
? `/?tag=${encodeURIComponent(tag)}`
: boardId
? `/?board=${boardId}`
: '/',
ogType: 'website',
});
@@ -60,7 +67,7 @@ export default function HomePage() {
const pageRef = useRef(1);
pageRef.current = page;
// 与当前筛选一致的列表快照(供卸载/切换筛选时写入缓存)
const feedSnapRef = useRef({ boardId, keyword, sort, posts, postTotal, page });
const feedSnapRef = useRef({ boardId, keyword, tag, sort, posts, postTotal, page });
const totalPages = Math.max(1, Math.ceil(Math.max(postTotal, 0) / pageSize));
const showPagination = totalPages > 1 && posts.length > 0;
@@ -87,7 +94,8 @@ export default function HomePage() {
page: p,
size: pageSize,
board_id: boardId || '',
keyword,
keyword: tag ? '' : keyword,
tag: tag || '',
sort: sort === 'latest' ? '' : sort,
});
const batch = Array.isArray(data.posts) ? data.posts : [];
@@ -106,7 +114,7 @@ export default function HomePage() {
loadingRef.current = false;
setLoading(false);
}
}, [boardId, keyword, sort, pageSize]);
}, [boardId, keyword, tag, sort, pageSize]);
const loadFirst = useCallback(() => fetchPage(1), [fetchPage]);
@@ -134,7 +142,7 @@ export default function HomePage() {
return;
}
const cached = getFeedCache(boardId, keyword, sort);
const cached = getFeedCache(boardId, keyword, sort, tag);
if (cached && cached.posts.length > 0) {
setPosts(cached.posts);
setPostTotal(cached.postTotal);
@@ -154,6 +162,7 @@ export default function HomePage() {
pageSize,
boardId,
keyword,
tag,
sort,
location.key,
location.state,
@@ -165,15 +174,16 @@ export default function HomePage() {
if (
feedSnapRef.current.boardId === boardId
&& feedSnapRef.current.keyword === keyword
&& feedSnapRef.current.tag === tag
&& feedSnapRef.current.sort === sort
) {
feedSnapRef.current = { boardId, keyword, sort, posts, postTotal, page };
feedSnapRef.current = { boardId, keyword, tag, sort, posts, postTotal, page };
}
// 仅在筛选变化 / 卸载时缓存;勿把 posts 放进 deps否则会用旧列表污染新 keyword
useEffect(() => {
// cleanup 先保存上一档;再把快照重置为当前筛选的空占位
feedSnapRef.current = { boardId, keyword, sort, posts: [], postTotal: 0, page: 1 };
feedSnapRef.current = { boardId, keyword, tag, sort, posts: [], postTotal: 0, page: 1 };
return () => {
if (skipCacheSaveRef.current) return;
const snap = feedSnapRef.current;
@@ -183,9 +193,9 @@ export default function HomePage() {
postTotal: snap.postTotal,
page: snap.page,
scrollTop: scrollTopRef.current,
});
}, snap.tag);
};
}, [boardId, keyword, sort]);
}, [boardId, keyword, tag, sort]);
useEffect(() => {
if (!loading && posts.length > 0) skipCacheSaveRef.current = false;
@@ -212,10 +222,10 @@ export default function HomePage() {
loadFirst();
return;
}
navigateFeed(nav, buildHomeUrl(boardId, next));
navigateFeed(nav, buildHomeUrl(boardId, next, { keyword, tag }));
};
const showSortBar = !keyword;
const showSortBar = !keyword && !tag;
// 首屏用同构骨架,避免标题/列表分区先后出现造成闪动
if ((loading || limitsLoading) && posts.length === 0) {
@@ -230,6 +240,7 @@ export default function HomePage() {
<FeedHeader
boardId={boardId}
keyword={keyword}
tag={tag}
boards={ctx?.boards ?? []}
stats={ctx?.stats ?? null}
postTotal={postTotal}
@@ -255,7 +266,7 @@ export default function HomePage() {
resetScrollKey={listResetKey}
onScrollTopChange={(top) => { scrollTopRef.current = top; }}
onScrollRestored={() => setRestoreScrollTop(null)}
keyword={keyword}
keyword={keyword || tag}
boardId={boardId}
boardName={ctx?.boards?.find(b => b.id === boardId)?.name || ''}
noBoards={!ctx?.boardsLoading && (ctx?.boards?.length ?? 0) === 0}