diff --git a/web/src/store/__tests__/reply-addressing.test.ts b/web/src/store/__tests__/reply-addressing.test.ts index 437aac8..d3ee2bb 100644 --- a/web/src/store/__tests__/reply-addressing.test.ts +++ b/web/src/store/__tests__/reply-addressing.test.ts @@ -153,6 +153,30 @@ describe("a message of mine with nobody obvious to reply to", () => { const d = await draftFor({ ...MINE, to: [ME], cc: [] } as Email, "reply"); expect(addrs(d.to)).toEqual([ME.email]); }); + + it("answers the Reply-To rather than my own desk when nobody else is on it", async () => { + /* + * A contact form: the site mails itself, From and To both its own address, + * and the person who filled the form in is in Reply-To. From alone makes + * this look like mine, and the fallback used to reply to me (#415). + */ + const form = { ...MINE, to: [ME], cc: [], replyTo: [{ name: "Michael", email: "michael@example.com" }] } as Email; + const d = await draftFor(form, "reply"); + expect(addrs(d.to)).toEqual(["michael@example.com"]); + }); + + it("does the same on a reply all, without cc-ing myself", async () => { + const form = { ...MINE, to: [ME], cc: [], replyTo: [{ name: "Michael", email: "michael@example.com" }] } as Email; + const d = await draftFor(form, "replyAll"); + expect(addrs(d.to)).toEqual(["michael@example.com"]); + expect(d.cc).toEqual([]); + }); + + it("still prefers somebody I actually wrote to over my own Reply-To", async () => { + // The Cc is a person; the Reply-To is where answers to me belong. + const d = await draftFor({ ...MINE, to: [ME], replyTo: [{ name: null, email: "desk@example.org" }] } as Email, "reply"); + expect(addrs(d.to)).toEqual([BOB.email]); + }); }); describe("forwarding", () => { diff --git a/web/src/store/compose.ts b/web/src/store/compose.ts index 660b8a5..04219d7 100644 --- a/web/src/store/compose.ts +++ b/web/src/store/compose.ts @@ -410,6 +410,19 @@ export const useCompose = create((set, get) => ({ // Addressed only to myself, or only in Cc: there is still somebody this // is a reply to, and an empty To is not it. if (!to.length) { to = cc.length ? cc : withoutOwn(full.cc ?? []); cc = []; } + /* + * Nobody but me on the message, and a Reply-To pointing somewhere that + * is not mine: that address is who this is really from. + * + * A contact form is the shape of it -- From and To are both the site's + * own mailbox, and the person who filled the form in is in Reply-To. + * The address test above calls that mine, correctly as far as it goes, + * and the fallback then addressed the reply to my own desk (#415). + * + * After the Cc, not before it: a message I really did send carries my + * own Reply-To, and somebody I actually wrote to beats it. + */ + if (!to.length) to = withoutOwn(full.replyTo ?? []); if (!to.length) to = uniqueAddresses([...(full.to ?? []), ...(full.cc ?? [])]); } else { to = uniqueAddresses(full.replyTo?.length ? full.replyTo : (full.from ?? []));