From c60cc702f7426ab1330b6772a1c1664cd28f2dd3 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 28 Oct 2026 10:45:12 +0100 Subject: [PATCH] fix: open Save to Files with My files selected so Save here works right away --- .../email/__tests__/save-to-files-dialog.test.tsx | 14 ++++++++++++++ components/email/save-to-files-dialog.tsx | 2 ++ components/files/folder-picker-dialog.tsx | 8 ++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/components/email/__tests__/save-to-files-dialog.test.tsx b/components/email/__tests__/save-to-files-dialog.test.tsx index 87b3ea588..ab2461046 100644 --- a/components/email/__tests__/save-to-files-dialog.test.tsx +++ b/components/email/__tests__/save-to-files-dialog.test.tsx @@ -48,4 +48,18 @@ describe('SaveToFilesDialog', () => { await waitFor(() => expect(createFileNode).toHaveBeenCalledWith('invoice.pdf', 'blob-mine', 'application/pdf', 1234, 'docs')); expect(copyBlobs).toHaveBeenCalledWith('team', ['blob-1'], 'me'); }); + + // "Save here" was greyed out until My files was clicked, with nothing on + // screen saying why. + it('starts with My files selected, so Save here saves there right away', async () => { + const createFileNode = vi.fn().mockResolvedValue({}); + const client = { listAllFileNodes: vi.fn().mockResolvedValue(nodes), createFileNode } as unknown as IJMAPClient; + render(); + + await screen.findByText('Documents'); + const saveHere = screen.getByRole('button', { name: 'save_here' }) as HTMLButtonElement; + expect(saveHere.disabled).toBe(false); + fireEvent.click(saveHere); + await waitFor(() => expect(createFileNode).toHaveBeenCalledWith('invoice.pdf', 'blob-1', 'application/pdf', 1234, null)); + }); }); diff --git a/components/email/save-to-files-dialog.tsx b/components/email/save-to-files-dialog.tsx index 907075174..6c3517698 100644 --- a/components/email/save-to-files-dialog.tsx +++ b/components/email/save-to-files-dialog.tsx @@ -101,6 +101,8 @@ export function SaveToFilesDialog({ attachment, client, blobAccountId, onClose } confirmText={t("save_here")} folders={folders} currentId={NO_CURRENT_FOLDER} + // My files, ready to take it: Save here works without a pick first. + initialSelectedId={null} onPick={(parentId) => void save(parentId)} /> ); diff --git a/components/files/folder-picker-dialog.tsx b/components/files/folder-picker-dialog.tsx index 908daaf47..017cbb14f 100644 --- a/components/files/folder-picker-dialog.tsx +++ b/components/files/folder-picker-dialog.tsx @@ -26,6 +26,8 @@ interface FolderPickerDialogProps { excludeIds?: Set; /** The folder that is pre-selected and can't be picked (where the items already are). */ currentId?: string | null; + /** A folder selected and ready to pick when the dialog opens; null is My files. */ + initialSelectedId?: string | null; } const ROOT = ""; @@ -35,10 +37,12 @@ const ROOT = ""; * tree with My files at the top, browsed with the mouse or the arrow keys. */ export function FolderPickerDialog({ - isOpen, onClose, onPick, title, confirmText, folders, excludeIds, currentId = null, + isOpen, onClose, onPick, title, confirmText, folders, excludeIds, currentId = null, initialSelectedId, }: FolderPickerDialogProps) { const t = useTranslations("files"); - const [selected, setSelected] = useState(null); + const [selected, setSelected] = useState( + initialSelectedId === undefined ? null : (initialSelectedId ?? ROOT), + ); const [expanded, setExpanded] = useState>(() => new Set([ROOT])); const listRef = useRef(null);