Send a message again as a new one #179

Closed
opened 2026-09-01 15:58:32 +00:00 by jcoffey-dev · 0 comments
Owner

Closes #176.

What it does

Compose as new sits with Reply / Reply all / Forward in the per-message menu (MessageView), and after Forward in the list's right-click menu (MessageList). It opens a composer prefilled from the message: recipients (To, Cc and Bcc where the server kept them), Reply-To, subject with nothing prefixed, body with nothing wrapped around it, and attachments.

What makes it new

Mostly what it leaves out, and two of those are load-bearing:

  • draftId stays null. saveDraftInternal and sendInternal both add destroy: [draftId] — a draft that carried it would delete the message it was made from on send.
  • relatedEmailId / relatedKeyword stay null, so sending doesn't stamp $answered or $forwarded on the original.
  • inReplyTo and references stay null, so it threads onto nothing.

The Message-ID and date come free: buildEmailObject never sets messageId (the server assigns one) and stamps sentAt at build time. The send path needed no changes at all.

Judgment calls worth your eye

Identity. A message you sent is composed as the identity you sent it as (matching on from, the same rule that reopens a draft). For a message somebody else sent, the issue asks for the default identity, and I agreed — matching on who it was addressed to would put a resend behind an alias that was only ever the receiving end.

No signature is added, and signatureHtml is deliberately left empty. The body is the sent one, which already ends in whatever signature went out with it; appending the identity's would give it two. Same as reopening a draft.

Priority and read-receipt are carried, though the issue's "not to keep" list says "other headers". I read that list as being about headers identifying the old message — Message-ID, Date, threading. X-Priority and Disposition-Notification-To are the sender's intent, and the main use case is sending the same mail again, so reproducing them seemed right. Easy to drop if you disagree.

Verification

npm run typecheck, npm test (513 pass, 11 new in store/__tests__/compose-as-new.test.ts) and npm run build green. The tests pin what's kept and — more importantly — what's dropped, since a regression there is silent and destructive.

Driven by hand against dev:mock:

  • item present in both menus, in the right place
  • composer opens with the recipients, subject verbatim (no Re:/Fwd: added), body with no quote wrapper or forwarded-header block
  • a message from someone else → account default identity, as specified
  • invite.ics (371 B) carried across with its blob intact
  • sent it, and the original was still in Sent afterwards — the draftId property proven end to end rather than just unit-tested

Screenshot of the prefilled composer: attached in the conversation.

Not translated yet

One new string ("Compose as new") falls back to English in all nine locales.

Merged 2026-09-01 as coffey-labs/ihasmail@5a7acb7306

Rebuilt from: git history, session transcript.

Closes #176. ## What it does **Compose as new** sits with Reply / Reply all / Forward in the per-message menu (`MessageView`), and after Forward in the list's right-click menu (`MessageList`). It opens a composer prefilled from the message: recipients (To, Cc **and** Bcc where the server kept them), Reply-To, subject with nothing prefixed, body with nothing wrapped around it, and attachments. ## What makes it *new* Mostly what it leaves out, and two of those are load-bearing: - **`draftId` stays null.** `saveDraftInternal` and `sendInternal` both add `destroy: [draftId]` — a draft that carried it would delete the message it was made from on send. - **`relatedEmailId` / `relatedKeyword` stay null**, so sending doesn't stamp `$answered` or `$forwarded` on the original. - `inReplyTo` and `references` stay null, so it threads onto nothing. The Message-ID and date come free: `buildEmailObject` never sets `messageId` (the server assigns one) and stamps `sentAt` at build time. **The send path needed no changes at all.** ## Judgment calls worth your eye **Identity.** A message you sent is composed as the identity you sent it as (matching on `from`, the same rule that reopens a draft). For a message somebody *else* sent, the issue asks for the default identity, and I agreed — matching on who it was addressed to would put a resend behind an alias that was only ever the receiving end. **No signature is added,** and `signatureHtml` is deliberately left empty. The body is the sent one, which already ends in whatever signature went out with it; appending the identity's would give it two. Same as reopening a draft. **Priority and read-receipt are carried**, though the issue's "not to keep" list says "other headers". I read that list as being about headers identifying the *old message* — Message-ID, Date, threading. `X-Priority` and `Disposition-Notification-To` are the sender's intent, and the main use case is sending the same mail again, so reproducing them seemed right. Easy to drop if you disagree. ## Verification `npm run typecheck`, `npm test` (513 pass, 11 new in `store/__tests__/compose-as-new.test.ts`) and `npm run build` green. The tests pin what's kept and — more importantly — what's dropped, since a regression there is silent and destructive. Driven by hand against `dev:mock`: - item present in both menus, in the right place - composer opens with the recipients, subject verbatim (no `Re:`/`Fwd:` added), body with no quote wrapper or forwarded-header block - a message from someone else → account default identity, as specified - `invite.ics` (371 B) carried across with its blob intact - **sent it, and the original was still in Sent afterwards** — the `draftId` property proven end to end rather than just unit-tested Screenshot of the prefilled composer: attached in the conversation. ## Not translated yet One new string ("Compose as new") falls back to English in all nine locales. **Merged** 2026-09-01 as coffey-labs/ihasmail@5a7acb7306d0 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.