Skip to content

Commit 6df827b

Browse files
committed
fix(queries): stop a table cell edit throwing, and an upload gate failing shut
Two functional bugs found auditing the query layer. patchCachedRows walked tableKeys.rowsRoot non-exact, but rowsRoot is a prefix: the find (search results) and write (pending writes) subtrees hang off it with non-paged shapes, and the updater's old.pages.map threw on them. It runs inside onMutate, so the whole cell edit rejected before reaching the server — reachable as soon as a find entry exists, i.e. after the user searches the table once. The sibling isDefaultOrderRowsQuery already excluded those subtrees and its docstring claimed they "never match"; that was only true of the sibling. Both now share one isRowListQueryKey helper so they cannot drift apart again. useCloudStorageConfigured combined staleTime: Infinity, retry: false, and the global retryOnMount: false on a workspace-independent key, so one transient failure left it errored for the tab's lifetime with no way back — navigating or switching workspace cannot change the key, and the upload path fails closed, so cloud-backed uploads stayed disabled until a full reload. useVoiceSettings carries the same three options and already escapes this with retryOnMount: true; this one now matches. Note: hooks/queries/workspace-files.test.tsx cannot load in a git worktree (pre-existing postcss resolution failure), so CI is the first place that file runs against this change.
1 parent 2516b5f commit 6df827b

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

‎apps/sim/hooks/queries/tables.ts‎

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,18 @@ function withOptimisticAutoFireExec(groups: WorkflowGroup[], row: TableRow): Tab
829829
return { ...row, executions: nextExecutions }
830830
}
831831

832+
/**
833+
* Whether a cache key under {@link tableKeys.rowsRoot} holds paged row-list data.
834+
*
835+
* `rowsRoot` is a shared prefix: the `find` (search results) and `write` (pending
836+
* writes) subtrees live beneath it with entirely different shapes, so any bulk
837+
* update walking the prefix has to exclude them or it will be handed a value whose
838+
* `pages` it cannot map.
839+
*/
840+
function isRowListQueryKey(queryKey: readonly unknown[]): boolean {
841+
return !queryKey.includes('find') && !queryKey.includes('write')
842+
}
843+
832844
/**
833845
* Apply a row-level transformation to all cached infinite row queries for this
834846
* table. Used for cell edits where positions don't change.
@@ -839,9 +851,20 @@ function patchCachedRows(
839851
patchRow: (row: TableRow) => TableRow
840852
) {
841853
queryClient.setQueriesData<InfiniteData<TableRowsResponse, TableRowsPageParam>>(
842-
{ queryKey: tableKeys.rowsRoot(tableId), exact: false },
854+
{
855+
queryKey: tableKeys.rowsRoot(tableId),
856+
exact: false,
857+
/**
858+
* `rowsRoot` is a prefix, not a leaf: `rowWrites` and `find` hang off it and
859+
* hold non-paged shapes. Without this they are handed to the updater below,
860+
* whose `old.pages.map` throws — and because this runs inside `onMutate`, the
861+
* whole cell edit rejects before it reaches the server. Only reachable once a
862+
* `find` entry exists, i.e. after the user has searched the table.
863+
*/
864+
predicate: (query) => isRowListQueryKey(query.queryKey),
865+
},
843866
(old) => {
844-
if (!old) return old
867+
if (!old?.pages) return old
845868
return {
846869
...old,
847870
pages: old.pages.map((page) => ({ ...page, rows: page.rows.map(patchRow) })),
@@ -859,7 +882,7 @@ function patchCachedRows(
859882
* `find`/`write` subtrees aren't row-list data and never match.
860883
*/
861884
function isDefaultOrderRowsQuery(queryKey: readonly unknown[]): boolean {
862-
if (queryKey.includes('find') || queryKey.includes('write')) return false
885+
if (!isRowListQueryKey(queryKey)) return false
863886
const last = queryKey[queryKey.length - 1]
864887
if (typeof last !== 'string') return false
865888
try {

‎apps/sim/hooks/queries/workspace-files.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,16 @@ export function useCloudStorageConfigured(enabled = true) {
521521
enabled,
522522
retry: false,
523523
staleTime: CLOUD_STORAGE_CONFIGURED_STALE_TIME,
524+
/**
525+
* Escapes the global `retryOnMount: false`, which an infinite `staleTime` and
526+
* `retry: false` would otherwise turn into a permanent failure: one transient
527+
* error leaves this query errored for the tab's lifetime, and consumers fail
528+
* closed — the upload path treats "unknown" as "not configured", so a single
529+
* blip would disable cloud-backed uploads until a full reload. The key is
530+
* global, so navigating or switching workspace cannot recover it either.
531+
* Matches {@link useVoiceSettings}, which carries the same three options.
532+
*/
533+
retryOnMount: true,
524534
})
525535
}
526536

0 commit comments

Comments
 (0)