From 132ca5c82cb0073aa1108bb1887bc69596a1ffe9 Mon Sep 17 00:00:00 2001 From: freefire Date: Tue, 1 Sep 2026 01:50:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=94=A8=20Zustand=20=E7=BC=93=E5=AD=98?= =?UTF-8?q?=20Feed=20=E5=88=97=E8=A1=A8=EF=BC=8C=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E6=97=B6=E6=81=A2=E5=A4=8D=E6=BB=9A=E5=8A=A8=E4=B8=94=E4=B8=8D?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 点击 Logo 仍强制刷新;后退忽略 history 上的 refreshFeed,并支持过期静默刷新与下拉刷新。 Co-authored-by: Cursor --- frontend/package-lock.json | 31 ++- frontend/package.json | 3 +- frontend/src/components/PullToRefresh.tsx | 13 + frontend/src/components/VirtualPostList.tsx | 10 +- frontend/src/layouts/MainLayout.tsx | 1 + frontend/src/pages/HomePage.tsx | 251 ++++++++++++++------ frontend/src/store/homeStore.ts | 106 +++++++++ frontend/src/utils/feedCache.ts | 61 +++-- 8 files changed, 384 insertions(+), 92 deletions(-) create mode 100644 frontend/src/store/homeStore.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 890a268..3b90cbb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -51,7 +51,8 @@ "tailwindcss": "^3.4.19", "tailwindcss-animate": "^1.0.7", "turndown": "^7.2.4", - "zod": "^4.4.3" + "zod": "^4.4.3", + "zustand": "^4.5.7" }, "devDependencies": { "@types/node": "^25.9.3", @@ -4480,6 +4481,34 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/frontend/package.json b/frontend/package.json index 394b148..10bf3ab 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -52,7 +52,8 @@ "tailwindcss": "^3.4.19", "tailwindcss-animate": "^1.0.7", "turndown": "^7.2.4", - "zod": "^4.4.3" + "zod": "^4.4.3", + "zustand": "^4.5.7" }, "devDependencies": { "@types/node": "^25.9.3", diff --git a/frontend/src/components/PullToRefresh.tsx b/frontend/src/components/PullToRefresh.tsx index 7e27f73..e371a99 100644 --- a/frontend/src/components/PullToRefresh.tsx +++ b/frontend/src/components/PullToRefresh.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { Loader2, ArrowDown } from 'lucide-react'; +import { FEED_PULL_REFRESH_EVENT } from '../utils/feedCache'; /** 触发刷新的下拉距离(px) */ const REFRESH_THRESHOLD = 68; @@ -167,6 +168,18 @@ export default function PullToRefresh() { setSettling(true); setPull(REFRESH_THRESHOLD * 0.7); window.setTimeout(() => { + // Feed 页:应用内强制重拉(保留 SPA 其它状态);其它页仍整页 reload + const isFeed = !!( + document.querySelector('.main-content--feed-mobile-scroll') + || document.querySelector('.page-wrap--feed .post-list-scroll') + ); + if (isFeed) { + window.dispatchEvent(new Event(FEED_PULL_REFRESH_EVENT)); + setRefreshing(false); + setSettling(true); + setPull(0); + return; + } window.location.reload(); }, 180); return; diff --git a/frontend/src/components/VirtualPostList.tsx b/frontend/src/components/VirtualPostList.tsx index ddef3de..b607107 100644 --- a/frontend/src/components/VirtualPostList.tsx +++ b/frontend/src/components/VirtualPostList.tsx @@ -161,6 +161,12 @@ export default function VirtualPostList({ onScrollTopChangeRef.current?.(0); }, [resetScrollKey, virtualizer, getScrollElement]); + useLayoutEffect(() => { + // 收到新的恢复目标时允许再次 restore;null 表示已消费,勿动标记 + if (restoreScrollTop == null) return; + restoredRef.current = false; + }, [restoreScrollTop]); + useLayoutEffect(() => { if (restoreScrollTop == null || restoredRef.current || posts.length === 0) return; const el = getScrollElement(); @@ -173,10 +179,6 @@ export default function VirtualPostList({ onScrollRestoredRef.current?.(); }, [restoreScrollTop, posts.length, virtualizer, getScrollElement]); - useEffect(() => { - restoredRef.current = false; - }, [restoreScrollTop]); - useEffect(() => { const el = getScrollElement(); if (!el) return; diff --git a/frontend/src/layouts/MainLayout.tsx b/frontend/src/layouts/MainLayout.tsx index 7987ceb..76c15c7 100644 --- a/frontend/src/layouts/MainLayout.tsx +++ b/frontend/src/layouts/MainLayout.tsx @@ -386,6 +386,7 @@ export default function MainLayout() { )} + {/* 点 Logo:回首页并强制刷新列表(已在首页时也会重拉) */}