import type { Metadata } from "next"; import { cookies } from "next/headers"; import Link from "next/link"; import { PenSquare, SearchX, MessagesSquare } from "lucide-react"; import { fetchPosts, fetchOverview, fetchBoardSidebar, type OverviewResponse, type BoardSidebarResponse, } from "@/lib/api"; import { authCookieHeader } from "@/lib/cookies"; import { getMeCached, getPublicSettingsCached } from "@/lib/serverData"; import { siteDocumentTitle } from "@/lib/brand"; import { isAdminOrAbove } from "@/lib/roles"; import Pagination from "@/components/Pagination"; import PostRow from "@/components/PostRow"; import FeedTabs from "@/components/home/FeedTabs"; import FeedNewTip from "@/components/home/FeedNewTip"; import { WorkspaceNav, WorkspaceNavMobile } from "@/components/home/WorkspaceNav"; import { SiteDocBreadcrumb } from "@/components/site-doc"; import { CommunityPanels, CommunityPanelsMobile } from "@/components/home/CommunityPanels"; import { BoardPanels, BoardPanelsMobile, BoardMissingPanel, } from "@/components/home/BoardPanels"; import { requestCanonical } from "@/lib/canonicalMeta"; import { defaultFeedSort, homeCanonicalQuery, normalizeFeedSort } from "@/lib/siteUrl"; import { listingJsonLd } from "@/lib/jsonLd"; import JsonLd from "@/components/JsonLd"; interface HomePageProps { searchParams: Promise<{ q?: string; page?: string; sort?: string; board?: string }>; } export async function generateMetadata({ searchParams }: HomePageProps): Promise { const settings = await getPublicSettingsCached(); const sp = await searchParams; const allowComments = settings.allow_comments !== false; return { title: { absolute: siteDocumentTitle(settings.site_name, settings.site_slogan) }, description: settings.site_description, ...(await requestCanonical("/", homeCanonicalQuery(sp, allowComments))), }; } // 空结果面板(无帖 / 无搜索结果) function EmptyState({ keyword }: { keyword: string }) { return (
{keyword ? : }

{keyword ? "没有找到相关帖子" : "还没有帖子"}

{keyword ? `换个关键词试试,或者发起“${keyword}”相关的话题` : "成为第一个发起讨论的人吧"}

{keyword ? (
清除搜索 发起这个话题
) : ( 写新帖 )}
); } export default async function HomePage({ searchParams }: HomePageProps) { const sp = await searchParams; const keyword = sp.q?.trim() || ""; const page = Math.max(1, parseInt(sp.page || "1", 10) || 1); const boardId = Math.max(0, parseInt(sp.board || "0", 10) || 0); const isSearch = keyword.length > 0; const cookie = authCookieHeader(await cookies()); const publicSettings = await getPublicSettingsCached(); const allowComments = publicSettings.allow_comments !== false; const sort = normalizeFeedSort(sp.sort, allowComments); // 与 layout 共享同一份 /me 请求(React cache 去重) const mePromise = getMeCached(cookie || undefined); const postsPromise = fetchPosts( page, 20, isSearch || boardId <= 0 ? undefined : boardId, sort, keyword, sort === "recommended", cookie ); // 聚合面板失败不阻塞帖子流 const overviewPromise: Promise = fetchOverview(cookie).catch(() => null); const boardSidebarPromise: Promise = boardId > 0 && !isSearch ? fetchBoardSidebar(boardId, cookie).catch(() => null) : Promise.resolve(null); const [me, postsData, overview, boardSidebar] = await Promise.all([ mePromise, postsPromise, overviewPromise, boardSidebarPromise, ]); const user = me.user; const boards = overview?.boards ?? []; const canManageAnnouncements = !!user && isAdminOrAbove(user.role); const canManagePages = canManageAnnouncements; const sidebarPages = overview?.sidebar_pages ?? []; const activeBoard = boards.find((b) => b.id === boardId) || boardSidebar?.board || null; const showBoardPanels = boardId > 0 && !isSearch; // boardId 有效但接口失败 → 显示简卡;成功则板级右栏;否则全站右栏 const boardPanelsMissing = showBoardPanels && !boardSidebar; const pageName = isSearch ? `搜索“${keyword}”` : activeBoard ? activeBoard.name : sort === "new" ? "最新发布" : sort === "recommended" ? "推荐帖" : "最新讨论"; const listingLd = listingJsonLd({ siteName: publicSettings.site_name || "姜十三论坛", siteDescription: publicSettings.site_description, pageName, path: "/", query: homeCanonicalQuery(sp, allowComments), items: postsData.posts.map((p) => ({ id: p.id, name: p.title })), page, pageSize: 20, }); // 搜索态:单栏占满,无 Tabs/侧栏 if (isSearch) { return (

搜索“{keyword}”

共 {postsData.total} 篇结果 · 按相关度与时间排序

{postsData.posts.length > 0 ? ( postsData.posts.map((post, i) => ( )) ) : ( )}
); } // 置顶帖直接按后端 pinned DESC 顺序并入统一信息流;标题仅供屏幕阅读器 const pageQuery: Record = {}; if (boardId > 0) pageQuery.board = String(boardId); if (sort !== defaultFeedSort(allowComments)) pageQuery.sort = sort; return (
{/* 中栏:tab 栏与帖子流为同一个整体卡片(左栏切板块只替换这一列) */}

{pageName}

{activeBoard ? (
) : null}
共 {postsData.total} 篇
{user && ( )} {postsData.posts.length > 0 ? ( postsData.posts.map((post, i) => ( )) ) : ( )}
0 ? pageQuery : undefined} />
{/* 右栏:选中板块时切板级面板,否则全站社区面板 */} {showBoardPanels ? ( boardSidebar ? ( ) : boardPanelsMissing ? ( ) : null ) : ( overview && ( ) )}
{/* 窄屏:面板下沉到帖子流之后 */} {showBoardPanels ? ( boardSidebar ? (
) : null ) : ( overview && (
) )}
); }