fix(frontend): sort messages by created_at to fix WS race condition
Two independent WS handlers (useMessagesWsSync for message_created/ updated/analyzed, and useMessagesStream for message_snapshot) both prepend live messages to the SWR list without enforcing order. When frames arrive out-of-order (common with batched WS delivery), the message feed gets scrambled. Fix: add sortMessages() helper that sorts newest-first by created_at (the list's stored order before .reverse() for display) and apply it in every patchLists/mutate updater: message_created, message_updated, message_analyzed, message_snapshot, and useLoadMore page appends. Function declaration is hoisted so useLoadMore (defined above the helper) can use it.
This commit is contained in:
committed by
asepharyana
parent
f5d5690401
commit
5658726ea5
@@ -93,7 +93,7 @@ export function useLoadMore() {
|
|||||||
(old: MessagePage | undefined): MessagePage | undefined =>
|
(old: MessagePage | undefined): MessagePage | undefined =>
|
||||||
old
|
old
|
||||||
? {
|
? {
|
||||||
data: [...old.data, ...result.data],
|
data: sortMessages([...old.data, ...result.data]),
|
||||||
nextCursor: result.nextCursor,
|
nextCursor: result.nextCursor,
|
||||||
}
|
}
|
||||||
: result,
|
: result,
|
||||||
@@ -201,6 +201,17 @@ export function useSemanticSearch(query: string, enabled: boolean) {
|
|||||||
|
|
||||||
// ── WS sync helpers ──────────────────────────────
|
// ── WS sync helpers ──────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort messages newest-first (descending by created_at). The message list is
|
||||||
|
* stored newest-first in SWR data (the view reverses it for display), so every
|
||||||
|
* WS insert must maintain this order regardless of arrival order. Without this,
|
||||||
|
* out-of-order `message_created` / `message_snapshot` frames produce a
|
||||||
|
* scrambled feed.
|
||||||
|
*/
|
||||||
|
function sortMessages(msgs: MessageRecord[]): MessageRecord[] {
|
||||||
|
return [...msgs].sort((a, b) => b.created_at - a.created_at);
|
||||||
|
}
|
||||||
|
|
||||||
export function useMessagesWsSync(ws: WsHook, guildId: string) {
|
export function useMessagesWsSync(ws: WsHook, guildId: string) {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -237,7 +248,8 @@ export function useMessagesWsSync(ws: WsHook, guildId: string) {
|
|||||||
const msg = data as MessageRecord;
|
const msg = data as MessageRecord;
|
||||||
patchLists(
|
patchLists(
|
||||||
(_k, m) => matchesFilter(_k as unknown[], m),
|
(_k, m) => matchesFilter(_k as unknown[], m),
|
||||||
(old) => (old ? { ...old, data: [msg, ...old.data] } : old),
|
(old) =>
|
||||||
|
old ? { ...old, data: sortMessages([msg, ...old.data]) } : old,
|
||||||
msg,
|
msg,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -254,8 +266,8 @@ export function useMessagesWsSync(ws: WsHook, guildId: string) {
|
|||||||
old
|
old
|
||||||
? {
|
? {
|
||||||
...old,
|
...old,
|
||||||
data: old.data.map((m) =>
|
data: sortMessages(
|
||||||
m.id === msg.id ? { ...m, ...msg } : m,
|
old.data.map((m) => (m.id === msg.id ? { ...m, ...msg } : m)),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: old,
|
: old,
|
||||||
@@ -283,7 +295,12 @@ export function useMessagesWsSync(ws: WsHook, guildId: string) {
|
|||||||
(_k, m) => matchesFilter(_k as unknown[], m),
|
(_k, m) => matchesFilter(_k as unknown[], m),
|
||||||
(old) =>
|
(old) =>
|
||||||
old
|
old
|
||||||
? { ...old, data: old.data.map((m) => (m.id === msg.id ? msg : m)) }
|
? {
|
||||||
|
...old,
|
||||||
|
data: sortMessages(
|
||||||
|
old.data.map((m) => (m.id === msg.id ? msg : m)),
|
||||||
|
),
|
||||||
|
}
|
||||||
: old,
|
: old,
|
||||||
msg,
|
msg,
|
||||||
);
|
);
|
||||||
@@ -331,7 +348,10 @@ export function useMessagesStream(
|
|||||||
const data2 = old?.data ?? [];
|
const data2 = old?.data ?? [];
|
||||||
if (data2.some((m) => m.id === msg.id))
|
if (data2.some((m) => m.id === msg.id))
|
||||||
return old ?? { data: [], nextCursor: null };
|
return old ?? { data: [], nextCursor: null };
|
||||||
return { data: [msg, ...data2], nextCursor: old?.nextCursor ?? null };
|
return {
|
||||||
|
data: sortMessages([msg, ...data2]),
|
||||||
|
nextCursor: old?.nextCursor ?? null,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
{ revalidate: false },
|
{ revalidate: false },
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user