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]);
|
||||
|
||||
/**
|
||||
* 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(
|
||||
async (cursor?: number | null) => {
|
||||
const client = wsClientRef.current;
|
||||
@ -284,7 +318,9 @@ export function RoomProvider({
|
||||
const minSeq = cached[0].seq;
|
||||
setNextCursor(minSeq > 0 ? minSeq - 1 : null);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -310,6 +346,8 @@ export function RoomProvider({
|
||||
setIsHistoryLoaded(true);
|
||||
}
|
||||
setIsLoadingMore(false);
|
||||
// Also fetch reactions for the IDB-loaded history messages.
|
||||
thisLoadReactions(activeRoomId, client, idbMessages);
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
|
||||
// Fetch reactions for all loaded messages
|
||||
const msgIds = newMessages.map((m) => m.id);
|
||||
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
|
||||
}
|
||||
}
|
||||
// Fetch reactions for all loaded messages (WS-first with HTTP fallback)
|
||||
thisLoadReactions(activeRoomId, client, newMessages);
|
||||
} catch (error) {
|
||||
if (abortController.signal.aborted) return;
|
||||
handleRoomError('Load messages', error);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user