Files
jiang13-forum/frontend/src/utils/feedCache.ts
freefire 44eed97fe0 feat: 首页 Go SSR 与 React hydrate 同构,消壳层与帖行闪动
补齐侧栏/右栏图标与徽章、鉴权种子、StaticFeedList,并修正嵌套 a 与标题徽章对齐。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 07:07:38 +08:00

109 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { NavigateFunction } from 'react-router-dom';
import type { PostItem } from '../api/types';
import type { FeedSort } from '../components/FeedSortBar';
import {
feedCacheKey,
getHomeStoreState,
type FeedCacheEntry,
} from '../store/homeStore';
import { clearSessionSnapshots } from './sessionPageCache';
import { softRefreshCurrentPage } from './softRefresh';
import { transitionTo } from './spaTransition';
/** 导航到帖子列表时附带的状态,用于同 URL 重复点击时强制刷新 */
export type FeedNavState = { refreshFeed?: boolean };
/** 对外暴露的缓存形状(兼容旧调用方;不含 lastFetchTime) */
export type FeedCache = {
posts: PostItem[];
postTotal: number;
page: number;
scrollTop: number;
};
function toPublic(entry: FeedCacheEntry): FeedCache {
return {
posts: entry.posts,
postTotal: entry.postTotal,
page: entry.page,
scrollTop: entry.scrollTop,
};
}
/** 读取帖子列表缓存(从详情页返回时恢复浏览位置) */
export function getFeedCache(
boardId: number,
keyword: string,
sort: FeedSort,
tag = '',
author = '',
titleOnly = false,
): FeedCache | null {
const key = feedCacheKey({ boardId, keyword, sort, tag, author, titleOnly });
const entry = getHomeStoreState().getFeed(key);
return entry ? toPublic(entry) : null;
}
/** 保存帖子列表缓存(写入 Zustand,并记录拉取时间) */
export function setFeedCache(
boardId: number,
keyword: string,
sort: FeedSort,
data: FeedCache,
tag = '',
author = '',
titleOnly = false,
) {
const key = feedCacheKey({ boardId, keyword, sort, tag, author, titleOnly });
getHomeStoreState().setFeed(key, data);
}
/** 清除所有帖子列表缓存(手动刷新 / 帖子变更时连详情快照一起作废) */
export function clearAllFeedCache() {
getHomeStoreState().clearAll();
clearSessionSnapshots('post:');
}
/** 主动刷新帖子列表时派发,用于同页内立即回到顶部 */
export const FEED_RESET_EVENT = 'feed-reset';
/** 手机下拉刷新(Feed 页):强制重拉列表并重置滚动,不整页 reload */
export const FEED_PULL_REFRESH_EVENT = 'feed-pull-refresh';
/** 手机下拉:强制刷新当前前台页(绕过会话快照,非整页 reload) */
export const PAGE_FORCE_REFRESH_EVENT = 'page-force-refresh';
/** 当前浏览器地址是否已是目标 Feed URL(忽略 hash) */
function isSameFeedUrl(url: string): boolean {
try {
const target = new URL(url, window.location.origin);
return (
window.location.pathname === target.pathname
&& window.location.search === target.search
);
} catch {
return false;
}
}
/**
* 导航到帖子列表。
* 默认:等待预热后再换页;同 URL 或 `refresh: true` 时软刷新(带顶栏进度条)。
*/
export function navigateFeed(nav: NavigateFunction, url: string, opts?: { refresh?: boolean }) {
const same = isSameFeedUrl(url);
const refresh = opts?.refresh ?? same;
if (refresh) {
if (same) {
void softRefreshCurrentPage(url, { progress: true });
return;
}
void transitionTo(nav, url, {
force: true,
state: { refreshFeed: true } satisfies FeedNavState,
});
return;
}
void transitionTo(nav, url);
}