mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(web): address review findings — prune stale push endpoints, keep running section open while searching, clear auto-expand guard on section transitions; update tests
This commit is contained in:
@@ -388,9 +388,9 @@ describe('SessionList collapse behavior', () => {
|
||||
]
|
||||
const { rerender } = render(renderSessionList(baseSessions))
|
||||
|
||||
expect(getProjectPanel().getAttribute('data-open')).toBe('true')
|
||||
|
||||
fireEvent.click(screen.getByTitle('/work/hapi'))
|
||||
// The running session is pinned in the "in progress" section; the
|
||||
// directory group now only holds inactive sessions and starts
|
||||
// collapsed.
|
||||
expect(getProjectPanel().getAttribute('data-open')).toBeNull()
|
||||
|
||||
rerender(renderSessionList([
|
||||
@@ -410,9 +410,7 @@ describe('SessionList collapse behavior', () => {
|
||||
it('auto-expands the path again when the selected session changes', async () => {
|
||||
const sessions = [
|
||||
makeSession({
|
||||
id: 'session-running',
|
||||
active: true,
|
||||
thinking: true,
|
||||
id: 'session-first',
|
||||
updatedAt: 100,
|
||||
metadata: { path: '/work/hapi', name: 'Running task', flavor: 'codex' },
|
||||
}),
|
||||
@@ -424,16 +422,58 @@ describe('SessionList collapse behavior', () => {
|
||||
]
|
||||
const { rerender } = render(renderSessionList(sessions))
|
||||
|
||||
fireEvent.click(screen.getByTitle('/work/hapi'))
|
||||
// Inactive-only groups start collapsed; selecting a session inside
|
||||
// one auto-expands it.
|
||||
expect(getProjectPanel().getAttribute('data-open')).toBeNull()
|
||||
|
||||
rerender(renderSessionList(sessions, 'session-next'))
|
||||
rerender(renderSessionList([
|
||||
...sessions,
|
||||
makeSession({
|
||||
id: 'session-running',
|
||||
active: true,
|
||||
thinking: true,
|
||||
updatedAt: 110,
|
||||
metadata: { path: '/work/hapi', name: 'Running task', flavor: 'codex' },
|
||||
}),
|
||||
], 'session-next'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getProjectPanel().getAttribute('data-open')).toBe('true')
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps the running section open while searching even when collapsed', () => {
|
||||
const sessions = [
|
||||
makeSession({
|
||||
id: 'session-running',
|
||||
active: true,
|
||||
thinking: true,
|
||||
updatedAt: 100,
|
||||
metadata: { path: '/work/hapi', name: 'Running task', flavor: 'codex' },
|
||||
}),
|
||||
makeSession({
|
||||
id: 'session-idle',
|
||||
updatedAt: 50,
|
||||
metadata: { path: '/work/hapi', name: 'Idle task', flavor: 'codex' },
|
||||
}),
|
||||
]
|
||||
render(renderSessionList(sessions))
|
||||
|
||||
const runningPanel = () => screen.getByTitle('In progress').nextElementSibling
|
||||
|
||||
expect(runningPanel()?.getAttribute('data-open')).toBe('true')
|
||||
|
||||
fireEvent.click(screen.getByTitle('In progress'))
|
||||
expect(runningPanel()?.getAttribute('data-open')).toBeNull()
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Search sessions' }))
|
||||
fireEvent.change(screen.getByPlaceholderText('Search sessions…'), {
|
||||
target: { value: 'Running' },
|
||||
})
|
||||
|
||||
expect(runningPanel()?.getAttribute('data-open')).toBe('true')
|
||||
})
|
||||
|
||||
it('keeps the previous selected path open when selection moves', async () => {
|
||||
const sessions = [
|
||||
makeSession({
|
||||
|
||||
@@ -1177,7 +1177,14 @@ export function SessionList(props: {
|
||||
const group = groups.find(g =>
|
||||
g.sessions.some(s => s.id === selectedSessionId)
|
||||
)
|
||||
if (!group) return
|
||||
if (!group) {
|
||||
// The selected session is not rendered inside any directory group
|
||||
// (e.g. it moved to the pinned "in progress" section). Drop the
|
||||
// guard so it auto-expands again when it transitions back into a
|
||||
// group later.
|
||||
autoExpandedSelectedSessionKeyRef.current = null
|
||||
return
|
||||
}
|
||||
|
||||
const autoExpandKey = `${selectedSessionId}::${group.key}`
|
||||
if (autoExpandedSelectedSessionKeyRef.current === autoExpandKey) return
|
||||
@@ -1432,7 +1439,7 @@ export function SessionList(props: {
|
||||
({runningSessions.length})
|
||||
</span>
|
||||
</div>
|
||||
<div className="collapsible-panel" data-open={!runningSectionCollapsed || undefined}>
|
||||
<div className="collapsible-panel" data-open={(!runningSectionCollapsed || isFiltering) || undefined}>
|
||||
<div className="collapsible-inner">
|
||||
<div className="flex flex-col gap-0.5 ml-3 pl-1 py-1">
|
||||
{runningSessions.map((s) => (
|
||||
|
||||
@@ -117,12 +117,24 @@ export function usePushNotifications(api: ApiClient | null) {
|
||||
// mismatch via the key recorded at subscribe time and recreate it.
|
||||
let subscription = existing
|
||||
if (existing && readStoredVapidKey() !== publicKey) {
|
||||
const staleEndpoint = existing.endpoint
|
||||
try {
|
||||
await existing.unsubscribe()
|
||||
} catch {
|
||||
// Ignore unsubscribe failures — subscribe() below still
|
||||
// issues a fresh subscription with the current key.
|
||||
}
|
||||
// Prune the obsolete endpoint from the hub so it stops
|
||||
// receiving failed sends (VapidPkHashMismatch) for a
|
||||
// subscription that can no longer be reached.
|
||||
if (staleEndpoint) {
|
||||
try {
|
||||
await api.unsubscribePushNotifications({ endpoint: staleEndpoint })
|
||||
} catch {
|
||||
// Best-effort cleanup — a stale hub registration is
|
||||
// harmless beyond repeated failed sends until pruned.
|
||||
}
|
||||
}
|
||||
subscription = null
|
||||
}
|
||||
if (!subscription) {
|
||||
|
||||
Reference in New Issue
Block a user