courrier preview
This commit is contained in:
parent
7f87e4f00a
commit
1ced248940
@ -56,50 +56,122 @@ interface ComposeEmailProps {
|
||||
export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
const { initialEmail, type = 'new', onClose, onSend, accounts = [] } = props;
|
||||
|
||||
// Email form state
|
||||
const [to, setTo] = useState<string>('');
|
||||
const [cc, setCc] = useState<string>('');
|
||||
const [bcc, setBcc] = useState<string>('');
|
||||
const [subject, setSubject] = useState<string>('');
|
||||
const [emailContent, setEmailContent] = useState<string>('');
|
||||
const [showCc, setShowCc] = useState<boolean>(false);
|
||||
const [showBcc, setShowBcc] = useState<boolean>(false);
|
||||
const [sending, setSending] = useState<boolean>(false);
|
||||
const [selectedAccount, setSelectedAccount] = useState<{
|
||||
id: string;
|
||||
email: string;
|
||||
display_name?: string;
|
||||
} | null>(accounts.length > 0 ? accounts[0] : null);
|
||||
const [attachments, setAttachments] = useState<Array<{
|
||||
name: string;
|
||||
content: string;
|
||||
type: string;
|
||||
}>>([]);
|
||||
// State for email form
|
||||
const [selectedAccount, setSelectedAccount] = useState<any>(accounts[0]);
|
||||
const [to, setTo] = useState('');
|
||||
const [cc, setCc] = useState('');
|
||||
const [bcc, setBcc] = useState('');
|
||||
const [subject, setSubject] = useState('');
|
||||
const [emailContent, setEmailContent] = useState('');
|
||||
const [showCc, setShowCc] = useState(false);
|
||||
const [showBcc, setShowBcc] = useState(false);
|
||||
const [sending, setSending] = useState(false);
|
||||
const [attachments, setAttachments] = useState<Array<{name: string; content: string; type: string;}>>([]);
|
||||
|
||||
// Reference to editor
|
||||
const editorRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Helper function to get formatted info from email
|
||||
function getFormattedInfoForEmail(email: any) {
|
||||
// Format the subject
|
||||
const subject = email.subject || '';
|
||||
|
||||
// Format the date
|
||||
const dateStr = email.date ? new Date(email.date).toLocaleString() : 'Unknown Date';
|
||||
|
||||
// Format sender
|
||||
const fromStr = Array.isArray(email.from)
|
||||
? email.from.map((addr: any) => {
|
||||
if (typeof addr === 'string') return addr;
|
||||
return addr.name ? `${addr.name} <${addr.address}>` : addr.address;
|
||||
}).join(', ')
|
||||
: typeof email.from === 'string'
|
||||
? email.from
|
||||
: email.from?.address
|
||||
? email.from.name
|
||||
? `${email.from.name} <${email.from.address}>`
|
||||
: email.from.address
|
||||
: 'Unknown Sender';
|
||||
|
||||
// Format recipients
|
||||
const toStr = Array.isArray(email.to)
|
||||
? email.to.map((addr: any) => {
|
||||
if (typeof addr === 'string') return addr;
|
||||
return addr.name ? `${addr.name} <${addr.address}>` : addr.address;
|
||||
}).join(', ')
|
||||
: typeof email.to === 'string'
|
||||
? email.to
|
||||
: '';
|
||||
|
||||
// Format CC
|
||||
const ccStr = Array.isArray(email.cc)
|
||||
? email.cc.map((addr: any) => {
|
||||
if (typeof addr === 'string') return addr;
|
||||
return addr.name ? `${addr.name} <${addr.address}>` : addr.address;
|
||||
}).join(', ')
|
||||
: typeof email.cc === 'string'
|
||||
? email.cc
|
||||
: '';
|
||||
|
||||
return { fromStr, toStr, ccStr, dateStr, subject };
|
||||
}
|
||||
|
||||
// Initialize the form when replying to or forwarding an email
|
||||
// Initialize email form based on initial email and type
|
||||
useEffect(() => {
|
||||
if (initialEmail && type !== 'new') {
|
||||
if (initialEmail) {
|
||||
try {
|
||||
// Set recipients based on type
|
||||
console.log('Initializing compose with email:', {
|
||||
id: initialEmail.id,
|
||||
subject: initialEmail.subject,
|
||||
hasContent: !!initialEmail.content,
|
||||
contentType: initialEmail.content ? typeof initialEmail.content : 'none'
|
||||
});
|
||||
|
||||
// Set default account from original email - use type assertion since accountId might be custom property
|
||||
const emailAny = initialEmail as any;
|
||||
if (emailAny.accountId && accounts?.length) {
|
||||
const account = accounts.find(a => a.id === emailAny.accountId);
|
||||
if (account) {
|
||||
setSelectedAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
// Get recipients based on type
|
||||
if (type === 'reply' || type === 'reply-all') {
|
||||
// Get formatted data for reply
|
||||
const formatted = formatReplyEmail(initialEmail, type);
|
||||
|
||||
// Set the recipients
|
||||
// Set reply addresses
|
||||
setTo(formatted.to);
|
||||
if (formatted.cc) {
|
||||
setCc(formatted.cc);
|
||||
setShowCc(true);
|
||||
setCc(formatted.cc);
|
||||
}
|
||||
|
||||
// Set subject
|
||||
setSubject(formatted.subject);
|
||||
|
||||
// Set content with original email
|
||||
const content = formatted.content.html || formatted.content.text;
|
||||
setEmailContent(content);
|
||||
// Set content with original email - ensure we have content
|
||||
const content = formatted.content.html || formatted.content.text || '';
|
||||
|
||||
if (!content) {
|
||||
console.warn('Reply content is empty, falling back to a basic template');
|
||||
// Provide a basic template if the content is empty
|
||||
const { fromStr, dateStr } = getFormattedInfoForEmail(initialEmail);
|
||||
const fallbackContent = `
|
||||
<div style="margin-top: 20px; margin-bottom: 10px; color: #666;">On ${dateStr}, ${fromStr} wrote:</div>
|
||||
<blockquote style="margin: 10px 0; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
[Original message content could not be loaded]
|
||||
</blockquote>
|
||||
`;
|
||||
setEmailContent(fallbackContent);
|
||||
} else {
|
||||
console.log('Setting reply content:', {
|
||||
length: content.length,
|
||||
isHtml: formatted.content.isHtml
|
||||
});
|
||||
setEmailContent(content);
|
||||
}
|
||||
}
|
||||
else if (type === 'forward') {
|
||||
// Get formatted data for forward
|
||||
@ -108,9 +180,34 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
// Set subject
|
||||
setSubject(formatted.subject);
|
||||
|
||||
// Set content with original email
|
||||
const content = formatted.content.html || formatted.content.text;
|
||||
setEmailContent(content);
|
||||
// Set content with original email - ensure we have content
|
||||
const content = formatted.content.html || formatted.content.text || '';
|
||||
|
||||
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 fallbackContent = `
|
||||
<div style="margin-top: 20px; color: #666;">
|
||||
<div>---------- Forwarded message ---------</div>
|
||||
<div><strong>From:</strong> ${fromStr}</div>
|
||||
<div><strong>Date:</strong> ${dateStr}</div>
|
||||
<div><strong>Subject:</strong> ${subject || ''}</div>
|
||||
<div><strong>To:</strong> ${toStr}</div>
|
||||
${ccStr ? `<div><strong>Cc:</strong> ${ccStr}</div>` : ''}
|
||||
</div>
|
||||
<blockquote style="margin-top: 10px; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
[Original message content could not be loaded]
|
||||
</blockquote>
|
||||
`;
|
||||
setEmailContent(fallbackContent);
|
||||
} else {
|
||||
console.log('Setting forward content:', {
|
||||
length: content.length,
|
||||
isHtml: formatted.content.isHtml
|
||||
});
|
||||
setEmailContent(content);
|
||||
}
|
||||
|
||||
// If the original email has attachments, include them
|
||||
if (initialEmail.attachments && initialEmail.attachments.length > 0) {
|
||||
@ -124,9 +221,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error initializing compose form:', error);
|
||||
// Provide a fallback in case of error
|
||||
setEmailContent('<p>Error loading email content</p>');
|
||||
}
|
||||
}
|
||||
}, [initialEmail, type]);
|
||||
}, [initialEmail, type, accounts]);
|
||||
|
||||
// Place cursor at beginning and ensure content is scrolled to top
|
||||
useEffect(() => {
|
||||
|
||||
@ -99,13 +99,30 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
// Simplify complex email content to something Quill can handle better
|
||||
const sanitizedContent = sanitizeHtml(processedContent || initialContent);
|
||||
|
||||
// Use direct innerHTML setting for the initial content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
// Check if sanitized content is valid
|
||||
if (sanitizedContent.trim().length === 0) {
|
||||
console.warn('Sanitized content is empty after processing, using fallback approach');
|
||||
// Try to extract text content if HTML processing failed
|
||||
try {
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = initialContent;
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || 'Empty content';
|
||||
|
||||
// Set text directly to ensure something displays
|
||||
quillRef.current.setText(textContent);
|
||||
} catch (e) {
|
||||
console.error('Text extraction fallback failed:', e);
|
||||
quillRef.current.setText('Error loading content');
|
||||
}
|
||||
} else {
|
||||
// Use direct innerHTML setting for the initial content
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
}
|
||||
}
|
||||
|
||||
// Set cursor at the beginning
|
||||
@ -131,19 +148,25 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error setting initial content:', err);
|
||||
// Fallback: just set text
|
||||
quillRef.current.setText('');
|
||||
|
||||
// Extract text as a last resort
|
||||
// Enhanced fallback mechanism for complex content
|
||||
try {
|
||||
// Create a temporary div to extract text from HTML
|
||||
// First try to extract text from HTML
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = initialContent;
|
||||
const textContent = tempDiv.textContent || tempDiv.innerText || '';
|
||||
quillRef.current.setText(textContent);
|
||||
|
||||
if (textContent.trim()) {
|
||||
console.log('Using extracted text fallback, length:', textContent.length);
|
||||
quillRef.current.setText(textContent);
|
||||
} else {
|
||||
// If text extraction fails or returns empty, provide a message
|
||||
console.log('Using empty content fallback');
|
||||
quillRef.current.setText('Unable to load original content');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Fallback failed too:', e);
|
||||
quillRef.current.setText('');
|
||||
console.error('All fallbacks failed:', e);
|
||||
quillRef.current.setText('Error loading content');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,19 +207,36 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
// Only update if content changed to avoid editor position reset
|
||||
if (initialContent !== currentContent) {
|
||||
try {
|
||||
console.log('Updating content in editor:', {
|
||||
contentLength: initialContent.length,
|
||||
startsWithHtml: initialContent.trim().startsWith('<')
|
||||
});
|
||||
|
||||
// Process content to ensure correct direction
|
||||
const { direction, html: processedContent } = processContentWithDirection(initialContent);
|
||||
|
||||
// Sanitize the HTML
|
||||
const sanitizedContent = sanitizeHtml(processedContent || initialContent);
|
||||
|
||||
// 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');
|
||||
// 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;
|
||||
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
|
||||
quillRef.current.root.innerHTML = sanitizedContent;
|
||||
|
||||
// Set the direction for the content
|
||||
quillRef.current.format('direction', direction);
|
||||
if (direction === 'rtl') {
|
||||
quillRef.current.format('align', 'right');
|
||||
}
|
||||
}
|
||||
|
||||
// Force update
|
||||
@ -215,6 +255,8 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
|
||||
quillRef.current.setText(textContent);
|
||||
} catch (e) {
|
||||
console.error('All fallbacks failed:', e);
|
||||
// Last resort
|
||||
quillRef.current.setText('Error loading content');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,35 @@ export function formatEmailContent(email: any): string {
|
||||
}
|
||||
|
||||
// Use the centralized sanitizeHtml function
|
||||
const sanitizedContent = sanitizeHtml(content);
|
||||
let sanitizedContent = sanitizeHtml(content);
|
||||
|
||||
// Fix URL encoding issues that might occur during sanitization
|
||||
try {
|
||||
// Temporary element to manipulate the HTML
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = sanitizedContent;
|
||||
|
||||
// Fix all links
|
||||
const links = tempDiv.querySelectorAll('a');
|
||||
links.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && href.includes('%')) {
|
||||
try {
|
||||
// Try to decode URLs that might have been double-encoded
|
||||
const decodedHref = decodeURIComponent(href);
|
||||
link.setAttribute('href', decodedHref);
|
||||
} catch (e) {
|
||||
// If decoding fails, keep the original
|
||||
console.warn('Failed to decode href:', href);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Get the fixed HTML
|
||||
sanitizedContent = tempDiv.innerHTML;
|
||||
} catch (e) {
|
||||
console.error('Error fixing URLs in content:', e);
|
||||
}
|
||||
|
||||
// Fix common email client quirks
|
||||
let fixedContent = sanitizedContent
|
||||
|
||||
@ -279,16 +279,51 @@ export function formatReplyEmail(originalEmail: EmailMessage | LegacyEmailMessag
|
||||
// Extract content using centralized utility
|
||||
const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail);
|
||||
|
||||
// Create a simpler HTML structure that's easier for Quill to handle
|
||||
const replyBody = `
|
||||
<br/>
|
||||
<div class="email-original-content">
|
||||
<div>On ${dateStr}, ${fromStr} wrote:</div>
|
||||
// Prefer HTML content when available, but simplify it for Quill compatibility
|
||||
let replyBody = '';
|
||||
|
||||
// Create a header that works in both HTML and plain text
|
||||
const headerHtml = `<div style="margin-top: 20px; margin-bottom: 10px; color: #666;">On ${dateStr}, ${fromStr} wrote:</div>`;
|
||||
|
||||
if (originalHtmlContent) {
|
||||
try {
|
||||
// Sanitize the original HTML to remove problematic elements
|
||||
const sanitizedHtml = sanitizeHtml(originalHtmlContent);
|
||||
|
||||
// Wrap the content in a blockquote with styling
|
||||
replyBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin: 10px 0; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
${sanitizedHtml}
|
||||
</blockquote>
|
||||
`;
|
||||
} catch (error) {
|
||||
console.error('Error processing HTML for reply:', error);
|
||||
// Fallback to text if HTML processing fails
|
||||
replyBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin: 10px 0; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
${originalTextContent.replace(/\n/g, '<br>')}
|
||||
</blockquote>
|
||||
`;
|
||||
}
|
||||
} else if (originalTextContent) {
|
||||
// Use text content with proper line breaks
|
||||
replyBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin: 10px 0; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
${originalTextContent.replace(/\n/g, '<br>')}
|
||||
</blockquote>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
} else {
|
||||
// Empty or unrecognized content
|
||||
replyBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin: 10px 0; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
[Original message content not available]
|
||||
</blockquote>
|
||||
`;
|
||||
}
|
||||
|
||||
// Process the content with proper direction
|
||||
const processed = processContentWithDirection(replyBody);
|
||||
@ -335,21 +370,60 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
|
||||
// Extract content using centralized utility
|
||||
const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail);
|
||||
|
||||
// Create a simpler forwarded content structure for better Quill compatibility
|
||||
const forwardBody = `
|
||||
<br/>
|
||||
<div class="email-original-content">
|
||||
// Prefer HTML content when available, but simplify it for Quill compatibility
|
||||
let forwardBody = '';
|
||||
|
||||
// Create metadata header that works in both HTML and plain text
|
||||
const headerHtml = `
|
||||
<div style="margin-top: 20px; color: #666;">
|
||||
<div>---------- Forwarded message ---------</div>
|
||||
<div><strong>From:</strong> ${fromStr}</div>
|
||||
<div><strong>Date:</strong> ${dateStr}</div>
|
||||
<div><strong>Subject:</strong> ${subject || ''}</div>
|
||||
<div><strong>To:</strong> ${toStr}</div>
|
||||
${ccStr ? `<div><strong>Cc:</strong> ${ccStr}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (originalHtmlContent) {
|
||||
try {
|
||||
// Sanitize the original HTML to remove problematic elements
|
||||
const sanitizedHtml = sanitizeHtml(originalHtmlContent);
|
||||
|
||||
// Wrap the content in a blockquote with styling
|
||||
forwardBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin-top: 10px; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
${sanitizedHtml}
|
||||
</blockquote>
|
||||
`;
|
||||
} catch (error) {
|
||||
console.error('Error processing HTML for forward:', error);
|
||||
// Fallback to text if HTML processing fails
|
||||
forwardBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin-top: 10px; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
${originalTextContent.replace(/\n/g, '<br>')}
|
||||
</blockquote>
|
||||
`;
|
||||
}
|
||||
} else if (originalTextContent) {
|
||||
// Use text content with proper line breaks
|
||||
forwardBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin-top: 10px; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
${originalTextContent.replace(/\n/g, '<br>')}
|
||||
</blockquote>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
} else {
|
||||
// Empty or unrecognized content
|
||||
forwardBody = `
|
||||
${headerHtml}
|
||||
<blockquote style="margin-top: 10px; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
|
||||
[Original message content not available]
|
||||
</blockquote>
|
||||
`;
|
||||
}
|
||||
|
||||
// Process the content with proper direction
|
||||
const processed = processContentWithDirection(forwardBody);
|
||||
|
||||
@ -78,8 +78,61 @@ export function extractEmailContent(email: any): { text: string; html: string }
|
||||
// Extract based on common formats
|
||||
if (email) {
|
||||
if (typeof email.content === 'object' && email.content) {
|
||||
// Standard format with content object
|
||||
textContent = email.content.text || '';
|
||||
htmlContent = email.content.html || '';
|
||||
|
||||
// Handle complex email formats where content might be nested
|
||||
if (!textContent && !htmlContent) {
|
||||
// Try to find content in deeper nested structure
|
||||
if (email.content.body) {
|
||||
if (typeof email.content.body === 'string') {
|
||||
// Determine if body is HTML or text
|
||||
if (email.content.body.includes('<') && (
|
||||
email.content.body.includes('<html') ||
|
||||
email.content.body.includes('<body') ||
|
||||
email.content.body.includes('<div')
|
||||
)) {
|
||||
htmlContent = email.content.body;
|
||||
} else {
|
||||
textContent = email.content.body;
|
||||
}
|
||||
} else if (typeof email.content.body === 'object' && email.content.body) {
|
||||
// Some email formats nest content inside body
|
||||
htmlContent = email.content.body.html || '';
|
||||
textContent = email.content.body.text || '';
|
||||
}
|
||||
}
|
||||
|
||||
// Check for data property which some email services use
|
||||
if (!textContent && !htmlContent && email.content.data) {
|
||||
if (typeof email.content.data === 'string') {
|
||||
// Check if data looks like HTML
|
||||
if (email.content.data.includes('<') && (
|
||||
email.content.data.includes('<html') ||
|
||||
email.content.data.includes('<body') ||
|
||||
email.content.data.includes('<div')
|
||||
)) {
|
||||
htmlContent = email.content.data;
|
||||
} else {
|
||||
textContent = email.content.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: try to convert the entire content object to string
|
||||
if (!textContent && !htmlContent) {
|
||||
try {
|
||||
// Some email servers encode content as JSON string
|
||||
const contentStr = JSON.stringify(email.content);
|
||||
if (contentStr && contentStr !== '{}') {
|
||||
textContent = `[Complex email content - please view in original format]`;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error extracting content from complex object:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof email.content === 'string') {
|
||||
// Check if content is likely HTML
|
||||
if (email.content.includes('<') && (
|
||||
@ -95,9 +148,39 @@ export function extractEmailContent(email: any): { text: string; html: string }
|
||||
// Check other common properties
|
||||
htmlContent = email.html || '';
|
||||
textContent = email.text || '';
|
||||
|
||||
// If still no content, check for less common properties
|
||||
if (!htmlContent && !textContent) {
|
||||
// Try additional properties that some email clients use
|
||||
htmlContent = email.body?.html || email.bodyHtml || email.htmlBody || '';
|
||||
textContent = email.body?.text || email.bodyText || email.plainText || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we always have at least some text content
|
||||
if (!textContent && htmlContent) {
|
||||
try {
|
||||
// Create a helper function to extract text from HTML
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = htmlContent;
|
||||
textContent = tempDiv.textContent || tempDiv.innerText || '';
|
||||
} catch (e) {
|
||||
// Fallback for non-browser environments or if extraction fails
|
||||
textContent = htmlContent.replace(/<[^>]*>/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim() || '[Email content]';
|
||||
}
|
||||
}
|
||||
|
||||
// Add debug logging to help troubleshoot content extraction
|
||||
console.log('Extracted email content:', {
|
||||
hasHtml: !!htmlContent,
|
||||
htmlLength: htmlContent.length,
|
||||
hasText: !!textContent,
|
||||
textLength: textContent.length
|
||||
});
|
||||
|
||||
return { text: textContent, html: htmlContent };
|
||||
}
|
||||
|
||||
@ -133,7 +216,8 @@ export function processContentWithDirection(content: string | EmailContent | nul
|
||||
if (content.includes('<') && (
|
||||
content.includes('<html') ||
|
||||
content.includes('<body') ||
|
||||
content.includes('<div')
|
||||
content.includes('<div') ||
|
||||
content.includes('<p>')
|
||||
)) {
|
||||
htmlContent = content;
|
||||
} else {
|
||||
@ -145,14 +229,73 @@ export function processContentWithDirection(content: string | EmailContent | nul
|
||||
htmlContent = content.html || '';
|
||||
}
|
||||
|
||||
// Handle complex email content that might not be properly detected
|
||||
if (!textContent && !htmlContent && typeof content === 'object') {
|
||||
console.log('Processing complex content object:', content);
|
||||
|
||||
// Try to extract content from complex object structure
|
||||
try {
|
||||
// Check for common email content formats
|
||||
// Type assertion to 'any' since we need to handle various email formats
|
||||
const contentAny = content as any;
|
||||
|
||||
if (contentAny.body) {
|
||||
if (typeof contentAny.body === 'string') {
|
||||
// Detect if body is HTML or text
|
||||
if (contentAny.body.includes('<') && (
|
||||
contentAny.body.includes('<html') ||
|
||||
contentAny.body.includes('<body') ||
|
||||
contentAny.body.includes('<div')
|
||||
)) {
|
||||
htmlContent = contentAny.body;
|
||||
} else {
|
||||
textContent = contentAny.body;
|
||||
}
|
||||
} else if (typeof contentAny.body === 'object' && contentAny.body) {
|
||||
// Extract from nested body object
|
||||
htmlContent = contentAny.body.html || '';
|
||||
textContent = contentAny.body.text || '';
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert complex content to string for debugging
|
||||
if (!textContent && !htmlContent) {
|
||||
try {
|
||||
const contentStr = JSON.stringify(content);
|
||||
console.log('Complex content structure:', contentStr.slice(0, 300) + '...');
|
||||
textContent = '[Complex email content]';
|
||||
} catch (e) {
|
||||
console.error('Failed to stringify complex content:', e);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error processing complex content:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Always ensure we have text for direction detection
|
||||
if (!textContent && htmlContent) {
|
||||
// Extract text from HTML for direction detection
|
||||
textContent = htmlContent.replace(/<[^>]*>/g, '')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&');
|
||||
try {
|
||||
// Use DOM API if available
|
||||
if (typeof document !== 'undefined') {
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = htmlContent;
|
||||
textContent = tempDiv.textContent || tempDiv.innerText || '';
|
||||
} else {
|
||||
// Simple regex fallback for non-browser environments
|
||||
textContent = htmlContent.replace(/<[^>]*>/g, ' ')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error extracting text from HTML:', e);
|
||||
textContent = 'Failed to extract text content';
|
||||
}
|
||||
}
|
||||
|
||||
// Detect direction from text
|
||||
@ -160,16 +303,35 @@ export function processContentWithDirection(content: string | EmailContent | nul
|
||||
|
||||
// Sanitize HTML if present
|
||||
if (htmlContent) {
|
||||
// Sanitize HTML first
|
||||
htmlContent = sanitizeHtml(htmlContent);
|
||||
|
||||
// Then apply direction
|
||||
htmlContent = applyTextDirection(htmlContent, textContent);
|
||||
try {
|
||||
// Sanitize HTML first using the centralized function
|
||||
htmlContent = sanitizeHtml(htmlContent);
|
||||
|
||||
// Then apply direction
|
||||
htmlContent = applyTextDirection(htmlContent, textContent);
|
||||
} catch (error) {
|
||||
console.error('Error sanitizing HTML content:', error);
|
||||
// Create fallback content if sanitization fails
|
||||
htmlContent = `<div dir="${direction}">${
|
||||
textContent ?
|
||||
textContent.replace(/\n/g, '<br>') :
|
||||
'Could not process HTML content'
|
||||
}</div>`;
|
||||
}
|
||||
} else if (textContent) {
|
||||
// Convert plain text to HTML with proper direction
|
||||
htmlContent = `<div dir="${direction}">${textContent.replace(/\n/g, '<br>')}</div>`;
|
||||
}
|
||||
|
||||
// Add debug logging for troubleshooting
|
||||
console.log('Processed content:', {
|
||||
direction,
|
||||
htmlLength: htmlContent.length,
|
||||
textLength: textContent.length,
|
||||
hasHtml: !!htmlContent,
|
||||
hasText: !!textContent
|
||||
});
|
||||
|
||||
// Return processed content
|
||||
return {
|
||||
text: textContent,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user