From fc50e5b56949ae3431f1ff1608b1c594950750bf Mon Sep 17 00:00:00 2001 From: Aaron Guise Date: Mon, 27 Jul 2026 17:32:18 +1200 Subject: [PATCH] fix: wire "Forward as attachment" into Pro's popped-out email tab The earlier fix (f3b68194) addressed the Pro/embedded composer-hoisting path (composing FROM the Mail tab, then getting hoisted into a Pro tab), but missed a second, entirely separate render path: viewing an email that's already been popped into its own Pro tab (components/pro/pro-email-tab-body.tsx). That component renders its own with its own self-contained handleForward - it fetches its own `email` and opens compose tabs directly via useProTabStore, with no dependency on page.tsx's pendingDraft/ selectedEmail plumbing at all - so it never had onForwardAsAttachment wired in the first place. The overflow menu there just silently had no such item, since EmailViewer only renders it when the prop is provided. Add handleForwardAsAttachment here, mirroring handleForward but using the shared buildForwardAsAttachmentPayload helper, with the same filename-options handling as the page.tsx fix (7a483b3d/a34314ce). No stale-closure risk here (unlike the list context menu fix) - `email` is this component's own local per-tab state, not a global selection being mutated synchronously before the call. --- components/pro/pro-email-tab-body.tsx | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/components/pro/pro-email-tab-body.tsx b/components/pro/pro-email-tab-body.tsx index 838aaa74..dc3d03cc 100644 --- a/components/pro/pro-email-tab-body.tsx +++ b/components/pro/pro-email-tab-body.tsx @@ -15,6 +15,7 @@ import { useProTabStore, type ProEmailTabData, type ProReplyContext } from "@/st import type { Email } from "@/lib/jmap/types"; import { buildReplySubject, buildForwardSubject } from "@/lib/subject-prefix"; import { getQuoteBodies } from "@/lib/email-composer-utils"; +import { buildForwardAsAttachmentPayload } from "@/lib/forward-as-attachment"; interface ProEmailTabBodyProps { tabId: string; @@ -136,6 +137,44 @@ export function ProEmailTabBody({ tabId, data }: ProEmailTabBodyProps) { }); }, [email, openComposeTab, t]); + // Mirrors handleForward, but attaches the original as a message/rfc822 + // file instead of quoting it inline - see lib/forward-as-attachment.ts. + // This is a separate, self-contained render path from the main Mail + // tab's EmailViewer (page.tsx) - Pro tabs fetch their own `email` and + // open compose tabs directly via useProTabStore, not through + // page.tsx's pendingDraft/selectedEmail plumbing - so it needed its own + // wiring rather than falling out of the page.tsx fix automatically. + const handleForwardAsAttachment = useCallback(() => { + if (!email) return; + const { + emailDownloadTemplate, + filenameSpaceReplacement, + filenameLowercase, + filenameStripDiacritics, + filenameCollapseSeparators, + } = useSettingsStore.getState(); + const payload = buildForwardAsAttachmentPayload(email, t('email_composer.prefix.forward'), { + template: emailDownloadTemplate, + spaceReplacement: filenameSpaceReplacement, + lowercase: filenameLowercase, + stripDiacritics: filenameStripDiacritics, + collapseSeparators: filenameCollapseSeparators, + }); + if (!payload) return; + + composerSessionIdRef.current += 1; + openComposeTab({ + sessionId: composerSessionIdRef.current, + mode: 'forward', + replyTo: { + subject: email.subject, + attachments: [payload.attachment], + }, + sourceEmailId: email.id, + title: payload.subject, + }); + }, [email, openComposeTab, t]); + const handleDelete = useCallback(async () => { if (!client || !email) return; try { @@ -283,6 +322,7 @@ export function ProEmailTabBody({ tabId, data }: ProEmailTabBodyProps) { onReply={handleReply} onReplyAll={handleReplyAll} onForward={handleForward} + onForwardAsAttachment={handleForwardAsAttachment} onDelete={handleDelete} onArchive={handleArchive} onToggleStar={handleToggleStar}