Skip to content

Commit 5246b69

Browse files
committed
Fix file tree restore after workspace switch
1 parent 882a476 commit 5246b69

2 files changed

Lines changed: 90 additions & 2 deletions

File tree

packages/web/src/features/workspace/actions/use-file-actions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ export function useFileActions({
9999

100100
const loadChildren = useCallback(
101101
async (dirPath: string) => {
102-
if (!workspaceId || isLoadingDir === dirPath || loadedDirs.has(dirPath)) return;
102+
const alreadyLoaded = loadedDirs.has(dirPath) && fileTree?.has(dirPath);
103+
if (!workspaceId || isLoadingDir === dirPath || alreadyLoaded) return;
103104

104105
setIsLoadingDir(dirPath);
105106
const result = await dispatch<ReadTreeResult>("file.readTree", {
@@ -119,7 +120,7 @@ export function useFileActions({
119120

120121
setIsLoadingDir(null);
121122
},
122-
[workspaceId, isLoadingDir, loadedDirs, dispatch, setFileTree, setLoadedDirs]
123+
[workspaceId, isLoadingDir, loadedDirs, fileTree, dispatch, setFileTree, setLoadedDirs]
123124
);
124125

125126
const loadSearchResults = useCallback(

packages/web/src/features/workspace/views/shared/file-tree-panel.test.tsx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,93 @@ describe("FileTreePanel", () => {
13031303
expect(screen.queryByText("old.ts")).not.toBeInTheDocument();
13041304
});
13051305

1306+
it("restores expanded directories after switching away and back to a workspace", async () => {
1307+
const sendCommand = vi
1308+
.fn()
1309+
.mockImplementation(async (_op: string, args?: { subPath?: string }) => {
1310+
if (args?.subPath === "src") {
1311+
return {
1312+
path: "src",
1313+
children: [{ path: "src/index.ts", name: "index.ts", kind: "file" }],
1314+
};
1315+
}
1316+
1317+
return {
1318+
path: "/workspace",
1319+
children: [{ path: "src", name: "src", kind: "dir" }],
1320+
};
1321+
});
1322+
const store = createStore();
1323+
store.set(wsClientAtom, { sendCommand } as never);
1324+
store.set(workspacesAtom, {
1325+
"ws-1": {
1326+
id: "ws-1",
1327+
path: "/workspace-1",
1328+
targetRuntime: "native",
1329+
openedAt: 1,
1330+
lastActiveAt: 1,
1331+
uiState: {
1332+
leftPanelWidth: 280,
1333+
bottomPanelHeight: 200,
1334+
focusMode: false,
1335+
fileTreeExpandedDirs: ["src"],
1336+
},
1337+
},
1338+
"ws-2": {
1339+
id: "ws-2",
1340+
path: "/workspace-2",
1341+
targetRuntime: "native",
1342+
openedAt: 2,
1343+
lastActiveAt: 2,
1344+
uiState: {
1345+
leftPanelWidth: 280,
1346+
bottomPanelHeight: 200,
1347+
focusMode: false,
1348+
},
1349+
},
1350+
} as never);
1351+
store.set(
1352+
fileTreeAtomFamily("ws-1"),
1353+
new Map([[".", [{ path: "src", name: "src", kind: "dir" }]]])
1354+
);
1355+
store.set(
1356+
fileTreeAtomFamily("ws-2"),
1357+
new Map([[".", [{ path: "docs", name: "docs", kind: "dir" }]]])
1358+
);
1359+
1360+
const { rerender } = render(
1361+
<Provider store={store}>
1362+
<FileTreePanel workspaceId="ws-1" />
1363+
</Provider>
1364+
);
1365+
1366+
expect(await screen.findByText("index.ts")).toBeInTheDocument();
1367+
expect(Array.from(store.get(expandedDirsAtomFamily("ws-1")) ?? [])).toEqual(["src"]);
1368+
expect(Array.from(store.get(loadedDirsAtomFamily("ws-1")))).toEqual(["src"]);
1369+
1370+
rerender(
1371+
<Provider store={store}>
1372+
<FileTreePanel workspaceId="ws-2" />
1373+
</Provider>
1374+
);
1375+
1376+
expect(screen.getByText("docs")).toBeInTheDocument();
1377+
expect(screen.queryByText("index.ts")).not.toBeInTheDocument();
1378+
1379+
store.set(
1380+
fileTreeAtomFamily("ws-1"),
1381+
new Map([[".", [{ path: "src", name: "src", kind: "dir" }]]])
1382+
);
1383+
1384+
rerender(
1385+
<Provider store={store}>
1386+
<FileTreePanel workspaceId="ws-1" />
1387+
</Provider>
1388+
);
1389+
1390+
expect(await screen.findByText("index.ts")).toBeInTheDocument();
1391+
});
1392+
13061393
it("reloads the file tree after deleting a file", async () => {
13071394
const sendCommand = vi.fn().mockResolvedValueOnce({ ok: true }).mockResolvedValueOnce({
13081395
path: "/workspace",

0 commit comments

Comments
 (0)