courrier preview
This commit is contained in:
parent
61c29ced21
commit
2b02742bc4
@ -20,7 +20,7 @@ export default function BulkActionsToolbar({
|
||||
onBulkAction
|
||||
}: BulkActionsToolbarProps) {
|
||||
return (
|
||||
<div className="bg-blue-50 border-b border-blue-100 px-4 py-2 flex items-center justify-between">
|
||||
<div className="bg-blue-50 border-b border-blue-100 px-4 py-2 flex items-center justify-between shadow-md transition-all duration-200">
|
||||
<span className="text-xs font-medium text-blue-700">
|
||||
{selectedCount} selected
|
||||
</span>
|
||||
@ -32,13 +32,13 @@ export default function BulkActionsToolbar({
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-blue-600 hover:text-blue-900 hover:bg-blue-100"
|
||||
onClick={() => onBulkAction('mark-read')}
|
||||
onClick={() => onBulkAction('mark-unread')}
|
||||
>
|
||||
<EyeOff className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Mark as read</p>
|
||||
<p>Mark as unread</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
@ -170,7 +170,9 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
} else {
|
||||
console.log('Setting reply content:', {
|
||||
length: content.length,
|
||||
isHtml: formatted.content.isHtml
|
||||
isHtml: formatted.content.isHtml,
|
||||
startsWithHtml: content.trim().startsWith('<'),
|
||||
contentType: typeof content
|
||||
});
|
||||
setEmailContent(content);
|
||||
}
|
||||
@ -198,7 +200,8 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
if (!content) {
|
||||
console.warn('Forward content is empty, falling back to a basic template');
|
||||
// Provide a basic template if the content is empty
|
||||
const { fromStr, toStr, ccStr, dateStr, subject } = getFormattedInfoForEmail(initialEmail);
|
||||
const { fromStr, dateStr, subject: origSubject, toStr, ccStr } = getFormattedInfoForEmail(initialEmail);
|
||||
console.log('Creating forward fallback with:', { fromStr, dateStr, origSubject });
|
||||
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;">
|
||||
@ -215,7 +218,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
</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>
|
||||
<td style="padding: 3px 0;">${origSubject || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 10px 3px 0; font-weight: bold; text-align: right; vertical-align: top;">To:</td>
|
||||
|
||||
@ -26,10 +26,19 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
||||
// Process content if provided
|
||||
const processedContent = useMemo(() => {
|
||||
if (!content) {
|
||||
console.log('EmailContentDisplay: No content provided');
|
||||
return { __html: '<div class="email-content-empty">No content available</div>' };
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('EmailContentDisplay processing:', {
|
||||
contentType: typeof content,
|
||||
isNull: content === null,
|
||||
isString: typeof content === 'string',
|
||||
isObject: typeof content === 'object',
|
||||
length: typeof content === 'string' ? content.length : null
|
||||
});
|
||||
|
||||
let formattedContent: string;
|
||||
|
||||
// If it's a string, we need to determine if it's HTML or plain text
|
||||
@ -41,6 +50,7 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
||||
formattedContent = formatEmailContent({ content });
|
||||
}
|
||||
|
||||
console.log('EmailContentDisplay processed result length:', formattedContent.length);
|
||||
return { __html: formattedContent };
|
||||
} catch (error) {
|
||||
console.error('Error processing email content:', error);
|
||||
|
||||
@ -119,12 +119,14 @@ export default function EmailList({
|
||||
|
||||
return (
|
||||
<div className="w-[320px] bg-white/95 backdrop-blur-sm border-r border-gray-100 flex flex-col">
|
||||
{/* Only show bulk actions when emails are explicitly selected via checkboxes */}
|
||||
{/* Sticky toolbar - always visible at the top when emails are selected */}
|
||||
{selectedEmailIds.length > 0 && (
|
||||
<BulkActionsToolbar
|
||||
selectedCount={selectedEmailIds.length}
|
||||
onBulkAction={onBulkAction}
|
||||
/>
|
||||
<div className="sticky top-0 z-10">
|
||||
<BulkActionsToolbar
|
||||
selectedCount={selectedEmailIds.length}
|
||||
onBulkAction={onBulkAction}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Search header */}
|
||||
|
||||
@ -119,9 +119,13 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
if (quillRef.current && quillRef.current.format) {
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
}
|
||||
} else {
|
||||
console.warn('Cannot format content: editor not fully initialized');
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,23 +231,38 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || '';
|
||||
|
||||
// Create simple HTML with text content
|
||||
quillRef.current.setText(textContent);
|
||||
if (quillRef.current) {
|
||||
quillRef.current.setText(textContent || 'No content available');
|
||||
}
|
||||
} else {
|
||||
// SIMPLIFIED: Set content directly to the root element rather than using clipboard
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
if (quillRef.current && quillRef.current.root) {
|
||||
// First set the content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Then safely apply formatting only if quillRef is valid
|
||||
try {
|
||||
if (quillRef.current && quillRef.current.format && quillRef.current.root.innerHTML.trim().length > 0) {
|
||||
// Set the direction for the content
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
}
|
||||
|
||||
// Force update
|
||||
quillRef.current.update();
|
||||
|
||||
// Set selection to beginning
|
||||
quillRef.current.setSelection(0, 0);
|
||||
} else {
|
||||
console.warn('Skipping format - either editor not ready or content empty');
|
||||
}
|
||||
} catch (formatError) {
|
||||
console.error('Error applying formatting:', formatError);
|
||||
// Continue without formatting if there's an error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Force update
|
||||
quillRef.current.update();
|
||||
|
||||
// Set selection to beginning
|
||||
quillRef.current.setSelection(0, 0);
|
||||
} catch (err) {
|
||||
console.error('Error updating content:', err);
|
||||
// Safer fallback that avoids clipboard API
|
||||
@ -252,11 +271,16 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = initialContent;
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || '';
|
||||
quillRef.current.setText(textContent);
|
||||
|
||||
if (quillRef.current) {
|
||||
quillRef.current.setText(textContent || 'Error loading content');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('All fallbacks failed:', e);
|
||||
// Last resort
|
||||
quillRef.current.setText('Error loading content');
|
||||
if (quillRef.current) {
|
||||
quillRef.current.setText('Error loading content');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,16 +162,30 @@ export function formatEmailContent(email: any): string {
|
||||
// Extract content from email
|
||||
const { text, html } = extractEmailContent(email);
|
||||
|
||||
console.log('formatEmailContent processing:', {
|
||||
hasHtml: !!html,
|
||||
htmlLength: html?.length || 0,
|
||||
hasText: !!text,
|
||||
textLength: text?.length || 0,
|
||||
emailType: typeof email === 'string' ? 'string' : 'object'
|
||||
});
|
||||
|
||||
// If we have HTML content, sanitize and standardize it
|
||||
if (html) {
|
||||
// Process HTML content
|
||||
let processedHtml = processHtmlContent(html, text);
|
||||
|
||||
console.log('HTML content processed:', {
|
||||
processedLength: processedHtml?.length || 0,
|
||||
isEmpty: !processedHtml || processedHtml.trim().length === 0
|
||||
});
|
||||
|
||||
// Apply styling
|
||||
return `<div class="email-content" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; color: #333; max-width: 100%; overflow-x: auto; overflow-wrap: break-word; word-wrap: break-word;" dir="${detectTextDirection(text)}">${processedHtml}</div>`;
|
||||
}
|
||||
// If we only have text content, format it properly
|
||||
else if (text) {
|
||||
console.log('Using plain text formatting');
|
||||
return formatPlainTextToHtml(text);
|
||||
}
|
||||
|
||||
|
||||
@ -252,162 +252,295 @@ function getFormattedHeaderInfo(email: any): {
|
||||
* Format email for reply
|
||||
*/
|
||||
export function formatReplyEmail(originalEmail: EmailMessage | LegacyEmailMessage | null, type: 'reply' | 'reply-all' = 'reply'): FormattedEmail {
|
||||
console.log('formatReplyEmail called:', { type, emailId: originalEmail?.id });
|
||||
|
||||
if (!originalEmail) {
|
||||
console.warn('formatReplyEmail: No original email provided');
|
||||
return {
|
||||
to: '',
|
||||
cc: '',
|
||||
subject: '',
|
||||
content: {
|
||||
text: '',
|
||||
html: '',
|
||||
isHtml: false,
|
||||
direction: 'ltr' as const
|
||||
}
|
||||
content: { text: '', html: '', isHtml: false, direction: 'ltr' }
|
||||
};
|
||||
}
|
||||
|
||||
// Extract recipient addresses
|
||||
const { to, cc } = getRecipientAddresses(originalEmail, type);
|
||||
|
||||
// Get header information
|
||||
const { fromStr, dateStr, subject } = getFormattedHeaderInfo(originalEmail);
|
||||
|
||||
// Extract content using the centralized extraction function
|
||||
const { text, html } = extractEmailContent(originalEmail);
|
||||
|
||||
// Create a clearer reply header with separator line
|
||||
const replyHeader = `
|
||||
<div style="margin: 20px 0 10px 0; color: #666; border-bottom: 1px solid #ddd; padding-bottom: 5px;">
|
||||
On ${dateStr}, ${fromStr} wrote:
|
||||
</div>
|
||||
`;
|
||||
// Adapt legacy format if needed
|
||||
const email = 'content' in originalEmail ? originalEmail : adaptLegacyEmail(originalEmail);
|
||||
|
||||
// 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>');
|
||||
// Format subject with Re: prefix
|
||||
const subject = email.subject ?
|
||||
(email.subject.toLowerCase().startsWith('re:') ? email.subject : `Re: ${email.subject}`) :
|
||||
'Re: ';
|
||||
|
||||
// Wrap the original content in proper styling without losing the HTML structure
|
||||
const cleanHtml = `
|
||||
${replyHeader}
|
||||
<blockquote style="margin: 0; padding-left: 10px; border-left: 3px solid #ddd; color: #505050; background-color: #f9f9f9; padding: 10px;">
|
||||
${contentHtml}
|
||||
</blockquote>
|
||||
`;
|
||||
// Get recipient addresses
|
||||
const { to, cc } = getRecipientAddresses(email, type);
|
||||
|
||||
// Plain text version
|
||||
const plainText = `
|
||||
On ${dateStr}, ${fromStr} wrote:
|
||||
-------------------------------------------------------------------
|
||||
${text.split('\n').join('\n> ')}
|
||||
`;
|
||||
|
||||
return {
|
||||
to,
|
||||
cc,
|
||||
subject: subject.startsWith('Re:') ? subject : `Re: ${subject}`,
|
||||
content: {
|
||||
text: plainText.trim(),
|
||||
html: cleanHtml,
|
||||
isHtml: true,
|
||||
direction: 'ltr'
|
||||
// Get email content and sanitize it
|
||||
const originalContent = email.content;
|
||||
|
||||
// Extract text and html content
|
||||
let htmlContent = '';
|
||||
let textContent = '';
|
||||
let direction: 'ltr' | 'rtl' = 'ltr';
|
||||
|
||||
// Handle different content formats
|
||||
if (typeof originalContent === 'string') {
|
||||
console.log('formatReplyEmail: content is string, length:', originalContent.length);
|
||||
// Simple string content
|
||||
textContent = originalContent;
|
||||
const isHtml = isHtmlContent(originalContent);
|
||||
if (isHtml) {
|
||||
htmlContent = originalContent;
|
||||
} else {
|
||||
// If it's plain text, convert to HTML
|
||||
htmlContent = formatPlainTextToHtml(originalContent);
|
||||
}
|
||||
}
|
||||
else if (originalContent) {
|
||||
console.log('formatReplyEmail: content is object:', {
|
||||
hasHtml: !!originalContent.html,
|
||||
htmlLength: originalContent.html?.length || 0,
|
||||
hasText: !!originalContent.text,
|
||||
textLength: originalContent.text?.length || 0,
|
||||
direction: originalContent.direction
|
||||
});
|
||||
|
||||
// Standard EmailContent object
|
||||
htmlContent = originalContent.html || '';
|
||||
textContent = originalContent.text || '';
|
||||
direction = originalContent.direction || 'ltr' as const;
|
||||
|
||||
// If no HTML but has text, convert text to HTML
|
||||
if (!htmlContent && textContent) {
|
||||
htmlContent = formatPlainTextToHtml(textContent);
|
||||
}
|
||||
}
|
||||
|
||||
// Get quote header
|
||||
const { fromStr, dateStr } = getFormattedHeaderInfo(email);
|
||||
|
||||
// Use the from name if available, otherwise use email address
|
||||
const sender = fromStr;
|
||||
const date = dateStr;
|
||||
|
||||
// Create the quoted reply content
|
||||
if (htmlContent) {
|
||||
// Format HTML reply
|
||||
console.log('Formatting HTML reply, quoted content length:', htmlContent.length);
|
||||
htmlContent = `
|
||||
<div style="margin: 20px 0 10px 0; color: #666; border-bottom: 1px solid #ddd; padding-bottom: 5px;">
|
||||
On ${date}, ${sender} wrote:
|
||||
</div>
|
||||
<blockquote style="margin: 0; padding-left: 10px; border-left: 3px solid #ddd; color: #505050; background-color: #f9f9f9; padding: 10px;">
|
||||
${sanitizeHtml(htmlContent)}
|
||||
</blockquote>
|
||||
`;
|
||||
}
|
||||
|
||||
if (textContent) {
|
||||
// Format plain text reply
|
||||
const lines = textContent.split(/\r\n|\r|\n/);
|
||||
textContent = `On ${date}, ${sender} wrote:\n\n${lines.map(line => `> ${line}`).join('\n')}`;
|
||||
}
|
||||
|
||||
const result = {
|
||||
to,
|
||||
cc: cc || undefined,
|
||||
subject,
|
||||
content: {
|
||||
html: htmlContent,
|
||||
text: textContent,
|
||||
isHtml: true,
|
||||
direction,
|
||||
},
|
||||
attachments: email.attachments?.map(att => {
|
||||
// Create properly typed attachment
|
||||
if ('name' in att) {
|
||||
return {
|
||||
filename: att.filename || att.name || 'attachment',
|
||||
contentType: att.contentType || 'application/octet-stream',
|
||||
content: att.content
|
||||
};
|
||||
}
|
||||
return {
|
||||
filename: att.filename || 'attachment',
|
||||
contentType: att.contentType || 'application/octet-stream',
|
||||
content: att.content
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
console.log('formatReplyEmail result:', {
|
||||
to: result.to,
|
||||
subject: result.subject,
|
||||
hasHtml: !!result.content.html,
|
||||
htmlLength: result.content.html?.length || 0,
|
||||
hasText: !!result.content.text,
|
||||
textLength: result.content.text?.length || 0
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format email for forwarding
|
||||
*/
|
||||
export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMessage | null): FormattedEmail {
|
||||
console.log('formatForwardedEmail called:', { emailId: originalEmail?.id });
|
||||
|
||||
if (!originalEmail) {
|
||||
console.warn('formatForwardedEmail: No original email provided');
|
||||
return {
|
||||
to: '',
|
||||
subject: '',
|
||||
content: {
|
||||
text: '',
|
||||
html: '',
|
||||
isHtml: false,
|
||||
direction: 'ltr' as const
|
||||
}
|
||||
content: { text: '', html: '', isHtml: false, direction: 'ltr' }
|
||||
};
|
||||
}
|
||||
|
||||
// Adapt legacy format if needed
|
||||
const email = 'content' in originalEmail ? originalEmail : adaptLegacyEmail(originalEmail);
|
||||
|
||||
// Format subject with Fwd: prefix
|
||||
const subject = email.subject ?
|
||||
(email.subject.toLowerCase().startsWith('fwd:') ? email.subject : `Fwd: ${email.subject}`) :
|
||||
'Fwd: ';
|
||||
|
||||
// Get original email info for headers
|
||||
const { fromStr, toStr, ccStr, dateStr } = getFormattedHeaderInfo(email);
|
||||
|
||||
console.log('Forward header info:', { fromStr, toStr, dateStr, subject });
|
||||
|
||||
// Original sent date
|
||||
const date = dateStr;
|
||||
|
||||
// Get email content
|
||||
const originalContent = email.content;
|
||||
|
||||
// Extract text and html content
|
||||
let htmlContent = '';
|
||||
let textContent = '';
|
||||
let direction: 'ltr' | 'rtl' = 'ltr';
|
||||
|
||||
// Handle different content formats
|
||||
if (typeof originalContent === 'string') {
|
||||
console.log('formatForwardedEmail: content is string, length:', originalContent.length);
|
||||
// Simple string content
|
||||
textContent = originalContent;
|
||||
const isHtml = isHtmlContent(originalContent);
|
||||
if (isHtml) {
|
||||
htmlContent = originalContent;
|
||||
} else {
|
||||
// If it's plain text, convert to HTML
|
||||
htmlContent = formatPlainTextToHtml(originalContent);
|
||||
}
|
||||
}
|
||||
else if (originalContent) {
|
||||
console.log('formatForwardedEmail: content is object:', {
|
||||
hasHtml: !!originalContent.html,
|
||||
htmlLength: originalContent.html?.length || 0,
|
||||
hasText: !!originalContent.text,
|
||||
textLength: originalContent.text?.length || 0,
|
||||
direction: originalContent.direction
|
||||
});
|
||||
|
||||
// Standard EmailContent object
|
||||
htmlContent = originalContent.html || '';
|
||||
textContent = originalContent.text || '';
|
||||
direction = originalContent.direction || 'ltr' as const;
|
||||
|
||||
// If no HTML but has text, convert text to HTML
|
||||
if (!htmlContent && textContent) {
|
||||
htmlContent = formatPlainTextToHtml(textContent);
|
||||
}
|
||||
}
|
||||
|
||||
// Get header information
|
||||
const { fromStr, toStr, ccStr, dateStr, subject } = getFormattedHeaderInfo(originalEmail);
|
||||
|
||||
// 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>
|
||||
// Create the forwarded email HTML content
|
||||
if (htmlContent) {
|
||||
console.log('Formatting HTML forward, original content length:', htmlContent.length);
|
||||
htmlContent = `
|
||||
<div style="margin: 20px 0 10px 0; color: #666; font-family: Arial, sans-serif;">
|
||||
---------- Forwarded message ----------<br>
|
||||
<table style="margin: 10px 0 15px 0; border-collapse: collapse; font-size: 13px; color: #333;">
|
||||
<tbody>
|
||||
<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;">${date}</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;">${email.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>
|
||||
` : ''}
|
||||
</tbody>
|
||||
</table>
|
||||
</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 style="padding: 10px 0; border-top: 1px solid #ddd;">
|
||||
${sanitizeHtml(htmlContent)}
|
||||
</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}`;
|
||||
|
||||
// Plain text version - with clearer formatting
|
||||
const plainText = `
|
||||
---------------------------- Forwarded Message ----------------------------
|
||||
// Format the plain text version
|
||||
if (textContent) {
|
||||
textContent = `
|
||||
---------- Forwarded message ----------
|
||||
From: ${fromStr}
|
||||
Date: ${dateStr}
|
||||
Subject: ${subject || ''}
|
||||
Date: ${date}
|
||||
Subject: ${email.subject || ''}
|
||||
To: ${toStr}
|
||||
${ccStr ? `Cc: ${ccStr}` : ''}
|
||||
----------------------------------------------------------------------
|
||||
${ccStr ? `Cc: ${ccStr}\n` : ''}
|
||||
|
||||
${text}
|
||||
`;
|
||||
|
||||
// Check if original has attachments
|
||||
const attachments = originalEmail.attachments || [];
|
||||
|
||||
return {
|
||||
${textContent}
|
||||
`.trim();
|
||||
}
|
||||
|
||||
const result = {
|
||||
to: '',
|
||||
subject: subject.startsWith('Fwd:') ? subject : `Fwd: ${subject}`,
|
||||
subject,
|
||||
content: {
|
||||
text: plainText.trim(),
|
||||
html: cleanHtml,
|
||||
html: htmlContent,
|
||||
text: textContent,
|
||||
isHtml: true,
|
||||
direction: 'ltr'
|
||||
direction,
|
||||
},
|
||||
// Only include attachments if they exist
|
||||
attachments: attachments.length > 0 ? attachments.map(att => ({
|
||||
filename: att.filename || 'attachment',
|
||||
contentType: att.contentType || 'application/octet-stream',
|
||||
content: att.content
|
||||
})) : undefined
|
||||
attachments: email.attachments?.map(att => {
|
||||
// Create properly typed attachment
|
||||
if ('name' in att) {
|
||||
return {
|
||||
filename: att.filename || att.name || 'attachment',
|
||||
contentType: att.contentType || 'application/octet-stream',
|
||||
content: att.content
|
||||
};
|
||||
}
|
||||
return {
|
||||
filename: att.filename || 'attachment',
|
||||
contentType: att.contentType || 'application/octet-stream',
|
||||
content: att.content
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
console.log('formatForwardedEmail result:', {
|
||||
subject: result.subject,
|
||||
hasHtml: !!result.content.html,
|
||||
htmlLength: result.content.html?.length || 0,
|
||||
hasText: !!result.content.text,
|
||||
textLength: result.content.text?.length || 0
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user