Fix stuck selectedEntityId and unread-during-search edge cases
Two small but user-visible UX issues in the Communication hub: 1. selectedEntityId was only consumed when the id resolved to a visible message / comment / task channel. When the referenced entity was deleted or filtered out, the pointer stuck and the effect re-ran on every store change, preventing the user from manually navigating to other channels. 2. The unread-marking effect explicitly skipped while search was active, even though the search results render in the same message list pane. Users who opened a channel via search saw unread badges freeze until they cleared the query and re-entered the channel. Consume the entity id on every resolution branch (including the no-match fallback) and drop the search guard — the viewer intent is the same in both list modes.
This commit is contained in:
1 parent
98896bb87b
commit
a99f5e581f
1 file changed
+30
-5
@@ -101,11 +101,30 @@ export function CommunicationHub({ messages, comments, tasks, agents, projects,
|
||||
useEffect(() => {
|
||||
if (!selectedEntityId) return
|
||||
const matchMsg = effectiveMessages.find((m) => m.id === selectedEntityId)
|
||||
if (matchMsg) { const ch = messageChannels.find((ch) => getChannelMessages(effectiveMessages, ch).some((m) => m.id === selectedEntityId)); if (ch) { setSelectedChannelKey(ch.key); onEntityConsumed?.() }; return }
|
||||
if (matchMsg) {
|
||||
const ch = messageChannels.find((ch) => getChannelMessages(effectiveMessages, ch).some((m) => m.id === selectedEntityId))
|
||||
if (ch) setSelectedChannelKey(ch.key)
|
||||
onEntityConsumed?.()
|
||||
return
|
||||
}
|
||||
const matchCom = comments.find((c) => c.id === selectedEntityId)
|
||||
if (matchCom) { setSelectedChannelKey(`task-${matchCom.taskId}`); onEntityConsumed?.(); return }
|
||||
if (matchCom) {
|
||||
setSelectedChannelKey(`task-${matchCom.taskId}`)
|
||||
onEntityConsumed?.()
|
||||
return
|
||||
}
|
||||
const matchTask = taskDiscussionChannels.find((d) => d.taskId === selectedEntityId)
|
||||
if (matchTask) { setSelectedChannelKey(matchTask.key); onEntityConsumed?.() }
|
||||
if (matchTask) {
|
||||
setSelectedChannelKey(matchTask.key)
|
||||
onEntityConsumed?.()
|
||||
return
|
||||
}
|
||||
// Entity id is stale or points outside this view (e.g. the message
|
||||
// was deleted between navigation and render). Consume it anyway so
|
||||
// the parent store clears the pointer — leaving it set would make
|
||||
// the effect refire on every snapshot and block the user from
|
||||
// navigating manually inside the communication hub.
|
||||
onEntityConsumed?.()
|
||||
}, [selectedEntityId, effectiveMessages, comments, messageChannels, onEntityConsumed, taskDiscussionChannels])
|
||||
|
||||
const activeChannel = useMemo(() => channels.find((c) => c.key === selectedChannelKey) ?? null, [channels, selectedChannelKey])
|
||||
@@ -201,7 +220,13 @@ export function CommunicationHub({ messages, comments, tasks, agents, projects,
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeChannel || activeChannel.kind !== "message") return
|
||||
if (isSearchActive) return // Don't mark as read during search
|
||||
// Search no longer blocks read marking — the original guard existed
|
||||
// to keep unread badges stable while the user was scanning the
|
||||
// results list, but the search panel renders inside the message
|
||||
// list pane (isSearchActive swaps the rendered list) so messages
|
||||
// shown through search are the same messages the user is actually
|
||||
// viewing. Blocking reads here left unreads stuck until the user
|
||||
// cleared search and re-opened the channel.
|
||||
const ids = filteredMessages.filter((m) => !m.readAt && !pendingReadIdsRef.current.has(m.id)).map((m) => m.id)
|
||||
if (ids.length === 0) return
|
||||
let cancelled = false
|
||||
@@ -214,7 +239,7 @@ export function CommunicationHub({ messages, comments, tasks, agents, projects,
|
||||
if (!cancelled && failed.length > 0) setOptimisticReadIds((c) => { const n = new Set(c); for (const id of failed) n.delete(id); return n })
|
||||
})()
|
||||
return () => { cancelled = true }
|
||||
}, [activeChannel, companyId, filteredMessages, isSearchActive])
|
||||
}, [activeChannel, companyId, filteredMessages])
|
||||
|
||||
const unreadByChannel = useMemo(() => {
|
||||
const counts = new Map<string, { unread: number; hasUrgent: boolean }>()
|
||||
|
||||
Reference in new issue
Block a user