Skip to content

Commit b49a061

Browse files
fix(tables): resolve views on list availability, not query success
A failed background refetch flips isError while the cached list stays usable, and every view mutation invalidates the views query — so one blip made the resolve effect treat views as terminally failed and stop applying switches until the next successful refetch. The axis is now whether a list exists: error with no list ever fetched settles to All; error with a cached list resolves normally against the cache; owner is unknown only while the initial fetch is in flight.
1 parent 1576c41 commit b49a061

1 file changed

Lines changed: 18 additions & 14 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/tables/[tableId]

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -359,15 +359,17 @@ export function Table({
359359
((previousName: string, newName: string) => void) | null
360360
>(null)
361361

362-
const {
363-
data: views = NO_VIEWS,
364-
isSuccess: viewsLoaded,
365-
isError: viewsFailed,
366-
} = useTableViews({
362+
const { data: viewsData, isError: viewsErrored } = useTableViews({
367363
workspaceId,
368364
tableId,
369365
enabled: viewsEnabled,
370366
})
367+
const views = viewsData ?? NO_VIEWS
368+
/** A views list exists — fresh or cached. A failed background refetch flips
369+
* `isError` while the cached list stays perfectly usable (and every view
370+
* mutation invalidates this query), so success/error is the wrong axis:
371+
* what matters is whether there is a list to resolve against. */
372+
const viewsAvailable = viewsData !== undefined
371373

372374
// Single source of truth for `useTable` — drives both the grid render and
373375
// the wrapper's slideouts/modals. The grid receives the bundle as props.
@@ -399,7 +401,7 @@ export function Table({
399401
* yet and layout writes must go nowhere. An ERROR counts as settled: the table
400402
* falls back to All and writes resume against shared metadata, rather than
401403
* staying silently suppressed for the rest of the session. */
402-
const viewOwnerUnknown = viewsEnabled && !viewsLoaded && !viewsFailed
404+
const viewOwnerUnknown = viewsEnabled && !viewsAvailable && !viewsErrored
403405

404406
const [viewModal, setViewModal] = useState<ViewModalState>(null)
405407
/** Which view id the local filter/sort/hidden state was last seeded from.
@@ -493,16 +495,18 @@ export function Table({
493495
*/
494496
useEffect(() => {
495497
if (!viewsEnabled) return
496-
// A failed fetch settles to All: stamp the owner so the layout router stops
497-
// buffering and writes shared metadata, then flush what was touched during
498-
// the load — no other branch of this effect ever runs on the error path,
499-
// and skipping it would silently drop the resize on refresh.
500-
if (viewsFailed) {
498+
// Terminal only when the fetch failed WITHOUT ever producing a list — then
499+
// the table settles to All: stamp the owner so the layout router stops
500+
// buffering, and flush what was touched during the load. An error with a
501+
// cached list falls through — the list is still resolvable, and treating a
502+
// failed background refetch as terminal would wedge view switching until
503+
// the next successful refetch.
504+
if (viewsErrored && !viewsAvailable) {
501505
seededViewIdRef.current = null
502506
resolvePendingLayout(false)
503507
return
504508
}
505-
if (!viewsLoaded) return
509+
if (!viewsAvailable) return
506510

507511
if (seededViewIdRef.current === undefined) {
508512
// Embedded tables bind these parsers to the HOST page's URL, which the
@@ -583,8 +587,8 @@ export function Table({
583587
applyViewConfig(activeView?.config ?? null)
584588
}, [
585589
viewsEnabled,
586-
viewsLoaded,
587-
viewsFailed,
590+
viewsAvailable,
591+
viewsErrored,
588592
views,
589593
activeView,
590594
activeViewId,

0 commit comments

Comments
 (0)