diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/files-app.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/files-app.js | 50 |
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); |