courrier preview
This commit is contained in:
parent
1211b43f46
commit
080b9ab795
@ -200,48 +200,58 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
// Provide a basic template if the content is empty
|
||||
const { fromStr, toStr, ccStr, dateStr, subject } = getFormattedInfoForEmail(initialEmail);
|
||||
const fallbackContent = `
|
||||
<div style="margin: 20px 0 10px 0; color: #666; font-family: Arial, sans-serif;">
|
||||
<div style="border-bottom: 1px solid #ccc; margin-bottom: 10px; padding-bottom: 5px;">
|
||||
<div>---------------------------- Forwarded Message ----------------------------</div>
|
||||
<div class="forwarded-email-container">
|
||||
<div class="forwarded-email-header" style="margin: 20px 0 10px 0; color: #666; font-family: Arial, sans-serif;">
|
||||
<div style="border-bottom: 1px solid #ccc; margin-bottom: 10px; padding-bottom: 5px;">
|
||||
<div>---------------------------- Forwarded Message ----------------------------</div>
|
||||
</div>
|
||||
<table style="margin-bottom: 10px; font-size: 14px; width: 100%;">
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">From:</td>
|
||||
<td style="padding: 3px 0;">${fromStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">Date:</td>
|
||||
<td style="padding: 3px 0;">${dateStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">Subject:</td>
|
||||
<td style="padding: 3px 0;">${subject || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">To:</td>
|
||||
<td style="padding: 3px 0;">${toStr}</td>
|
||||
</tr>
|
||||
${ccStr ? `
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">Cc:</td>
|
||||
<td style="padding: 3px 0;">${ccStr}</td>
|
||||
</tr>` : ''}
|
||||
</table>
|
||||
<div style="border-bottom: 1px solid #ccc; margin-top: 5px; margin-bottom: 15px; padding-bottom: 5px;">
|
||||
<div>----------------------------------------------------------------------</div>
|
||||
</div>
|
||||
</div>
|
||||
<table style="margin-bottom: 10px; font-size: 14px;">
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">From:</td>
|
||||
<td style="padding: 3px 0;">${fromStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Date:</td>
|
||||
<td style="padding: 3px 0;">${dateStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Subject:</td>
|
||||
<td style="padding: 3px 0;">${subject || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">To:</td>
|
||||
<td style="padding: 3px 0;">${toStr}</td>
|
||||
</tr>
|
||||
${ccStr ? `
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Cc:</td>
|
||||
<td style="padding: 3px 0;">${ccStr}</td>
|
||||
</tr>` : ''}
|
||||
</table>
|
||||
<div style="border-bottom: 1px solid #ccc; margin-top: 5px; margin-bottom: 15px; padding-bottom: 5px;">
|
||||
<div>----------------------------------------------------------------------</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forwarded-content" style="margin: 0; color: #333;">
|
||||
[Original message content could not be loaded]
|
||||
<div class="forwarded-email-content">[Original message content could not be loaded]</div>
|
||||
</div>
|
||||
`;
|
||||
setEmailContent(fallbackContent);
|
||||
} else {
|
||||
console.log('Setting forward content:', {
|
||||
length: content.length,
|
||||
isHtml: formatted.content.isHtml
|
||||
isHtml: formatted.content.isHtml,
|
||||
hasForwardedTable: content.includes('---------------------------- Forwarded Message ----------------------------'),
|
||||
hasClosingContainer: content.includes('</div></div>')
|
||||
});
|
||||
setEmailContent(content);
|
||||
|
||||
// Ensure the content is wrapped in a div to preserve structure
|
||||
// Add a distinctive id to help with debugging
|
||||
if (!content.startsWith('<div')) {
|
||||
const wrappedContent = `<div id="wrapped-forward-content">${content}</div>`;
|
||||
setEmailContent(wrappedContent);
|
||||
} else {
|
||||
setEmailContent(content);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle attachments for forward (original attachments + extracted inline images)
|
||||
|
||||
@ -73,7 +73,11 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
||||
<div className="mt-4 p-2 text-xs bg-gray-100 border rounded">
|
||||
<p><strong>Content Type:</strong> {typeof content === 'string' ? 'Text' : 'HTML'}</p>
|
||||
<p><strong>HTML Length:</strong> {typeof content === 'string' ? content.length : content?.html?.length || 0}</p>
|
||||
<p><strong>Text Length:</strong> {typeof content === 'string' ? content.length : content?.text?.length || 0}</p>
|
||||
<p><strong>Text Length:</strong> {typeof content === 'string' ? 0 : content?.text?.length || 0}</p>
|
||||
<p><strong>Has Forwarded Header:</strong> {typeof content === 'string' ?
|
||||
content.includes('Forwarded Message') :
|
||||
(content?.html?.includes('Forwarded Message') || content?.text?.includes('Forwarded Message')) ? 'Yes' : 'No'
|
||||
}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -103,6 +107,29 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
||||
border-left: none;
|
||||
border-right: 2px solid #ddd;
|
||||
}
|
||||
|
||||
/* Specific styling for forwarded email header */
|
||||
:global(.forwarded-email-header) {
|
||||
margin: 20px 0 10px 0 !important;
|
||||
color: #666 !important;
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
|
||||
:global(.forwarded-email-header table) {
|
||||
margin-bottom: 10px !important;
|
||||
font-size: 14px !important;
|
||||
width: 100% !important;
|
||||
border-collapse: collapse !important;
|
||||
}
|
||||
|
||||
:global(.forwarded-email-header td) {
|
||||
padding: 3px 5px !important;
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
:global(.forwarded-email-content) {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -48,39 +48,19 @@ export default function EmailDetailView({
|
||||
console.log('EmailDetailView renderEmailContent', {
|
||||
hasContent: !!email.content,
|
||||
contentType: typeof email.content,
|
||||
hasHtml: !!email.html,
|
||||
hasText: !!email.text
|
||||
hasHtml: !!email.content?.html,
|
||||
hasText: !!email.content?.text
|
||||
});
|
||||
|
||||
// Determine what content to use and how to handle it
|
||||
let contentToUse = '';
|
||||
// Import the centralized rendering function
|
||||
const { formatEmailContent } = require('@/lib/utils/email-utils');
|
||||
|
||||
if (email.content) {
|
||||
// If content is a string, use it directly
|
||||
if (typeof email.content === 'string') {
|
||||
contentToUse = email.content;
|
||||
}
|
||||
// If content is an object with html/text properties
|
||||
else if (typeof email.content === 'object') {
|
||||
contentToUse = email.content.html || email.content.text || '';
|
||||
}
|
||||
}
|
||||
// Fall back to html or text properties if content is not available
|
||||
else if (email.html) {
|
||||
contentToUse = email.html;
|
||||
}
|
||||
else if (email.text) {
|
||||
// Convert plain text to HTML with line breaks
|
||||
contentToUse = email.text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\n/g, '<br>');
|
||||
}
|
||||
// Use the centralized formatting function
|
||||
const formattedContent = formatEmailContent(email);
|
||||
|
||||
// Return content or fallback message
|
||||
return contentToUse ?
|
||||
<div dangerouslySetInnerHTML={{ __html: contentToUse }} /> :
|
||||
// Return formatted content or fallback message
|
||||
return formattedContent ?
|
||||
<div dangerouslySetInnerHTML={{ __html: formattedContent }} /> :
|
||||
<div className="text-gray-500">No content available</div>;
|
||||
} catch (e) {
|
||||
console.error('Error rendering email:', e);
|
||||
|
||||
@ -91,13 +91,22 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
console.log('Setting initial content in editor', {
|
||||
length: initialContent.length,
|
||||
startsWithHtml: initialContent.trim().startsWith('<'),
|
||||
hasForwardedHeader: initialContent.includes('Forwarded Message'),
|
||||
});
|
||||
|
||||
// Detect text direction
|
||||
const direction = detectTextDirection(initialContent);
|
||||
|
||||
// Preserve forwarded email structure by wrapping in a container if needed
|
||||
let contentToSet = initialContent;
|
||||
if (initialContent.includes('Forwarded Message') && !initialContent.includes('forwarded-email-container')) {
|
||||
// This might be a forwarded email that wasn't properly wrapped
|
||||
contentToSet = `<div class="forwarded-email-container">${initialContent}</div>`;
|
||||
console.log('Adding container to forwarded email content');
|
||||
}
|
||||
|
||||
// Process HTML content using centralized utility
|
||||
const sanitizedContent = processHtmlContent(initialContent);
|
||||
const sanitizedContent = processHtmlContent(contentToSet);
|
||||
|
||||
// Check if sanitized content is valid
|
||||
if (sanitizedContent.trim().length === 0) {
|
||||
@ -105,7 +114,7 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
// Try to extract text content if HTML processing failed
|
||||
try {
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = initialContent;
|
||||
tempDiv.innerHTML = contentToSet;
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || 'Empty content';
|
||||
|
||||
// Set text directly to ensure something displays
|
||||
@ -209,27 +218,36 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
try {
|
||||
console.log('Updating content in editor:', {
|
||||
contentLength: initialContent.length,
|
||||
startsWithHtml: initialContent.trim().startsWith('<')
|
||||
startsWithHtml: initialContent.trim().startsWith('<'),
|
||||
hasForwardedHeader: initialContent.includes('Forwarded Message'),
|
||||
});
|
||||
|
||||
// Detect text direction
|
||||
const direction = detectTextDirection(initialContent);
|
||||
|
||||
// Special handling for forwarded email content
|
||||
let contentToSet = initialContent;
|
||||
if (initialContent.includes('Forwarded Message') && !initialContent.includes('forwarded-email-container')) {
|
||||
// This might be a forwarded email that wasn't properly wrapped
|
||||
contentToSet = `<div class="forwarded-email-container">${initialContent}</div>`;
|
||||
console.log('Adding container to forwarded email content during update');
|
||||
}
|
||||
|
||||
// Process HTML content using centralized utility
|
||||
const sanitizedContent = processHtmlContent(initialContent);
|
||||
const sanitizedContent = processHtmlContent(contentToSet);
|
||||
|
||||
// Check if content is valid HTML
|
||||
if (sanitizedContent.trim().length === 0) {
|
||||
console.warn('Sanitized content is empty, using original content');
|
||||
// If sanitized content is empty, try to extract text from original
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = initialContent;
|
||||
tempDiv.innerHTML = contentToSet;
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || '';
|
||||
|
||||
// Create simple HTML with text content
|
||||
quillRef.current.setText(textContent);
|
||||
} else {
|
||||
// SIMPLIFIED: Set content directly to the root element rather than using clipboard
|
||||
// Set content directly to the root element rather than using clipboard
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
|
||||
@ -65,6 +65,27 @@ export function extractEmailContent(email: any): { text: string; html: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special case for YCharts and similar emails where content is detected as object
|
||||
// but doesn't have standard html/text properties - stringify and try to extract
|
||||
if (!textContent && !htmlContent) {
|
||||
try {
|
||||
// Try to extract from stringified version
|
||||
const contentString = JSON.stringify(email.content);
|
||||
if (contentString && contentString.includes('<html') || contentString.includes('<body')) {
|
||||
// Extract HTML content from the stringified object
|
||||
const htmlMatch = contentString.match(/<html[^>]*>([\s\S]*)<\/html>/i) ||
|
||||
contentString.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
||||
|
||||
if (htmlMatch && htmlMatch[1]) {
|
||||
htmlContent = htmlMatch[1];
|
||||
console.log('Extracted HTML from stringified content object');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Failed to extract from stringified content:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof email.content === 'string') {
|
||||
// Check if content is likely HTML
|
||||
@ -213,6 +234,16 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
|
||||
// Use the centralized sanitizeHtml function
|
||||
let sanitizedContent = sanitizeHtml(htmlContent);
|
||||
|
||||
// If sanitized content is empty but original content wasn't,
|
||||
// try to extract text as a fallback
|
||||
if (!sanitizedContent.trim() && htmlContent.trim()) {
|
||||
console.warn('Sanitized content is empty, attempting to extract text as fallback');
|
||||
const extractedText = extractTextFromHtml(htmlContent);
|
||||
if (extractedText) {
|
||||
return `<div style="white-space: pre-wrap;">${extractedText}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix URL encoding issues and clean up content
|
||||
try {
|
||||
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
||||
@ -243,12 +274,14 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
|
||||
if (src) {
|
||||
// Don't modify cid: URLs as they are handled specially in email clients
|
||||
if (src.startsWith('cid:')) {
|
||||
// Keep cid: URLs as they are
|
||||
// Keep cid: URLs as they are but add data attribute for debugging
|
||||
img.setAttribute('data-cid-preserved', 'true');
|
||||
console.log('Preserving CID reference:', src);
|
||||
}
|
||||
// Fix http:// URLs to https:// for security
|
||||
// Fix http:// URLs to https:// for security - unless they're to known image hosts
|
||||
else if (src.startsWith('http://')) {
|
||||
img.setAttribute('src', src.replace('http://', 'https://'));
|
||||
console.log('Fixed HTTP image URL:', src);
|
||||
}
|
||||
// Handle relative URLs that might be broken
|
||||
else if (!src.startsWith('https://') && !src.startsWith('data:')) {
|
||||
@ -303,17 +336,21 @@ export function processHtmlContent(htmlContent: string, textContent?: string): s
|
||||
}
|
||||
|
||||
// Fix common email client quirks without breaking cid: URLs
|
||||
return sanitizedContent
|
||||
sanitizedContent = sanitizedContent
|
||||
// Fix for Outlook WebVML content
|
||||
.replace(/<!--\[if\s+gte\s+mso/g, '<!--[if gte mso')
|
||||
// Fix for broken image paths WITHOUT replacing cid: URLs
|
||||
.replace(/(src|background)="(?!(?:https?:|data:|cid:))/gi, '$1="https://')
|
||||
.replace(/(<img[^>]+src=")(?!(?:https?:|data:|cid:))([^"]+)(")/gi, '$1https://example.com/$2$3')
|
||||
// Fix for broken background URLs WITHOUT replacing cid: URLs
|
||||
.replace(/(background=")(?!(?:https?:|data:|cid:))([^"]+)(")/gi, '$1https://example.com/$2$3')
|
||||
// Fix for base64 images that might be broken across lines
|
||||
.replace(/src="data:image\/[^;]+;base64,\s*([^"]+)\s*"/gi, (match, p1) => {
|
||||
return `src="data:image/png;base64,${p1.replace(/\s+/g, '')}"`;
|
||||
})
|
||||
// Remove excessive whitespace from the HTML string itself
|
||||
.replace(/>\s+</g, '> <');
|
||||
|
||||
return sanitizedContent;
|
||||
} catch (error) {
|
||||
console.error('Error processing HTML content:', error);
|
||||
return htmlContent;
|
||||
|
||||
@ -336,45 +336,47 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
|
||||
// Extract content using the centralized extraction function
|
||||
const { text, html } = extractEmailContent(originalEmail);
|
||||
|
||||
// Create a traditional forward format with dashed separator
|
||||
const forwardHeader = `
|
||||
<div style="margin: 20px 0 10px 0; color: #666; font-family: Arial, sans-serif;">
|
||||
<div style="border-bottom: 1px solid #ccc; margin-bottom: 10px; padding-bottom: 5px;">
|
||||
<div>---------------------------- Forwarded Message ----------------------------</div>
|
||||
</div>
|
||||
<table style="margin-bottom: 10px; font-size: 14px;">
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">From:</td>
|
||||
<td style="padding: 3px 0;">${fromStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Date:</td>
|
||||
<td style="padding: 3px 0;">${dateStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Subject:</td>
|
||||
<td style="padding: 3px 0;">${subject || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">To:</td>
|
||||
<td style="padding: 3px 0;">${toStr}</td>
|
||||
</tr>
|
||||
${ccStr ? `
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">Cc:</td>
|
||||
<td style="padding: 3px 0;">${ccStr}</td>
|
||||
</tr>` : ''}
|
||||
</table>
|
||||
<div style="border-bottom: 1px solid #ccc; margin-top: 5px; margin-bottom: 15px; padding-bottom: 5px;">
|
||||
<div>----------------------------------------------------------------------</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Use the original HTML content if available, otherwise format the text
|
||||
const contentHtml = html || (text ? `<p>${text.replace(/\n/g, '</p><p>')}</p>` : '<p>No content available</p>');
|
||||
|
||||
const cleanHtml = `${forwardHeader}${contentHtml}`;
|
||||
// Create a traditional forward format with dashed separator
|
||||
// Wrap everything in a single containing element to prevent structure loss
|
||||
const cleanHtml = `
|
||||
<div class="forwarded-email-container">
|
||||
<div class="forwarded-email-header" style="margin: 20px 0 10px 0; color: #666; font-family: Arial, sans-serif;">
|
||||
<div style="border-bottom: 1px solid #ccc; margin-bottom: 10px; padding-bottom: 5px;">
|
||||
<div>---------------------------- Forwarded Message ----------------------------</div>
|
||||
</div>
|
||||
<table style="margin-bottom: 10px; font-size: 14px; width: 100%;">
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">From:</td>
|
||||
<td style="padding: 3px 0;">${fromStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">Date:</td>
|
||||
<td style="padding: 3px 0;">${dateStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">Subject:</td>
|
||||
<td style="padding: 3px 0;">${subject || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">To:</td>
|
||||
<td style="padding: 3px 0;">${toStr}</td>
|
||||
</tr>
|
||||
${ccStr ? `
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top; white-space: nowrap;">Cc:</td>
|
||||
<td style="padding: 3px 0;">${ccStr}</td>
|
||||
</tr>` : ''}
|
||||
</table>
|
||||
<div style="border-bottom: 1px solid #ccc; margin-top: 5px; margin-bottom: 15px; padding-bottom: 5px;">
|
||||
<div>----------------------------------------------------------------------</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forwarded-email-content">${contentHtml}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Plain text version - with clearer formatting
|
||||
const plainText = `
|
||||
|
||||
Loading…
Reference in New Issue
Block a user