From 766fb69f28c33451b54be543f4cf702e2a280d5a Mon Sep 17 00:00:00 2001 From: Ahmet Kilinc Date: Tue, 10 Jun 2025 21:37:06 +0100 Subject: [PATCH] use aliases in email-composer (#1233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Add email alias selection to the email composer ## Description This PR adds the ability for users to select which email alias to send from in the email composer. It displays a dropdown of available email aliases when composing a new email, allowing users to choose their sending identity. The feature is only shown when creating new emails, not when replying to existing messages. ## Type of Change - [x] ✨ New feature (non-breaking change which adds functionality) - [x] 🎨 UI/UX improvement ## Areas Affected - [x] User Interface/Experience ## Summary by CodeRabbit - **New Features** - Added the ability to select a "From" email address when composing a new email, allowing users to choose from multiple email aliases. - Introduced a dropdown menu to display and select available email aliases, with clear indication of the primary alias. - Enhanced reply email sender selection to automatically match the sender address with recipients from the original message for more accurate "From" email choice. - **Style** - Updated the subject input area with a new bottom border for improved visual separation. --- .gitignore | 4 +- apps/mail/components/create/create-email.tsx | 5 +- .../mail/components/create/email-composer.tsx | 54 ++++++++++++++++++- apps/mail/components/mail/reply-composer.tsx | 27 +++++++++- 4 files changed, 83 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 891a6845e..0a9ca6224 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,6 @@ worker-configuration.d.ts # devcontainer .pnpm-store -tsx-0/ \ No newline at end of file +tsx-0/ + +tools.json \ No newline at end of file diff --git a/apps/mail/components/create/create-email.tsx b/apps/mail/components/create/create-email.tsx index 930ae0d5c..26bade119 100644 --- a/apps/mail/components/create/create-email.tsx +++ b/apps/mail/components/create/create-email.tsx @@ -101,9 +101,9 @@ export function CreateEmail({ subject: string; message: string; attachments: File[]; + fromEmail?: string; }) => { - // Use the selected from email or the first alias (or default user email) - const fromEmail = aliases?.[0]?.email ?? userEmail; + const fromEmail = data.fromEmail || aliases?.[0]?.email || userEmail; const zeroSignature = settings?.settings.zeroSignature ? '

Sent via Zero

' @@ -185,6 +185,7 @@ export function CreateEmail({ Promise; onClose?: () => void; className?: string; @@ -92,6 +106,8 @@ export function EmailComposer({ className, autofocus = false, settingsLoading = false, + replyingTo, + aliases = [], editorClassName, }: EmailComposerProps) { const [showCc, setShowCc] = useState(initialCc.length > 0); @@ -164,6 +180,7 @@ export function EmailComposer({ subject: initialSubject, message: initialMessage, attachments: initialAttachments, + fromEmail: aliases?.find((alias) => alias.primary)?.email || aliases?.[0]?.email || '', }, }); @@ -247,6 +264,7 @@ export function EmailComposer({ const bccEmails = watch('bcc'); const subjectInput = watch('subject'); const attachments = watch('attachments'); + const fromEmail = watch('fromEmail'); const handleAttachment = (files: File[]) => { if (files && files.length > 0) { @@ -304,6 +322,7 @@ export function EmailComposer({ subject: values.subject, message: editor.getHTML(), attachments: values.attachments || [], + fromEmail: values.fromEmail, }); setHasUnsavedChanges(false); editor.commands.clearContent(true); @@ -908,7 +927,7 @@ export function EmailComposer({ {/* Subject */} -
+

Subject:

+ {/* From */} + {aliases.length > 0 && !replyingTo && ( +
+

From:

+ +
+ )} + {/* Message Content */}
diff --git a/apps/mail/components/mail/reply-composer.tsx b/apps/mail/components/mail/reply-composer.tsx index d59b3bdcd..8efef655e 100644 --- a/apps/mail/components/mail/reply-composer.tsx +++ b/apps/mail/components/mail/reply-composer.tsx @@ -112,7 +112,29 @@ export default function ReplyCompose({ messageId }: ReplyComposeProps) { try { const userEmail = activeConnection.email.toLowerCase(); - // Convert email strings to Sender objects + let fromEmail = userEmail; + + if (aliases && aliases.length > 0 && replyToMessage) { + const allRecipients = [ + ...(replyToMessage.to || []), + ...(replyToMessage.cc || []), + ...(replyToMessage.bcc || []), + ]; + + const matchingAlias = aliases.find((alias) => + allRecipients.some( + (recipient) => recipient.email.toLowerCase() === alias.email.toLowerCase(), + ), + ); + + if (matchingAlias) { + fromEmail = matchingAlias.email; + } else { + fromEmail = + aliases.find((alias) => alias.primary)?.email || aliases[0]?.email || userEmail; + } + } + const toRecipients: Sender[] = data.to.map((email) => ({ email, name: email.split('@')[0] || 'User', @@ -159,7 +181,7 @@ export default function ReplyCompose({ messageId }: ReplyComposeProps) { subject: data.subject, message: emailBody, attachments: await serializeFiles(data.attachments), - fromEmail: aliases?.[0]?.email || userEmail, + fromEmail: fromEmail, headers: { 'In-Reply-To': replyToMessage?.messageId ?? '', References: [ @@ -246,6 +268,7 @@ export default function ReplyCompose({ messageId }: ReplyComposeProps) { })} autofocus={shouldFocus} settingsLoading={settingsLoading} + replyingTo={replyToMessage?.sender.email} />
);