fix: 软刷新齐套前保留旧画面,避免一点击就卸光
Logo/下拉静默预热后一次覆盖;commit 不再先 reset/loading;顺带会话预取与可配置 Feed 排序标签。
This commit is contained in:
@@ -2,38 +2,115 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { notify } from '@/lib/notify';
|
||||
import { api } from '../api/client';
|
||||
import type { CheckInStatus } from '../api/types';
|
||||
import { getSessionSnapshot, setSessionSnapshot } from '../utils/sessionPageCache';
|
||||
import { PAGE_FORCE_REFRESH_EVENT } from '../utils/feedCache';
|
||||
import { PAGE_SOFT_REFRESH_COMMIT_EVENT } from '../utils/softRefresh';
|
||||
import { useAuth } from './useAuth';
|
||||
|
||||
/** 签到会话快照键(与 prefetchRoute 共用) */
|
||||
export function checkInCacheKey(userId: number) {
|
||||
return `checkin:${userId}`;
|
||||
}
|
||||
|
||||
/** 每日签到状态与操作(侧栏、积分页等复用) */
|
||||
export function useCheckIn(enabled = true) {
|
||||
const { user, refresh } = useAuth();
|
||||
const [status, setStatus] = useState<CheckInStatus | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const userId = user?.id;
|
||||
|
||||
const [status, setStatus] = useState<CheckInStatus | null>(() => {
|
||||
if (!userId || !enabled) return null;
|
||||
return getSessionSnapshot<CheckInStatus>(checkInCacheKey(userId)) ?? null;
|
||||
});
|
||||
const [loading, setLoading] = useState(() => {
|
||||
if (!userId || !enabled) return false;
|
||||
return getSessionSnapshot<CheckInStatus>(checkInCacheKey(userId)) === undefined;
|
||||
});
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const load = useCallback(() => {
|
||||
if (!user || !enabled) {
|
||||
const load = useCallback((opts?: { force?: boolean }) => {
|
||||
if (!userId || !enabled) {
|
||||
setStatus(null);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const key = checkInCacheKey(userId);
|
||||
if (!opts?.force) {
|
||||
const hit = getSessionSnapshot<CheckInStatus>(key);
|
||||
if (hit !== undefined) {
|
||||
setStatus(hit);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setLoading(true);
|
||||
api.checkInStatus()
|
||||
.then(d => setStatus(d.check_in))
|
||||
.catch(e => notify.error(e instanceof Error ? e.message : '加载签到状态失败'))
|
||||
.then((d) => {
|
||||
setSessionSnapshot(key, d.check_in);
|
||||
setStatus(d.check_in);
|
||||
})
|
||||
.catch((e) => notify.error(e instanceof Error ? e.message : '加载签到状态失败'))
|
||||
.finally(() => setLoading(false));
|
||||
}, [user, enabled]);
|
||||
}, [userId, enabled]);
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
}, [load]);
|
||||
if (!userId || !enabled) {
|
||||
setStatus(null);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const key = checkInCacheKey(userId);
|
||||
const hit = getSessionSnapshot<CheckInStatus>(key);
|
||||
if (hit !== undefined) {
|
||||
setStatus(hit);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setStatus(null);
|
||||
setLoading(true);
|
||||
load({ force: true });
|
||||
}, [userId, enabled, load]);
|
||||
|
||||
// 软刷新 commit:仅快照命中才盖;未命中保持旧 UI,后台静默再拉
|
||||
useEffect(() => {
|
||||
const applyHitOrSilent = (forceLoading: boolean) => {
|
||||
if (!userId || !enabled) return;
|
||||
const key = checkInCacheKey(userId);
|
||||
const hit = getSessionSnapshot<CheckInStatus>(key);
|
||||
if (hit !== undefined) {
|
||||
setStatus(hit);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (forceLoading) {
|
||||
load({ force: true });
|
||||
return;
|
||||
}
|
||||
// 软刷新未命中:不 setLoading,静默覆盖
|
||||
api.checkInStatus()
|
||||
.then((d) => {
|
||||
setSessionSnapshot(key, d.check_in);
|
||||
setStatus(d.check_in);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => { /* 保持旧 UI */ });
|
||||
};
|
||||
const onForce = () => applyHitOrSilent(true);
|
||||
const onCommit = () => applyHitOrSilent(false);
|
||||
window.addEventListener(PAGE_FORCE_REFRESH_EVENT, onForce);
|
||||
window.addEventListener(PAGE_SOFT_REFRESH_COMMIT_EVENT, onCommit);
|
||||
return () => {
|
||||
window.removeEventListener(PAGE_FORCE_REFRESH_EVENT, onForce);
|
||||
window.removeEventListener(PAGE_SOFT_REFRESH_COMMIT_EVENT, onCommit);
|
||||
};
|
||||
}, [userId, enabled, load]);
|
||||
|
||||
const doCheckIn = useCallback(async () => {
|
||||
if (!user) return;
|
||||
if (!userId) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
const r = await api.checkIn();
|
||||
notify.success(`签到成功,+${r.check_in.today_points} 积分`);
|
||||
setSessionSnapshot(checkInCacheKey(userId), r.check_in);
|
||||
setStatus(r.check_in);
|
||||
await refresh();
|
||||
} catch (e: unknown) {
|
||||
@@ -41,13 +118,13 @@ export function useCheckIn(enabled = true) {
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}, [user, refresh]);
|
||||
}, [userId, refresh]);
|
||||
|
||||
return {
|
||||
status,
|
||||
loading,
|
||||
busy,
|
||||
doCheckIn,
|
||||
reload: load,
|
||||
reload: () => load({ force: true }),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user