From 4a20d80f6884467bf7810e9fcf3914b83d70df25 Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 02:37:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A1=B6=E6=A0=8F=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E9=A1=B5=E5=90=8D,=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E6=A0=87=E9=A2=98=E8=A1=8C;Logo=E5=9B=9E?= =?UTF-8?q?=E9=A6=96=E9=A1=B5/=E9=A1=B5=E5=90=8D=E5=9B=9E=E9=A1=B6?= =?UTF-8?q?=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 PageTitleContext:页面上报标题,顶栏 brand 位显示当前页名(全部资料/ 热门资料/最新/官方/分类名/搜索/我的收藏),未上报则回退品牌名 - AssetStreamPage、Favorites 上报标题;移除资料流内单独的标题行,省出空间 - 顶栏拆分点击:Logo→首页(首页则回顶部);页名文字→回到当前页顶部 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/App.tsx | 69 +++++++++------- src/components/AssetStreamPage.tsx | 6 +- src/components/PageTitleContext.tsx | 41 ++++++++++ .../messageStream/MessageStream.tsx | 13 +--- src/layouts/PublicLayout.tsx | 78 ++++++++++++------- src/pages/Favorites/index.tsx | 3 + 6 files changed, 140 insertions(+), 70 deletions(-) create mode 100644 src/components/PageTitleContext.tsx diff --git a/src/App.tsx b/src/App.tsx index ff79fed..076f0ca 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,6 +11,7 @@ import { OfficialRecommendationsPage } from "./pages/OfficialRecommendations"; import { SearchPage } from "./pages/Search"; import { PostRedirect } from "./pages/PostRedirect"; import { ScrollToTop } from "./components/ScrollToTop"; +import { PageTitleProvider } from "./components/PageTitleContext"; import Favorites from "./pages/Favorites"; import { adminUiPrefix } from "./adminPaths"; import { AdminRouteTree } from "./adminRouteTree"; @@ -28,38 +29,46 @@ export default function App() { - - - - }> - } /> - } /> - } /> - } - /> - } - /> - } /> - } /> - } /> - + + + + + }> + } /> + } /> + } + /> + } + /> + } + /> + } /> + } + /> + } /> + - {adminEnabled ? ( - AdminRouteTree() - ) : ( - } - /> - )} + {adminEnabled ? ( + AdminRouteTree() + ) : ( + } + /> + )} - } /> - - + } /> + + + diff --git a/src/components/AssetStreamPage.tsx b/src/components/AssetStreamPage.tsx index a9c3983..26acb71 100644 --- a/src/components/AssetStreamPage.tsx +++ b/src/components/AssetStreamPage.tsx @@ -1,5 +1,6 @@ import type { PostScope } from "../types/post"; import { MessageStream } from "./messageStream/MessageStream"; +import { useSetPageTitle } from "./PageTitleContext"; type AssetStreamPageProps = { title: string; @@ -7,9 +8,12 @@ type AssetStreamPageProps = { }; export function AssetStreamPage({ title, scope }: AssetStreamPageProps) { + // Show the page name in the global header instead of a separate title row, + // saving vertical space. + useSetPageTitle(title); return (
- +
); } diff --git a/src/components/PageTitleContext.tsx b/src/components/PageTitleContext.tsx new file mode 100644 index 0000000..53e15ab --- /dev/null +++ b/src/components/PageTitleContext.tsx @@ -0,0 +1,41 @@ +import { + createContext, + useContext, + useEffect, + useState, + type PropsWithChildren, +} from "react"; + +type PageTitleCtx = { + title: string | null; + setTitle: (title: string | null) => void; +}; + +const PageTitleContext = createContext(null); + +/** + * Lets a page publish its title to the global header so the header can show the + * current page name (e.g. "全部资料" / "热门资料") in place of the brand, avoiding + * a separate on-page title row. Pages that don't set one fall back to the brand. + */ +export function PageTitleProvider({ children }: PropsWithChildren) { + const [title, setTitle] = useState(null); + return ( + + {children} + + ); +} + +export function usePageTitle(): string | null { + return useContext(PageTitleContext)?.title ?? null; +} + +/** Publish the current page's title; clears it again when the page unmounts. */ +export function useSetPageTitle(title: string | null): void { + const setTitle = useContext(PageTitleContext)?.setTitle; + useEffect(() => { + setTitle?.(title); + return () => setTitle?.(null); + }, [setTitle, title]); +} diff --git a/src/components/messageStream/MessageStream.tsx b/src/components/messageStream/MessageStream.tsx index ca3a86e..7e4f071 100644 --- a/src/components/messageStream/MessageStream.tsx +++ b/src/components/messageStream/MessageStream.tsx @@ -6,17 +6,15 @@ import type { PostScope } from "../../types/post"; import { Reveal } from "../../motion"; import { Skeleton } from "../Skeleton"; import { FilterChips } from "./FilterChips"; -import { SectionHeader } from "../SectionHeader"; import { MessageBubble } from "./MessageBubble"; import { useGroupedByDay } from "./hooks/useGroupedByDay"; import { usePostStream } from "./hooks/usePostStream"; export type MessageStreamProps = { scope: PostScope; - title?: string; }; -export function MessageStream({ scope, title }: MessageStreamProps) { +export function MessageStream({ scope }: MessageStreamProps) { const { t, lang } = useI18n(); const [sp, setSp] = useSearchParams(); const { hash } = useLocation(); @@ -116,14 +114,9 @@ export function MessageStream({ scope, title }: MessageStreamProps) { return (
- {/* Title + filters stay pinned below the global header so users always - see which page they're on and can switch filters while scrolling. */} + {/* Filters stay pinned below the global header (which shows the page + name) so users can switch filters while scrolling. */}
- {title ? ( -
- -
- ) : null} updateParam("type", v)} />
diff --git a/src/layouts/PublicLayout.tsx b/src/layouts/PublicLayout.tsx index b6611d0..c49646f 100644 --- a/src/layouts/PublicLayout.tsx +++ b/src/layouts/PublicLayout.tsx @@ -4,6 +4,7 @@ import { useEffect, useRef, useState } from "react"; import { Link, useLocation, useNavigate, useOutlet } from "react-router-dom"; import { pageTransition } from "../motion"; import { ArkLogoMark } from "../components/ArkLogoMark"; +import { usePageTitle } from "../components/PageTitleContext"; import { BackToTop } from "../components/BackToTop"; import { DocumentMeta } from "../components/DocumentMeta"; import { SearchPanel } from "../components/SearchPanel"; @@ -289,6 +290,8 @@ export function PublicLayout() { navIsActive(pathname, search, hash, which); const isHome = pathname === "/"; const footerInContentFlow = pathname === "/browse"; + // Current page name shown in the header brand slot (falls back to the brand). + const pageTitle = usePageTitle(); const popularHref = "/browse?sort=popular"; const goSearch = () => { @@ -365,20 +368,29 @@ export function PublicLayout() {
- { - if (isHome) { - e.preventDefault(); - window.scrollTo({ top: 0, behavior: "smooth" }); - } - }} - > - - {t("brand")} - +
+ {/* Logo → home; page-name text → scroll to top of the current page. */} + { + if (isHome) { + e.preventDefault(); + window.scrollTo({ top: 0, behavior: "smooth" }); + } + }} + className="shrink-0 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070c]" + > + + + +
+