feat(ST-1111.4): forward ClaudeJob events on solo SSE stream + initial state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-29 19:03:48 +02:00
parent 9d9fb4b4c0
commit ece0aa963d
3 changed files with 129 additions and 5 deletions

View file

@ -20,7 +20,7 @@
import { useEffect, useRef } from 'react'
import { flushSync } from 'react-dom'
import { useSoloStore } from '@/stores/solo-store'
import type { RealtimeEvent, RealtimeStatus } from '@/stores/solo-store'
import type { ClaudeJobEvent, JobState, RealtimeEvent, RealtimeStatus } from '@/stores/solo-store'
const BACKOFF_START_MS = 1_000
const BACKOFF_MAX_MS = 30_000
@ -35,6 +35,8 @@ export function useSoloRealtime(productId: string | null) {
useEffect(() => {
const setStatus = useSoloStore.getState().setRealtimeStatus
const handleEvent = useSoloStore.getState().handleRealtimeEvent
const handleJobEvent = useSoloStore.getState().handleJobEvent
const initJobs = useSoloStore.getState().initJobs
if (!productId) {
// Geen actief product (gebruiker zit niet op /solo) — stream uit
@ -84,10 +86,24 @@ export function useSoloRealtime(productId: string | null) {
scheduleIndicator('open')
})
source.addEventListener('claude_jobs_initial', (e) => {
if (!e.data) return
try {
initJobs(JSON.parse(e.data) as JobState[])
} catch {
// ignore malformed payload
}
})
source.onmessage = (e) => {
if (!e.data) return
try {
const payload = JSON.parse(e.data) as RealtimeEvent
const raw = JSON.parse(e.data) as RealtimeEvent | ClaudeJobEvent
if ('type' in raw && (raw.type === 'claude_job_enqueued' || raw.type === 'claude_job_status')) {
handleJobEvent(raw)
return
}
const payload = raw as RealtimeEvent
// Animatie A: kanban-move animeren via View Transitions API. Voor
// task UPDATE-events wrap'en we de store-update in een view
// transition. flushSync forceert React om synchroon te renderen