courrier clean 2$
This commit is contained in:
parent
4c9fcdeb29
commit
afc7f64027
@ -1,6 +1,46 @@
|
|||||||
# Deprecated Functions and Code
|
# Deprecated Functions and Files
|
||||||
|
|
||||||
This document tracks functions that have been marked as deprecated and should be removed in future releases.
|
This document lists functions and files that have been deprecated and should not be used in new code.
|
||||||
|
|
||||||
|
## Deprecated Files
|
||||||
|
|
||||||
|
### 1. `lib/email-formatter.ts` (REMOVED)
|
||||||
|
- **Status**: Removed
|
||||||
|
- **Replacement**: Use `lib/utils/email-formatter.ts` instead
|
||||||
|
- **Reason**: Consolidated email formatting to a single source of truth
|
||||||
|
|
||||||
|
### 2. `cleanHtml` in `lib/mail-parser-wrapper.ts`
|
||||||
|
- **Status**: Deprecated for email composition
|
||||||
|
- **Replacement**: Use `cleanHtmlContent` from `lib/utils/email-formatter.ts` instead
|
||||||
|
- **Reason**: Better handling of text direction and HTML sanitization
|
||||||
|
|
||||||
|
## Deprecated Functions
|
||||||
|
|
||||||
|
### 1. `formatEmailForReplyOrForward` in `lib/services/email-service.ts` (REMOVED)
|
||||||
|
- **Status**: Removed
|
||||||
|
- **Replacement**: Use `formatEmailForReplyOrForward` from `lib/utils/email-formatter.ts`
|
||||||
|
- **Reason**: Consolidated email formatting to a single source of truth
|
||||||
|
|
||||||
|
### 2. `formatSubject` in `lib/services/email-service.ts` (REMOVED)
|
||||||
|
- **Status**: Removed
|
||||||
|
- **Replacement**: None specific, handled by centralized formatter
|
||||||
|
- **Reason**: Internal function of the email formatter
|
||||||
|
|
||||||
|
### 3. `createQuoteHeader` in `lib/services/email-service.ts` (REMOVED)
|
||||||
|
- **Status**: Removed
|
||||||
|
- **Replacement**: None specific, handled by centralized formatter
|
||||||
|
- **Reason**: Internal function of the email formatter
|
||||||
|
|
||||||
|
## Centralized Email Formatting
|
||||||
|
|
||||||
|
All email formatting is now handled by the centralized formatter in `lib/utils/email-formatter.ts`. This file contains:
|
||||||
|
|
||||||
|
1. `formatForwardedEmail`: Format emails for forwarding
|
||||||
|
2. `formatReplyEmail`: Format emails for replying or replying to all
|
||||||
|
3. `formatEmailForReplyOrForward`: Compatibility function that maps to the above two
|
||||||
|
4. `cleanHtmlContent`: Safely sanitize HTML content while preserving direction attributes
|
||||||
|
|
||||||
|
Use these functions for all email formatting needs.
|
||||||
|
|
||||||
## Email Parsing and Processing Functions
|
## Email Parsing and Processing Functions
|
||||||
|
|
||||||
|
|||||||
21
README.md
21
README.md
@ -56,4 +56,23 @@ npm run dev
|
|||||||
|
|
||||||
# Build for production
|
# Build for production
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Email Formatting
|
||||||
|
|
||||||
|
## Centralized Email Formatter
|
||||||
|
|
||||||
|
All email formatting is now handled by a centralized formatter in `lib/utils/email-formatter.ts`. This ensures consistent handling of:
|
||||||
|
|
||||||
|
- Text direction (RTL/LTR)
|
||||||
|
- HTML sanitization
|
||||||
|
- Content formatting for forwards and replies
|
||||||
|
|
||||||
|
### Key Functions
|
||||||
|
|
||||||
|
- `formatForwardedEmail`: Format emails for forwarding
|
||||||
|
- `formatReplyEmail`: Format emails for replying
|
||||||
|
- `cleanHtmlContent`: Sanitize HTML while preserving direction attributes
|
||||||
|
- `formatEmailForReplyOrForward`: Compatibility function for both
|
||||||
|
|
||||||
|
This centralized approach prevents formatting inconsistencies and direction problems when dealing with emails in different languages.
|
||||||
@ -1,6 +1,7 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { getEmails, formatEmailForReplyOrForward, EmailMessage, EmailAddress } from '@/lib/services/email-service';
|
import { getEmails, EmailMessage, EmailAddress } from '@/lib/services/email-service';
|
||||||
|
import { formatEmailForReplyOrForward } from '@/lib/utils/email-formatter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server action to fetch emails
|
* Server action to fetch emails
|
||||||
@ -20,6 +21,7 @@ export async function fetchEmails(userId: string, folder = 'INBOX', page = 1, pe
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Server action to format email for reply or forward operations
|
* Server action to format email for reply or forward operations
|
||||||
|
* Uses the centralized email formatter
|
||||||
*/
|
*/
|
||||||
export async function formatEmailServerSide(
|
export async function formatEmailServerSide(
|
||||||
email: {
|
email: {
|
||||||
@ -71,7 +73,7 @@ export async function formatEmailServerSide(
|
|||||||
contentFetched: true
|
contentFetched: true
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use the server-side formatter
|
// Use the centralized formatter
|
||||||
const formatted = formatEmailForReplyOrForward(serverEmail, type);
|
const formatted = formatEmailForReplyOrForward(serverEmail, type);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,225 +0,0 @@
|
|||||||
'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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -628,138 +628,5 @@ export async function testEmailConnection(credentials: EmailCredentials): Promis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Email formatting functions have been moved to lib/utils/email-formatter.ts
|
||||||
* Format email for reply/forward
|
// Use those functions instead of the ones previously defined here
|
||||||
*/
|
|
||||||
export async function formatEmailForReplyOrForward(
|
|
||||||
email: EmailMessage,
|
|
||||||
type: 'reply' | 'reply-all' | 'forward'
|
|
||||||
): Promise<{
|
|
||||||
to: string;
|
|
||||||
cc?: string;
|
|
||||||
subject: string;
|
|
||||||
body: string;
|
|
||||||
}> {
|
|
||||||
// Format the subject with Re: or Fwd: prefix
|
|
||||||
const subject = await formatSubject(email.subject, type);
|
|
||||||
|
|
||||||
// Create the email quote with proper formatting
|
|
||||||
const quoteHeader = await createQuoteHeader(email);
|
|
||||||
const quotedContent = email.html || email.text || '';
|
|
||||||
|
|
||||||
// Format recipients
|
|
||||||
let to = '';
|
|
||||||
let cc = '';
|
|
||||||
|
|
||||||
if (type === 'reply') {
|
|
||||||
// Reply to sender only
|
|
||||||
to = email.from.map(addr => `${addr.name} <${addr.address}>`).join(', ');
|
|
||||||
} else if (type === 'reply-all') {
|
|
||||||
// Reply to sender and all recipients
|
|
||||||
to = email.from.map(addr => `${addr.name} <${addr.address}>`).join(', ');
|
|
||||||
|
|
||||||
// Add all original recipients to CC, except ourselves
|
|
||||||
const allRecipients = [
|
|
||||||
...(email.to || []),
|
|
||||||
...(email.cc || [])
|
|
||||||
];
|
|
||||||
|
|
||||||
cc = allRecipients
|
|
||||||
.map(addr => `${addr.name} <${addr.address}>`)
|
|
||||||
.join(', ');
|
|
||||||
} else if (type === 'forward') {
|
|
||||||
// Forward case doesn't need to set recipients
|
|
||||||
to = '';
|
|
||||||
|
|
||||||
// Instead, we format the content differently
|
|
||||||
const formattedDate = email.date ? new Date(email.date).toLocaleString() : '';
|
|
||||||
const fromText = email.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', ');
|
|
||||||
const toText = email.to.map(t => t.name ? `${t.name} <${t.address}>` : t.address).join(', ');
|
|
||||||
|
|
||||||
// Return specialized body for forward with improved styling
|
|
||||||
return {
|
|
||||||
to: '',
|
|
||||||
subject,
|
|
||||||
body: `
|
|
||||||
<div class="forwarded-message" style="margin-top: 20px;">
|
|
||||||
<div style="background-color: #f5f5f5; border-left: 3px solid #ddd; padding: 12px; margin-bottom: 15px; border-radius: 4px;">
|
|
||||||
<p style="margin: 0 0 8px 0; font-weight: 500; color: #555; font-size: 14px;">---------- Forwarded message ---------</p>
|
|
||||||
<p style="margin: 0 0 6px 0; font-size: 13px;"><span style="font-weight: 600; color: #444;">From:</span> ${fromText}</p>
|
|
||||||
<p style="margin: 0 0 6px 0; font-size: 13px;"><span style="font-weight: 600; color: #444;">Date:</span> ${formattedDate}</p>
|
|
||||||
<p style="margin: 0 0 6px 0; font-size: 13px;"><span style="font-weight: 600; color: #444;">Subject:</span> ${email.subject || ''}</p>
|
|
||||||
<p style="margin: 0 0 6px 0; font-size: 13px;"><span style="font-weight: 600; color: #444;">To:</span> ${toText}</p>
|
|
||||||
</div>
|
|
||||||
<div class="forwarded-content" style="border-left: 2px solid #ddd; padding-left: 15px; color: #333;">
|
|
||||||
${email.html || email.text ? (email.html || `<pre style="white-space: pre-wrap; font-family: inherit; margin: 0;">${email.text || ''}</pre>`) : '<p>No content available</p>'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the email body with improved quote styling for replies
|
|
||||||
const body = `
|
|
||||||
<div class="reply-body">
|
|
||||||
<br/>
|
|
||||||
<div class="quote-header" style="color: #555; font-size: 13px; margin: 20px 0 10px 0;">${quoteHeader}</div>
|
|
||||||
<blockquote style="margin: 0; padding: 10px 0 10px 15px; border-left: 3px solid #ddd; color: #555; background-color: #f8f8f8; border-radius: 4px;">
|
|
||||||
<div class="quoted-content" style="font-size: 13px;">
|
|
||||||
${quotedContent}
|
|
||||||
</div>
|
|
||||||
</blockquote>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
to,
|
|
||||||
cc: cc || undefined,
|
|
||||||
subject,
|
|
||||||
body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format subject with appropriate prefix (Re:, Fwd:)
|
|
||||||
*/
|
|
||||||
export async function formatSubject(subject: string, type: 'reply' | 'reply-all' | 'forward'): Promise<string> {
|
|
||||||
// Clean up any existing prefixes
|
|
||||||
let cleanSubject = subject
|
|
||||||
.replace(/^(Re|Fwd|FW|Forward):\s*/i, '')
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// Add appropriate prefix
|
|
||||||
if (type === 'reply' || type === 'reply-all') {
|
|
||||||
if (!subject.match(/^Re:/i)) {
|
|
||||||
return `Re: ${cleanSubject}`;
|
|
||||||
}
|
|
||||||
} else if (type === 'forward') {
|
|
||||||
if (!subject.match(/^(Fwd|FW|Forward):/i)) {
|
|
||||||
return `Fwd: ${cleanSubject}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a quote header for reply/forward
|
|
||||||
*/
|
|
||||||
export async function createQuoteHeader(email: EmailMessage): Promise<string> {
|
|
||||||
// Format the date
|
|
||||||
const date = new Date(email.date);
|
|
||||||
const formattedDate = date.toLocaleString('en-US', {
|
|
||||||
weekday: 'short',
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Format the sender
|
|
||||||
const sender = email.from[0];
|
|
||||||
const fromText = sender?.name
|
|
||||||
? `${sender.name} <${sender.address}>`
|
|
||||||
: sender?.address || 'Unknown sender';
|
|
||||||
|
|
||||||
return `<div style="font-weight: 500;">On ${formattedDate}, ${fromText} wrote:</div>`;
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user