Compare commits

...

1 Commits

Author SHA1 Message Date
Jiayuan Zhang
dc1f520eb5 fix(views): show agent live card immediately without waiting for messages
When navigating to an issue where an agent is already working, the
"Agent is working" card was delayed because it waited for both
getActiveTasksForIssue() AND listTaskMessages() to complete before
rendering. Now the card renders immediately after active tasks are
fetched, and messages load progressively in the background. Also
properly merges HTTP-loaded messages with any WebSocket-delivered
messages to avoid race conditions.
2026-04-12 00:08:09 +08:00

View File

@@ -119,20 +119,40 @@ export function AgentLiveCard({ issueId }: AgentLiveCardProps) {
let cancelled = false;
api.getActiveTasksForIssue(issueId).then(({ tasks }) => {
if (cancelled || tasks.length === 0) return;
const newStates = new Map<string, TaskState>();
const loadPromises = tasks.map(async (task) => {
try {
const msgs = await api.listTaskMessages(task.id);
// Show cards immediately with empty timeline
setTaskStates((prev) => {
const next = new Map(prev);
for (const task of tasks) {
if (!next.has(task.id)) {
next.set(task.id, { task, items: [] });
}
}
return next;
});
// Load messages per task in the background
for (const task of tasks) {
api.listTaskMessages(task.id).then((msgs) => {
if (cancelled) return;
const timeline = buildTimeline(msgs);
for (const m of msgs) seenSeqs.current.add(`${m.task_id}:${m.seq}`);
newStates.set(task.id, { task, items: timeline });
} catch {
newStates.set(task.id, { task, items: [] });
}
});
Promise.all(loadPromises).then(() => {
if (!cancelled) setTaskStates(newStates);
});
setTaskStates((prev) => {
const next = new Map(prev);
const existing = next.get(task.id);
if (existing) {
// Merge: keep any WS-delivered items not in the loaded batch
const loadedSeqs = new Set(timeline.map((i) => i.seq));
const wsOnly = existing.items.filter((i) => !loadedSeqs.has(i.seq));
const merged = [...timeline, ...wsOnly].sort((a, b) => a.seq - b.seq);
next.set(task.id, { task: existing.task, items: merged });
} else {
next.set(task.id, { task, items: timeline });
}
return next;
});
}).catch(console.error);
}
}).catch(console.error);
return () => { cancelled = true; };