fix: exclude voice channels from unread computation to eliminate ghost notifications

Voice channels rendered with VoiceChannel component have no text reading/acking
UI, so messages in them created phantom unread indicators on the space sidebar
that users could never clear. Root cause was a message in the counter-strike
voice channel with no read state.

Three-layer fix:
- spaceStore: track voiceChannelIds set, exclude voice channels from
  channelLastMessageIds so setReadStates never marks them unread
- useWebSocket: skip markChannelUnread for voice channels on message_created,
  prune orphaned unreads on every ready event
- chatStore: validate preserved unreads against channelToSpaceMap to drop
  orphans that don't map to any known channel
This commit is contained in:
Jannis Braun
2026-03-16 23:57:24 +01:00
parent bf64c4678b
commit bc4dd81632
3 changed files with 40 additions and 5 deletions
+8 -2
View File
@@ -523,12 +523,18 @@ export const useChatStore = create<ChatState>((set, get) => ({
}
// 2. Rebuild unreadChannels ONLY for channels from this origin
// Keep existing unread entries from other origins untouched
// Keep existing unread entries from other origins untouched,
// but prune orphans that don't map to any known channel
const currentChannelId = get().currentChannelId;
const { channelToSpaceMap, dmChannels: knownDms } = useSpaceStore.getState();
const knownDmIds = new Set(knownDms.map(dm => dm.id));
const unread = new Set<string>();
for (const id of get().unreadChannels) {
if (!originChannelIds || !originChannelIds.has(id)) {
unread.add(id); // preserve other-origin unreads
// Only preserve if the channel still maps to a known space or DM
if (channelToSpaceMap.has(id) || knownDmIds.has(id)) {
unread.add(id);
}
}
}