225 lines
6.9 KiB
TypeScript
225 lines
6.9 KiB
TypeScript
'use client';
|
|
|
|
import DOMPurify from 'dompurify';
|
|
|
|
/**
|
|
* Client-side utilities for formatting email content
|
|
* This file contains functions for formatting email content in the browser
|
|
* without any server dependencies.
|
|
*/
|
|
|
|
export interface EmailAddress {
|
|
address: string;
|
|
name?: string;
|
|
}
|
|
|
|
export interface FormattedEmail {
|
|
subject: string;
|
|
to?: EmailAddress[];
|
|
cc?: EmailAddress[];
|
|
bcc?: EmailAddress[];
|
|
body: string;
|
|
}
|
|
|
|
export interface EmailMessageForFormatting {
|
|
subject?: string;
|
|
from?: EmailAddress | EmailAddress[];
|
|
to?: EmailAddress | EmailAddress[];
|
|
date?: Date | string;
|
|
html?: string;
|
|
text?: string;
|
|
cc?: EmailAddress | EmailAddress[];
|
|
bcc?: EmailAddress | EmailAddress[];
|
|
}
|
|
|
|
/**
|
|
* Format an email for replying or forwarding
|
|
* Client-side friendly version that doesn't depend on server modules
|
|
*/
|
|
export function formatEmailForReply(
|
|
originalEmail: EmailMessageForFormatting,
|
|
type: 'reply' | 'replyAll' | 'forward' = 'reply'
|
|
): FormattedEmail {
|
|
// Format the subject with Re: or Fwd: prefix
|
|
const subject = formatSubject(originalEmail.subject || '', type);
|
|
|
|
// Initialize recipients based on reply type
|
|
let to: EmailAddress[] = [];
|
|
let cc: EmailAddress[] = [];
|
|
|
|
if (type === 'reply' && originalEmail.from) {
|
|
to = Array.isArray(originalEmail.from) ? originalEmail.from : [originalEmail.from];
|
|
} else if (type === 'replyAll') {
|
|
// To: original sender
|
|
if (originalEmail.from) {
|
|
to = Array.isArray(originalEmail.from) ? originalEmail.from : [originalEmail.from];
|
|
}
|
|
|
|
// CC: all other recipients
|
|
if (originalEmail.to) {
|
|
cc = Array.isArray(originalEmail.to) ? originalEmail.to : [originalEmail.to];
|
|
}
|
|
|
|
if (originalEmail.cc) {
|
|
const existingCc = Array.isArray(originalEmail.cc) ? originalEmail.cc : [originalEmail.cc];
|
|
cc = [...cc, ...existingCc];
|
|
}
|
|
|
|
// Remove duplicates and self from CC (would need user's email here)
|
|
// This is simplified - in a real app you'd filter out the current user
|
|
cc = cc.filter((value, index, self) =>
|
|
index === self.findIndex((t) => t.address === value.address)
|
|
);
|
|
}
|
|
|
|
// Create the quoted content with header
|
|
const quoteHeader = createQuoteHeader(originalEmail);
|
|
|
|
// Get the original content, preferring HTML over plain text
|
|
let originalContent = '';
|
|
if (originalEmail.html) {
|
|
// Sanitize any potentially unsafe HTML
|
|
originalContent = DOMPurify.sanitize(originalEmail.html);
|
|
} else if (originalEmail.text) {
|
|
// Convert text to HTML by replacing newlines with br tags
|
|
originalContent = originalEmail.text.replace(/\n/g, '<br>');
|
|
}
|
|
|
|
// Combine the header with the original content, ensuring proper direction
|
|
const body = `
|
|
<div dir="ltr" style="text-align: left;">
|
|
<br>
|
|
<blockquote style="margin: 0 0 0 0.8ex; border-left: 1px solid #ccc; padding-left: 1ex;">
|
|
${quoteHeader}
|
|
<div style="direction: ltr; text-align: left;">${originalContent || 'No content available'}</div>
|
|
</blockquote>
|
|
</div>
|
|
`;
|
|
|
|
return {
|
|
subject,
|
|
to,
|
|
cc,
|
|
body
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Format email subject with appropriate prefix
|
|
*/
|
|
export function formatSubject(
|
|
originalSubject: string,
|
|
type: 'reply' | 'replyAll' | 'forward'
|
|
): string {
|
|
// Trim whitespace
|
|
let subject = originalSubject.trim();
|
|
|
|
// Remove existing prefixes to avoid duplication
|
|
subject = subject.replace(/^(Re|Fwd):\s*/gi, '');
|
|
|
|
// Add appropriate prefix based on action type
|
|
if (type === 'forward') {
|
|
return `Fwd: ${subject}`;
|
|
} else {
|
|
return `Re: ${subject}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a formatted quote header with sender and date information
|
|
*/
|
|
export function createQuoteHeader(email: EmailMessageForFormatting): string {
|
|
let fromName = 'Unknown Sender';
|
|
let fromEmail = '';
|
|
|
|
// Extract sender information
|
|
if (email.from) {
|
|
if (Array.isArray(email.from)) {
|
|
fromName = email.from[0].name || email.from[0].address;
|
|
fromEmail = email.from[0].address;
|
|
} else {
|
|
fromName = email.from.name || email.from.address;
|
|
fromEmail = email.from.address;
|
|
}
|
|
}
|
|
|
|
// Format the date
|
|
let dateFormatted = '';
|
|
if (email.date) {
|
|
const date = typeof email.date === 'string' ? new Date(email.date) : email.date;
|
|
|
|
// Check if the date is valid
|
|
if (!isNaN(date.getTime())) {
|
|
dateFormatted = date.toLocaleString('en-US', {
|
|
weekday: 'short',
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
}
|
|
}
|
|
|
|
// Generate recipients string
|
|
let recipients = '';
|
|
if (email.to) {
|
|
if (Array.isArray(email.to)) {
|
|
recipients = email.to.map(r => r.name || r.address).join(', ');
|
|
} else {
|
|
recipients = email.to.name || email.to.address;
|
|
}
|
|
}
|
|
|
|
// Create the header HTML with explicit LTR direction
|
|
return `
|
|
<div style="margin-bottom: 10px; color: #555; font-size: 0.9em; direction: ltr; text-align: left;">
|
|
<div><strong>From:</strong> ${fromName} <${fromEmail}></div>
|
|
${dateFormatted ? `<div><strong>Date:</strong> ${dateFormatted}</div>` : ''}
|
|
<div><strong>Subject:</strong> ${email.subject || 'No Subject'}</div>
|
|
<div><strong>To:</strong> ${recipients || 'No Recipients'}</div>
|
|
${email.cc ? `<div><strong>Cc:</strong> ${Array.isArray(email.cc) ?
|
|
email.cc.map(r => r.name || r.address).join(', ') :
|
|
(email.cc.name || email.cc.address)}</div>` : ''}
|
|
</div>
|
|
<hr style="border: none; border-top: 1px solid #ddd; margin: 10px 0;">
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Format an email for forwarding
|
|
*/
|
|
export function formatEmailForForward(email: EmailMessageForFormatting): FormattedEmail {
|
|
// Format the subject with Fwd: prefix
|
|
const subject = formatSubject(email.subject || '', 'forward');
|
|
|
|
// Create the forward header
|
|
const forwardHeader = createQuoteHeader(email);
|
|
|
|
// Get the original content, preferring HTML over plain text
|
|
let originalContent = '';
|
|
if (email.html) {
|
|
// Sanitize any potentially unsafe HTML
|
|
originalContent = DOMPurify.sanitize(email.html);
|
|
} else if (email.text) {
|
|
// Convert text to HTML by replacing newlines with br tags
|
|
originalContent = email.text.replace(/\n/g, '<br>');
|
|
}
|
|
|
|
// Combine the header with the original content, ensuring proper direction
|
|
const body = `
|
|
<div dir="ltr" style="text-align: left;">
|
|
<br>
|
|
<div style="border-left: 1px solid #ccc; padding-left: 1ex; margin-left: 0.8ex; direction: ltr; text-align: left;">
|
|
<div style="font-weight: bold; margin-bottom: 10px; direction: ltr; text-align: left;">---------- Forwarded message ---------</div>
|
|
${forwardHeader}
|
|
<div style="direction: ltr; text-align: left;">${originalContent || '<div style="padding: 10px; text-align: center; background-color: #f8f8f8; border: 1px dashed #ccc; margin: 10px 0;">No content available</div>'}</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
return {
|
|
subject,
|
|
body
|
|
};
|
|
}
|