Files
webmail/lib/compose/draft-close-toast.ts
T
Linus Rath d98132b8e1 fix: close every composer the same way, keeping the draft with Draft saved and Discard
X, Escape, clicking another message and Escape in a pop-out window
now all save the draft and close, with a "Draft saved" toast that has
Discard (with Undo). The Save or discard question shows only when that
save fails. A Reply or Forward closed before the user changed anything
leaves no draft. Undo send being on by default no longer asks "Send
without a delay?" on servers without delayed sending; only a delay the
user picked does.

Changed test assertions: the close-guard, priority, account-switch,
threading, sign-out, mention, draft-attachments, discard-uploads and
save-pending-attachments tests reached saving or discarding through the
Save or discard dialog on close, which now only appears after a failed
save. They now close directly, or use the Discard button, or check the
failed-save question. The send-delay test marks its delay as chosen.
2026-11-04 08:22:56 +01:00

57 lines
1.9 KiB
TypeScript

import { toast, useToastStore } from "@/stores/toast-store";
import { UNDO_TOAST_DURATION } from "@/lib/email-action-toast";
export interface DiscardToastLabels {
discarded: string;
undo: string;
}
export interface DraftSavedToastLabels extends DiscardToastLabels {
saved: string;
discard: string;
}
/**
* "Draft discarded" with Undo. `discard` runs once the toast is gone without
* an Undo (or the page goes away), so Undo needs nothing from the server:
* the draft is still there. `restore` puts back what the caller took off
* screen.
*/
export function discardWithUndo(discard: () => void, labels: DiscardToastLabels, restore?: () => void): void {
let settled = false;
let timer: ReturnType<typeof setTimeout> | null = null;
const commit = () => {
if (settled) return;
settled = true;
if (timer) clearTimeout(timer);
window.removeEventListener("pagehide", commit);
discard();
};
const id = toast.success(labels.discarded, {
action: {
label: labels.undo,
onClick: () => {
if (settled) return;
settled = true;
if (timer) clearTimeout(timer);
window.removeEventListener("pagehide", commit);
restore?.();
},
},
duration: UNDO_TOAST_DURATION,
// The toast and the delete end together: a pause on hover would leave
// an Undo on screen for a draft that is already gone.
pauseOnHover: false,
});
const shown = useToastStore.getState().toasts.find((t) => t.id === id)?.duration;
timer = setTimeout(commit, shown && shown > 0 ? shown : UNDO_TOAST_DURATION);
window.addEventListener("pagehide", commit);
}
/** After a composer closed and kept its draft: "Draft saved", with Discard. */
export function showDraftSavedToast(discard: () => void, labels: DraftSavedToastLabels): void {
toast.success(labels.saved, {
action: { label: labels.discard, onClick: () => discardWithUndo(discard, labels) },
});
}