import { format, parseISO, isValid as isValidDate } from 'date-fns'; /** * Extracts plain text from HTML content * @param {string} html - HTML content to convert * @returns {string} Plain text content */ export const extractPlainTextFromHtml = html => { if (!html) { return ''; } if (typeof document === 'undefined') { return html.replace(/<[^>]*>/g, ' '); } const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; return tempDiv.textContent || tempDiv.innerText || ''; }; /** * Extracts sender name from email message * @param {Object} lastEmail - Last email message object * @param {Object} contact - Contact object * @returns {string} Sender name */ export const getEmailSenderName = (lastEmail, contact) => { const senderName = lastEmail?.sender?.name; if (senderName && senderName.trim()) { return senderName.trim(); } const contactName = contact?.name; return contactName && contactName.trim() ? contactName.trim() : ''; }; /** * Extracts sender email from email message * @param {Object} lastEmail - Last email message object * @param {Object} contact - Contact object * @returns {string} Sender email address */ export const getEmailSenderEmail = (lastEmail, contact) => { const senderEmail = lastEmail?.sender?.email; if (senderEmail && senderEmail.trim()) { return senderEmail.trim(); } const contentAttributes = lastEmail?.contentAttributes || lastEmail?.content_attributes || {}; const emailMeta = contentAttributes.email || {}; if (Array.isArray(emailMeta.from) && emailMeta.from.length > 0) { const fromAddress = emailMeta.from[0]; if (fromAddress && fromAddress.trim()) { return fromAddress.trim(); } } const contactEmail = contact?.email; return contactEmail && contactEmail.trim() ? contactEmail.trim() : ''; }; /** * Extracts date from email message * @param {Object} lastEmail - Last email message object * @returns {Date|null} Email date */ export const getEmailDate = lastEmail => { const contentAttributes = lastEmail?.contentAttributes || lastEmail?.content_attributes || {}; const emailMeta = contentAttributes.email || {}; if (emailMeta.date) { const parsedDate = parseISO(emailMeta.date); if (isValidDate(parsedDate)) { return parsedDate; } } const createdAt = lastEmail?.created_at; if (createdAt) { const timestamp = Number(createdAt); if (!Number.isNaN(timestamp)) { const milliseconds = timestamp > 1e12 ? timestamp : timestamp * 1000; const derivedDate = new Date(milliseconds); if (!Number.isNaN(derivedDate.getTime())) { return derivedDate; } } } return null; }; /** * Formats date for quoted email header * @param {Date} date - Date to format * @returns {string} Formatted date string */ export const formatQuotedEmailDate = date => { try { return format(date, "EEE, MMM d, yyyy 'at' p"); } catch (error) { const fallbackDate = new Date(date); if (!Number.isNaN(fallbackDate.getTime())) { return format(fallbackDate, "EEE, MMM d, yyyy 'at' p"); } } return ''; }; /** * Extracts inbox email address from last email message * @param {Object} lastEmail - Last email message object * @param {Object} inbox - Inbox object * @returns {string} Inbox email address */ export const getInboxEmail = (lastEmail, inbox) => { const contentAttributes = lastEmail?.contentAttributes || lastEmail?.content_attributes || {}; const emailMeta = contentAttributes.email || {}; if (Array.isArray(emailMeta.to) && emailMeta.to.length > 0) { const toAddress = emailMeta.to[0]; if (toAddress && toAddress.trim()) { return toAddress.trim(); } } const inboxEmail = inbox?.email; return inboxEmail && inboxEmail.trim() ? inboxEmail.trim() : ''; }; /** * Builds quoted email header from contact (for incoming messages) * @param {Object} lastEmail - Last email message object * @param {Object} contact - Contact object * @returns {string} Formatted header string */ export const buildQuotedEmailHeaderFromContact = (lastEmail, contact) => { if (!lastEmail) { return ''; } const quotedDate = getEmailDate(lastEmail); const senderEmail = getEmailSenderEmail(lastEmail, contact); if (!quotedDate || !senderEmail) { return ''; } const formattedDate = formatQuotedEmailDate(quotedDate); if (!formattedDate) { return ''; } const senderName = getEmailSenderName(lastEmail, contact); const hasName = !!senderName; const contactLabel = hasName ? `${senderName} <${senderEmail}>` : `<${senderEmail}>`; return `On ${formattedDate} ${contactLabel} wrote:`; }; /** * Builds quoted email header from inbox (for outgoing messages) * @param {Object} lastEmail - Last email message object * @param {Object} inbox - Inbox object * @returns {string} Formatted header string */ export const buildQuotedEmailHeaderFromInbox = (lastEmail, inbox) => { if (!lastEmail) { return ''; } const quotedDate = getEmailDate(lastEmail); const inboxEmail = getInboxEmail(lastEmail, inbox); if (!quotedDate || !inboxEmail) { return ''; } const formattedDate = formatQuotedEmailDate(quotedDate); if (!formattedDate) { return ''; } const inboxName = inbox?.name; const hasName = !!inboxName; const inboxLabel = hasName ? `${inboxName} <${inboxEmail}>` : `<${inboxEmail}>`; return `On ${formattedDate} ${inboxLabel} wrote:`; }; /** * Builds quoted email header based on message type * @param {Object} lastEmail - Last email message object * @param {Object} contact - Contact object * @param {Object} inbox - Inbox object * @returns {string} Formatted header string */ export const buildQuotedEmailHeader = (lastEmail, contact, inbox) => { if (!lastEmail) { return ''; } // MESSAGE_TYPE.OUTGOING = 1, MESSAGE_TYPE.INCOMING = 0 const isOutgoing = lastEmail.message_type === 1; if (isOutgoing) { return buildQuotedEmailHeaderFromInbox(lastEmail, inbox); } return buildQuotedEmailHeaderFromContact(lastEmail, contact); }; /** * Formats text as markdown blockquote * @param {string} text - Text to format * @param {string} header - Optional header to prepend * @returns {string} Formatted blockquote */ export const formatQuotedTextAsBlockquote = (text, header = '') => { const normalizedLines = text ? String(text).replace(/\r\n/g, '\n').split('\n') : []; if (!header && !normalizedLines.length) { return ''; } const quotedLines = []; if (header) { quotedLines.push(`> ${header}`); quotedLines.push('>'); } normalizedLines.forEach(line => { const trimmedLine = line.trimEnd(); quotedLines.push(trimmedLine ? `> ${trimmedLine}` : '>'); }); return quotedLines.join('\n'); }; /** * Extracts quoted email text from last email message * @param {Object} lastEmail - Last email message object * @returns {string} Quoted email text */ export const extractQuotedEmailText = lastEmail => { if (!lastEmail) { return ''; } const contentAttributes = lastEmail.contentAttributes || lastEmail.content_attributes || {}; const emailContent = contentAttributes.email || {}; const textContent = emailContent.textContent || emailContent.text_content; if (textContent?.reply) { return textContent.reply; } if (textContent?.full) { return textContent.full; } const htmlContent = emailContent.htmlContent || emailContent.html_content; if (htmlContent?.reply) { return extractPlainTextFromHtml(htmlContent.reply); } if (htmlContent?.full) { return extractPlainTextFromHtml(htmlContent.full); } const fallbackContent = lastEmail.content || lastEmail.processed_message_content || ''; return fallbackContent; }; /** * Truncates text for preview display * @param {string} text - Text to truncate * @param {number} maxLength - Maximum length (default: 80) * @returns {string} Truncated text */ export const truncatePreviewText = (text, maxLength = 80) => { const preview = text.trim().replace(/\s+/g, ' '); if (!preview) { return ''; } if (preview.length <= maxLength) { return preview; } return `${preview.slice(0, maxLength - 3)}...`; }; /** * Appends quoted text to message * @param {string} message - Original message * @param {string} quotedText - Text to quote * @param {string} header - Quote header * @returns {string} Message with quoted text appended */ export const appendQuotedTextToMessage = (message, quotedText, header) => { const baseMessage = message ? String(message) : ''; const quotedBlock = formatQuotedTextAsBlockquote(quotedText, header); if (!quotedBlock) { return baseMessage; } if (!baseMessage) { return quotedBlock; } let separator = '\n\n'; if (baseMessage.endsWith('\n\n')) { separator = ''; } else if (baseMessage.endsWith('\n')) { separator = '\n'; } return `${baseMessage}${separator}${quotedBlock}`; };