diff --git a/src/components/messageStream/hooks/usePostStream.ts b/src/components/messageStream/hooks/usePostStream.ts index 907a1b5..5a12617 100644 --- a/src/components/messageStream/hooks/usePostStream.ts +++ b/src/components/messageStream/hooks/usePostStream.ts @@ -112,6 +112,18 @@ function streamKey(params: PostStreamParams): string { return buildRealUrl(params); } +/** + * Warm the cache for a stream view before the user navigates to it, so opening + * the page shows content immediately instead of starting to load on arrival. + * No-op for the mock backend or when the first page is already cached. + */ +export function prefetchPostStream(params: PostStreamParams): void { + if (USE_MOCK) return; + const url = buildRealUrl(params); + if (readJSONCache(url)) return; + getJSON(url).catch(() => {}); +} + export function usePostStream(params: PostStreamParams): PostStreamResult { const [items, setItems] = useState([]); const [hasMore, setHasMore] = useState(true); diff --git a/src/layouts/PublicLayout.tsx b/src/layouts/PublicLayout.tsx index c49646f..2eff651 100644 --- a/src/layouts/PublicLayout.tsx +++ b/src/layouts/PublicLayout.tsx @@ -5,6 +5,7 @@ import { Link, useLocation, useNavigate, useOutlet } from "react-router-dom"; import { pageTransition } from "../motion"; import { ArkLogoMark } from "../components/ArkLogoMark"; import { usePageTitle } from "../components/PageTitleContext"; +import { prefetchPostStream } from "../components/messageStream/hooks/usePostStream"; import { BackToTop } from "../components/BackToTop"; import { DocumentMeta } from "../components/DocumentMeta"; import { SearchPanel } from "../components/SearchPanel"; @@ -292,6 +293,31 @@ export function PublicLayout() { const footerInContentFlow = pathname === "/browse"; // Current page name shown in the header brand slot (falls back to the brand). const pageTitle = usePageTitle(); + + // Warm "全部资料" and "热门资料" in the background (when idle) so tapping them + // shows content immediately instead of loading on arrival. + useEffect(() => { + const run = () => { + const base = { + scope: { kind: "all" as const }, + type: "all", + q: "", + lang, + }; + prefetchPostStream({ ...base, sort: "" }); + prefetchPostStream({ ...base, sort: "popular" }); + }; + const ric = window as typeof window & { + requestIdleCallback?: (cb: () => void) => number; + cancelIdleCallback?: (id: number) => void; + }; + if (ric.requestIdleCallback) { + const id = ric.requestIdleCallback(run); + return () => ric.cancelIdleCallback?.(id); + } + const id = window.setTimeout(run, 600); + return () => window.clearTimeout(id); + }, [lang]); const popularHref = "/browse?sort=popular"; const goSearch = () => {