import { useEffect, useState } from "react"; import { ChevronRight, File as FileIcon, Folder, HardDrive, Users } from "lucide-react"; import { Dialog } from "@/ui/dialog"; import { Spinner } from "@/ui/misc"; import { useFiles } from "@/store/files"; import type { AttachableFile } from "@/store/compose"; import type { FileNode } from "@/jmap/types"; import { formatSize } from "@/lib/format"; import { t } from "@/lib/i18n"; /** * Pick something already in Files to attach. * * Browsing is the store's, so this shows the same folders the Files view does, * shared accounts included -- a file somebody shared with you is a file you can * send on, and having to download it first only to upload it again would be * the sort of detour the rest of this avoids. * * It borrows the Files store rather than keeping its own copy, which means * opening the picker moves where Files is browsing. Closing it puts that back: * a detour through somebody's shared folder to find an attachment should not * leave the file manager somewhere else afterwards. */ export function FilePicker({ onPick, onClose }: { onPick: (files: AttachableFile[]) => void; onClose: () => void }) { const files = useFiles(); const [cur, setCur] = useState(null); const [picked, setPicked] = useState>({}); const [returnTo] = useState(() => files.accountId); // Shared accounts are not looked for at sign-in; the picker lists them, so it asks. useEffect(() => { void useFiles.getState().discoverShared(); }, []); useEffect(() => { void files.loadChildren(cur); // eslint-disable-next-line react-hooks/exhaustive-deps }, [cur, files.accountId]); const close = () => { if (files.accountId !== returnTo) files.openAccount(returnTo); onClose(); }; const openAccount = (accountId: string | null) => { files.openAccount(accountId); setCur(null); setPicked({}); }; const nodes = (files.children[cur ?? "root"] ?? []).map((id) => files.nodes[id]).filter((n): n is FileNode => Boolean(n)); const path = files.pathTo(cur); const chosen = Object.values(picked); const viewingShare = files.accountId !== files.ownAccountId; return ( } > {files.sharedAccounts.length > 0 && (
{files.sharedAccounts.map((a) => ( ))}
)}
{path.map((n) => ( ))}
{files.loading && !nodes.length ? ( ) : !nodes.length ? (

{t("This folder is empty.")}

) : ( nodes.map((n) => n.nodeType === "directory" ? ( ) : ( ), ) )} {viewingShare && chosen.length > 0 && ( // Blobs belong to the account holding them, so one from a share has to // be copied into yours before a draft can reference it. Worth saying, // because it is the difference between instant and a wait.

{t("Shared files are copied to your account when attached.")}

)}
); }