From 7a483b3dd4f701c8a75f8fcfb2785008c33313d5 Mon Sep 17 00:00:00 2001 From: Aaron Guise Date: Mon, 27 Jul 2026 16:45:53 +1200 Subject: [PATCH] fix: honor the user's filename template for forward-as-attachment buildForwardAsAttachmentPayload called emailExportFilename(email) with no options, always using the default naming template regardless of the user's configured emailDownloadTemplate (and space/case/diacritics transforms) - the same settings the neighboring "Export as .eml" action already respects. That could produce inconsistent .eml filenames between the two actions for the same message. Accept an optional EmailFilenameOptions parameter and pass it through. handleForwardAsAttachment now reads the same settings email-viewer.tsx's emailFilenameOptions useMemo does, via useSettingsStore.getState() (a one-off read inside an event handler, matching this file's existing pattern, rather than a new reactive subscription). Add a unit test covering a custom template. Caught by GitHub Copilot's automated PR review. --- app/(main)/[locale]/page.tsx | 19 ++++++++++++++++++- lib/__tests__/forward-as-attachment.test.ts | 10 ++++++++++ lib/forward-as-attachment.ts | 12 ++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/app/(main)/[locale]/page.tsx b/app/(main)/[locale]/page.tsx index 9a08f6c7..f5091d73 100644 --- a/app/(main)/[locale]/page.tsx +++ b/app/(main)/[locale]/page.tsx @@ -1561,7 +1561,24 @@ export default function Home() { // entirely, so the body starts blank instead of quoting the original. const handleForwardAsAttachment = async () => { if (!selectedEmail) return; - const payload = buildForwardAsAttachmentPayload(selectedEmail, t('email_composer.prefix.forward')); + // Same filename options "Export as .eml" uses (see emailFilenameOptions + // in email-viewer.tsx), so the two actions produce consistent filenames + // for the same message rather than the synthetic attachment silently + // ignoring the user's configured naming template. + const { + emailDownloadTemplate, + filenameSpaceReplacement, + filenameLowercase, + filenameStripDiacritics, + filenameCollapseSeparators, + } = useSettingsStore.getState(); + const payload = buildForwardAsAttachmentPayload(selectedEmail, t('email_composer.prefix.forward'), { + template: emailDownloadTemplate, + spaceReplacement: filenameSpaceReplacement, + lowercase: filenameLowercase, + stripDiacritics: filenameStripDiacritics, + collapseSeparators: filenameCollapseSeparators, + }); if (!payload) return; const ok = await emailHooks.onBeforeForward.intercept({ diff --git a/lib/__tests__/forward-as-attachment.test.ts b/lib/__tests__/forward-as-attachment.test.ts index 6d092fe3..c52aeb13 100644 --- a/lib/__tests__/forward-as-attachment.test.ts +++ b/lib/__tests__/forward-as-attachment.test.ts @@ -45,4 +45,14 @@ describe('buildForwardAsAttachmentPayload', () => { const payload = buildForwardAsAttachmentPayload(email, 'Fwd:'); expect(payload?.subject).toBe('Fwd: already forwarded once'); }); + + it('honors a custom filename template, matching "Export as .eml" naming instead of always using the default', () => { + const email = makeEmail({ subject: 'Missed spam example' }); + const payload = buildForwardAsAttachmentPayload(email, 'Fwd:', { + template: 'custom-{subject}', + lowercase: true, + spaceReplacement: 'dash', + }); + expect(payload?.attachment.name).toBe('custom-missed-spam-example.eml'); + }); }); diff --git a/lib/forward-as-attachment.ts b/lib/forward-as-attachment.ts index 38067d21..3058c2ec 100644 --- a/lib/forward-as-attachment.ts +++ b/lib/forward-as-attachment.ts @@ -1,6 +1,6 @@ import type { Email } from "@/lib/jmap/types"; import { buildForwardSubject } from "@/lib/subject-prefix"; -import { emailExportFilename } from "@/lib/download-filename"; +import { emailExportFilename, type EmailFilenameOptions } from "@/lib/download-filename"; export interface ForwardAsAttachmentEntry { blobId: string; @@ -24,11 +24,19 @@ export interface ForwardAsAttachmentPayload { * not per-email, so the same blobId a message already has can be attached * to a brand new outgoing email directly. * + * `filenameOptions`, when passed, should be the same options the caller + * uses for "Export as .eml" / drag-out (the user's configured filename + * template, space/case/diacritics transforms - see + * useSettingsStore's emailDownloadTemplate and friends), so the two + * actions produce consistent filenames for the same message. Falls back + * to emailExportFilename's own default template when omitted. + * * Returns null when the email has no blobId (nothing to reference). */ export function buildForwardAsAttachmentPayload( email: Email, forwardPrefix: string, + filenameOptions?: EmailFilenameOptions, ): ForwardAsAttachmentPayload | null { if (!email.blobId) return null; @@ -36,7 +44,7 @@ export function buildForwardAsAttachmentPayload( subject: buildForwardSubject(email.subject, forwardPrefix), attachment: { blobId: email.blobId, - name: emailExportFilename(email), + name: emailExportFilename(email, filenameOptions), type: "message/rfc822", size: email.size, },