"use client"; import { useEffect, useLayoutEffect, useRef, useState } from "react"; import { usePathname } from "next/navigation"; import Header from "@/components/Header"; import Footer from "@/components/Footer"; import BackToTop from "@/components/BackToTop"; import type { User } from "@/lib/api"; import { useSiteBrand } from "@/components/SiteBrandProvider"; function pathIsAdmin(pathname: string | null) { if (!pathname) return false; return pathname === "/admin" || pathname.startsWith("/admin/"); } function pathIsChat(pathname: string | null) { if (!pathname) return false; return pathname === "/chat" || pathname.startsWith("/chat/"); } function pathIsComposeStudio(pathname: string | null) { if (!pathname) return false; return pathname === "/compose" || /^\/post\/[^/]+\/edit\/?$/.test(pathname); } /** 帖子详情(非 edit)移动端有底栏,回顶需抬高 */ function pathIsPostDetail(pathname: string | null) { if (!pathname) return false; return /^\/post\/[^/]+$/.test(pathname); } /** * 公开站 chrome 闸门:/admin 不渲染 Header/Footer/1440 main。 * 首屏用 middleware 的 x-pathname 初值保证 hydration 一致; * 挂载后同步跟 usePathname(渲染期判定,避免 useEffect 晚一拍闪框)。 */ export default function SiteChrome({ initialIsAdmin, user, unread, chatUnread, theme, children, }: { initialIsAdmin: boolean; user: User | null; unread: number; chatUnread: number; theme: "light" | "dark"; children: React.ReactNode; }) { const pathname = usePathname(); const [mounted, setMounted] = useState(false); const isFirstPath = useRef(true); useEffect(() => { setMounted(true); }, []); // Next.js App Router:新页顶若仍在视口内会跳过滚顶。sticky Header 把 // 「微滚后的页顶」判成已可见,软导航就会停在资料卡上沿。父级 layoutEffect // 晚于 Next.js 子级滚动处理,且不影响 F5 / 带 hash 的定位。 useLayoutEffect(() => { if (isFirstPath.current) { isFirstPath.current = false; return; } if (pathIsAdmin(pathname)) return; if (window.location.hash) return; window.scrollTo({ top: 0, left: 0, behavior: "auto" }); }, [pathname]); // 首屏跟 SSR;之后在渲染期直接读 pathname,软导航无 Header 闪动 const isAdmin = mounted ? pathIsAdmin(pathname) : initialIsAdmin; useLayoutEffect(() => { document.documentElement.dataset.j13Chrome = isAdmin ? "admin" : "site"; }, [isAdmin]); const brand = useSiteBrand(); if (isAdmin) { return <>{children}; } // 聊天 / 发帖工作台锁视口,窗口回顶无意义 const showBackToTop = !pathIsChat(pathname) && !pathIsComposeStudio(pathname); return ( <>
{children}