fix: delete a finished export's file from the browser when its account signs out

This commit is contained in:
Linus Rath
2026-11-01 05:42:00 +01:00
parent 58f66c4fdc
commit d8f6be7b1e
2 changed files with 26 additions and 2 deletions
@@ -83,6 +83,27 @@ describe('MailExportDialog', () => {
expect(useMailTransferStore.getState().request).toBeNull();
});
it('signing out stops a running export, or frees a finished one\'s file', async () => {
const { runBeforeSignOut } = await import('@/lib/sign-out-cleanup');
const controller = new AbortController();
render(<MailTransferHost />);
act(() => {
openMailExport({ mailboxId: 'inbox' });
useMailTransferStore.getState().startJob({ kind: 'export', controller, progress: { phase: 'running', exported: 1, failed: 0, failures: [], total: 4, fraction: 0.25 } });
});
await act(() => runBeforeSignOut('acc1'));
expect(controller.signal.aborted).toBe(true);
const discard = vi.fn(async () => {});
act(() => {
useMailTransferStore.setState({ request: null, visible: false, job: null });
openMailExport({ mailboxId: 'inbox' });
useMailTransferStore.getState().startJob({ kind: 'export', controller: new AbortController(), progress: { phase: 'done', exported: 3, failed: 0, failures: [], total: 3, fraction: 1, file: { name: 'a.mbox', size: 1, discard } } });
});
await act(() => runBeforeSignOut('acc1'));
expect(discard).toHaveBeenCalled();
});
it('runs on in the background and the chip names the export', () => {
const controller = new AbortController();
render(<MailTransferHost />);
@@ -20,9 +20,12 @@ export function MailTransferHost() {
const visible = useMailTransferStore((s) => s.visible);
const job = useMailTransferStore((s) => s.job);
// An account that signs out takes its running job and its checkpoints with it.
// An account that signs out takes its running job, a finished export's
// file in the browser's private storage, and its checkpoints with it.
useEffect(() => onBeforeSignOut(async (accountId) => {
useMailTransferStore.getState().cancelJob();
const store = useMailTransferStore.getState();
if (isJobActive(store.job)) store.cancelJob();
else store.close();
if (accountId !== "all") await clearCheckpointsFor(accountId);
}), []);