diff --git a/components/ComposeEmail.tsx b/components/ComposeEmail.tsx index 252ceb88..d207721d 100644 --- a/components/ComposeEmail.tsx +++ b/components/ComposeEmail.tsx @@ -174,7 +174,7 @@ export default function ComposeEmail({ setUserMessage(content); // Combine user message with quoted content for the full email body - const combined = `${content}${quotedContent ? quotedContent : ''}`; + const combined = `${content}${quotedContent ? `
${quotedContent}
` : ''}`; setComposeBody(combined); } }; @@ -189,8 +189,8 @@ export default function ComposeEmail({ // For rich editor, combine user message with quoted content if (useRichEditor) { // Wrap the content with proper direction styles - const userContent = userMessage ? `
${userMessage}
` : ''; - const quotedWithStyles = quotedContent ? `
${quotedContent}
` : ''; + const userContent = userMessage ? `
${userMessage}
` : ''; + const quotedWithStyles = quotedContent ? `
${quotedContent}
` : ''; const combinedContent = `${userContent}${quotedWithStyles}`; setComposeBody(combinedContent); @@ -341,12 +341,12 @@ export default function ComposeEmail({ {useRichEditor ? ( -
- {/* User input area - completely separate from quoted content */} +
+ {/* User input area - clearly separated from quoted content */}
{ // Clear placeholder text when user focuses if they haven't started typing @@ -364,39 +364,33 @@ export default function ComposeEmail({ style={{ direction: 'ltr', textAlign: 'left' }} /> - {/* Original email content - also editable */} + {/* Original email content with clear visual separation */} {quotedContent && ( -
+ <> +
+ {composeSubject.startsWith('Re:') ? 'Original message' : 'Forwarded message'} +
{ - const target = e.currentTarget; - // Force LTR on any content added/edited using setAttribute - target.querySelectorAll('*').forEach(el => { - el.setAttribute('style', 'direction: ltr !important; text-align: left !important;'); - // Also add the class to ensure styles are applied - el.classList.add('force-ltr'); - }); - setQuotedContent(target.innerHTML); - // Update the combined content - if (contentEditableRef.current) { - const combined = `${contentEditableRef.current.innerHTML}${target.innerHTML}`; - setComposeBody(combined); - } - }} - style={{ - direction: 'ltr', - textAlign: 'left', - unicodeBidi: 'isolate', - writingMode: 'horizontal-tb' - }} - /> -
+ className="w-full bg-gray-50 border-t border-gray-200 email-content-wrapper" + style={{ direction: 'ltr' }} + > +
+
+ )}
) : ( @@ -409,7 +403,7 @@ export default function ComposeEmail({ style={{ direction: 'ltr', textAlign: 'left' }} /> )} -
+
diff --git a/components/email/ComposeEmail.tsx b/components/email/ComposeEmail.tsx index 5f049d11..86127002 100644 --- a/components/email/ComposeEmail.tsx +++ b/components/email/ComposeEmail.tsx @@ -2,7 +2,10 @@ import { useState, useRef, useEffect } from 'react'; import { formatEmailForReplyOrForward, EmailMessage, EmailAddress } from '@/lib/services/email-service'; -import { X, Paperclip, ChevronDown, ChevronUp, SendHorizontal, Loader2, TextAlignLeft, TextAlignRight } from 'lucide-react'; +import { + X, Paperclip, ChevronDown, ChevronUp, SendHorizontal, Loader2, + AlignLeft, AlignRight +} from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'; @@ -38,6 +41,8 @@ export default function ComposeEmail({ const [bcc, setBcc] = useState(''); const [subject, setSubject] = useState(''); const [body, setBody] = useState(''); + const [userMessage, setUserMessage] = useState(''); + const [originalContent, setOriginalContent] = useState(''); const [showCc, setShowCc] = useState(false); const [showBcc, setShowBcc] = useState(false); const [sending, setSending] = useState(false); @@ -69,7 +74,16 @@ export default function ComposeEmail({ } setSubject(formattedEmail.subject); - setBody(formattedEmail.body); + + // Split the formatted body to separate user's message area from quoted content + const bodyContent = formattedEmail.body; + setBody(bodyContent); + + // Set the original content for the quoted part + const quoteMatch = bodyContent.match(/]*>([\s\S]*?)<\/blockquote>/i); + if (quoteMatch && quoteMatch[0]) { + setOriginalContent(quoteMatch[0]); + } } // Focus editor after initializing @@ -198,92 +212,23 @@ export default function ComposeEmail({ // Check if we have content to forward if (initialEmail.content || initialEmail.html || initialEmail.text) { - try { - // Use the parse-email API endpoint which centralizes our email parsing logic - const response = await fetch('/api/parse-email', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - email: initialEmail.content || initialEmail.html || initialEmail.text || '' - }), - }); - - if (response.ok) { - const parsedEmail = await response.json(); - - // Use the parsed HTML content if available - if (parsedEmail.html) { - contentHtml = parsedEmail.html; - console.log('Successfully parsed HTML content'); - } else if (parsedEmail.text) { - // Text-only content is wrapped in pre-formatted styling - contentHtml = `
${parsedEmail.text}
`; - console.log('Using text content'); - } else { - console.warn('API returned success but no content'); - } - } else { - console.error('API returned error:', await response.text()); - throw new Error('API call failed'); - } - } catch (error) { - console.error('Error parsing email:', error); - - // Fallback processing - using our cleanHtml utility directly - if (initialEmail.html) { - // Import the cleanHtml function dynamically if needed - const { cleanHtml } = await import('@/lib/mail-parser-wrapper'); - contentHtml = cleanHtml(initialEmail.html, { - preserveStyles: true, - scopeStyles: true, - addWrapper: true - }); - console.log('Using direct HTML cleaning fallback'); - } else if (initialEmail.content) { - contentHtml = DOMPurify.sanitize(initialEmail.content, { - ADD_TAGS: ['style'], - FORBID_TAGS: ['script', 'iframe'] - }); - console.log('Using DOMPurify sanitized content'); - } else if (initialEmail.text) { - contentHtml = `
${initialEmail.text}
`; - console.log('Using plain text fallback'); - } - } - } else { - console.warn('No email content available for forwarding'); + // Use the most complete version of content available + contentHtml = DOMPurify.sanitize( + initialEmail.content || initialEmail.html || + `
${initialEmail.text || ''}
` + ); } - // Combine the header and content - using containment - const forwardedContent = ` - ${headerHtml} -
-
- ${contentHtml} -
-
- `; + // Store the complete forwarded content in state + const forwardedContent = `${headerHtml}
${contentHtml}
`; + setOriginalContent(forwardedContent); + + // Set the complete body + setBody(`
${forwardedContent}`); - console.log('Setting body with forwarded content'); - setBody(forwardedContent); } catch (error) { - console.error('Error initializing forwarded email:', error); - - // Provide a minimal template even in error case - setBody(` -
-
-
---------- Forwarded message ---------
-
From: ${initialEmail.from ? formatSender(initialEmail.from) : 'Unknown'}
-
Date: ${new Date().toLocaleString()}
-
Subject: ${initialEmail.subject || '(No subject)'}
-
To: ${initialEmail.to ? formatRecipients(initialEmail.to) : ''}
-
-
-
- Error loading original message content. The original message may still be viewable in your inbox. -
- `); + console.error('Error formatting forwarded email:', error); + setBody('
Error formatting forwarded email content
'); } }; @@ -364,8 +309,14 @@ 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); + if (editorRef.current) { + const content = editorRef.current.innerHTML; + setUserMessage(content); + + // Combine user message with original content for the full email + const combined = `
${content}
${originalContent}`; + setBody(combined); + } }; // Toggle text direction @@ -377,23 +328,17 @@ export default function ComposeEmail({ }; return ( - - -
- - {type === 'new' ? 'New Message' : - type === 'reply' ? 'Reply' : - type === 'reply-all' ? 'Reply All' : - 'Forward'} - + + + + {type === 'new' ? 'New Message' : type === 'forward' ? 'Forward Email' : 'Reply to Email'} -
+
- - - {/* Email header fields */} + + {/* Recipients, Subject fields remain the same */}
To: @@ -477,31 +422,52 @@ export default function ComposeEmail({
- {/* Editor toolbar */} -
- + {/* Updated Email Body Editor */} +
+
+ + +
+ + {/* Email editor with clear separation between user message and original content */} +
+ {/* User's message input area */} +
+ + {/* Original content display with visual separation - only shown for replies/forwards */} + {type !== 'new' && originalContent && ( +
+
+ {type === 'forward' ? 'Forwarded content' : 'Original message'} +
+
+
+ )} +
- {/* Email body editor */} -
- - {/* Attachments list */} + {/* Attachments section remains the same */} {attachments.length > 0 && (
Attachments:
diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index a1b05022..ccbb6f0a 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -673,32 +673,36 @@ export function formatEmailForReplyOrForward( const fromText = email.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', '); const toText = email.to.map(t => t.name ? `${t.name} <${t.address}>` : t.address).join(', '); - // Return specialized body for forward + // Return specialized body for forward with improved styling return { to: '', subject, body: ` -
-

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

-

From: ${fromText}

-

Date: ${formattedDate}

-

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

-

To: ${toText}

-
- ${email.html || email.text ? (email.html || `
${email.text || ''}
`) : '

No content available

'} +
+
+

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

+

From: ${fromText}

+

Date: ${formattedDate}

+

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

+

To: ${toText}

+
+
+ ${email.html || email.text ? (email.html || `
${email.text || ''}
`) : '

No content available

'} +
` }; } - // Format the email body with quote + // Format the email body with improved quote styling for replies const body = ` -
+

-
-
${quoteHeader}
-
- ${quotedContent} +
${quoteHeader}
+
+
+ ${quotedContent} +
`; @@ -754,5 +758,5 @@ function createQuoteHeader(email: EmailMessage): string { ? `${sender.name} <${sender.address}>` : sender?.address || 'Unknown sender'; - return `
On ${formattedDate}, ${fromText} wrote:
`; + return `
On ${formattedDate}, ${fromText} wrote:
`; } \ No newline at end of file