From ffbb2ffd06262b059b8d1976057d6337ef75c640 Mon Sep 17 00:00:00 2001 From: Rui Fan <1996fanrui@gmail.com> Date: Thu, 16 Apr 2026 04:28:02 +0200 Subject: [PATCH] fix: retry file explorer init when client reconnects after page refresh (#442) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 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 --- .../app/src/components/file-explorer-pane.tsx | 31 ++++++++++++------- .../src/hooks/use-file-explorer-actions.ts | 13 +++++--- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/app/src/components/file-explorer-pane.tsx b/packages/app/src/components/file-explorer-pane.tsx index 5d952e135..9575eb891 100644 --- a/packages/app/src/components/file-explorer-pane.tsx +++ b/packages/app/src/components/file-explorer-pane.tsx @@ -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) diff --git a/packages/app/src/hooks/use-file-explorer-actions.ts b/packages/app/src/hooks/use-file-explorer-actions.ts index 766b1f978..f23c4056e 100644 --- a/packages/app/src/hooks/use-file-explorer-actions.ts +++ b/packages/app/src/hooks/use-file-explorer-actions.ts @@ -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 => { 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],