Files
webmail/lib/draft-composer-data.ts
T

61 lines
2.8 KiB
TypeScript

import type { ComposerDraftData } from '@/components/email/email-composer';
import { findDraftIdentityIdForLogin, type DraftSource } from '@/lib/draft-source';
import { getDraftComposerBody } from '@/lib/email-body-selection';
import type { Email, Identity } from '@/lib/jmap/types';
import { messagePriorityFromHeaders } from '@/lib/message-priority';
import { reopenedLabelChoice } from '@/lib/outgoing/classification';
/**
* What a composer starts from when a saved draft is opened: from the list, or from
* last time the desktop app was running. `draft` has its full body.
*/
export function composerDataFromDraft(
draft: Email,
{ accountId, localAccountId, identities, noSubject }: DraftSource & { identities: Identity[]; noSubject: string },
): ComposerDraftData {
const { body, plainTextMode } = getDraftComposerBody(draft);
return {
to: draft.to?.map((a) => a.email).filter(Boolean).join(', ') || '',
cc: draft.cc?.map((a) => a.email).filter(Boolean).join(', ') || '',
bcc: draft.bcc?.map((a) => a.email).filter(Boolean).join(', ') || '',
// Drafts saved before #1189 stored an empty subject as the composer's
// "(No Subject)" placeholder; reopen those with an empty field.
subject: draft.subject && draft.subject !== noSubject ? draft.subject : '',
body,
// Re-open in the format the draft was written in (#1022).
plainTextMode,
showCc: (draft.cc?.length || 0) > 0,
showBcc: (draft.bcc?.length || 0) > 0,
// Restore the identity the draft was composed with: the saved From (name and
// address) against the list the composer renders, so a draft from a non-active
// account, or one of two identities sharing an address, isn't reset to the default.
selectedIdentityId: findDraftIdentityIdForLogin(identities, draft.from?.[0], localAccountId),
subAddressTag: '',
mode: 'compose',
draftId: draft.id,
draftAccountId: accountId,
draftLocalAccountId: localAccountId,
// A reply draft re-opens in compose mode; its threading headers must
// come along or the reply leaves its thread.
inReplyTo: draft.inReplyTo ?? undefined,
references: draft.references ?? undefined,
priority: messagePriorityFromHeaders(draft.headers),
// A sent message brought back (Undo, Cancel and edit) has no draft
// marker: its SIO-Label says which label it went out with.
classification: reopenedLabelChoice(draft.headers, localAccountId),
// Existing server-side attachments must ride along, or the composer
// starts empty and the next save/send silently rebuilds the draft
// without them (#849).
attachments: (draft.attachments ?? [])
.filter((a) => !!a.blobId)
.map((a) => ({
blobId: a.blobId,
name: a.name,
type: a.type,
size: a.size,
cid: a.cid,
disposition: a.disposition,
})),
};
}