From aad6bbd028ce242458bda08625a8eada06d13a82 Mon Sep 17 00:00:00 2001 From: Aaron Guise Date: Wed, 29 Jul 2026 11:40:34 +1200 Subject: [PATCH] fix: keep the message/rfc822 attachment visible after inline unwrapping The "empty outer body + message/rfc822 attachment" unwrap heuristic (built for Outlook's blank-body forwards) was also matching Forward as attachment sends, which intentionally use a blank body. Once unwrapped for inline preview, the .eml attachment itself was filtered out of the attachment list, so a sent forward-as-attachment message with a large attached .eml appeared to have no attachments at all. --- components/email/email-viewer.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 0aa7fb97..d75b388d 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -847,7 +847,6 @@ export function EmailViewer({ const [embeddedEmailHtml, setEmbeddedEmailHtml] = useState(null); const [embeddedEmailText, setEmbeddedEmailText] = useState(null); const [embeddedEmailAttachments, setEmbeddedEmailAttachments] = useState([]); - const [embeddedEmailUnwrapped, setEmbeddedEmailUnwrapped] = useState(false); // Plugin detail sidebar state. Collapsed/width persist across opens and // sessions so the panel reopens the way the user last left it. @@ -1155,7 +1154,6 @@ export function EmailViewer({ setEmbeddedEmailHtml(null); setEmbeddedEmailText(null); setEmbeddedEmailAttachments([]); - setEmbeddedEmailUnwrapped(false); }, [email?.id, externalContentPolicy]); // Crypto-plugin body takeover (S/MIME, PGP, …). A privileged crypto plugin @@ -1389,7 +1387,6 @@ export function EmailViewer({ a => (a.filename || 'unnamed') + ' (' + a.mimeType + ')' ).join(', ')); } - setEmbeddedEmailUnwrapped(true); debug.groupEnd(); } catch (err) { debug.error('Embedded RFC822 unwrapping failed:', err); @@ -1488,8 +1485,9 @@ export function EmailViewer({ const jmapAttachments = (email?.attachments ?? []) // Hide winmail.dat when we have successfully extracted TNEF content or attachments .filter(att => !(tnefHtml || tnefText || tnefAttachments.length > 0) || !isTnefAttachment(att.name, att.type)) - // Hide message/rfc822 when we have unwrapped the embedded email - .filter(att => !embeddedEmailUnwrapped || att.type !== 'message/rfc822') + // Keep the message/rfc822 attachment itself visible in the attachment list even + // when we've unwrapped it for inline preview - it's a real, downloadable + // attachment (e.g. "Forward as attachment" sends), not just preview scaffolding. // Hide calendar MIME parts (text/calendar, application/ics) when the invitation // banner is shown - prevents raw ICS files appearing as spurious attachments. .filter(att => !hasCalInvitation || !isCalendarMimeType(att.type)) @@ -1534,7 +1532,7 @@ export function EmailViewer({ // attachment list — and its downstream layout measurement — on every email // field change. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [email?.attachments, pluginRenderedAttachments, tnefHtml, tnefText, tnefAttachments, embeddedEmailUnwrapped, embeddedEmailAttachments, calendarInvitationParsingEnabled, hideInlineImageAttachments]); + }, [email?.attachments, pluginRenderedAttachments, tnefHtml, tnefText, tnefAttachments, embeddedEmailAttachments, calendarInvitationParsingEnabled, hideInlineImageAttachments]); // Measure attachment chips in the below-header row to determine how many fit // on a single line; the rest collapse into a "+N attachments" overflow pill.