diff --git a/components/email/ComposeEmail.tsx b/components/email/ComposeEmail.tsx index 6849e377..4c9aef4a 100644 --- a/components/email/ComposeEmail.tsx +++ b/components/email/ComposeEmail.tsx @@ -132,7 +132,31 @@ export default function ComposeEmail({ ).join(', '); }; - // New function to initialize forwarded email using same approach as Panel 3 + // Handle editor input + const handleEditorInput = (e: React.FormEvent) => { + // Only store the content of the editable area, not the entire HTML with CSS + // This prevents breaking complex CSS when editing + if (editorRef.current) { + // If we're in forward mode and the editor contains our wrapper structure + const editableContent = editorRef.current.querySelector('.editable-content'); + if (type === 'forward' && editableContent) { + // Only update the editable portion, preserving the CSS and header + const headerSection = editorRef.current.querySelector('.forwarded-header'); + const styleSection = editorRef.current.querySelector('.email-styles'); + + // Combine the preserved sections with the updated editable content + const updatedContent = (styleSection?.outerHTML || '') + + (headerSection?.outerHTML || '') + + editableContent.innerHTML; + setBody(updatedContent); + } else { + // For new emails or replies, we can use the entire content + setBody(e.currentTarget.innerHTML); + } + } + }; + + // Modified initializeForwardedEmail to separate CSS from content const initializeForwardedEmail = async () => { if (!initialEmail) { console.error('No email available for forwarding'); @@ -140,6 +164,7 @@ export default function ComposeEmail({ return; } + // Helper functions remain the same try { setSending(true); // Use sending state to show loading @@ -153,7 +178,7 @@ export default function ComposeEmail({ // Create a forwarded message header with proper formatting const headerContent = ` -
+

---------- Forwarded message ---------

From: ${formatSender(initialEmail.from)}

Date: ${formatDate(initialEmail.date)}

@@ -161,17 +186,35 @@ export default function ComposeEmail({

To: ${formatRecipients(initialEmail.to)}

`; - // Use enhanced DOMPurify sanitization options similar to EmailPreview component - // This will preserve more styles and formatting from the original email + // Process content let contentBody = ''; + let styleContent = ''; // Check if email content exists if (!initialEmail.content || initialEmail.content.trim() === '') { - contentBody = '
No content available
'; + contentBody = '
No content available
'; } else { try { - // Use enhanced DOMPurify sanitization options directly from Panel 3 - const sanitizedContent = DOMPurify.sanitize(initialEmail.content, { + // Parse content to extract styles and make content editable + const content = initialEmail.content; + + // Extract style tags to preserve them + const styleRegex = /]*>([\s\S]*?)<\/style>/gi; + const styles: string[] = []; + let styleMatch; + + // Find all style tags and collect them + while ((styleMatch = styleRegex.exec(content)) !== null) { + styles.push(styleMatch[0]); + } + + // Combine all styles into one non-editable section + if (styles.length > 0) { + styleContent = ``; + } + + // Use DOMPurify to sanitize the rest of the HTML content + const sanitizedContent = DOMPurify.sanitize(content, { ADD_TAGS: ['style', 'meta', 'link', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr', 'font', 'div', 'span', 'a', 'img', 'b', 'strong', 'i', 'em', 'u', 'br', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'center', 'section', 'header', 'footer', 'article', 'nav', 'keyframes'], ADD_ATTR: ['*', 'colspan', 'rowspan', 'cellpadding', 'cellspacing', 'border', 'bgcolor', 'width', 'height', 'align', 'valign', 'class', 'id', 'style', 'color', 'face', 'size', 'background', 'src', 'href', 'target', 'rel', 'alt', 'title', 'name', 'animation', 'animation-name', 'animation-duration', 'animation-fill-mode'], ALLOW_UNKNOWN_PROTOCOLS: true, @@ -182,27 +225,32 @@ export default function ComposeEmail({ USE_PROFILES: { html: true, svg: false, svgFilters: false, mathMl: false }, FORCE_BODY: true }); - contentBody = sanitizedContent; + + // Remove style tags from sanitized content (we'll add them back separately) + let contentWithoutStyles = sanitizedContent.replace(/]*>[\s\S]*?<\/style>/gi, ''); + + // Wrap the remaining content in an editable div + contentBody = `
${contentWithoutStyles}
`; } catch (e) { console.error('Error sanitizing HTML content:', e); - contentBody = '
Error processing original content
'; + contentBody = '
Error processing original content
'; } } - // Set the complete forwarded email with enhanced preservation of styles - setBody(headerContent + contentBody); + // Set the complete forwarded email with styles preserved separately + setBody(styleContent + headerContent + contentBody); } catch (error) { console.error('Error initializing forwarded email:', error); // Still provide the headers even if there's an error with the content const errorHeaderContent = ` -
+

---------- Forwarded message ---------

From: ${initialEmail.from ? formatSender(initialEmail.from) : 'Unknown'}

Date: ${initialEmail.date ? formatDate(initialEmail.date) : ''}

Subject: ${initialEmail.subject || ''}

To: ${initialEmail.to ? formatRecipients(initialEmail.to) : ''}

-
Error loading forwarded content
`; +
Error loading forwarded content
`; setBody(errorHeaderContent); } finally { setSending(false); @@ -266,12 +314,39 @@ export default function ComposeEmail({ try { setSending(true); + // Get the email content + let emailBody = ''; + if (editorRef.current) { + // For forwarded emails, make sure to include both the style and content sections + if (type === 'forward') { + // Gather all parts: styles, header, and editable content + const styleSection = editorRef.current.querySelector('.email-styles'); + const headerSection = editorRef.current.querySelector('.forwarded-header'); + const editableContent = editorRef.current.querySelector('.editable-content'); + + // Combine all sections for the final email body + emailBody = + (styleSection?.outerHTML || '') + + (headerSection?.outerHTML || '') + + (editableContent?.innerHTML || editorRef.current.innerHTML); + + // Remove contenteditable attributes as they're not needed in the sent email + emailBody = emailBody.replace(/contenteditable="[^"]*"/g, ''); + } else { + // For new emails or replies, use the entire content + emailBody = editorRef.current.innerHTML; + } + } else { + // Fallback to using body state + emailBody = body; + } + await onSend({ to, cc: cc || undefined, bcc: bcc || undefined, subject, - body: editorRef.current?.innerHTML || body, + body: emailBody, attachments }); @@ -284,12 +359,6 @@ export default function ComposeEmail({ } }; - // Handle editor input - const handleEditorInput = (e: React.FormEvent) => { - // Store the HTML content for use in the send function - setBody(e.currentTarget.innerHTML); - }; - return (