53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
|
|
/**
|
||
|
|
* useRealtimeSync — mounts the SSE connection once at the app level and
|
||
|
|
* wires incoming events to React Query cache invalidation.
|
||
|
|
*
|
||
|
|
* Event → query key mapping:
|
||
|
|
* agent.status → ['agents']
|
||
|
|
* agent.task → ['tasks'], ['agents']
|
||
|
|
* agent.progress → ['tasks'], ['agents']
|
||
|
|
* fleet.update → ['agents'], ['sessions'], ['tasks']
|
||
|
|
*/
|
||
|
|
import { useQueryClient } from '@tanstack/react-query'
|
||
|
|
import { useCallback } from 'react'
|
||
|
|
import { useSSE, type SSEMessage, type SSEStatus } from './useSSE'
|
||
|
|
|
||
|
|
export function useRealtimeSync(): { sseStatus: SSEStatus } {
|
||
|
|
const queryClient = useQueryClient()
|
||
|
|
|
||
|
|
const handleMessage = useCallback(
|
||
|
|
(msg: SSEMessage) => {
|
||
|
|
switch (msg.type) {
|
||
|
|
case 'agent.status':
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||
|
|
break
|
||
|
|
|
||
|
|
case 'agent.task':
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['tasks'] })
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||
|
|
break
|
||
|
|
|
||
|
|
case 'agent.progress':
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['tasks'] })
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||
|
|
break
|
||
|
|
|
||
|
|
case 'fleet.update':
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['sessions'] })
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['tasks'] })
|
||
|
|
break
|
||
|
|
|
||
|
|
default:
|
||
|
|
// 'connected' and unknown events — no action needed
|
||
|
|
break
|
||
|
|
}
|
||
|
|
},
|
||
|
|
[queryClient],
|
||
|
|
)
|
||
|
|
|
||
|
|
const { status: sseStatus } = useSSE({ onMessage: handleMessage })
|
||
|
|
|
||
|
|
return { sseStatus }
|
||
|
|
}
|