fix: prevent false empty states during stack hydration (#1659)

* fix: prevent false empty states during stack hydration

Only show confirmed-empty UI after successful stack, status, and container
fetches. Distinguish loading and recoverable error states in the sidebar,
dashboard, and container health panel.

* fix: arbitrate overlapping stack status and container fetches

Prevent older dashboard status and same-owner container responses from overwriting newer load state after concurrent poll, invalidation, retry, or lifecycle refresh.

* fix: do not let soft status polls starve slow foreground loads

Skip soft /stacks/statuses poll and invalidation while a statuses request is already in flight so a deferred foreground hydration can still commit after the ten-second cadence.

* fix(stacks): surface recoverable errors for confirmed-empty soft failures

Sidebar and dashboard soft (background) refresh failures after a
confirmed-empty state silently kept showing the empty/adopt prompt
instead of a recoverable error, since only the error message was set
without flipping the load status. Also reject malformed non-array
/stacks responses instead of coercing them into a confirmed-empty list,
and drop malformed per-stack status entries before they reach the
dashboard table, which previously crashed the entire app on a null
entry.

* fix(stacks): close two review-found gaps in the load-failure fix

A non-empty stack-statuses map where every entry failed validation was
still committed as a confirmed-empty success; it now surfaces as a
recoverable error instead, and dropped entries are logged. The sidebar's
background-failure helper also checked a stale closure snapshot of the
file list, which could wipe a list that had just loaded non-empty in the
same attempt if the follow-up statuses fetch then failed; it now tracks
the freshest committed list for that decision. Also collapses two refs
tracking dashboard status-map emptiness into one.
This commit is contained in:
Anso
2026-07-22 08:01:59 -04:00
committed by GitHub
parent a3edee5e6a
commit b06dfd7175
21 changed files with 1498 additions and 148 deletions
@@ -135,6 +135,9 @@ export interface EditorViewProps {
envFiles: string[];
selectedEnvFile: string;
isFileLoading: boolean;
containersLoadStatus?: 'idle' | 'loading' | 'success' | 'error';
containersLoadError?: string | null;
onRetryContainersLoad?: () => void;
backupInfo: { exists: boolean; timestamp: number | null };
gitSourcePendingMap: Record<string, boolean>;
notifications: NotificationItem[];
@@ -244,6 +247,9 @@ export function EditorView(props: EditorViewProps) {
envFiles,
selectedEnvFile,
isFileLoading,
containersLoadStatus = 'success',
containersLoadError = null,
onRetryContainersLoad,
backupInfo,
gitSourcePendingMap,
notifications,
@@ -475,6 +481,9 @@ export function EditorView(props: EditorViewProps) {
onRequestServiceUpdate={onRequestServiceUpdate}
containersExpanded={containersExpanded}
onToggleContainersExpand={toggleContainersExpand}
containersLoadStatus={containersLoadStatus}
containersLoadError={containersLoadError}
onRetryContainersLoad={onRetryContainersLoad}
key={`${activeNode?.id ?? 'local'}:${stackName}`}
/>
</ScrollArea>
@@ -494,6 +503,9 @@ export function EditorView(props: EditorViewProps) {
serviceUpdateStatuses={serviceUpdateStatuses}
serviceUpdateInProgress={serviceUpdateInProgress}
onRequestServiceUpdate={onRequestServiceUpdate}
containersLoadStatus={containersLoadStatus}
containersLoadError={containersLoadError}
onRetryContainersLoad={onRetryContainersLoad}
key={`${activeNode?.id ?? 'local'}:${stackName}`}
/>
</CardContent>