diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 5060f5b4..8ff180b1 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -905,6 +905,24 @@ export function EmailViewer({ const dragOutActive = useMemo(() => isDragOutSupported(), []); const emailDownloadTemplate = useSettingsStore((state) => state.emailDownloadTemplate) || DEFAULT_EMAIL_TEMPLATE; const attachmentDownloadTemplate = useSettingsStore((state) => state.attachmentDownloadTemplate) || DEFAULT_ATTACHMENT_TEMPLATE; + const filenameSpaceReplacement = useSettingsStore((state) => state.filenameSpaceReplacement); + const filenameLowercase = useSettingsStore((state) => state.filenameLowercase); + const filenameStripDiacritics = useSettingsStore((state) => state.filenameStripDiacritics); + const filenameCollapseSeparators = useSettingsStore((state) => state.filenameCollapseSeparators); + const emailFilenameOptions = useMemo(() => ({ + template: emailDownloadTemplate, + spaceReplacement: filenameSpaceReplacement, + lowercase: filenameLowercase, + stripDiacritics: filenameStripDiacritics, + collapseSeparators: filenameCollapseSeparators, + }), [emailDownloadTemplate, filenameSpaceReplacement, filenameLowercase, filenameStripDiacritics, filenameCollapseSeparators]); + const attachmentFilenameOptions = useMemo(() => ({ + template: attachmentDownloadTemplate, + spaceReplacement: filenameSpaceReplacement, + lowercase: filenameLowercase, + stripDiacritics: filenameStripDiacritics, + collapseSeparators: filenameCollapseSeparators, + }), [attachmentDownloadTemplate, filenameSpaceReplacement, filenameLowercase, filenameStripDiacritics, filenameCollapseSeparators]); const timeFormat = useSettingsStore((state) => state.timeFormat); const isFocusedMailLayout = mailLayout === 'focus'; @@ -2569,9 +2587,9 @@ export function EmailViewer({ (attachment: EffectiveAttachment) => { const fallback = attachment.name || 'download'; if (!email) return fallback; - return attachmentDownloadFilename(email, { name: attachment.name, type: attachment.type }, attachmentDownloadTemplate) || fallback; + return attachmentDownloadFilename(email, { name: attachment.name, type: attachment.type }, attachmentFilenameOptions) || fallback; }, - [email, attachmentDownloadTemplate], + [email, attachmentFilenameOptions], ); const handleEffectiveAttachmentOpen = useCallback(async (attachment: EffectiveAttachment) => { @@ -3072,7 +3090,7 @@ export function EmailViewer({ const handleExportEmail = async () => { if (!email?.blobId || !client) return; try { - await client.downloadBlob(email.blobId, emailExportFilename(email, emailDownloadTemplate), 'message/rfc822'); + await client.downloadBlob(email.blobId, emailExportFilename(email, emailFilenameOptions), 'message/rfc822'); } catch { toast.error(tNotifications('export_email_error')); } diff --git a/components/settings/downloads-settings.tsx b/components/settings/downloads-settings.tsx index 1f6406c2..eabf46e3 100644 --- a/components/settings/downloads-settings.tsx +++ b/components/settings/downloads-settings.tsx @@ -3,7 +3,7 @@ import { useMemo, useRef } from "react"; import { useTranslations } from "next-intl"; import { useSettingsStore } from "@/stores/settings-store"; -import { SettingsSection } from "./settings-section"; +import { SettingsSection, SettingItem, Select, ToggleSwitch } from "./settings-section"; import { RotateCcw } from "lucide-react"; import { cn } from "@/lib/utils"; import { @@ -14,6 +14,7 @@ import { emailExportFilename, attachmentDownloadFilename, buildSampleEmail, + type EmailFilenameOptions, } from "@/lib/download-filename"; function insertTokenAtCursor( @@ -46,6 +47,7 @@ interface TemplateEditorProps { preview: string; onChange: (next: string) => void; resetLabel: string; + previewLabel: string; placeholder?: string; } @@ -58,6 +60,7 @@ function TemplateEditor({ preview, onChange, resetLabel, + previewLabel, placeholder, }: TemplateEditorProps) { const inputRef = useRef(null); @@ -114,7 +117,7 @@ function TemplateEditor({ ))}
- Preview: + {previewLabel} {preview}
@@ -124,23 +127,45 @@ function TemplateEditor({ export function DownloadsSettings() { const t = useTranslations("settings.downloads"); - const { emailDownloadTemplate, attachmentDownloadTemplate, updateSetting } = useSettingsStore(); + const { + emailDownloadTemplate, + attachmentDownloadTemplate, + filenameSpaceReplacement, + filenameLowercase, + filenameStripDiacritics, + filenameCollapseSeparators, + updateSetting, + } = useSettingsStore(); const sampleEmail = useMemo(() => buildSampleEmail(), []); const sampleAttachment = useMemo(() => ({ name: "Invoice-2026-05.pdf", type: "application/pdf" }), []); + const transform = useMemo( + () => ({ + spaceReplacement: filenameSpaceReplacement, + lowercase: filenameLowercase, + stripDiacritics: filenameStripDiacritics, + collapseSeparators: filenameCollapseSeparators, + }), + [filenameSpaceReplacement, filenameLowercase, filenameStripDiacritics, filenameCollapseSeparators], + ); + + const emailOptions: EmailFilenameOptions = useMemo( + () => ({ ...transform, template: emailDownloadTemplate || DEFAULT_EMAIL_TEMPLATE }), + [transform, emailDownloadTemplate], + ); + const attachmentOptions: EmailFilenameOptions = useMemo( + () => ({ ...transform, template: attachmentDownloadTemplate || DEFAULT_ATTACHMENT_TEMPLATE }), + [transform, attachmentDownloadTemplate], + ); + const emlPreview = useMemo( - () => emailExportFilename(sampleEmail, emailDownloadTemplate || DEFAULT_EMAIL_TEMPLATE), - [sampleEmail, emailDownloadTemplate], + () => emailExportFilename(sampleEmail, emailOptions), + [sampleEmail, emailOptions], ); const attachmentPreview = useMemo( - () => - attachmentDownloadFilename( - sampleEmail, - sampleAttachment, - attachmentDownloadTemplate || DEFAULT_ATTACHMENT_TEMPLATE, - ), - [sampleEmail, sampleAttachment, attachmentDownloadTemplate], + () => attachmentDownloadFilename(sampleEmail, sampleAttachment, attachmentOptions), + [sampleEmail, sampleAttachment, attachmentOptions], ); return ( @@ -154,6 +179,7 @@ export function DownloadsSettings() { preview={emlPreview} onChange={(next) => updateSetting("emailDownloadTemplate", next)} resetLabel={t("reset")} + previewLabel={t("preview")} placeholder={DEFAULT_EMAIL_TEMPLATE} /> updateSetting("attachmentDownloadTemplate", next)} resetLabel={t("reset")} + previewLabel={t("preview")} placeholder={DEFAULT_ATTACHMENT_TEMPLATE} /> + +