feat: render history_loaded in web UI with earlier messages divider

- store: handle history_loaded event — prepend gossipped messages,
  dedup by mid, sort by ts, record cutoff timestamp per room
- MessagePane: show "earlier messages" divider between history and
  live messages based on the cutoff timestamp
- types: add history_loaded, room_created, create_room to IpcMsgType;
  add messages field to IpcMessage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-06-28 23:50:48 +02:00
parent 15306dc0c2
commit 9de625d617
4 changed files with 42 additions and 5 deletions

View File

@@ -32,6 +32,8 @@ interface WasteState {
// chat — keyed by room
messages: Record<string, ChatMessage[]>
// rooms for which we have received history: room → ts of last history message
historyCutoff: Record<string, number>
activeRoom: string
// user-created rooms, keyed by networkId
customRooms: Record<string, string[]>
@@ -84,6 +86,7 @@ export const useWaste = create<WasteState>((set, get) => ({
activeNetworkId: null,
connectedPeers: [],
messages: {},
historyCutoff: {},
activeRoom: 'general',
customRooms: {},
fileLists: {},
@@ -352,6 +355,24 @@ export const useWaste = create<WasteState>((set, get) => ({
}
break
}
case 'history_loaded': {
const room = msg.room
const incoming = (msg.messages ?? []) as ChatMessage[]
if (!room || incoming.length === 0) break
set(s => {
const existing = s.messages[room] ?? []
const existingMids = new Set(existing.map(m => m.mid).filter(Boolean))
const fresh = incoming.filter(m => !m.mid || !existingMids.has(m.mid))
if (fresh.length === 0) return s
const merged = [...fresh, ...existing].sort((a, b) => a.ts - b.ts)
const cutoff = fresh[fresh.length - 1]?.ts ?? 0
return {
messages: { ...s.messages, [room]: merged },
historyCutoff: { ...s.historyCutoff, [room]: cutoff },
}
})
break
}
}
},
}))