perf: move unread dock badge cleanup to app root

This commit is contained in:
Neil
2026-05-31 07:19:31 -07:00
parent 632a55f602
commit becedda8bc
3 changed files with 62 additions and 12 deletions
+4 -3
View File
@@ -262,7 +262,7 @@ function applyRemoteWorkspacePatchStatus(
}
function App(): React.JSX.Element {
useUnreadDockBadge()
const clearUnreadDockBadge = useUnreadDockBadge()
useRadixBodyPointerEventsRecovery()
useWebSessionTabsSync()
const [floatingTerminalOpen, setFloatingTerminalOpen] = useState(false)
@@ -374,12 +374,13 @@ function App(): React.JSX.Element {
const setAppRootNode = useCallback(
(node: HTMLDivElement | null): void => {
// Why: return-focus frames are only valid while the App root is mounted.
// Why: these best-effort App chrome cleanups share the App root lifetime.
if (!node) {
cancelFloatingTerminalReturnFocusFrame()
clearUnreadDockBadge()
}
},
[cancelFloatingTerminalReturnFocusFrame]
[cancelFloatingTerminalReturnFocusFrame, clearUnreadDockBadge]
)
const rememberFloatingTerminalReturnFocus = useCallback((): void => {
@@ -0,0 +1,45 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('@/lib/unread-badge-count', () => ({
getUnreadBadgeCount: vi.fn()
}))
vi.mock('@/store', () => ({
useAppStore: vi.fn()
}))
import { clearUnreadDockBadgeCount } from './useUnreadDockBadge'
describe('clearUnreadDockBadgeCount', () => {
let setUnreadDockBadgeCount: ReturnType<typeof vi.fn>
beforeEach(() => {
setUnreadDockBadgeCount = vi.fn().mockResolvedValue(undefined)
vi.stubGlobal('window', {
api: {
app: {
setUnreadDockBadgeCount
}
}
})
})
afterEach(() => {
vi.unstubAllGlobals()
})
it('clears the app badge', () => {
clearUnreadDockBadgeCount()
expect(setUnreadDockBadgeCount).toHaveBeenCalledWith(0)
})
it('treats badge clearing as best-effort', async () => {
setUnreadDockBadgeCount.mockRejectedValueOnce(new Error('dock unavailable'))
clearUnreadDockBadgeCount()
await Promise.resolve()
expect(setUnreadDockBadgeCount).toHaveBeenCalledWith(0)
})
})
+13 -9
View File
@@ -2,7 +2,17 @@ import { useEffect } from 'react'
import { getUnreadBadgeCount } from '@/lib/unread-badge-count'
import { useAppStore } from '@/store'
export function useUnreadDockBadge(): void {
function setUnreadDockBadgeCountBestEffort(count: number): void {
void window.api.app.setUnreadDockBadgeCount(count).catch(() => {
// Dock sync is best-effort chrome; stale badge state should not affect app use.
})
}
export function clearUnreadDockBadgeCount(): void {
setUnreadDockBadgeCountBestEffort(0)
}
export function useUnreadDockBadge(): typeof clearUnreadDockBadgeCount {
const unreadCount = useAppStore((state) =>
getUnreadBadgeCount({
worktreesByRepo: state.worktreesByRepo,
@@ -12,14 +22,8 @@ export function useUnreadDockBadge(): void {
)
useEffect(() => {
void window.api.app.setUnreadDockBadgeCount(unreadCount).catch(() => {
// Dock sync is best-effort chrome; stale badge state should not affect app use.
})
setUnreadDockBadgeCountBestEffort(unreadCount)
}, [unreadCount])
useEffect(() => {
return () => {
void window.api.app.setUnreadDockBadgeCount(0).catch(() => {})
}
}, [])
return clearUnreadDockBadgeCount
}