fix: retry file explorer init when client reconnects after page refresh (#442)

hasInitializedRef was set to true before confirming the directory
listing request actually succeeded. On page refresh the WebSocket
client is still reconnecting, so requestDirectoryListing returned
early with "Host is not connected" and the ref stayed true —
preventing any retry once the client became available.

Fix: make requestDirectoryListing return Promise<boolean> and reset
hasInitializedRef on failure so the init effect re-runs automatically
when requestDirectoryListing is recreated after the client reconnects.

Fixes #441

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Rui Fan
2026-04-16 04:28:02 +02:00
committed by GitHub
parent 45a5ba8637
commit ffbb2ffd06
2 changed files with 29 additions and 15 deletions

View File

@@ -167,23 +167,32 @@ export function FileExplorerPane({
if (hasInitializedRef.current) {
return;
}
// Mark initialized eagerly so concurrent effect re-runs don't double-fetch.
// If the root listing fails (e.g. client not yet connected), we reset the
// flag so the next time requestDirectoryListing is recreated (when client
// becomes available) this effect retries automatically.
hasInitializedRef.current = true;
void requestDirectoryListing(".", {
recordHistory: false,
setCurrentPath: false,
});
const persistedPaths =
usePanelStore.getState().expandedPathsByWorkspace[workspaceStateKey ?? ""];
if (persistedPaths) {
for (const path of persistedPaths) {
if (path !== ".") {
void requestDirectoryListing(path, {
recordHistory: false,
setCurrentPath: false,
});
}).then((succeeded) => {
if (!succeeded) {
hasInitializedRef.current = false;
return;
}
const persistedPaths =
usePanelStore.getState().expandedPathsByWorkspace[workspaceStateKey ?? ""];
if (persistedPaths) {
for (const path of persistedPaths) {
if (path !== ".") {
void requestDirectoryListing(path, {
recordHistory: false,
setCurrentPath: false,
});
}
}
}
}
});
}, [hasWorkspaceScope, requestDirectoryListing, workspaceStateKey]);
// Expand ancestor directories when a file is selected (e.g., from an inline path click)

View File

@@ -82,9 +82,12 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor
);
const requestDirectoryListing = useCallback(
async (path: string, options?: { recordHistory?: boolean; setCurrentPath?: boolean }) => {
async (
path: string,
options?: { recordHistory?: boolean; setCurrentPath?: boolean },
): Promise<boolean> => {
if (!workspaceStateKey) {
return;
return false;
}
const normalizedPath = path && path.length > 0 ? path : ".";
const shouldSetCurrentPath = options?.setCurrentPath ?? true;
@@ -113,7 +116,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor
lastError: "Workspace is unavailable",
pendingRequest: null,
}));
return;
return false;
}
if (!client) {
@@ -123,7 +126,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor
lastError: "Host is not connected",
pendingRequest: null,
}));
return;
return false;
}
try {
@@ -150,6 +153,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor
return nextState;
});
return true;
} catch (error) {
updateExplorerState((state) => ({
...state,
@@ -157,6 +161,7 @@ export function useFileExplorerActions(params: { serverId: string } & FileExplor
lastError: error instanceof Error ? error.message : "Failed to list directory",
pendingRequest: null,
}));
return false;
}
},
[client, normalizedWorkspaceRoot, updateExplorerState, workspaceStateKey],