From db0e41b8b6def9f073b928e21cec2356d042a7d1 Mon Sep 17 00:00:00 2001 From: Tofi Buzali Date: Tue, 14 Apr 2026 22:50:42 -0600 Subject: [PATCH 1/2] fix: exclude selected From alias from CC on reply-all When reply-all is used on an email addressed to a send-as alias, extractReplyInfo doesn't know the alias belongs to the current user and includes it in the CC list. The user ends up CC'ing themselves. Filter the selected From address out of CC in buildSendOptions, where both values are known. Other aliases are left alone since they may be intentional recipients (e.g. emailing yourself at a different alias). --- src/renderer/hooks/useComposeForm.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/renderer/hooks/useComposeForm.ts b/src/renderer/hooks/useComposeForm.ts index 09571938..9aa5d4ad 100644 --- a/src/renderer/hooks/useComposeForm.ts +++ b/src/renderer/hooks/useComposeForm.ts @@ -318,11 +318,17 @@ export function useComposeForm({ // Convert nameMap to a plain object for IPC serialization const recipientNames = nameMap.size > 0 ? Object.fromEntries(nameMap) : undefined; + // Strip the selected From alias from CC so we don't CC ourselves when + // reply-all puts our alias in the CC list (other aliases stay — they may + // be intentional recipients). + const fromBare = from ? extractBareEmail(from).toLowerCase() : ""; + const filteredCc = fromBare ? cc.filter((addr) => extractBareEmail(addr).toLowerCase() !== fromBare) : cc; + return { accountId, from, to, - cc: cc.length > 0 ? cc : undefined, + cc: filteredCc.length > 0 ? filteredCc : undefined, bcc: bcc.length > 0 ? bcc : undefined, subject, bodyHtml: fullBodyHtml, From adc74061d69b524de6d383e3eb74c6fe3ac46ede Mon Sep 17 00:00:00 2001 From: Tofi Buzali Date: Tue, 14 Apr 2026 22:56:40 -0600 Subject: [PATCH 2/2] Address review feedback: filter alias from CC at state level Move the from-alias CC filtering from buildSendOptions (send-time) to a useEffect on `from` changes. This fixes: - CC chips in the UI showing the user's own alias during compose - hasAnyRecipient guards using unfiltered cc (Devin review) - Undo-send restoring the alias back into CC (Greptile review) --- src/renderer/hooks/useComposeForm.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/renderer/hooks/useComposeForm.ts b/src/renderer/hooks/useComposeForm.ts index 9aa5d4ad..e55a359b 100644 --- a/src/renderer/hooks/useComposeForm.ts +++ b/src/renderer/hooks/useComposeForm.ts @@ -194,6 +194,18 @@ export function useComposeForm({ }); }, [accountId]); + // When the From alias is resolved, strip it from CC so the user doesn't + // CC themselves on reply-all. Only removes the selected From — other aliases + // stay since they may be intentional recipients. + useEffect(() => { + if (!from) return; + const fromBare = extractBareEmail(from).toLowerCase(); + setCc((prev) => { + const filtered = prev.filter((addr) => extractBareEmail(addr).toLowerCase() !== fromBare); + return filtered.length === prev.length ? prev : filtered; + }); + }, [from]); + // --- Send state --- const [isSending, setIsSending] = useState(false); const [isScheduling, setIsScheduling] = useState(false); @@ -318,17 +330,11 @@ export function useComposeForm({ // Convert nameMap to a plain object for IPC serialization const recipientNames = nameMap.size > 0 ? Object.fromEntries(nameMap) : undefined; - // Strip the selected From alias from CC so we don't CC ourselves when - // reply-all puts our alias in the CC list (other aliases stay — they may - // be intentional recipients). - const fromBare = from ? extractBareEmail(from).toLowerCase() : ""; - const filteredCc = fromBare ? cc.filter((addr) => extractBareEmail(addr).toLowerCase() !== fromBare) : cc; - return { accountId, from, to, - cc: filteredCc.length > 0 ? filteredCc : undefined, + cc: cc.length > 0 ? cc : undefined, bcc: bcc.length > 0 ? bcc : undefined, subject, bodyHtml: fullBodyHtml,