Private
Public Access
Fix desktop tray unread indicator: clear only on genuine room entry
The tray indicator must stay visible while a message is unread and clear only when the user actually opens the room containing it -- never merely because the window was shown, focused, restored, or opened from the tray. Adds window.dsDesktop.roomOpened() (called exactly once per genuine transition to a real room id, via useDesktopRoomBridge) and wires up window.dsDesktop.setUnreadCount() (declared but never actually called before this) to fire whenever the unread-room count changes. Both are no-ops in browser builds, matching this bridge's existing optional- chaining convention. Frontend unread state stays authoritative -- clearRoomIndicators is pulled out of ChatShellPage as a pure function (same behavior, now directly testable) so opening one room only clears that room's unread/mention flags, leaving every other room's state untouched. Also adds a frontend test runner (vitest + React Testing Library) -- none existed in this project before. 10 new tests cover: roomOpened firing once on entry, not refiring on an unrelated rerender with the same room id, refiring on a genuine room-to-room transition, never firing with no room id, setUnreadCount firing independently of roomOpened, clearing one room preserving every other room's unread state, and the desktop bridge remaining fully optional. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
047f47d524
commit
3349cadea2
9 files changed
+1155
-16
No files matched your search
@@ -15,6 +15,12 @@ declare global {
|
||||
interface Window {
|
||||
dsDesktop?: {
|
||||
setUnreadCount?(unreadRoomCount: number): void
|
||||
// Tells the tray/dock icon the user has genuinely entered a room --
|
||||
// the desktop wrapper's own signal to clear that room's tray
|
||||
// indicator. Deliberately not fired just because the window was
|
||||
// shown/focused/restored, or opened from the tray: none of those mean
|
||||
// the user actually looked at the room the unread message is in.
|
||||
roomOpened?(): void
|
||||
showNotification?(notification: DesktopNotificationRequest): void
|
||||
onNotificationClick?(callback: (roomId: string) => void): () => void
|
||||
}
|
||||
@@ -38,6 +44,16 @@ export function isDesktopClickListenerSupported(): boolean {
|
||||
return typeof window.dsDesktop?.onNotificationClick === 'function'
|
||||
}
|
||||
|
||||
// Both no-op silently if the bridge or this specific method isn't present,
|
||||
// same as showDesktopNotification below -- callers never need to guard.
|
||||
export function notifyRoomOpened(): void {
|
||||
window.dsDesktop?.roomOpened?.()
|
||||
}
|
||||
|
||||
export function notifyUnreadCount(unreadRoomCount: number): void {
|
||||
window.dsDesktop?.setUnreadCount?.(unreadRoomCount)
|
||||
}
|
||||
|
||||
// No-ops silently if the bridge or this specific method isn't present --
|
||||
// callers don't need to guard, matching the rest of this module's
|
||||
// capability-detect-per-method philosophy (see #49's "feature-test each
|
||||
|
||||
Reference in new issue
Block a user