diff options
Diffstat (limited to 'makima/frontend')
| -rw-r--r-- | makima/frontend/src/components/directives/DirectiveDetail.tsx | 12 | ||||
| -rw-r--r-- | makima/frontend/src/hooks/useDirectives.ts | 23 | ||||
| -rw-r--r-- | makima/frontend/src/hooks/useMultiTaskSubscription.ts | 5 |
3 files changed, 34 insertions, 6 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx index f9e7eed..b73463d 100644 --- a/makima/frontend/src/components/directives/DirectiveDetail.tsx +++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx @@ -54,6 +54,16 @@ export function DirectiveDetail({ const hasTerminalTasks = directive.steps.some((s) => s.taskId && terminalStatuses.has(s.status)); // Build task map from directive steps and orchestrator + // Derive a stable key from the actual task IDs to avoid recreating the map on every poll + const taskMapKey = useMemo(() => { + const parts: string[] = []; + if (directive.orchestratorTaskId) parts.push(`o:${directive.orchestratorTaskId}`); + for (const step of directive.steps) { + if (step.taskId) parts.push(`${step.id}:${step.taskId}`); + } + return parts.join(","); + }, [directive.orchestratorTaskId, directive.steps]); + const taskMap = useMemo(() => { const map = new Map<string, string>(); if (directive.orchestratorTaskId) { @@ -65,7 +75,7 @@ export function DirectiveDetail({ } } return map; - }, [directive.orchestratorTaskId, directive.steps]); + }, [taskMapKey]); // eslint-disable-line react-hooks/exhaustive-deps // Subscribe to all task outputs const { connected, entries, clearEntries } = useMultiTaskSubscription({ diff --git a/makima/frontend/src/hooks/useDirectives.ts b/makima/frontend/src/hooks/useDirectives.ts index e67733c..0453d14 100644 --- a/makima/frontend/src/hooks/useDirectives.ts +++ b/makima/frontend/src/hooks/useDirectives.ts @@ -63,6 +63,19 @@ export function useDirective(id: string | undefined) { const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); + // Silently refresh without setting loading state (for polls) + const silentRefresh = useCallback(async () => { + if (!id) return; + try { + const d = await getDirective(id); + setDirective(d); + setError(null); + } catch (e) { + // Don't overwrite existing data on poll failure + } + }, [id]); + + // Full refresh with loading state (for initial load / explicit refresh) const refresh = useCallback(async () => { if (!id) return; try { @@ -77,9 +90,13 @@ export function useDirective(id: string | undefined) { } }, [id]); + // Reset state and fetch when ID changes useEffect(() => { + setDirective(null); + setError(null); + setLoading(true); refresh(); - }, [refresh]); + }, [id]); // eslint-disable-line react-hooks/exhaustive-deps // Auto-poll while directive is active, has an orchestrator task, or has a completion task useEffect(() => { @@ -90,9 +107,9 @@ export function useDirective(id: string | undefined) { directive.completionTaskId != null; if (!needsPolling) return; - const interval = setInterval(refresh, 5000); + const interval = setInterval(silentRefresh, 5000); return () => clearInterval(interval); - }, [directive?.status, directive?.orchestratorTaskId, directive?.completionTaskId, refresh]); + }, [directive?.status, directive?.orchestratorTaskId, directive?.completionTaskId, silentRefresh]); const update = useCallback(async (req: UpdateDirectiveRequest) => { if (!id) return; diff --git a/makima/frontend/src/hooks/useMultiTaskSubscription.ts b/makima/frontend/src/hooks/useMultiTaskSubscription.ts index 19d6dea..4303f1b 100644 --- a/makima/frontend/src/hooks/useMultiTaskSubscription.ts +++ b/makima/frontend/src/hooks/useMultiTaskSubscription.ts @@ -38,8 +38,9 @@ export function useMultiTaskSubscription(options: UseMultiTaskSubscriptionOption enabledRef.current = enabled; }, [enabled]); - // Derive task IDs from the map - const taskIds = useMemo(() => Array.from(taskMap.keys()), [taskMap]); + // Derive task IDs from the map, stabilized to avoid unnecessary effect triggers + const taskIdsKey = useMemo(() => Array.from(taskMap.keys()).sort().join(","), [taskMap]); + const taskIds = useMemo(() => Array.from(taskMap.keys()), [taskIdsKey]); // eslint-disable-line react-hooks/exhaustive-deps const subscribeToTask = useCallback((ws: WebSocket, taskId: string) => { if (ws.readyState === WebSocket.OPEN) { |
