aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-16 01:06:03 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-16 01:06:03 +0200
commitd5933448cf7c8186afbcf4d52fccd16d1b827a5c (patch)
tree8dcda6dedf7654f316b968bcc33552b8b05e0dcc /packages/meshbay-hub/src/meshbay_hub
parent1f84c047914cf21df1a1de196d990d193539523d (diff)
downloadmeshbay-d5933448cf7c8186afbcf4d52fccd16d1b827a5c.tar.gz
fix(hub): the Files listing sorts its folders by the chosen column too
Folders were always ordered by name, whatever the column and direction, so reversing the name sort or sorting by size moved only the files. A folder's size is what it holds and its date its newest file; ties go to the name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/files-app.js50
1 files changed, 39 insertions, 11 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js
index 518c917..7629459 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js
@@ -112,6 +112,28 @@ async function walkEntries(roots) {
return out;
}
+// One ordering for the whole listing. Folders stay above files and are ordered
+// among themselves by the same column: a folder's size is everything under it,
+// its date the newest file in it. It has no type, so sorted by type folders
+// fall back to their names. Rows are `{ name, size, date, type }`; a tie goes
+// to the name, ascending, so equal sizes do not shuffle between renders.
+//
+// Folders used to be `[...dirs].sort()` whatever the column — so reversing the
+// name sort or sorting by size moved the files and left every folder where it
+// was, which in a folder of folders is nothing moving at all.
+function sortRows(rows, key, asc) {
+ const byName = (a, b) => (a.name || '').localeCompare(b.name || '');
+ return [...rows].sort((a, b) => {
+ let cmp;
+ if (key === 'size') cmp = (a.size || 0) - (b.size || 0);
+ else if (key === 'date') cmp = (a.date || 0) - (b.date || 0);
+ else if (key === 'type') cmp = (a.type || '').localeCompare(b.type || '');
+ else cmp = byName(a, b);
+ if (!asc) cmp = -cmp;
+ return cmp || byName(a, b);
+ });
+}
+
function FilesPanel({
groupId, transportRef, gekRef, status,
entries, nodeDirs, nodeRoots, setEntries, setNodeDirs, setNodeRoots, applyIndex,
@@ -364,14 +386,9 @@ function FilesPanel({
return false;
});
- const sorted = [...filteredEntries].sort((a, b) => {
- let cmp = 0;
- if (sortKey === 'name') cmp = (a.name || '').localeCompare(b.name || '');
- else if (sortKey === 'size') cmp = a.size - b.size;
- else if (sortKey === 'type') cmp = (a.type || '').localeCompare(b.type || '');
- else if (sortKey === 'date') cmp = a.added_at - b.added_at;
- return sortAsc ? cmp : -cmp;
- });
+ const sorted = sortRows(
+ filteredEntries.map((e) => ({ name: e.name, size: e.size, date: e.added_at, type: e.type, entry: e })),
+ sortKey, sortAsc).map((r) => r.entry);
// The node's own listing, so an empty folder is visible, plus anything implied
// by a file path in case the two ever disagree. Skipped while searching — the
@@ -383,7 +400,19 @@ function FilesPanel({
if (!rest.includes('/')) dirs.add(rest);
}
}
- const subdirs = [...dirs].sort();
+ // What each folder holds, once — its size and date are sort keys now, not
+ // only the figure in its row.
+ const dirInfo = new Map([...dirs].map((d) => {
+ const inside = entriesUnder(entries, currentPath ? currentPath + '/' + d : d);
+ return [d, {
+ inside,
+ bytes: inside.reduce((n, f) => n + (f.entry.size || 0), 0),
+ newest: inside.reduce((n, f) => Math.max(n, f.entry.added_at || 0), 0),
+ }];
+ }));
+ const subdirs = sortRows(
+ [...dirs].map((d) => ({ name: d, size: dirInfo.get(d).bytes, date: dirInfo.get(d).newest })),
+ sortKey, sortAsc).map((r) => r.name);
// At the top of a group the folders on screen ARE the roots, so their state
// belongs there. Deeper in, everything shown lives inside one readable root
@@ -744,8 +773,7 @@ function FilesPanel({
`}
${subdirs.map(d => {
const full = currentPath ? currentPath + '/' + d : d;
- const inside = entriesUnder(entries, full);
- const bytes = inside.reduce((n, f) => n + (f.entry.size || 0), 0);
+ const { inside, bytes } = dirInfo.get(d);
const rs = rootState.get(d);
const isEjected = rs && rs.ejected;
const isUnavail = unavailableHere.includes(d);