/** * Unified Email Utilities * * This file provides backward compatibility for email utilities. * New code should import directly from the specialized modules: * - email-content.ts (content processing) * - text-direction.ts (direction handling) * - dom-purify-config.ts (sanitization) */ // Import from specialized modules import { sanitizeHtml } from './dom-purify-config'; import { detectTextDirection, applyTextDirection } from './text-direction'; import { extractEmailContent, formatEmailContent, processHtmlContent, formatPlainTextToHtml, isHtmlContent, extractTextFromHtml } from './email-content'; import { EmailMessage, EmailContent, EmailAddress, LegacyEmailMessage } from '@/types/email'; import { adaptLegacyEmail } from '@/lib/utils/email-adapters'; import { decodeInfomaniakEmail, adaptMimeEmail, isMimeFormat } from './email-mime-decoder'; import { format } from 'date-fns'; // Re-export important functions for backward compatibility export { sanitizeHtml, extractEmailContent, formatEmailContent, processHtmlContent, formatPlainTextToHtml, detectTextDirection, applyTextDirection }; /** * Standard interface for formatted email responses */ export interface FormattedEmail { to: string; cc?: string; subject: string; content: EmailContent; attachments?: Array<{ filename: string; contentType: string; content?: string; }>; } /** * Format email addresses for display * Can handle both array of EmailAddress objects or a string */ export function formatEmailAddresses(addresses: EmailAddress[] | string | undefined): string { if (!addresses) return ''; // If already a string, return as is if (typeof addresses === 'string') { return addresses; } // If array, format each address if (Array.isArray(addresses) && addresses.length > 0) { return addresses.map(addr => addr.name && addr.name !== addr.address ? `${addr.name} <${addr.address}>` : addr.address ).join(', '); } return ''; } /** * Format date for display */ export function formatEmailDate(date: Date | string | undefined): string { if (!date) return ''; try { const dateObj = typeof date === 'string' ? new Date(date) : date; return dateObj.toLocaleString('en-US', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (e) { return typeof date === 'string' ? date : date.toString(); } } /** * Normalize email content to our standard format regardless of input format */ export function normalizeEmailContent(email: any): EmailMessage { if (!email) { throw new Error('Cannot normalize null or undefined email'); } // First check if this is a MIME format email that needs decoding if (email.content && isMimeFormat(email.content)) { try { console.log('Detected MIME format email, decoding...'); // We need to force cast here due to type incompatibility between EmailMessage and the mime result const adaptedEmail = adaptMimeEmail(email); return { ...adaptedEmail, flags: adaptedEmail.flags || [] // Ensure flags is always an array } as EmailMessage; } catch (error) { console.error('Error decoding MIME email:', error); // Continue with regular normalization if MIME decoding fails } } // Check if it's already in the standardized format if (email.content && typeof email.content === 'object' && (email.content.html !== undefined || email.content.text !== undefined)) { // Already in the correct format return email as EmailMessage; } // Otherwise, adapt from legacy format // We need to force cast here due to type incompatibility const adaptedEmail = adaptLegacyEmail(email); return { ...adaptedEmail, flags: adaptedEmail.flags || [] // Ensure flags is always an array } as EmailMessage; } /** * Render normalized email content into HTML for display */ export function renderEmailContent(content: EmailContent | null): string { if (!content) { return '
${text.replace(/\n/g, '
')}
` : 'No content available
'); // Wrap the original content in proper styling without losing the HTML structure const cleanHtml = ` ${replyHeader}${contentHtml}`; // 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' } }; } /** * Format email for forwarding */ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMessage | null): FormattedEmail { if (!originalEmail) { return { to: '', subject: '', content: { text: '', html: '', isHtml: false, direction: 'ltr' as const } }; } // 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 = `
| From: | ${fromStr} |
| Date: | ${dateStr} |
| Subject: | ${subject || ''} |
| To: | ${toStr} |
| Cc: | ${ccStr} |
${text.replace(/\n/g, '
')}
` : 'No content available
'); const cleanHtml = `${forwardHeader}${contentHtml}`; // Plain text version - with clearer formatting const plainText = ` ---------------------------- Forwarded Message ---------------------------- From: ${fromStr} Date: ${dateStr} Subject: ${subject || ''} To: ${toStr} ${ccStr ? `Cc: ${ccStr}` : ''} ---------------------------------------------------------------------- ${text} `; // Check if original has attachments const attachments = originalEmail.attachments || []; return { to: '', subject: subject.startsWith('Fwd:') ? subject : `Fwd: ${subject}`, content: { text: plainText.trim(), html: cleanHtml, isHtml: true, direction: 'ltr' }, // 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 }; } /** * Format an email for reply or reply-all - canonical implementation */ export function formatEmailForReplyOrForward( email: EmailMessage | LegacyEmailMessage | null, type: 'reply' | 'reply-all' | 'forward' ): FormattedEmail { // Use our dedicated formatters if (type === 'forward') { return formatForwardedEmail(email); } else { return formatReplyEmail(email, type as 'reply' | 'reply-all'); } }