diff --git a/apps/mail/components/mail/reply-composer.tsx b/apps/mail/components/mail/reply-composer.tsx index 18e128139..0677ba753 100644 --- a/apps/mail/components/mail/reply-composer.tsx +++ b/apps/mail/components/mail/reply-composer.tsx @@ -5,7 +5,7 @@ import { useHotkeysContext } from 'react-hotkeys-hook'; import { useTRPC } from '@/providers/query-provider'; import { useMutation } from '@tanstack/react-query'; import { useSettings } from '@/hooks/use-settings'; -import { constructReplyBody } from '@/lib/utils'; +import { constructReplyBody, constructForwardBody } from '@/lib/utils'; import { useThread } from '@/hooks/use-threads'; import { serializeFiles } from '@/lib/schemas'; import { useDraft } from '@/hooks/use-drafts'; @@ -136,20 +136,28 @@ export default function ReplyCompose({ messageId }: ReplyComposeProps) { ? '

Sent via Zero

' : ''; - const replyBody = constructReplyBody( - data.message + zeroSignature, - new Date(replyToMessage.receivedOn || '').toLocaleString(), - replyToMessage.sender, - toRecipients, - replyToMessage.decodedBody, - ); + const emailBody = mode === 'forward' + ? constructForwardBody( + data.message + zeroSignature, + new Date(replyToMessage.receivedOn || '').toLocaleString(), + { ...replyToMessage.sender, subject: replyToMessage.subject }, + toRecipients, + replyToMessage.decodedBody, + ) + : constructReplyBody( + data.message + zeroSignature, + new Date(replyToMessage.receivedOn || '').toLocaleString(), + replyToMessage.sender, + toRecipients, + replyToMessage.decodedBody, + ); await sendEmail({ to: toRecipients, cc: ccRecipients, bcc: bccRecipients, subject: data.subject, - message: replyBody, + message: emailBody, attachments: await serializeFiles(data.attachments), fromEmail: aliases?.[0]?.email || userEmail, headers: { @@ -163,6 +171,8 @@ export default function ReplyCompose({ messageId }: ReplyComposeProps) { 'Thread-Id': replyToMessage?.threadId ?? '', }, threadId: replyToMessage?.threadId, + isForward: mode === 'forward', + originalMessage: replyToMessage.decodedBody, }); posthog.capture('Reply Email Sent'); diff --git a/apps/mail/lib/utils.ts b/apps/mail/lib/utils.ts index 1ca64a421..7961024c5 100644 --- a/apps/mail/lib/utils.ts +++ b/apps/mail/lib/utils.ts @@ -287,8 +287,33 @@ export const constructReplyBody = (
On ${originalDate}, ${senderName} ${recipientEmails ? `<${recipientEmails}>` : ''} wrote:
-
- ${quotedMessage || ''} +
+ + `; +}; + +export const constructForwardBody = ( + formattedMessage: string, + originalDate: string, + originalSender: Sender | undefined, + otherRecipients: Sender[], + quotedMessage?: string, +) => { + const senderName = originalSender?.name || originalSender?.email || 'Unknown Sender'; + const recipientEmails = otherRecipients.map((r) => r.email).join(', '); + + return ` +
+
+ ${formattedMessage} +
+
+
+ ---------- Forwarded message ----------
+ From: ${senderName} ${originalSender?.email ? `<${originalSender.email}>` : ''}
+ Date: ${originalDate}
+ Subject: ${originalSender?.subject || 'No Subject'}
+ To: ${recipientEmails || 'No Recipients'}
diff --git a/apps/mail/types/index.ts b/apps/mail/types/index.ts index a2a17cb25..c1a3eca3b 100644 --- a/apps/mail/types/index.ts +++ b/apps/mail/types/index.ts @@ -55,6 +55,7 @@ export interface SidebarData { export interface Sender { name?: string; email: string; + subject?: string; } export interface ParsedMessage { @@ -113,14 +114,16 @@ export type ThreadProps = { export interface IOutgoingMessage { to: Sender[]; - cc?: Sender[]; - bcc?: Sender[]; subject: string; message: string; - attachments: File[]; - headers: Record; + attachments?: File[]; + headers?: Record; + cc?: Sender[]; + bcc?: Sender[]; threadId?: string; fromEmail?: string; + isForward?: boolean; + originalMessage?: string; } export interface Note { diff --git a/apps/server/src/lib/driver/google.ts b/apps/server/src/lib/driver/google.ts index 1f25608fc..b6d766a1c 100644 --- a/apps/server/src/lib/driver/google.ts +++ b/apps/server/src/lib/driver/google.ts @@ -898,6 +898,8 @@ export class GoogleMailManager implements MailManager { cc, bcc, fromEmail, + isForward = false, + originalMessage = null, }: IOutgoingMessage) { const msg = createMimeMessage(); @@ -990,10 +992,17 @@ export class GoogleMailManager implements MailManager { msg.setSubject(subject); - msg.addMessage({ - contentType: 'text/html', - data: await sanitizeTipTapHtml(message.trim()), - }); + if (originalMessage) { + msg.addMessage({ + contentType: 'text/html', + data: `${await sanitizeTipTapHtml(message.trim())}${originalMessage}`, + }); + } else { + msg.addMessage({ + contentType: 'text/html', + data: await sanitizeTipTapHtml(message.trim()), + }); + } if (headers) { Object.entries(headers).forEach(([key, value]) => { diff --git a/apps/server/src/trpc/routes/mail.ts b/apps/server/src/trpc/routes/mail.ts index d5500b100..7eb182d73 100644 --- a/apps/server/src/trpc/routes/mail.ts +++ b/apps/server/src/trpc/routes/mail.ts @@ -273,6 +273,8 @@ export const mailRouter = router({ threadId: z.string().optional(), fromEmail: z.string().optional(), draftId: z.string().optional(), + isForward: z.boolean().optional(), + originalMessage: z.string().optional(), }), ) .mutation(async ({ ctx, input }) => { diff --git a/apps/server/src/types.ts b/apps/server/src/types.ts index cb35dd42d..ddf79858e 100644 --- a/apps/server/src/types.ts +++ b/apps/server/src/types.ts @@ -136,6 +136,8 @@ export interface IOutgoingMessage { headers: Record; threadId?: string; fromEmail?: string; + isForward?: boolean; + originalMessage?: string | null; } export interface DeleteAllSpamResponse { success: boolean;