fix: 修复消息未读数同步和帖子详情快照逻辑

- MessagesPage: 新增 updateConvUnread 统一更新会话未读数及 session 缓存,
  缓存命中时仍调用 markConversationRead 并触发未读刷新,
  全部标为已读时同步更新 session 缓存避免 stale 数据
- PostDetailPage: 仅 POP 导航类型使用 session 快照恢复,
  非 POP 导航和下拉刷新始终强制重拉确保拿到最新数据
This commit is contained in:
2026-09-02 19:29:01 +08:00
parent 76be8926f2
commit cad53942e8
2 changed files with 34 additions and 17 deletions

View File

@@ -346,6 +346,23 @@ export default function MessagesPage() {
}); });
}, []); }, []);
/** 更新会话未读数,同时同步 session 缓存防止后续恢复 stale 数据 */
const updateConvUnread = useCallback((peerId: number, unread: number) => {
setConversations((prev) => {
const next = prev.map((c) =>
c.peer_user_id === peerId ? { ...c, unread_count: unread } : c
);
const cached = getSessionSnapshot<ConvSnap>('messages:conv:1');
if (cached) {
setSessionSnapshot('messages:conv:1', {
...cached,
conversations: next,
});
}
return next;
});
}, []);
const clearConvSearch = useCallback(() => { const clearConvSearch = useCallback(() => {
setConvSearchQuery(''); setConvSearchQuery('');
setConvSearchResults([]); setConvSearchResults([]);
@@ -584,6 +601,11 @@ export default function MessagesPage() {
setPeerUser(cached.peerUser); setPeerUser(cached.peerUser);
if (cached.peerUser) ensurePeerConversation(cached.peerUser); if (cached.peerUser) ensurePeerConversation(cached.peerUser);
setThreadLoading(false); setThreadLoading(false);
// 缓存命中时仍需标记后端已读,并更新前端会话列表未读数
void api.markConversationRead(selectedPeer).catch(() => undefined);
updateConvUnread(selectedPeer, 0);
window.dispatchEvent(new Event('messages-unread-refresh'));
void refreshUnreadSplit();
return; return;
} }
let cancelled = false; let cancelled = false;
@@ -603,9 +625,7 @@ export default function MessagesPage() {
total: r.total || 0, total: r.total || 0,
peerUser: peer, peerUser: peer,
}); });
setConversations((prev) => prev.map((c) => ( updateConvUnread(selectedPeer, 0);
c.peer_user_id === selectedPeer ? { ...c, unread_count: 0 } : c
)));
window.dispatchEvent(new Event('messages-unread-refresh')); window.dispatchEvent(new Event('messages-unread-refresh'));
void refreshUnreadSplit(); void refreshUnreadSplit();
}) })
@@ -662,7 +682,14 @@ export default function MessagesPage() {
notify.success('通知已全部标为已读'); notify.success('通知已全部标为已读');
} else { } else {
await api.markAllMessagesRead(); await api.markAllMessagesRead();
setConversations((prev) => prev.map((c) => ({ ...c, unread_count: 0 }))); setConversations((prev) => {
const next = prev.map((c) => ({ ...c, unread_count: 0 }));
const cached = getSessionSnapshot<ConvSnap>('messages:conv:1');
if (cached) {
setSessionSnapshot('messages:conv:1', { ...cached, conversations: next });
}
return next;
});
setDmUnread(0); setDmUnread(0);
setNotifyUnread(0); setNotifyUnread(0);
notify.success('已全部标为已读'); notify.success('已全部标为已读');

View File

@@ -109,7 +109,7 @@ export default function PostDetailPage() {
const { limits } = useForumLimits(); const { limits } = useForumLimits();
const { setPostOutline, isMobile } = useOutletContext<LayoutCtx>(); const { setPostOutline, isMobile } = useOutletContext<LayoutCtx>();
const initialSnap = (postId && !Number.isNaN(postId)) const initialSnap = (postId && !Number.isNaN(postId) && navType === 'POP')
? getSessionSnapshot<PostDetailSnapshot>(postDetailCacheKey(postId)) ? getSessionSnapshot<PostDetailSnapshot>(postDetailCacheKey(postId))
: undefined; : undefined;
@@ -259,7 +259,7 @@ export default function PostDetailPage() {
if (mode === 'force') { if (mode === 'force') {
deleteSessionSnapshot(postDetailCacheKey(postId)); deleteSessionSnapshot(postDetailCacheKey(postId));
} }
const cached = mode === 'force' const cached = mode === 'force' || navType !== 'POP'
? undefined ? undefined
: getSessionSnapshot<PostDetailSnapshot>(postDetailCacheKey(postId)); : getSessionSnapshot<PostDetailSnapshot>(postDetailCacheKey(postId));
if (cached) { if (cached) {
@@ -329,19 +329,9 @@ export default function PostDetailPage() {
}, [postId]); }, [postId]);
useEffect(() => { useEffect(() => {
// 下拉已预热则应用快照;否则强制重拉 // 下拉/软刷新:始终强制重拉,确保拿到最新数据
const onForce = () => { const onForce = () => {
if (!postId || Number.isNaN(postId)) return; if (!postId || Number.isNaN(postId)) return;
const warm = getSessionSnapshot<PostDetailSnapshot>(postDetailCacheKey(postId));
if (warm) {
loadSeq.current += 1;
applySnapshot(warm, { restoreScroll: false });
setLoading(false);
const el = pageRef.current;
if (el) el.scrollTop = 0;
scrollTopRef.current = 0;
return;
}
loadPostRef.current('force'); loadPostRef.current('force');
}; };
window.addEventListener(PAGE_FORCE_REFRESH_EVENT, onForce); window.addEventListener(PAGE_FORCE_REFRESH_EVENT, onForce);