Skip to content

Commit 4445e31

Browse files
fix(table): bump run counter on edit/auto-run so Stop shows for queued cells (#4682)
The "X running" badge + per-row gutter Stop only updated on manual Run (useRunColumn bumped the run-state counter). Edit-triggered auto-runs (useUpdateTableRow, useBatchUpdateTableRows, useCreateTableRow) stamped cells pending in the rows cache but never bumped runningCellCount/runningByRowId, so Stop stayed hidden even though cells were queued (the counter is already queued-inclusive). Extracted countNewlyInFlight + bumpRunState helpers and wired them into all the optimistic auto-fire paths with onError rollback; reused them in useRunColumn.
1 parent 9347da5 commit 4445e31

1 file changed

Lines changed: 70 additions & 25 deletions

File tree

apps/sim/hooks/queries/tables.ts

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,43 @@ async function fetchTableRunState(tableId: string, signal?: AbortSignal): Promis
225225
}
226226
}
227227

228+
/** Count groups flipped to in-flight (`pending`) by an optimistic schedule that
229+
* weren't in-flight before — the delta to add to the run-state counter. */
230+
function countNewlyInFlight(before: RowExecutions, after: RowExecutions): number {
231+
let n = 0
232+
for (const gid of Object.keys(after)) {
233+
if (after[gid]?.status === 'pending' && !isExecInFlight(before[gid])) n++
234+
}
235+
return n
236+
}
237+
238+
/** Add optimistically-stamped cells to the run-state counter so the "X running"
239+
* badge + per-row gutter Stop reflect them instantly (the optimistic stamp
240+
* eats the dispatcher's `pending` SSE, so `applyCell` never bumps the count).
241+
* Returns the prior snapshot for rollback, or `null` when nothing was bumped. */
242+
function bumpRunState(
243+
queryClient: ReturnType<typeof useQueryClient>,
244+
tableId: string,
245+
stampedByRow: Record<string, number>
246+
): { snapshot: TableRunState | undefined } | null {
247+
const total = Object.values(stampedByRow).reduce((s, n) => s + n, 0)
248+
if (total === 0) return null
249+
const snapshot = queryClient.getQueryData<TableRunState>(tableKeys.activeDispatches(tableId))
250+
queryClient.setQueryData<TableRunState>(tableKeys.activeDispatches(tableId), (prev) => {
251+
const base = prev ?? { dispatches: [], runningCellCount: 0, runningByRowId: {} }
252+
const nextByRow = { ...base.runningByRowId }
253+
for (const [rid, n] of Object.entries(stampedByRow)) {
254+
nextByRow[rid] = (nextByRow[rid] ?? 0) + n
255+
}
256+
return {
257+
...base,
258+
runningCellCount: base.runningCellCount + total,
259+
runningByRowId: nextByRow,
260+
}
261+
})
262+
return { snapshot }
263+
}
264+
228265
/**
229266
* Aggregate live state for a table: active dispatches (drives the "about to
230267
* run" overlay), the running-cell count (top-right counter), and per-row
@@ -453,6 +490,11 @@ export function useCreateTableRow({ workspaceId, tableId }: RowMutationContext)
453490
.workflowGroups ?? []
454491
const stamped = withOptimisticAutoFireExec(groups, row)
455492
reconcileCreatedRow(queryClient, tableId, stamped)
493+
// Bump the run-state counter for any auto-fire groups stamped pending so
494+
// the "X running" badge + gutter Stop show immediately (the row had no
495+
// prior executions, so the stamped set is the full delta).
496+
const stampedCount = countNewlyInFlight({}, stamped.executions ?? {})
497+
if (stampedCount > 0) bumpRunState(queryClient, tableId, { [row.id]: stampedCount })
456498
},
457499
onError: (error) => {
458500
if (isValidationError(error)) return
@@ -618,18 +660,27 @@ export function useUpdateTableRow({ workspaceId, tableId }: RowMutationContext)
618660
queryClient.getQueryData<TableDefinition>(tableKeys.detail(tableId))?.schema
619661
.workflowGroups ?? []
620662

663+
const stampedByRow: Record<string, number> = {}
621664
patchCachedRows(queryClient, tableId, (row) => {
622665
if (row.id !== rowId) return row
623666
const patch = data as Partial<RowData>
624667
const nextExecutions = optimisticallyScheduleNewlyEligibleGroups(groups, row, patch)
668+
if (nextExecutions) {
669+
stampedByRow[row.id] = countNewlyInFlight(row.executions ?? {}, nextExecutions)
670+
}
625671
return {
626672
...row,
627673
data: { ...row.data, ...patch } as RowData,
628674
...(nextExecutions ? { executions: nextExecutions } : {}),
629675
}
630676
})
631677

632-
return { previousQueries }
678+
const bumped = bumpRunState(queryClient, tableId, stampedByRow)
679+
return {
680+
previousQueries,
681+
runStateSnapshot: bumped?.snapshot,
682+
didBumpRunState: bumped !== null,
683+
}
633684
},
634685
onSuccess: (response, { rowId, data: mutatedData }) => {
635686
const serverRow = response.data.row
@@ -655,6 +706,9 @@ export function useUpdateTableRow({ workspaceId, tableId }: RowMutationContext)
655706
queryClient.setQueryData(queryKey, data)
656707
}
657708
}
709+
if (context?.didBumpRunState) {
710+
queryClient.setQueryData(tableKeys.activeDispatches(tableId), context.runStateSnapshot)
711+
}
658712
if (isValidationError(error)) return
659713
toast.error(error.message, { duration: 5000 })
660714
},
@@ -694,26 +748,38 @@ export function useBatchUpdateTableRows({ workspaceId, tableId }: RowMutationCon
694748
queryClient.getQueryData<TableDefinition>(tableKeys.detail(tableId))?.schema
695749
.workflowGroups ?? []
696750

751+
const stampedByRow: Record<string, number> = {}
697752
patchCachedRows(queryClient, tableId, (row) => {
698753
const raw = updateMap.get(row.id)
699754
if (!raw) return row
700755
const patch = raw as Partial<RowData>
701756
const nextExecutions = optimisticallyScheduleNewlyEligibleGroups(groups, row, patch)
757+
if (nextExecutions) {
758+
stampedByRow[row.id] = countNewlyInFlight(row.executions ?? {}, nextExecutions)
759+
}
702760
return {
703761
...row,
704762
data: { ...row.data, ...patch } as RowData,
705763
...(nextExecutions ? { executions: nextExecutions } : {}),
706764
}
707765
})
708766

709-
return { previousQueries }
767+
const bumped = bumpRunState(queryClient, tableId, stampedByRow)
768+
return {
769+
previousQueries,
770+
runStateSnapshot: bumped?.snapshot,
771+
didBumpRunState: bumped !== null,
772+
}
710773
},
711774
onError: (error, _vars, context) => {
712775
if (context?.previousQueries) {
713776
for (const [queryKey, data] of context.previousQueries) {
714777
queryClient.setQueryData(queryKey, data)
715778
}
716779
}
780+
if (context?.didBumpRunState) {
781+
queryClient.setQueryData(tableKeys.activeDispatches(tableId), context.runStateSnapshot)
782+
}
717783
if (isValidationError(error)) return
718784
toast.error(error.message, { duration: 5000 })
719785
},
@@ -1376,29 +1442,8 @@ export function useRunColumn({ workspaceId, tableId }: RowMutationContext) {
13761442
return { ...r, data: nextData, executions: next }
13771443
})
13781444

1379-
// Bump the counter to match the stamped cells. Without it the "X running"
1380-
// badge + gutter Stop stay at zero until a refetch: the optimistic stamp
1381-
// already marks the cell in-flight, so the dispatcher's `pending` SSE
1382-
// sees no `wasInFlight` transition and never bumps the counter.
1383-
const runStateSnapshot = queryClient.getQueryData<TableRunState>(
1384-
tableKeys.activeDispatches(tableId)
1385-
)
1386-
const totalStamped = Object.values(stampedByRow).reduce((s, n) => s + n, 0)
1387-
if (totalStamped > 0) {
1388-
queryClient.setQueryData<TableRunState>(tableKeys.activeDispatches(tableId), (prev) => {
1389-
const base = prev ?? { dispatches: [], runningCellCount: 0, runningByRowId: {} }
1390-
const nextByRow = { ...base.runningByRowId }
1391-
for (const [rid, n] of Object.entries(stampedByRow)) {
1392-
nextByRow[rid] = (nextByRow[rid] ?? 0) + n
1393-
}
1394-
return {
1395-
...base,
1396-
runningCellCount: base.runningCellCount + totalStamped,
1397-
runningByRowId: nextByRow,
1398-
}
1399-
})
1400-
}
1401-
return { snapshots, runStateSnapshot, didBumpRunState: totalStamped > 0 }
1445+
const bumped = bumpRunState(queryClient, tableId, stampedByRow)
1446+
return { snapshots, runStateSnapshot: bumped?.snapshot, didBumpRunState: bumped !== null }
14021447
},
14031448
onError: (_err, _variables, context) => {
14041449
if (context?.snapshots) restoreCachedWorkflowCells(queryClient, context.snapshots)

0 commit comments

Comments
 (0)