From bc5809520d3092bd856682f5e684a148914e6b04 Mon Sep 17 00:00:00 2001 From: alma Date: Sat, 26 Apr 2025 21:16:03 +0200 Subject: [PATCH] courrier clean 2$ --- components/email/ComposeEmail.tsx | 68 +++++++++++++++++++------------ lib/utils/email-formatter.ts | 56 +++++++++++-------------- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/components/email/ComposeEmail.tsx b/components/email/ComposeEmail.tsx index 99fbe49f..12018b43 100644 --- a/components/email/ComposeEmail.tsx +++ b/components/email/ComposeEmail.tsx @@ -18,6 +18,19 @@ import { sanitizeHtml } from '@/lib/utils/email-formatter'; +/** + * CENTRAL EMAIL COMPOSER COMPONENT + * + * This is the unified, centralized email composer component used throughout the application. + * It handles new emails, replies, and forwards with proper text direction. + * + * All code that needs to compose emails should import this component from: + * @/components/email/ComposeEmail + * + * It uses the centralized email formatter from @/lib/utils/email-formatter.ts + * for consistent handling of email content and text direction. + */ + // Define EmailMessage interface locally instead of importing from server-only file interface EmailAddress { name: string; @@ -246,31 +259,33 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { // Handle editor input const handleEditorInput = () => { - if (editorRef.current) { - // Store the current selection/cursor position - const selection = window.getSelection(); - const range = selection?.getRangeAt(0); - const offset = range?.startOffset || 0; - const container = range?.startContainer; + if (!editorRef.current) return; + + // Store the current selection/cursor position + const selection = window.getSelection(); + const range = selection?.getRangeAt(0); + const offset = range?.startOffset || 0; + const container = range?.startContainer; + + // Capture the content + setEmailContent(editorRef.current.innerHTML); + + // Try to restore the cursor position after React updates + setTimeout(() => { + if (!selection || !range || !container || !editorRef.current) return; - // Capture the content - setEmailContent(editorRef.current.innerHTML); - - // Try to restore the cursor position after React updates - setTimeout(() => { - try { - if (selection && range && container && editorRef.current && editorRef.current.contains(container)) { - const newRange = document.createRange(); - newRange.setStart(container, offset); - newRange.collapse(true); - selection.removeAllRanges(); - selection.addRange(newRange); - } - } catch (e) { - console.error('Error restoring cursor position:', e); + try { + if (editorRef.current.contains(container)) { + const newRange = document.createRange(); + newRange.setStart(container, offset); + newRange.collapse(true); + selection.removeAllRanges(); + selection.addRange(newRange); } - }, 0); - } + } catch (e) { + console.error('Error restoring cursor position:', e); + } + }, 0); }; // Send email @@ -402,7 +417,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { - {/* Email editor - standard LTR for English content */} + {/* Email editor - uses auto direction to detect content language */}
diff --git a/lib/utils/email-formatter.ts b/lib/utils/email-formatter.ts index f3c1b139..c00941d3 100644 --- a/lib/utils/email-formatter.ts +++ b/lib/utils/email-formatter.ts @@ -1,12 +1,11 @@ /** - * CENTRAL EMAIL FORMATTER + * CENTRAL EMAIL FORMATTING UTILITY * - * This is the primary and only email formatting utility to be used. - * All email formatting should go through here to ensure consistent - * handling of text direction and HTML sanitization. + * This is the centralized email formatting utility used throughout the application. + * It provides consistent handling of email content, sanitization, and text direction. * - * IMPORTANT: This formatter is configured for English-only content - * and enforces left-to-right text direction. + * All code that needs to format email content should import from this file. + * Text direction is standardized to LTR (left-to-right) for consistency. */ import DOMPurify from 'isomorphic-dompurify'; @@ -16,38 +15,29 @@ DOMPurify.removeAllHooks(); // Configure DOMPurify for English-only content (always LTR) DOMPurify.addHook('afterSanitizeAttributes', function(node) { - // Force LTR direction on all elements that can have a dir attribute + // We no longer force LTR direction on all elements + // This allows the natural text direction to be preserved if (node instanceof HTMLElement) { - node.setAttribute('dir', 'ltr'); - - // Handle style attribute - if (node.hasAttribute('style')) { - let style = node.getAttribute('style') || ''; - - // Remove any RTL-related styles - style = style.replace(/direction\s*:\s*rtl\s*;?/gi, ''); - style = style.replace(/text-align\s*:\s*right\s*;?/gi, ''); - style = style.replace(/unicode-bidi\s*:[^;]*;?/gi, ''); - - // Add explicit LTR styles - style = style.trim(); - if (style && !style.endsWith(';')) style += ';'; - style += ' direction: ltr; text-align: left;'; - - node.setAttribute('style', style); - } else { - // If no style exists, add default LTR styles - node.setAttribute('style', 'direction: ltr; text-align: left;'); + // Only set direction if not already specified + if (!node.hasAttribute('dir')) { + // Add dir attribute only if not present + node.setAttribute('dir', 'auto'); } + + // Don't forcibly modify text alignment or direction in style attributes + // This allows the component to control text direction instead } }); -// Configure DOMPurify to add certain attributes and forbid others +// Configure DOMPurify to preserve direction attributes DOMPurify.setConfig({ ADD_ATTR: ['dir'], - FORBID_ATTR: ['lang', 'bidi'] + ALLOWED_ATTR: ['style', 'class', 'id', 'dir'] }); +// Note: We ensure LTR text direction is applied in the component level +// when rendering email content + // Interface definitions export interface EmailAddress { name: string; @@ -147,7 +137,7 @@ export function formatForwardedEmail(email: EmailMessage): { const toString = formatEmailAddresses(email.to || []); const dateString = formatEmailDate(email.date); - // Get and sanitize original content (sanitization enforces LTR) + // Get and sanitize original content (sanitization preserves content direction) const originalContent = sanitizeHtml(email.content || email.html || email.text || ''); // Check if the content already has a forwarded message header @@ -177,7 +167,7 @@ export function formatForwardedEmail(email: EmailMessage): {
Subject: ${email.subject || ''}
To: ${toString}
-
+
@@ -223,7 +213,7 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all // Create quote header const quoteHeader = `
On ${formattedDate}, ${fromText} wrote:
`; - // Get and sanitize original content (sanitization enforces LTR) + // Get and sanitize original content (sanitization preserves content direction) const quotedContent = sanitizeHtml(email.html || email.content || email.text || ''); // Format recipients @@ -246,7 +236,7 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
${quoteHeader}
-
+
${quotedContent}