Private
Public Access
Fix chat/room ordering that could differ between devices (#45)
Two independent gaps, both fixed since the report was ambiguous about which "chats" meant: - ChatPane.tsx concatenated history (REST-fetched) and live (WS-pushed) without sorting, so anything that could desync receipt order from send order -- a rejoin/resync racing a still-in-flight WS message, which opening the same room on another device triggers directly via a fresh socket connection -- could render messages out of chronological order. Now sorted by created_at (stable sort, so same-timestamp messages keep their relative order). - list_member_rooms/list_open_rooms/list_recent_messages ordered by created_at alone, with no secondary tiebreaker. Postgres doesn't guarantee a stable order for tied rows across separate query executions, so two rooms/messages sharing an identical timestamp (a real possibility -- rapid sends, bulk-created rooms) could come back in a different order on two separate fetches, i.e. two devices. Added id as a secondary sort key everywhere this showed up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { NetworkError } from '../api/client'
|
||||
import { getRoomMessages, markRoomRead } from '../api/rooms'
|
||||
import type { ChatSocketHandle } from '../ws/useChatSocket'
|
||||
@@ -127,6 +127,22 @@ export function ChatPane({
|
||||
[socket, room.id, refreshHistory, markRead],
|
||||
)
|
||||
|
||||
// history and live are just concatenated, not merge-sorted -- live is
|
||||
// strictly receipt order, which isn't always send order. A rejoin (a
|
||||
// reconnect, or opening the same room on another device) refetches
|
||||
// history but doesn't guarantee anything about the timing of whatever
|
||||
// WS messages land in live afterward relative to it, so without this
|
||||
// sort a message can render above one that was actually sent earlier.
|
||||
// Stable sort (guaranteed since ES2019) keeps same-timestamp messages in
|
||||
// their original relative order rather than shuffling them.
|
||||
const messages = useMemo(
|
||||
() =>
|
||||
[...history, ...live].sort(
|
||||
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime(),
|
||||
),
|
||||
[history, live],
|
||||
)
|
||||
|
||||
const connected = socket.connected
|
||||
const send = useCallback(
|
||||
(content: string, imageId?: string, fileId?: string) => socket.send(room.id, content, imageId, fileId),
|
||||
@@ -179,7 +195,7 @@ export function ChatPane({
|
||||
|
||||
<MessageList
|
||||
roomId={room.id}
|
||||
messages={[...history, ...live]}
|
||||
messages={messages}
|
||||
members={members}
|
||||
onEdit={sendEdit}
|
||||
onReact={sendReaction}
|
||||
|
||||
Reference in New Issue
Block a user