Skip to content

Commit b6ae63c

Browse files
authored
fix(sidebar): refresh folder tree after creating files (Acode-Foundation#2022)
1 parent 31b8cc9 commit b6ae63c

File tree

2 files changed

+112
-39
lines changed

2 files changed

+112
-39
lines changed

src/components/fileTree/index.js

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export default class FileTree {
167167

168168
// Child file tree for nested folders
169169
let childTree = null;
170+
$content._fileTree = null;
170171

171172
const toggle = async () => {
172173
const isExpanded = !$wrapper.classList.contains("hidden");
@@ -179,6 +180,7 @@ export default class FileTree {
179180
childTree.destroy();
180181
this.childTrees.delete(url);
181182
childTree = null;
183+
$content._fileTree = null;
182184
}
183185
this.options.onExpandedChange?.(url, false);
184186
} else {
@@ -192,6 +194,7 @@ export default class FileTree {
192194
_depth: this.depth + 1,
193195
});
194196
this.childTrees.set(url, childTree);
197+
$content._fileTree = childTree;
195198
try {
196199
await childTree.load(url);
197200
} finally {
@@ -216,20 +219,34 @@ export default class FileTree {
216219
queueMicrotask(() => toggle());
217220
}
218221

219-
// Add properties for external access (keep unclasped for collapsableList compatibility)
220-
Object.defineProperties($wrapper, {
221-
collapsed: { get: () => $wrapper.classList.contains("hidden") },
222-
expanded: { get: () => !$wrapper.classList.contains("hidden") },
223-
unclasped: { get: () => !$wrapper.classList.contains("hidden") }, // Legacy compatibility
224-
$title: { get: () => $title },
225-
$ul: { get: () => $content },
226-
expand: {
227-
value: () => !$wrapper.classList.contains("hidden") || toggle(),
228-
},
229-
collapse: {
230-
value: () => $wrapper.classList.contains("hidden") || toggle(),
231-
},
232-
});
222+
const defineCollapsibleAccessors = ($el, { includeTitle = true } = {}) => {
223+
const properties = {
224+
collapsed: { get: () => $wrapper.classList.contains("hidden") },
225+
expanded: { get: () => !$wrapper.classList.contains("hidden") },
226+
unclasped: { get: () => !$wrapper.classList.contains("hidden") }, // Legacy compatibility
227+
$ul: { get: () => $content },
228+
fileTree: { get: () => childTree },
229+
refresh: {
230+
value: () => childTree?.refresh(),
231+
},
232+
expand: {
233+
value: () => !$wrapper.classList.contains("hidden") || toggle(),
234+
},
235+
collapse: {
236+
value: () => $wrapper.classList.contains("hidden") || toggle(),
237+
},
238+
};
239+
240+
if (includeTitle) {
241+
properties.$title = { get: () => $title };
242+
}
243+
244+
Object.defineProperties($el, properties);
245+
};
246+
247+
// Keep nested folders compatible with the legacy collapsableList API.
248+
defineCollapsibleAccessors($wrapper);
249+
defineCollapsibleAccessors($title, { includeTitle: false });
233250

234251
return $wrapper;
235252
}
@@ -281,11 +298,7 @@ export default class FileTree {
281298
* Clear all rendered content
282299
*/
283300
clear() {
284-
// Destroy all child trees
285-
for (const childTree of this.childTrees.values()) {
286-
childTree.destroy();
287-
}
288-
this.childTrees.clear();
301+
this.destroyChildTrees();
289302

290303
if (this.virtualList) {
291304
this.virtualList.destroy();
@@ -322,6 +335,16 @@ export default class FileTree {
322335
}
323336
}
324337

338+
/**
339+
* Destroy all expanded child trees and clear their references.
340+
*/
341+
destroyChildTrees() {
342+
for (const childTree of this.childTrees.values()) {
343+
childTree.destroy();
344+
}
345+
this.childTrees.clear();
346+
}
347+
325348
/**
326349
* Append a new entry to the tree
327350
* @param {string} name
@@ -356,6 +379,7 @@ export default class FileTree {
356379
this.virtualList.setItems(this.entries);
357380
} else {
358381
// Fragment mode: re-render
382+
this.destroyChildTrees();
359383
this.container.innerHTML = "";
360384
this.renderWithFragment();
361385
}

src/lib/openFolder.js

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -702,20 +702,14 @@ function execOperation(type, action, url, $target, name) {
702702
if (!newUrl.created) return;
703703

704704
if (isNestedPath) {
705-
openFolder.find(url)?.reload();
705+
await refreshOpenFolder(url);
706706
await FileList.refresh();
707707
toast(strings.success);
708708
return;
709709
}
710710

711711
newName = Url.basename(newUrl.uri);
712-
if ($target.unclasped) {
713-
if (newUrl.type === "file") {
714-
appendTile($target, createFileTile(newName, newUrl.uri));
715-
} else if (newUrl.type === "folder") {
716-
appendList($target, createFolderTile(newName, newUrl.uri));
717-
}
718-
}
712+
appendEntryToOpenFolder(url, newUrl.uri, newUrl.type);
719713

720714
FileList.append(url, newUrl.uri);
721715
toast(strings.success);
@@ -995,6 +989,72 @@ function appendList($target, $list) {
995989
else $target.append($list);
996990
}
997991

992+
/**
993+
* Get the active file tree for a folder element, if it has been loaded.
994+
* @param {HTMLElement} $el
995+
* @returns {FileTree|null}
996+
*/
997+
function getLoadedFileTree($el) {
998+
return (
999+
$el?.$ul?._fileTree || $el?.fileTree || $el?.nextElementSibling?._fileTree
1000+
);
1001+
}
1002+
1003+
/**
1004+
* Update matching expanded folder views with a new entry.
1005+
* @param {string} parentUrl
1006+
* @param {string} entryUrl
1007+
* @param {"file"|"folder"} type
1008+
*/
1009+
function appendEntryToOpenFolder(parentUrl, entryUrl, type) {
1010+
const filesApp = sidebarApps.get("files");
1011+
const $els = filesApp.getAll(`[data-url="${parentUrl}"]`);
1012+
const isDirectory = type === "folder";
1013+
const name = Url.basename(entryUrl);
1014+
1015+
Array.from($els).forEach(($el) => {
1016+
if (!(helpers.isDir($el.dataset.type) || $el.dataset.type === "root")) {
1017+
return;
1018+
}
1019+
1020+
if (!$el.unclasped) return;
1021+
1022+
const fileTree = getLoadedFileTree($el);
1023+
if (fileTree) {
1024+
fileTree.appendEntry(name, entryUrl, isDirectory);
1025+
return;
1026+
}
1027+
1028+
if (isDirectory) {
1029+
appendList($el, createFolderTile(name, entryUrl));
1030+
} else {
1031+
appendTile($el, createFileTile(name, entryUrl));
1032+
}
1033+
});
1034+
}
1035+
1036+
/**
1037+
* Refresh matching expanded folder views.
1038+
* @param {string} folderUrl
1039+
*/
1040+
async function refreshOpenFolder(folderUrl) {
1041+
const filesApp = sidebarApps.get("files");
1042+
const $els = filesApp.getAll(`[data-url="${folderUrl}"]`);
1043+
1044+
await Promise.all(
1045+
Array.from($els).map(async ($el) => {
1046+
if (!(helpers.isDir($el.dataset.type) || $el.dataset.type === "root")) {
1047+
return;
1048+
}
1049+
1050+
const fileTree = getLoadedFileTree($el);
1051+
if (fileTree) {
1052+
await fileTree.refresh();
1053+
}
1054+
}),
1055+
);
1056+
}
1057+
9981058
/**
9991059
* Create a folder tile
10001060
* @param {string} name
@@ -1039,18 +1099,7 @@ function createFileTile(name, url) {
10391099
openFolder.add = async (url, type) => {
10401100
const { url: parent } = await fsOperation(Url.dirname(url)).stat();
10411101
FileList.append(parent, url);
1042-
1043-
const filesApp = sidebarApps.get("files");
1044-
const $els = filesApp.getAll(`[data-url="${parent}"]`);
1045-
Array.from($els).forEach(($el) => {
1046-
if ($el.dataset.type !== "dir") return;
1047-
1048-
if (type === "file") {
1049-
appendTile($el, createFileTile(Url.basename(url), url));
1050-
} else {
1051-
appendList($el, createFolderTile(Url.basename(url), url));
1052-
}
1053-
});
1102+
appendEntryToOpenFolder(parent, url, type);
10541103
};
10551104

10561105
openFolder.renameItem = (oldFile, newFile, newFilename) => {

0 commit comments

Comments
 (0)