feat(drive): add drive picker in sidebar
- select by default the home drive
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Drives store — caches `GET /api/drives` so the picker, the breadcrumb
|
||||
* icon, and the session bootstrap all share one fetch. Idempotent `load()`.
|
||||
*
|
||||
* Identifying the user's home: always via `default_for_user`, never by
|
||||
* folder name (users can rename "Personal").
|
||||
*/
|
||||
import { listDrives } from '$lib/api/endpoints/drives';
|
||||
import type { Drive } from '$lib/api/types';
|
||||
|
||||
class DrivesStore {
|
||||
drives = $state<Drive[]>([]);
|
||||
loaded = $state(false);
|
||||
private inflight: Promise<Drive[]> | null = null;
|
||||
|
||||
async load(): Promise<Drive[]> {
|
||||
if (this.loaded) return this.drives;
|
||||
if (this.inflight) return this.inflight;
|
||||
this.inflight = (async () => {
|
||||
try {
|
||||
this.drives = await listDrives();
|
||||
} catch {
|
||||
this.drives = [];
|
||||
} finally {
|
||||
this.loaded = true;
|
||||
this.inflight = null;
|
||||
}
|
||||
return this.drives;
|
||||
})();
|
||||
return this.inflight;
|
||||
}
|
||||
|
||||
/** Force a refresh after a mutation (rename, member change, …). */
|
||||
invalidate(): void {
|
||||
this.loaded = false;
|
||||
this.drives = [];
|
||||
}
|
||||
|
||||
/** Caller's default-personal drive (one per internal user), or null. */
|
||||
findDefault(): Drive | null {
|
||||
return this.drives.find((d) => d.default_for_user != null) ?? null;
|
||||
}
|
||||
|
||||
/** Drive whose root folder UUID matches `id`, or null. */
|
||||
findByRootFolderId(id: string | null | undefined): Drive | null {
|
||||
if (!id) return null;
|
||||
return this.drives.find((d) => d.root_folder_id === id) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
export const drives = new DrivesStore();
|
||||
|
||||
/**
|
||||
* Picker / breadcrumb icon for a drive:
|
||||
* home — default-personal (the user's home)
|
||||
* folder — secondary personal drive
|
||||
* users — shared / team drive
|
||||
*/
|
||||
export function driveIcon(d: Drive): string {
|
||||
if (d.default_for_user) return 'home';
|
||||
return d.kind === 'shared' ? 'users' : 'folder';
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* folder and land on the shared-with-me view.
|
||||
*/
|
||||
import { fetchMe, tryRefresh } from '$lib/api/endpoints/auth';
|
||||
import { listRootFolders } from '$lib/api/endpoints/folders';
|
||||
import { drives } from '$lib/stores/drives.svelte';
|
||||
import type { User } from '$lib/api/types';
|
||||
|
||||
class SessionStore {
|
||||
@@ -41,20 +41,21 @@ class SessionStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the home folder (first entry of GET /api/folders). Externals
|
||||
* (grant-only) have no home folder, so this is skipped for them.
|
||||
* Resolve the caller's default personal drive's root folder — the landing
|
||||
* point for `/files` and the `/` redirect. Externals (grant-only) have no
|
||||
* personal drive, so this is skipped for them.
|
||||
*
|
||||
* Identifies the default via `default_for_user`, not folder name: users
|
||||
* can rename "Personal" without breaking this lookup.
|
||||
*/
|
||||
async loadHomeFolder(): Promise<string | null> {
|
||||
if (this.homeFolderId) return this.homeFolderId;
|
||||
if (this.isExternalUser) return null;
|
||||
try {
|
||||
const folders = await listRootFolders();
|
||||
if (folders.length > 0) {
|
||||
this.homeFolderId = folders[0].id;
|
||||
this.homeFolderName = folders[0].name;
|
||||
}
|
||||
} catch {
|
||||
/* leave null — caller handles */
|
||||
await drives.load();
|
||||
const def = drives.findDefault();
|
||||
if (def) {
|
||||
this.homeFolderId = def.root_folder_id;
|
||||
this.homeFolderName = def.name;
|
||||
}
|
||||
return this.homeFolderId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user