fix: don't break folder lazy-load and file navigation

Two regressions stacked from the recent tree work:

1. expandAllFolders (#496) was marking every folder open, including
   folders whose children weren't fetched yet. The directive then
   rendered an empty <ul> after each <a>, and the openFolder handler's
   "no sibling means we need to load" check silently treated the empty
   <ul> as already-loaded — so clicking the folder toggled the class
   but the children never appeared.

   Skip folders with empty children when pre-expanding, and harden the
   click handler so an empty <ul> still triggers a fetch.

2. The $routeUpdate handler (#510 follow-up) became async and called
   $scope.$apply(updateContent) at the end. Inside an already-running
   digest cycle this no-ops or throws, leaving file navigation stuck.

   Run updateContent() synchronously like before, and kick off any
   missing parent-directory fetches in the background — getContent()
   already falls back to sha "0" when the metadata isn't loaded yet.
This commit is contained in:
tdurieux
2026-05-04 11:25:32 +02:00
parent 652299cda6
commit 03826fe58b
2 changed files with 28 additions and 19 deletions
+27 -18
View File
@@ -509,14 +509,17 @@ angular
}); });
} }
// #496 — expand every folder on first load so reviewers see the // #496 — expand folders whose children are already loaded so
// whole tree without clicking through. Folders the user has // reviewers see the whole tree without clicking through. Skip
// already toggled (state recorded in $scope.opens) are left // folders with empty children to avoid emitting an empty <ul>
// alone, so collapsing a folder still works. // that breaks the click-time lazy-load (#496-followup): the
// openFolder handler used to detect "needs to load" by looking
// at the absence of a sibling node, but a pre-expanded empty
// <ul> is a non-null sibling and silently suppressed the fetch.
function expandAllFolders(nodes, parentPath) { function expandAllFolders(nodes, parentPath) {
if (!nodes) return; if (!nodes) return;
for (const f of nodes) { for (const f of nodes) {
if (!f.child) continue; if (!f.child || f.child.length === 0) continue;
const path = `${parentPath}/${f.name}`; const path = `${parentPath}/${f.name}`;
if (!(path in $scope.opens)) { if (!(path in $scope.opens)) {
$scope.opens[path] = true; $scope.opens[path] = true;
@@ -544,7 +547,14 @@ angular
$scope.openFolder = async function (folder, event) { $scope.openFolder = async function (folder, event) {
$scope.opens[folder] = !$scope.opens[folder]; $scope.opens[folder] = !$scope.opens[folder];
if (event.srcElement.nextSibling == null) { const sib = event.srcElement.nextSibling;
// Lazy-load when there's no sibling (folder never expanded) or
// when the sibling is an empty <ul> from a pre-expanded folder
// whose children weren't fetched yet (#496-followup).
const needsLoad =
sib == null ||
(sib.tagName === "UL" && sib.children.length === 0);
if (needsLoad) {
await $scope.$parent.getFiles(folder.substring(1)); await $scope.$parent.getFiles(folder.substring(1));
$scope.$apply(); $scope.$apply();
} }
@@ -1696,7 +1706,7 @@ angular
"f4b", "f4b",
]; ];
$scope.$on("$routeUpdate", async function (event, current) { $scope.$on("$routeUpdate", function (event, current) {
if (($routeParams.path || "") == $scope.filePath) { if (($routeParams.path || "") == $scope.filePath) {
return; return;
} }
@@ -1709,21 +1719,20 @@ angular
return init(); return init();
} }
// #510 — when the user clicks a markdown link into a subdirectory, updateContent();
// the file listing for that directory may not be loaded yet, so
// getSelectedFile() returns undefined and updateContent() fires a // #510 — if we navigated into a subdirectory whose file listing
// request without the right sha. Walk the new path and load any // hasn't been fetched, lazy-load the parent directories in the
// listings we don't have before rendering. // background so getSelectedFile() can populate $scope.file with the
for (let i = 0; i < $scope.paths.length; i++) { // right sha for the next interaction. Done after updateContent so
// the request fires immediately (getContent falls back to sha "0").
for (let i = 0; i < $scope.paths.length - 1; i++) {
const dirPath = i > 0 ? $scope.paths.slice(0, i).join("/") : ""; const dirPath = i > 0 ? $scope.paths.slice(0, i).join("/") : "";
const alreadyLoaded = $scope.files.some( const alreadyLoaded = $scope.files.some((f) => f.path === dirPath);
(f) => f.path === dirPath
);
if (!alreadyLoaded) { if (!alreadyLoaded) {
await $scope.getFiles(dirPath); $scope.getFiles(dirPath);
} }
} }
$scope.$apply(updateContent);
}); });
function selectFile() { function selectFile() {
+1 -1
View File
File diff suppressed because one or more lines are too long