panel 2 courier api restore
This commit is contained in:
parent
c192fb09a7
commit
dab82e212b
@ -6,8 +6,6 @@ import { X, Paperclip, ChevronDown, ChevronUp, SendHorizontal, Loader2 } from 'l
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
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 { decodeEmail } from '@/lib/mail-parser-wrapper';
|
|
||||||
import DOMPurify from 'isomorphic-dompurify';
|
|
||||||
|
|
||||||
interface ComposeEmailProps {
|
interface ComposeEmailProps {
|
||||||
initialEmail?: EmailMessage | null;
|
initialEmail?: EmailMessage | null;
|
||||||
@ -39,8 +37,6 @@ export default function ComposeEmail({
|
|||||||
const [bcc, setBcc] = useState<string>('');
|
const [bcc, setBcc] = useState<string>('');
|
||||||
const [subject, setSubject] = useState<string>('');
|
const [subject, setSubject] = useState<string>('');
|
||||||
const [body, setBody] = useState<string>('');
|
const [body, setBody] = useState<string>('');
|
||||||
|
|
||||||
// UI state
|
|
||||||
const [showCc, setShowCc] = useState<boolean>(false);
|
const [showCc, setShowCc] = useState<boolean>(false);
|
||||||
const [showBcc, setShowBcc] = useState<boolean>(false);
|
const [showBcc, setShowBcc] = useState<boolean>(false);
|
||||||
const [sending, setSending] = useState<boolean>(false);
|
const [sending, setSending] = useState<boolean>(false);
|
||||||
@ -50,13 +46,13 @@ export default function ComposeEmail({
|
|||||||
type: string;
|
type: string;
|
||||||
}>>([]);
|
}>>([]);
|
||||||
|
|
||||||
const editorRef = useRef<HTMLDivElement | null>(null);
|
// Refs
|
||||||
const attachmentInputRef = useRef<HTMLInputElement | null>(null);
|
const editorRef = useRef<HTMLDivElement>(null);
|
||||||
|
const attachmentInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
// Initialize the form when replying to or forwarding an email
|
// Initialize the form when replying to or forwarding an email
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialEmail && type !== 'new') {
|
if (initialEmail && type !== 'new') {
|
||||||
// If it's a forward, use the same approach as Panel 3
|
|
||||||
if (type === 'forward') {
|
if (type === 'forward') {
|
||||||
initializeForwardedEmail();
|
initializeForwardedEmail();
|
||||||
} else {
|
} else {
|
||||||
@ -93,8 +89,6 @@ export default function ComposeEmail({
|
|||||||
}
|
}
|
||||||
}, [initialEmail, type]);
|
}, [initialEmail, type]);
|
||||||
|
|
||||||
// Helper functions for formatting the forwarded message - moved outside try block
|
|
||||||
|
|
||||||
// Format date for the forwarded message header
|
// Format date for the forwarded message header
|
||||||
const formatDate = (date: Date | null): string => {
|
const formatDate = (date: Date | null): string => {
|
||||||
if (!date) return '';
|
if (!date) return '';
|
||||||
@ -132,31 +126,7 @@ export default function ComposeEmail({
|
|||||||
).join(', ');
|
).join(', ');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle editor input
|
// Initialize forwarded email
|
||||||
const handleEditorInput = (e: React.FormEvent<HTMLDivElement>) => {
|
|
||||||
// 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 () => {
|
const initializeForwardedEmail = async () => {
|
||||||
if (!initialEmail) {
|
if (!initialEmail) {
|
||||||
console.error('No email available for forwarding');
|
console.error('No email available for forwarding');
|
||||||
@ -164,7 +134,6 @@ export default function ComposeEmail({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions remain the same
|
|
||||||
try {
|
try {
|
||||||
setSending(true); // Use sending state to show loading
|
setSending(true); // Use sending state to show loading
|
||||||
|
|
||||||
@ -176,82 +145,65 @@ export default function ComposeEmail({
|
|||||||
|
|
||||||
setSubject(formattedSubject);
|
setSubject(formattedSubject);
|
||||||
|
|
||||||
// Create a forwarded message header with proper formatting
|
// Process the email content using the API
|
||||||
const headerContent = `
|
const emailContent = initialEmail.content || '';
|
||||||
<div class="forwarded-header" contenteditable="false" style="border-bottom: 1px solid #e2e2e2; margin-bottom: 15px; padding-bottom: 15px; font-family: Arial, sans-serif; color: #333;">
|
let processedContent = '';
|
||||||
<p style="margin: 5px 0; font-size: 14px;">---------- Forwarded message ---------</p>
|
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>From:</b> ${formatSender(initialEmail.from)}</p>
|
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>Date:</b> ${formatDate(initialEmail.date)}</p>
|
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>Subject:</b> ${initialEmail.subject || ''}</p>
|
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>To:</b> ${formatRecipients(initialEmail.to)}</p>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
// Process content
|
// Only attempt to parse if we have content
|
||||||
let contentBody = '';
|
if (emailContent.trim()) {
|
||||||
let styleContent = '';
|
|
||||||
|
|
||||||
// Check if email content exists
|
|
||||||
if (!initialEmail.content || initialEmail.content.trim() === '') {
|
|
||||||
contentBody = '<div class="editable-content" style="color: #666; font-style: italic; margin-top: 10px;">No content available</div>';
|
|
||||||
} else {
|
|
||||||
try {
|
try {
|
||||||
// Parse content to extract styles and make content editable
|
// Send to server API for parsing
|
||||||
const content = initialEmail.content;
|
const response = await fetch('/api/parse-email', {
|
||||||
|
method: 'POST',
|
||||||
// Extract style tags to preserve them
|
headers: {
|
||||||
const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
'Content-Type': 'application/json',
|
||||||
const styles: string[] = [];
|
},
|
||||||
let styleMatch;
|
body: JSON.stringify({ email: emailContent }),
|
||||||
|
|
||||||
// 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 = `<div class="email-styles" contenteditable="false">${styles.join('')}</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
WHOLE_DOCUMENT: true,
|
|
||||||
KEEP_CONTENT: true,
|
|
||||||
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'option', 'textarea', 'canvas', 'video', 'audio'],
|
|
||||||
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onchange', 'onsubmit'],
|
|
||||||
USE_PROFILES: { html: true, svg: false, svgFilters: false, mathMl: false },
|
|
||||||
FORCE_BODY: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove style tags from sanitized content (we'll add them back separately)
|
const data = await response.json();
|
||||||
let contentWithoutStyles = sanitizedContent.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '');
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || 'Failed to parse email');
|
||||||
|
}
|
||||||
|
|
||||||
// Wrap the remaining content in an editable div
|
processedContent = data.html || data.text || '';
|
||||||
contentBody = `<div class="editable-content">${contentWithoutStyles}</div>`;
|
} catch (error) {
|
||||||
} catch (e) {
|
console.error('Error parsing email content:', error);
|
||||||
console.error('Error sanitizing HTML content:', e);
|
processedContent = '<div style="color: #666; font-style: italic;">Error processing original content</div>';
|
||||||
contentBody = '<div class="editable-content" style="color: #666; font-style: italic; margin-top: 10px;">Error processing original content</div>';
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
processedContent = '<div style="color: #666; font-style: italic;">No content available</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the complete forwarded email with styles preserved separately
|
// Create a clean, well-formatted forwarded message
|
||||||
setBody(styleContent + headerContent + contentBody);
|
const forwardedContent = `
|
||||||
|
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||||
|
---------- Forwarded message ---------<br/>
|
||||||
|
From: ${formatSender(initialEmail.from)}<br/>
|
||||||
|
Date: ${formatDate(initialEmail.date)}<br/>
|
||||||
|
Subject: ${initialEmail.subject || ''}<br/>
|
||||||
|
To: ${formatRecipients(initialEmail.to)}<br/>
|
||||||
|
${initialEmail.cc && initialEmail.cc.length ? `Cc: ${formatRecipients(initialEmail.cc)}<br/>` : ''}
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 10px; color: #374151;">
|
||||||
|
${processedContent}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Set the formatted content
|
||||||
|
setBody(forwardedContent);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing forwarded email:', error);
|
console.error('Error initializing forwarded email:', error);
|
||||||
// Still provide the headers even if there's an error with the content
|
const errorContent = `
|
||||||
const errorHeaderContent = `
|
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||||
<div class="forwarded-header" contenteditable="false" style="border-bottom: 1px solid #e2e2e2; margin-bottom: 15px; padding-bottom: 15px; font-family: Arial, sans-serif; color: #333;">
|
---------- Forwarded message ---------<br/>
|
||||||
<p style="margin: 5px 0; font-size: 14px;">---------- Forwarded message ---------</p>
|
From: ${initialEmail.from ? formatSender(initialEmail.from) : 'Unknown'}<br/>
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>From:</b> ${initialEmail.from ? formatSender(initialEmail.from) : 'Unknown'}</p>
|
Date: ${initialEmail.date ? formatDate(initialEmail.date) : ''}<br/>
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>Date:</b> ${initialEmail.date ? formatDate(initialEmail.date) : ''}</p>
|
Subject: ${initialEmail.subject || ''}<br/>
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>Subject:</b> ${initialEmail.subject || ''}</p>
|
To: ${initialEmail.to ? formatRecipients(initialEmail.to) : ''}<br/>
|
||||||
<p style="margin: 5px 0; font-size: 14px;"><b>To:</b> ${initialEmail.to ? formatRecipients(initialEmail.to) : ''}</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="editable-content" style="color: #ef4444; font-style: italic; margin-top: 10px;">Error loading forwarded content</div>`;
|
<div style="color: #ef4444; font-style: italic; margin-top: 10px;">Error loading forwarded content</div>`;
|
||||||
setBody(errorHeaderContent);
|
setBody(errorContent);
|
||||||
} finally {
|
} finally {
|
||||||
setSending(false);
|
setSending(false);
|
||||||
}
|
}
|
||||||
@ -314,39 +266,12 @@ export default function ComposeEmail({
|
|||||||
try {
|
try {
|
||||||
setSending(true);
|
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({
|
await onSend({
|
||||||
to,
|
to,
|
||||||
cc: cc || undefined,
|
cc: cc || undefined,
|
||||||
bcc: bcc || undefined,
|
bcc: bcc || undefined,
|
||||||
subject,
|
subject,
|
||||||
body: emailBody,
|
body: editorRef.current?.innerHTML || body,
|
||||||
attachments
|
attachments
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -359,6 +284,12 @@ export default function ComposeEmail({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle editor input
|
||||||
|
const handleEditorInput = (e: React.FormEvent<HTMLDivElement>) => {
|
||||||
|
// Store the HTML content for use in the send function
|
||||||
|
setBody(e.currentTarget.innerHTML);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full h-full flex flex-col overflow-hidden shadow-lg">
|
<Card className="w-full h-full flex flex-col overflow-hidden shadow-lg">
|
||||||
<CardHeader className="border-b py-2 px-4">
|
<CardHeader className="border-b py-2 px-4">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user