feat: add "Forward as attachment" next to Export as .eml
Adds a "Forward as attachment" action to the message overflow menu (desktop and mobile), right beside the existing "Export as .eml" action. Opens a new forward-mode compose window with the original message attached as a message/rfc822 file instead of quoted inline - useful for reporting spam/phishing to an upstream gateway that expects the raw original as an attachment (the primary motivating use case: gateways like MxGuarddog require complete original headers, including the full mail path, for scanning), or for preserving a message's exact formatting/headers when forwarding. Implementation reuses the composer's existing attachment-carry-forward mechanism (the `attachments` useState initializer in email-composer.tsx already carries a forwarded message's own attachments into the new compose via `replyTo.attachments`) - this just adds one synthetic entry representing the whole original message, referenced by its existing blobId. No re-fetch or re-upload needed, since JMAP blobs are account-scoped rather than per-email. The inline quote-header step (prepareComposerQuoteHeader) is skipped, so the body starts blank instead of quoting the original. The core "build subject + attachment entry" logic is extracted into a pure, unit-tested helper (lib/forward-as-attachment.ts) rather than left inline in the already-large page component. Adds the forward_as_attachment locale key to all 24 locales (English text as a placeholder pending translation, following the existing add-a-key convention) to satisfy the translations completeness test.
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
Reply,
|
||||
ReplyAll,
|
||||
Forward,
|
||||
Paperclip,
|
||||
Trash2,
|
||||
Archive,
|
||||
Star,
|
||||
@@ -109,6 +110,7 @@ interface EmailViewerProps {
|
||||
onReply?: (draftText?: string) => void;
|
||||
onReplyAll?: () => void;
|
||||
onForward?: () => void;
|
||||
onForwardAsAttachment?: () => void;
|
||||
onDelete?: () => void;
|
||||
onArchive?: () => void;
|
||||
onToggleStar?: () => void;
|
||||
@@ -621,6 +623,7 @@ export function EmailViewer({
|
||||
onReply,
|
||||
onReplyAll,
|
||||
onForward,
|
||||
onForwardAsAttachment,
|
||||
onDelete,
|
||||
onArchive,
|
||||
onToggleStar,
|
||||
@@ -3340,6 +3343,16 @@ export function EmailViewer({
|
||||
</button>
|
||||
)}
|
||||
<div className="h-px bg-border my-1" />
|
||||
{/* Forward as attachment */}
|
||||
{onForwardAsAttachment && (
|
||||
<button
|
||||
onClick={() => { onForwardAsAttachment(); setMoreMenuOpen(false); setMoreMenuSub(null); }}
|
||||
className="w-full px-3 py-1.5 text-sm text-start hover:bg-muted text-foreground flex items-center gap-2"
|
||||
>
|
||||
<Paperclip className="w-4 h-4" />
|
||||
{t('forward_as_attachment')}
|
||||
</button>
|
||||
)}
|
||||
{/* Export email */}
|
||||
<button
|
||||
onClick={() => { handleExportEmail(); setMoreMenuOpen(false); setMoreMenuSub(null); }}
|
||||
@@ -3462,6 +3475,15 @@ export function EmailViewer({
|
||||
</button>
|
||||
)}
|
||||
<div className="h-px bg-border my-1" />
|
||||
{onForwardAsAttachment && (
|
||||
<button
|
||||
onClick={() => { onForwardAsAttachment(); setMoreMenuOpen(false); }}
|
||||
className="w-full px-4 py-3 min-h-[44px] text-sm text-start hover:bg-muted text-foreground flex items-center gap-3"
|
||||
>
|
||||
<Paperclip className="w-5 h-5" />
|
||||
{t('forward_as_attachment')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => { handleExportEmail(); setMoreMenuOpen(false); }}
|
||||
className="w-full px-4 py-3 min-h-[44px] text-sm text-start hover:bg-muted text-foreground flex items-center gap-3"
|
||||
|
||||
Reference in New Issue
Block a user