mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-30 16:16:19 +00:00
fix: show attachment names without directional override characters
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { FileText, FileSpreadsheet, FileImage, FileArchive, File as FileIcon } from "@/components/icons";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { stripBidiControls } from "@/lib/bidi";
|
||||
import type { Attachment, Email } from "@/lib/jmap/types";
|
||||
import type { LoadListAttachments } from "@/lib/list-attachments";
|
||||
|
||||
@@ -78,11 +79,11 @@ export function AttachmentChips({ attachments, onOpen, max = 2, leading, classNa
|
||||
// The row itself opens the message; a chip must not do both.
|
||||
onClick={(e) => { e.stopPropagation(); onOpen(a); }}
|
||||
onDoubleClick={(e) => e.stopPropagation()}
|
||||
title={a.name}
|
||||
title={stripBidiControls(a.name ?? "")}
|
||||
className={cn(CHIP_BOX_CLASS, "max-w-[12rem] bg-background/60 text-foreground/80 hover:bg-muted hover:text-foreground")}
|
||||
>
|
||||
<Icon className={cn(CHIP_ICON_CLASS, iconClass)} />
|
||||
<span className="truncate">{shortName(a.name ?? "")}</span>
|
||||
<span className="truncate">{shortName(stripBidiControls(a.name ?? ""))}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { collapsePlainTextQuotes, setupQuoteCollapse } from "@/lib/quote-collaps
|
||||
import { DARK_PENDING_CLASS, applyEmailDarkMode, emailColorScheme, emailDarkModeCss, emailHasNativeDarkMode as htmlHasNativeDarkMode, resolveColorSchemeMedia } from "@/lib/email-dark-mode";
|
||||
import { fitEmailBodyWidth } from "@/lib/email-fit-width";
|
||||
import { hasFullBleedCanvas } from "@/lib/email-full-bleed";
|
||||
import { stripBidiControls } from "@/lib/bidi";
|
||||
import { withBasePath } from "@/lib/browser-navigation";
|
||||
import { buildContactsPath, buildMailPath } from "@/lib/deep-links";
|
||||
import { useCopyLink } from "@/hooks/use-copy-link";
|
||||
@@ -269,7 +270,9 @@ const MIME_TYPE_LABELS: Record<string, string> = {
|
||||
};
|
||||
|
||||
const getAttachmentDisplayName = (name: string | null | undefined, mimeType?: string): string => {
|
||||
if (name) return name;
|
||||
// Without directional overrides, which would show `reportfdp.exe`
|
||||
// as `reportexe.pdf` (the download name is cleaned separately).
|
||||
if (name) return stripBidiControls(name);
|
||||
if (mimeType) {
|
||||
const label = MIME_TYPE_LABELS[mimeType.toLowerCase()];
|
||||
if (label) return label;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { stripBidiControls } from '@/lib/bidi';
|
||||
|
||||
describe('stripBidiControls', () => {
|
||||
it('removes the characters that reorder what the reader sees', () => {
|
||||
// An RTL override makes `reportfdp.exe` read as `reportexe.pdf`.
|
||||
expect(stripBidiControls('reportfdp.exe')).toBe('reportfdp.exe');
|
||||
expect(stripBidiControls('ab')).toBe('ab');
|
||||
});
|
||||
|
||||
it('leaves ordinary text, including right-to-left scripts, alone', () => {
|
||||
expect(stripBidiControls('Rechnung März.pdf')).toBe('Rechnung März.pdf');
|
||||
expect(stripBidiControls('تقرير.pdf')).toBe('تقرير.pdf');
|
||||
});
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Unicode directional formatting characters: the explicit embeddings,
|
||||
* overrides and isolates (U+202A-202E, U+2066-2069) and the LRM/RLM marks.
|
||||
* In a sender-controlled name they reorder what the reader sees, so
|
||||
* `reportfdp.exe` shows as `reportexe.pdf`.
|
||||
*/
|
||||
const BIDI_CONTROLS = /[--]/g;
|
||||
|
||||
/** `text` without directional formatting characters, for display. */
|
||||
export function stripBidiControls(text: string): string {
|
||||
return text.replace(BIDI_CONTROLS, '');
|
||||
}
|
||||
Reference in New Issue
Block a user