courrier clean 2$

This commit is contained in:
alma 2025-04-26 19:20:52 +02:00
parent 3e6b8cae1f
commit 4c9fcdeb29
2 changed files with 71 additions and 14 deletions

View File

@ -11,10 +11,11 @@ import { Input } from '@/components/ui/input';
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card';
import DOMPurify from 'isomorphic-dompurify'; import DOMPurify from 'isomorphic-dompurify';
// Import the new email formatter utilities // Import ONLY from the centralized formatter
import { import {
formatForwardedEmail, formatForwardedEmail,
formatReplyEmail, formatReplyEmail,
formatEmailForReplyOrForward,
EmailMessage as FormatterEmailMessage EmailMessage as FormatterEmailMessage
} from '@/lib/utils/email-formatter'; } from '@/lib/utils/email-formatter';
@ -169,7 +170,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
// For forwarding, use the dedicated formatter // For forwarding, use the dedicated formatter
const { subject, content } = formatForwardedEmail(formatterEmail); const { subject, content } = formatForwardedEmail(formatterEmail);
setSubject(subject); setSubject(subject);
// Apply the formatted content directly without additional sanitization
setEmailContent(content); setEmailContent(content);
} else { } else {
// For reply/reply-all, use the reply formatter // For reply/reply-all, use the reply formatter
@ -180,7 +180,6 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
setShowCc(true); setShowCc(true);
} }
setSubject(subject); setSubject(subject);
// Apply the formatted content directly without additional sanitization
setEmailContent(content); setEmailContent(content);
} }
@ -258,7 +257,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
// Handle editor input without re-sanitizing content // Handle editor input without re-sanitizing content
const handleEditorInput = () => { const handleEditorInput = () => {
if (editorRef.current) { if (editorRef.current) {
// Capture innerHTML directly without reapplying DOMPurify // Capture innerHTML directly without reapplying sanitization
setEmailContent(editorRef.current.innerHTML); setEmailContent(editorRef.current.innerHTML);
} }
}; };
@ -307,7 +306,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
{/* Recipients, Subject fields remain the same */} {/* Recipients, Subject fields */}
<div className="p-3 border-b space-y-2"> <div className="p-3 border-b space-y-2">
<div className="flex items-center"> <div className="flex items-center">
<span className="w-16 text-sm font-medium">To:</span> <span className="w-16 text-sm font-medium">To:</span>
@ -391,7 +390,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
</div> </div>
</div> </div>
{/* Updated Email Body Editor */} {/* Email Body Editor */}
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<label htmlFor="body" className="text-sm font-medium">Message</label> <label htmlFor="body" className="text-sm font-medium">Message</label>
@ -415,7 +414,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
contentEditable={!sending} contentEditable={!sending}
className="w-full p-4 min-h-[300px] focus:outline-none" className="w-full p-4 min-h-[300px] focus:outline-none"
onInput={handleEditorInput} onInput={handleEditorInput}
// Use dangerouslySetInnerHTML without sanitizing again // Use dangerouslySetInnerHTML without sanitizing again to preserve our formatting
dangerouslySetInnerHTML={{ __html: emailContent }} dangerouslySetInnerHTML={{ __html: emailContent }}
dir={isRTL ? 'rtl' : 'ltr'} dir={isRTL ? 'rtl' : 'ltr'}
style={{ style={{
@ -425,7 +424,7 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
</div> </div>
</div> </div>
{/* Attachments section remains the same */} {/* Attachments section */}
{attachments.length > 0 && ( {attachments.length > 0 && (
<div className="border-t p-2"> <div className="border-t p-2">
<div className="text-sm font-medium mb-1">Attachments:</div> <div className="text-sm font-medium mb-1">Attachments:</div>

View File

@ -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'; 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 // Interface definitions
export interface EmailAddress { export interface EmailAddress {
name: string; name: string;
@ -73,17 +94,19 @@ export function formatEmailDate(date: Date | string | undefined): string {
/** /**
* Clean HTML content to prevent RTL/LTR issues * 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 { export function cleanHtmlContent(content: string): string {
if (!content) return ''; if (!content) return '';
// First sanitize the HTML // First sanitize the HTML with our configured DOMPurify
const sanitized = DOMPurify.sanitize(content); const sanitized = DOMPurify.sanitize(content);
// Process content to ensure consistent direction // Process content to ensure consistent direction
let processed = sanitized; 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(/dir\s*=\s*["']rtl["']/gi, 'dir="ltr"');
processed = processed.replace(/style\s*=\s*["']([^"']*)direction\s*:\s*rtl;?([^"']*)["']/gi, processed = processed.replace(/style\s*=\s*["']([^"']*)direction\s*:\s*rtl;?([^"']*)["']/gi,
(match, before, after) => `style="${before}direction: ltr;${after}"`); (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): { export function formatForwardedEmail(email: EmailMessage): {
subject: string; 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'): { export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all'): {
to: string; to: string;
@ -206,3 +231,36 @@ export function formatReplyEmail(email: EmailMessage, type: 'reply' | 'reply-all
content 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
};
}
}