diff --git a/components/email/ComposeEmail.tsx b/components/email/ComposeEmail.tsx index 62656c72..432af0bf 100644 --- a/components/email/ComposeEmail.tsx +++ b/components/email/ComposeEmail.tsx @@ -11,10 +11,11 @@ import { Input } from '@/components/ui/input'; import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'; import DOMPurify from 'isomorphic-dompurify'; -// Import the new email formatter utilities +// Import ONLY from the centralized formatter import { formatForwardedEmail, - formatReplyEmail, + formatReplyEmail, + formatEmailForReplyOrForward, EmailMessage as FormatterEmailMessage } from '@/lib/utils/email-formatter'; @@ -169,7 +170,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { // For forwarding, use the dedicated formatter const { subject, content } = formatForwardedEmail(formatterEmail); setSubject(subject); - // Apply the formatted content directly without additional sanitization setEmailContent(content); } else { // For reply/reply-all, use the reply formatter @@ -180,7 +180,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { setShowCc(true); } setSubject(subject); - // Apply the formatted content directly without additional sanitization setEmailContent(content); } @@ -258,7 +257,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { // Handle editor input without re-sanitizing content const handleEditorInput = () => { if (editorRef.current) { - // Capture innerHTML directly without reapplying DOMPurify + // Capture innerHTML directly without reapplying sanitization setEmailContent(editorRef.current.innerHTML); } }; @@ -307,7 +306,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { - {/* Recipients, Subject fields remain the same */} + {/* Recipients, Subject fields */}
To: @@ -391,7 +390,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
- {/* Updated Email Body Editor */} + {/* Email Body Editor */}
@@ -415,7 +414,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) { contentEditable={!sending} className="w-full p-4 min-h-[300px] focus:outline-none" onInput={handleEditorInput} - // Use dangerouslySetInnerHTML without sanitizing again + // Use dangerouslySetInnerHTML without sanitizing again to preserve our formatting dangerouslySetInnerHTML={{ __html: emailContent }} dir={isRTL ? 'rtl' : 'ltr'} style={{ @@ -425,7 +424,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
- {/* Attachments section remains the same */} + {/* Attachments section */} {attachments.length > 0 && (
Attachments:
diff --git a/lib/utils/email-formatter.ts b/lib/utils/email-formatter.ts index 8c894190..e1f7dfd0 100644 --- a/lib/utils/email-formatter.ts +++ b/lib/utils/email-formatter.ts @@ -1,9 +1,30 @@ /** - * Utilities for email formatting with proper text direction handling + * CENTRAL EMAIL FORMATTER + * + * 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. */ import DOMPurify from 'isomorphic-dompurify'; +// Configure DOMPurify to preserve direction attributes +DOMPurify.addHook('afterSanitizeAttributes', function(node) { + // Preserve direction attributes + if (node.hasAttribute('dir')) { + node.setAttribute('dir', node.getAttribute('dir') || 'ltr'); + } + // Preserve text-align in styles + if (node.hasAttribute('style')) { + const style = node.getAttribute('style') || ''; + if (style.includes('text-align')) { + // Keep existing alignment + } else if (node.hasAttribute('dir') && node.getAttribute('dir') === 'rtl') { + node.setAttribute('style', style + '; text-align: right;'); + } + } +}); + // Interface definitions export interface EmailAddress { name: string; @@ -73,17 +94,19 @@ export function formatEmailDate(date: Date | string | undefined): string { /** * Clean HTML content to prevent RTL/LTR issues + * This is the ONLY function that should be used for cleaning HTML content */ export function cleanHtmlContent(content: string): string { if (!content) return ''; - // First sanitize the HTML + // First sanitize the HTML with our configured DOMPurify const sanitized = DOMPurify.sanitize(content); // Process content to ensure consistent direction let processed = sanitized; - // Replace RTL attributes with LTR + // Replace RTL attributes with LTR if needed + // We're now more careful to only modify direction attributes if needed processed = processed.replace(/dir\s*=\s*["']rtl["']/gi, 'dir="ltr"'); processed = processed.replace(/style\s*=\s*["']([^"']*)direction\s*:\s*rtl;?([^"']*)["']/gi, (match, before, after) => `style="${before}direction: ltr;${after}"`); @@ -92,7 +115,8 @@ export function cleanHtmlContent(content: string): string { } /** - * Format an email for forwarding + * Format an email for forwarding - CENTRAL IMPLEMENTATION + * All other formatting functions should be deprecated in favor of this one */ export function formatForwardedEmail(email: EmailMessage): { subject: string; @@ -135,7 +159,8 @@ export function formatForwardedEmail(email: EmailMessage): { } /** - * Format an email for reply or reply-all + * Format an email for reply or reply-all - CENTRAL IMPLEMENTATION + * All other formatting functions should be deprecated in favor of this one */ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all'): { to: string; @@ -205,4 +230,37 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all subject, content }; +} + +/** + * COMPATIBILITY LAYER: For backward compatibility with the old email-formatter.ts + * These functions map to our new implementation but preserve the old interface + */ + +// For compatibility with old code that might be using the other email-formatter.ts +export function formatEmailForReplyOrForward( + email: EmailMessage, + type: 'reply' | 'reply-all' | 'forward' +): { + to: string; + cc?: string; + subject: string; + body: string; +} { + if (type === 'forward') { + const { subject, content } = formatForwardedEmail(email); + return { + to: '', + subject, + body: content + }; + } else { + const { to, cc, subject, content } = formatReplyEmail(email, type as 'reply' | 'reply-all'); + return { + to, + cc, + subject, + body: content + }; + } } \ No newline at end of file