diff --git a/frontend/src/lib/components/MoveDialog.svelte b/frontend/src/lib/components/MoveDialog.svelte index 3b7fa193..d9bfe240 100644 --- a/frontend/src/lib/components/MoveDialog.svelte +++ b/frontend/src/lib/components/MoveDialog.svelte @@ -3,13 +3,28 @@ import { listFolder, moveFolder } from '$lib/api/endpoints/folders'; import { moveFile } from '$lib/api/endpoints/files'; import { copyFiles, copyFolders } from '$lib/api/endpoints/batch'; - import type { FolderItem } from '$lib/api/types'; + import type { Drive, DriveRole, FolderItem } from '$lib/api/types'; import Icon from '$lib/icons/Icon.svelte'; import Modal from '$lib/components/Modal.svelte'; import { t } from '$lib/i18n/index.svelte'; - import { session } from '$lib/stores/session.svelte'; + import { drives as drivesStore, driveIcon } from '$lib/stores/drives.svelte'; import { ui } from '$lib/stores/ui.svelte'; + // A drive accepts new items only if the caller can Create on its root. + // Owner / Editor / Contributor cover that; Commenter + Viewer cannot. + const WRITABLE_ROLES: readonly DriveRole[] = ['owner', 'editor', 'contributor'] as const; + function isWritable(d: Drive): boolean { + return d.caller_role != null && WRITABLE_ROLES.includes(d.caller_role); + } + + // Default-personal first, then secondary personals, then shared; within + // a group, alphabetical. Mirrors DrivePicker so the sidebar and this + // dialog rank drives identically. + function driveRank(d: Drive): number { + if (d.default_for_user) return 0; + return d.kind === 'personal' ? 1 : 2; + } + interface Target { id: string; name: string; @@ -34,9 +49,21 @@ let crumbs = $state>([]); let folders = $state([]); let currentId = $state(null); + let selectedDriveId = $state(null); let loading = $state(false); let working = $state(false); + const writableDrives = $derived( + [...drivesStore.drives].filter(isWritable).sort((a, b) => { + const r = driveRank(a) - driveRank(b); + return r !== 0 ? r : a.name.localeCompare(b.name); + }) + ); + + // The chip strip only earns its vertical space when there's a real + // choice. One writable drive → identical to the single-drive UI. + const showDriveSwitcher = $derived(writableDrives.length > 1); + async function loadInto(id: string) { loading = true; try { @@ -50,10 +77,23 @@ } async function init() { - const home = await session.loadHomeFolder(); - if (!home) return; - crumbs = [{ id: home, name: session.homeFolderName ?? t('nav.files', 'Files') }]; - await loadInto(home); + await drivesStore.load(); + const home = drivesStore.findDefault(); + // Prefer the user's home drive when it's writable (covers the + // common case: moving stuff around inside Personal). Otherwise + // fall back to the first writable drive, sorted as above. + const start = home && isWritable(home) ? home : writableDrives[0]; + if (!start) return; + selectedDriveId = start.id; + crumbs = [{ id: start.root_folder_id, name: start.name }]; + await loadInto(start.root_folder_id); + } + + async function switchDrive(d: Drive) { + if (d.id === selectedDriveId) return; + selectedDriveId = d.id; + crumbs = [{ id: d.root_folder_id, name: d.name }]; + await loadInto(d.root_folder_id); } function enter(f: FolderItem) { @@ -124,6 +164,30 @@
+ {#if showDriveSwitcher} +
+ {#each writableDrives as d (d.id)} + + {/each} +
+ {/if} +