From 896cb77b5c0b2feef24dceef28680404280916f0 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:13:47 +0100 Subject: [PATCH] feat: add markUnread store action and WS handler --- packages/web/src/hooks/useWebSocket.ts | 6 +++++ packages/web/src/stores/chatStore.ts | 35 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/packages/web/src/hooks/useWebSocket.ts b/packages/web/src/hooks/useWebSocket.ts index 075f9555..3aace391 100644 --- a/packages/web/src/hooks/useWebSocket.ts +++ b/packages/web/src/hooks/useWebSocket.ts @@ -571,6 +571,12 @@ function handleEvent(origin: string, event: ServerEvent): void { break; } + case 'mark_unread': { + const { onMarkUnread } = useChatStore.getState(); + onMarkUnread(event.channelId, event.messageId); + break; + } + // ─── DM call events (all origins) ────────────────────────────────────── case 'dm_call_incoming': { diff --git a/packages/web/src/stores/chatStore.ts b/packages/web/src/stores/chatStore.ts index 95686053..3ab653c2 100644 --- a/packages/web/src/stores/chatStore.ts +++ b/packages/web/src/stores/chatStore.ts @@ -57,8 +57,10 @@ interface ChatState { getTypingUsers: (channelId: string) => TypingUser[]; setReadStates: (readStates: ReadState[], channelLastMessageIds: Map, originChannelIds?: Set) => void; markChannelUnread: (channelId: string) => void; + markUnread: (channelId: string, messageId: string) => void; ackChannel: (channelId: string) => void; onChannelAck: (channelId: string, messageId: string) => void; + onMarkUnread: (channelId: string, messageId: string) => void; removeChannelStates: (channelIds: Set) => void; updateUserInMessages: (user: { id: string; [key: string]: any }) => void; } @@ -585,6 +587,25 @@ export const useChatStore = create((set, get) => ({ }); }, + markUnread: (channelId: string, messageId: string) => { + // Optimistically update local state + set((state) => { + const newReadStates = new Map(state.readStates); + const newUnread = new Set(state.unreadChannels); + if (messageId === '0') { + newReadStates.delete(channelId); + } else { + newReadStates.set(channelId, messageId); + } + newUnread.add(channelId); + return { readStates: newReadStates, unreadChannels: newUnread }; + }); + + // Send to the correct federated instance + const origin = getChannelOrigin(channelId); + wsSend({ type: 'mark_unread', channelId, messageId }, origin); + }, + ackChannel: (channelId: string) => { const msgs = get().messages.get(channelId); if (!msgs || msgs.length === 0) return; @@ -624,6 +645,20 @@ export const useChatStore = create((set, get) => ({ }); }, + onMarkUnread: (channelId: string, messageId: string) => { + set((state) => { + const newReadStates = new Map(state.readStates); + const newUnread = new Set(state.unreadChannels); + if (messageId === '0') { + newReadStates.delete(channelId); + } else { + newReadStates.set(channelId, messageId); + } + newUnread.add(channelId); + return { readStates: newReadStates, unreadChannels: newUnread }; + }); + }, + removeChannelStates: (channelIds: Set) => { if (channelIds.size === 0) return; set((state) => {