feat(breadcrumb): build breadcrumb in 1 API call

add /api/folders/{id}/ancestors

    this API to iterate parent up to the drive root or the shared folder
    this will help UI to build the breadcrumb in 1 API call
    and to identify the root element (is it a drive users has access to or
    a shared folder ?)

    ui: now only 1 API call is now required to build the breadcrumb
This commit is contained in:
Edouard Vanbelle
2026-07-26 21:31:04 +02:00
parent 0efbf0ff85
commit 3b31b8911b
33 changed files with 1342 additions and 221 deletions
+30 -1
View File
@@ -1,7 +1,7 @@
/** Folder endpoints — ported from filesModel.js + fileOperations.js. */
import { apiFetch, apiJson } from '$lib/api/client';
import { getCsrfHeaders } from '$lib/api/csrf';
import type { FileItem, FolderItem, ItemType } from '$lib/api/types';
import type { FileItem, FolderAncestorsResponse, FolderItem, ItemType } from '$lib/api/types';
const JSON_HEADERS = { 'Content-Type': 'application/json' };
const NO_CACHE: RequestInit = {
@@ -109,6 +109,35 @@ export function getFolder(id: string): Promise<FolderItem> {
return request;
}
// ── Ancestor chain (breadcrumb) ──────────────────────────────────────────
// Backing store + inflight dedup for `GET /api/folders/{id}/ancestors` —
// mirrors the folderInflight pattern for `getFolder`. Rapid navigation
// (files → sub → sub-sub in <1s) folds concurrent requests for the same
// leaf into one round-trip. Response also seeds `folderNames` for every
// ancestor, so subsequent `getFolderName(id)` lookups are cache-free.
const ancestorsInflight = new Map<string, Promise<FolderAncestorsResponse>>();
export function getFolderAncestors(id: string): Promise<FolderAncestorsResponse> {
const inflight = ancestorsInflight.get(id);
if (inflight) return inflight;
const request = (async () => {
try {
const chain = await apiJson<FolderAncestorsResponse>(
`/api/folders/${id}/ancestors`,
NO_CACHE
);
// Prime the shared folder-name cache — the breadcrumb walk
// happens to be the exact input that populates it.
for (const a of chain.ancestors) rememberFolderName(a.id, a.name);
return chain;
} finally {
ancestorsInflight.delete(id);
}
})();
ancestorsInflight.set(id, request);
return request;
}
/** One page of `/api/folders/{id}/resources`. */
export interface FolderPage {
/**