fix(room): load reactions for IDB-cached messages via WS-first fallback
- thisLoadReactions helper: batch-fetches reactions for loaded messages via WS (RoomWsClient.request() does WS-first → HTTP fallback automatically) - Called after both IDB paths (initial load + loadMore) so reactions are populated even when messages come from IndexedDB cache - Also deduplicated API-path reaction loading to use the same helper
This commit is contained in:
parent
b70d91866c
commit
50f9cc40fe
@ -260,6 +260,40 @@ export function RoomProvider({
|
|||||||
};
|
};
|
||||||
}, [activeRoomId, wsClient]);
|
}, [activeRoomId, wsClient]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch reactions for a batch of messages via WS (with HTTP fallback),
|
||||||
|
* then merge them into the messages state. Fires-and-forgets so it
|
||||||
|
* does not block the caller.
|
||||||
|
*/
|
||||||
|
const thisLoadReactions = (
|
||||||
|
roomId: string,
|
||||||
|
client: NonNullable<ReturnType<typeof wsClientRef.current>>,
|
||||||
|
msgs: MessageWithMeta[],
|
||||||
|
) => {
|
||||||
|
const msgIds = msgs.map((m) => m.id);
|
||||||
|
if (msgIds.length === 0) return;
|
||||||
|
client
|
||||||
|
.reactionListBatch(roomId, msgIds)
|
||||||
|
.then((reactionResults) => {
|
||||||
|
const reactionMap = new Map<string, import('@/lib/room-ws-client').ReactionItem[]>();
|
||||||
|
for (const result of reactionResults) {
|
||||||
|
if (result.reactions.length > 0) {
|
||||||
|
reactionMap.set(result.message_id, result.reactions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reactionMap.size > 0) {
|
||||||
|
setMessages((prev) =>
|
||||||
|
prev.map((m) =>
|
||||||
|
reactionMap.has(m.id) ? { ...m, reactions: reactionMap.get(m.id) } : m,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Non-fatal: WS push will keep reactions up to date
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const loadMore = useCallback(
|
const loadMore = useCallback(
|
||||||
async (cursor?: number | null) => {
|
async (cursor?: number | null) => {
|
||||||
const client = wsClientRef.current;
|
const client = wsClientRef.current;
|
||||||
@ -284,7 +318,9 @@ export function RoomProvider({
|
|||||||
const minSeq = cached[0].seq;
|
const minSeq = cached[0].seq;
|
||||||
setNextCursor(minSeq > 0 ? minSeq - 1 : null);
|
setNextCursor(minSeq > 0 ? minSeq - 1 : null);
|
||||||
setIsLoadingMore(false);
|
setIsLoadingMore(false);
|
||||||
// No API call needed — WS will push any new messages that arrived while away
|
// No API call needed — WS will push any new messages that arrived while away.
|
||||||
|
// Fetch reactions via WS (with HTTP fallback) so reactions appear without extra latency.
|
||||||
|
thisLoadReactions(activeRoomId, client, cached);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,6 +346,8 @@ export function RoomProvider({
|
|||||||
setIsHistoryLoaded(true);
|
setIsHistoryLoaded(true);
|
||||||
}
|
}
|
||||||
setIsLoadingMore(false);
|
setIsLoadingMore(false);
|
||||||
|
// Also fetch reactions for the IDB-loaded history messages.
|
||||||
|
thisLoadReactions(activeRoomId, client, idbMessages);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// IDB empty for this range — fall through to API
|
// IDB empty for this range — fall through to API
|
||||||
@ -355,28 +393,8 @@ export function RoomProvider({
|
|||||||
}
|
}
|
||||||
setNextCursor(resp.messages.length > 0 ? resp.messages[resp.messages.length - 1].seq : null);
|
setNextCursor(resp.messages.length > 0 ? resp.messages[resp.messages.length - 1].seq : null);
|
||||||
|
|
||||||
// Fetch reactions for all loaded messages
|
// Fetch reactions for all loaded messages (WS-first with HTTP fallback)
|
||||||
const msgIds = newMessages.map((m) => m.id);
|
thisLoadReactions(activeRoomId, client, newMessages);
|
||||||
if (msgIds.length > 0) {
|
|
||||||
try {
|
|
||||||
const reactionResults = await client.reactionListBatch(activeRoomId, msgIds);
|
|
||||||
const reactionMap = new Map<string, import('@/lib/room-ws-client').ReactionItem[]>();
|
|
||||||
for (const result of reactionResults) {
|
|
||||||
if (result.reactions.length > 0) {
|
|
||||||
reactionMap.set(result.message_id, result.reactions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (reactionMap.size > 0) {
|
|
||||||
setMessages((prev) =>
|
|
||||||
prev.map((m) =>
|
|
||||||
reactionMap.has(m.id) ? { ...m, reactions: reactionMap.get(m.id) } : m,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Reactions will be loaded via WebSocket updates if backend supports it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (abortController.signal.aborted) return;
|
if (abortController.signal.aborted) return;
|
||||||
handleRoomError('Load messages', error);
|
handleRoomError('Load messages', error);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user