From 3f017f82f8010151d4ba850c3068bb37f4752bfd Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 30 Apr 2025 20:39:11 +0200 Subject: [PATCH] courrier formatting --- components/email/EmailContentDisplay.tsx | 181 ----------------------- components/email/EmailPanel.tsx | 41 ++--- components/email/EmailPreview.tsx | 59 +++++--- lib/utils/email-content.ts | 61 +++----- 4 files changed, 74 insertions(+), 268 deletions(-) delete mode 100644 components/email/EmailContentDisplay.tsx diff --git a/components/email/EmailContentDisplay.tsx b/components/email/EmailContentDisplay.tsx deleted file mode 100644 index c1e4c54e..00000000 --- a/components/email/EmailContentDisplay.tsx +++ /dev/null @@ -1,181 +0,0 @@ -'use client'; - -import React, { useEffect, useState } from 'react'; -import DOMPurify from 'dompurify'; -import { formatEmailContent } from '@/lib/utils/email-content'; -import { sanitizeHtml } from '@/lib/utils/email-formatter'; - -/** - * Interface for email content types - */ -interface ProcessedContent { - html: string; - text: string; - isHtml: boolean; -} - -/** - * Interface for component props - */ -interface EmailContentDisplayProps { - content: string; - type?: 'html' | 'text' | 'auto'; - isRawEmail?: boolean; - className?: string; -} - -/** - * Parse raw email content into HTML and text parts - * This is a helper function used when processing raw email formats - */ -function parseRawEmail(rawContent: string): { html: string; text: string } { - // Simple parser for demonstration - in production, use a proper MIME parser - const hasHtmlPart = rawContent.includes('/i) || - rawContent.match(//i); - - if (htmlMatch) { - htmlPart = htmlMatch[0]; - } else { - // Fallback extraction - const parts = rawContent.split(/(?:--boundary|\r\n\r\n)/); - htmlPart = parts.find(part => - part.includes('Content-Type: text/html') || - part.includes(']+>/g, ' ').replace(/\s+/g, ' ').trim() - }; - } - - return { - html: '', - text: rawContent - }; -} - -/** - * EmailContentDisplay component - displays formatted email content - * with proper security, styling and support for different email formats - */ -const EmailContentDisplay: React.FC = ({ - content, - type = 'auto', - isRawEmail = false, - className = '' -}) => { - const [processedContent, setProcessedContent] = useState({ - html: '', - text: '', - isHtml: false - }); - - // Process the email content when it changes - useEffect(() => { - if (!content) { - setProcessedContent({ - html: '', - text: '', - isHtml: false - }); - return; - } - - try { - if (isRawEmail) { - // Parse raw email content - const parsed = parseRawEmail(content); - - // Check which content to use based on type and availability - const useHtml = (type === 'html' || (type === 'auto' && parsed.html)) && !!parsed.html; - - if (useHtml) { - // Use the enhanced sanitizeHtml function from email-formatter - const sanitizedHtml = sanitizeHtml(parsed.html); - - setProcessedContent({ - html: sanitizedHtml, - text: parsed.text, - isHtml: true - }); - } else { - // Format plain text properly - const formattedText = formatEmailContent({ text: parsed.text }); - - setProcessedContent({ - html: formattedText, - text: parsed.text, - isHtml: false - }); - } - } else { - // Treat as direct content (not raw email) - const isHtmlContent = type === 'html' || ( - type === 'auto' && ( - content.includes('') || - content.includes(']+>/g, ' ').replace(/\s+/g, ' ').trim(), - isHtml: true - }); - } else { - // Format plain text properly using formatEmailContent - const formattedText = formatEmailContent({ text: content }); - - setProcessedContent({ - html: formattedText, - text: content, - isHtml: false - }); - } - } - } catch (err) { - console.error('Error processing email content:', err); - // Fallback to plain text with basic formatting - setProcessedContent({ - html: `
${content.replace(//g, '>').replace(/\n/g, '
')}
`, - text: content, - isHtml: false - }); - } - }, [content, type, isRawEmail]); - - return ( -
-
-
- ); -}; - -export default EmailContentDisplay; \ No newline at end of file diff --git a/components/email/EmailPanel.tsx b/components/email/EmailPanel.tsx index 986e2136..6031a42b 100644 --- a/components/email/EmailPanel.tsx +++ b/components/email/EmailPanel.tsx @@ -79,37 +79,26 @@ export default function EmailPanel({ console.log('EmailPanel: Raw email:', email); - // If content is already an object with html/text, use it directly - if (email.content && typeof email.content === 'object') { - console.log('EmailPanel: Using existing content object'); - return { - ...email, - content: { - text: email.content.text || '', - html: email.content.html || '' - } - }; + // CRITICAL FIX: Simplify email formatting to prevent double processing + // Just normalize the content structure, don't try to format content here + // The actual formatting will happen in EmailPreview with formatEmailContent + + // If all fields are already present, just return as is + if (email.content && typeof email.content === 'object' && email.content.html && email.content.text) { + return email; } - // If content is a string, convert it to object format - if (typeof email.content === 'string') { - console.log('EmailPanel: Converting string content to object'); - return { - ...email, - content: { - text: email.text || email.content, - html: email.html || email.content - } - }; - } - - // Fallback to html/text properties - console.log('EmailPanel: Using html/text properties'); + // Create a standardized email object with consistent content structure return { ...email, + // Ensure content is an object with html and text properties content: { - text: email.text || '', - html: email.html || '' + text: typeof email.content === 'object' ? email.content.text : + typeof email.text === 'string' ? email.text : + typeof email.content === 'string' ? email.content : '', + html: typeof email.content === 'object' ? email.content.html : + typeof email.html === 'string' ? email.html : + typeof email.content === 'string' ? email.content : '' } }; }, [email]); diff --git a/components/email/EmailPreview.tsx b/components/email/EmailPreview.tsx index 2ce03bcf..2395e3ae 100644 --- a/components/email/EmailPreview.tsx +++ b/components/email/EmailPreview.tsx @@ -107,8 +107,25 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP return ''; } - // Use the improved, standardized email content formatter - return formatEmailContent(email); + // CRITICAL FIX: Send consistent input format to formatEmailContent + try { + // Log what we're sending to formatEmailContent for debugging + console.log('EmailPreview: Calling formatEmailContent with email:', + JSON.stringify({ + id: email.id, + contentType: typeof email.content, + hasHtml: typeof email.content === 'object' ? !!email.content.html : false, + hasText: typeof email.content === 'object' ? !!email.content.text : false, + hasHtmlProp: !!email.html, + hasTextProp: !!email.text + }) + ); + + return formatEmailContent(email); + } catch (error) { + console.error('Error formatting email content:', error); + return `
Error rendering email content: ${error instanceof Error ? error.message : 'Unknown error'}
`; + } }, [email]); // Display loading state @@ -219,35 +236,33 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP {/* Email content */}
+ {/* IMPROVED: Simplified email content container with better styling */}
-
- {formattedContent ? ( -
- ) : ( -
-

This email does not contain any content.

-
- )} -
+ {/* Render the formatted content directly */} + {formattedContent ? ( +
+ ) : ( +
+

This email does not contain any content.

+
+ )}
+ {/* Only in development mode: Show debugging info */} {process.env.NODE_ENV === 'development' && ( -
+
Email Debug Info -
+

Email ID: {email.id}

Content Type: { typeof email.content === 'object' && email.content?.html diff --git a/lib/utils/email-content.ts b/lib/utils/email-content.ts index b6ceb09a..56b61adc 100644 --- a/lib/utils/email-content.ts +++ b/lib/utils/email-content.ts @@ -51,12 +51,12 @@ export function formatEmailContent(email: any): string { // If we have HTML content, sanitize and standardize it if (isHtml && content) { - // Make sure we have a complete HTML structure + // CRITICAL FIX: Check for browser environment since DOMParser is browser-only const hasHtmlTag = content.includes(' { + return `src="data:image/png;base64,${p1.replace(/\s+/g, '')}"`; + }); // Check for RTL content and set appropriate direction const rtlLangPattern = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/; const containsRtlText = rtlLangPattern.test(textContent); const dirAttribute = containsRtlText ? 'dir="rtl"' : 'dir="ltr"'; - // Wrap the content in standard email container with responsive styling - return ` -

- `; + // CRITICAL FIX: Use a single wrapper with all necessary styles for better email client compatibility + return ``; } // If we only have text content, format it properly else if (textContent) { @@ -148,25 +141,15 @@ export function formatEmailContent(email: any): string { .replace(/((?:
){2,})/g, '

') // Convert multiple newlines to paragraphs .replace(/
<\/p>/g, '

') // Fix any

combinations .replace(/


/g, '

'); // Fix any


combinations - - return ` -

- `; + + // CRITICAL FIX: Use consistent structure with HTML emails for better compatibility + return ``; } // Default case: empty or unrecognized content - return ''; + return ''; } catch (error) { console.error('formatEmailContent: Error formatting email content:', error); - return ` - - `; + return ``; } } \ No newline at end of file