Every navigation rebuilt the breadcrumb with one `GET /api/folders/{id}` per path
segment (a depth-D folder = D requests, each no-store) purely to label the trail.
Add an id→name cache, populated wherever a name is already known:
- every listing names its children, so `cacheFolder` records them, and
- `getFolder` records the folder it fetched.
`buildCrumbs` now reads names from the cache and only fetches the ids it hasn't
seen. During normal step-by-step navigation each ancestor was named by its
parent's listing, so the breadcrumb resolves with ZERO extra requests; only a
cold deep-link fetches its unknown ancestors (still in parallel). Folder renames
update the cache immediately so the trail stays correct.
The cache is a small LRU (cap 1000 — names are tiny) and is independent of the
listing cache (names survive a listing invalidation).
Validated: 3 new unit tests (listing populates child names, getFolder records,
rename overwrites) → 46 frontend tests green; npm run check; headless render of
the real files route (list + grid) — no errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8Vb9QHmLZnEMzHz7MrFy6
Every folder navigation re-downloaded the full listing: `listFolder` cache-busted
with `?t=<ts>` + `Cache-Control: no-store`, so back/forward and re-entering a
folder always paid a full round-trip + payload.
Now the files browser caches listings in memory and serves SWR:
- On navigation it paints a previously-visited folder instantly from cache, then
revalidates with `If-None-Match` (the backend ETag covers folders + files +
favorite/share badges, so it's a faithful validator). Unchanged → 304 with an
empty body; changed → 200 refreshes cache + UI.
- A generation token guards against a slow in-flight response clobbering a newer
navigation; breadcrumbs now resolve independently so they never block the grid
paint.
- Mutations (create/upload/rename/move/copy/delete, incl. the move dialog) go
through `reload()`, which drops the cache and refetches fresh — no stale view
after an action.
API layer (`folders.ts`):
- `fetchFolderListing(id, { etag?, forceRefresh? })` does the conditional fetch
(200 → parsed listing + ETag, 304 → empty); `listFolder` stays as a
non-conditional wrapper for the move-dialog tree.
- A small LRU (cap 40) cache with `getCachedFolder` / `cacheFolder` /
`invalidateFolderCache`. `cache: 'no-store'` keeps the browser HTTP cache out
of the way; revalidation is driven entirely by our own ETag.
Net: instant back/forward navigation, and an unchanged folder revalidates with a
0-byte 304 instead of re-downloading the whole listing. Validated: 7 new unit
tests (conditional If-None-Match + 304, LRU eviction/recency, invalidation),
npm run check, and a headless render of the real files route (list + grid).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8Vb9QHmLZnEMzHz7MrFy6