From f5fb0862ff37a7d25343ac023d82577d81fb439a Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Mon, 29 Jun 2026 16:55:16 +0200 Subject: [PATCH] fix: key messages by networkId:room to prevent collision across networks Two networks sharing a room name (e.g. "general") were clobbering each other's message lists. Now keyed by `${networkId}:${room}` throughout the web store, MessagePane, and Sidebar DM detection. Co-Authored-By: Claude Sonnet 4.6 --- web/src/components/MessagePane.tsx | 5 +++-- web/src/components/Sidebar.tsx | 12 +++++++++--- web/src/store/index.ts | 13 +++++++------ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/web/src/components/MessagePane.tsx b/web/src/components/MessagePane.tsx index c8330ac..536605c 100644 --- a/web/src/components/MessagePane.tsx +++ b/web/src/components/MessagePane.tsx @@ -16,8 +16,9 @@ export function MessagePane() { const { messages, historyCutoff, activeRoom, activeNetworkId, localPeer, connectedPeers, knownPeers, send } = useWaste() const [draft, setDraft] = useState('') const bottomRef = useRef(null) - const roomMessages = messages[activeRoom] ?? [] - const cutoff = historyCutoff[activeRoom] ?? 0 + const msgKey = activeNetworkId ? `${activeNetworkId}:${activeRoom}` : activeRoom + const roomMessages = messages[msgKey] ?? [] + const cutoff = historyCutoff[msgKey] ?? 0 // Find the index of the first live message (ts > cutoff). // The divider appears just before this index, or at the top if all are history. diff --git a/web/src/components/Sidebar.tsx b/web/src/components/Sidebar.tsx index 583d4f9..b4b6358 100644 --- a/web/src/components/Sidebar.tsx +++ b/web/src/components/Sidebar.tsx @@ -42,9 +42,15 @@ export function Sidebar() { const netCustomRooms = activeNetworkId ? (customRooms[activeNetworkId] ?? []) : [] const rooms = ['general', ...netCustomRooms] - Object.keys(messages).forEach(r => { - if (r.startsWith('dm:') && !rooms.includes(r)) rooms.push(r) - }) + if (activeNetworkId) { + const prefix = `${activeNetworkId}:dm:` + Object.keys(messages).forEach(k => { + if (k.startsWith(prefix)) { + const r = k.slice(activeNetworkId.length + 1) + if (!rooms.includes(r)) rooms.push(r) + } + }) + } function submitNewRoom(e: React.FormEvent) { e.preventDefault() diff --git a/web/src/store/index.ts b/web/src/store/index.ts index e719cdb..157417f 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -283,14 +283,14 @@ export const useWaste = create((set, get) => ({ case 'message_received': { if (msg.message) { const m = msg.message - const room = m.room + const key = `${msg.network_id}:${m.room}` const fromId = String(m.from) set(s => { - const existing = s.messages[room] ?? [] + const existing = s.messages[key] ?? [] if (m.mid && existing.some(e => e.mid === m.mid)) return s const prev = s.peerStatus[fromId] ?? {} return { - messages: { ...s.messages, [room]: [...existing, m] }, + messages: { ...s.messages, [key]: [...existing, m] }, peerStatus: { ...s.peerStatus, [fromId]: { ...prev, lastSeen: m.ts } }, } }) @@ -378,16 +378,17 @@ export const useWaste = create((set, get) => ({ const room = msg.room const incoming = (msg.messages ?? []) as ChatMessage[] if (!room || incoming.length === 0) break + const key = `${msg.network_id}:${room}` set(s => { - const existing = s.messages[room] ?? [] + const existing = s.messages[key] ?? [] 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 }, + messages: { ...s.messages, [key]: merged }, + historyCutoff: { ...s.historyCutoff, [key]: cutoff }, } }) break