mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
90 lines
2.8 KiB
TypeScript
90 lines
2.8 KiB
TypeScript
// onAfterEmailSend: told once the server accepted a message.
|
|
//
|
|
// - A plain send is reported straight away, scheduledFor null.
|
|
// - A send the user scheduled for later is reported straight away too, with
|
|
// scheduledFor: the server accepted it, even though it goes out later.
|
|
// - A send held back for undo ("Sending in 10 s") is reported only when the
|
|
// hold runs out or the user picks "Send now". Undo drops it: that message
|
|
// never leaves.
|
|
// A failed send never gets here; the callers only report a resolved send.
|
|
|
|
import type { SendEmailResult } from './jmap/types';
|
|
import { emailHooks } from './plugin-hooks';
|
|
|
|
export interface SentMessage {
|
|
to: string[];
|
|
cc: string[];
|
|
bcc: string[];
|
|
subject: string;
|
|
/** Set when the user scheduled the message; absent for an undo-send hold. */
|
|
delayedUntil?: string;
|
|
}
|
|
|
|
interface AfterSendPayload {
|
|
emailId: string | null;
|
|
submissionId: string | null;
|
|
to: string[];
|
|
cc: string[];
|
|
bcc: string[];
|
|
subject: string;
|
|
scheduledFor: string | null;
|
|
}
|
|
|
|
const held = new Map<string, { timer: ReturnType<typeof setTimeout>; payload: AfterSendPayload }>();
|
|
|
|
function emit(payload: AfterSendPayload): void {
|
|
if (emailHooks.onAfterEmailSend.size === 0) return;
|
|
void emailHooks.onAfterEmailSend.emit(payload);
|
|
}
|
|
|
|
/** Report a send the server accepted (see the rules above). */
|
|
export function announceSent(result: SendEmailResult, message: SentMessage): void {
|
|
const payload: AfterSendPayload = {
|
|
emailId: result.emailId ?? null,
|
|
submissionId: result.emailSubmissionId ?? null,
|
|
to: [...message.to],
|
|
cc: [...message.cc],
|
|
bcc: [...message.bcc],
|
|
subject: message.subject,
|
|
scheduledFor: null,
|
|
};
|
|
if (message.delayedUntil) {
|
|
emit({ ...payload, scheduledFor: result.sendAt ?? message.delayedUntil });
|
|
return;
|
|
}
|
|
const submissionId = result.emailSubmissionId;
|
|
if (result.scheduled && submissionId && result.sendAt) {
|
|
const wait = Math.max(0, Date.parse(result.sendAt) - Date.now());
|
|
const timer = setTimeout(() => {
|
|
held.delete(submissionId);
|
|
emit(payload);
|
|
}, Number.isFinite(wait) ? wait : 0);
|
|
held.set(submissionId, { timer, payload });
|
|
return;
|
|
}
|
|
emit(payload);
|
|
}
|
|
|
|
/** The user undid a held send: it is not reported. */
|
|
export function cancelAnnouncedSend(submissionId: string): void {
|
|
const entry = held.get(submissionId);
|
|
if (!entry) return;
|
|
clearTimeout(entry.timer);
|
|
held.delete(submissionId);
|
|
}
|
|
|
|
/** The user chose "Send now" for a held send: report it now. */
|
|
export function releaseAnnouncedSend(submissionId: string): void {
|
|
const entry = held.get(submissionId);
|
|
if (!entry) return;
|
|
clearTimeout(entry.timer);
|
|
held.delete(submissionId);
|
|
emit(entry.payload);
|
|
}
|
|
|
|
/** Drop every held send (tests). */
|
|
export function resetAnnouncedSends(): void {
|
|
for (const { timer } of held.values()) clearTimeout(timer);
|
|
held.clear();
|
|
}
|