From afc7f640275e74082ed9970e6096a058496e461c Mon Sep 17 00:00:00 2001 From: alma Date: Sat, 26 Apr 2025 19:23:31 +0200 Subject: [PATCH] courrier clean 2$ --- DEPRECATED_FUNCTIONS.md | 44 ++++++- README.md | 21 +++- lib/actions/email-actions.ts | 6 +- lib/email-formatter.ts | 225 ---------------------------------- lib/services/email-service.ts | 137 +-------------------- 5 files changed, 68 insertions(+), 365 deletions(-) delete mode 100644 lib/email-formatter.ts diff --git a/DEPRECATED_FUNCTIONS.md b/DEPRECATED_FUNCTIONS.md index f389c850..cf4282e8 100644 --- a/DEPRECATED_FUNCTIONS.md +++ b/DEPRECATED_FUNCTIONS.md @@ -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 diff --git a/README.md b/README.md index eab1f3ec..1d42fcac 100644 --- a/README.md +++ b/README.md @@ -56,4 +56,23 @@ npm run dev # Build for production npm run build -``` \ No newline at end of file +``` + +# 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. \ No newline at end of file diff --git a/lib/actions/email-actions.ts b/lib/actions/email-actions.ts index 844b610f..2a8fb922 100644 --- a/lib/actions/email-actions.ts +++ b/lib/actions/email-actions.ts @@ -1,6 +1,7 @@ '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 @@ -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 + * Uses the centralized email formatter */ export async function formatEmailServerSide( email: { @@ -71,7 +73,7 @@ export async function formatEmailServerSide( contentFetched: true }; - // Use the server-side formatter + // Use the centralized formatter const formatted = formatEmailForReplyOrForward(serverEmail, type); return { diff --git a/lib/email-formatter.ts b/lib/email-formatter.ts deleted file mode 100644 index 57ca9071..00000000 --- a/lib/email-formatter.ts +++ /dev/null @@ -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, '
'); - } - - // Combine the header with the original content, ensuring proper direction - const body = ` -
-
-
- ${quoteHeader} -
${originalContent || 'No content available'}
-
-
- `; - - 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 ` -
-
From: ${fromName} <${fromEmail}>
- ${dateFormatted ? `
Date: ${dateFormatted}
` : ''} -
Subject: ${email.subject || 'No Subject'}
-
To: ${recipients || 'No Recipients'}
- ${email.cc ? `
Cc: ${Array.isArray(email.cc) ? - email.cc.map(r => r.name || r.address).join(', ') : - (email.cc.name || email.cc.address)}
` : ''} -
-
- `; -} - -/** - * 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, '
'); - } - - // Combine the header with the original content, ensuring proper direction - const body = ` -
-
-
-
---------- Forwarded message ---------
- ${forwardHeader} -
${originalContent || '
No content available
'}
-
-
- `; - - return { - subject, - body - }; -} \ No newline at end of file diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index a8b096b5..561e6a58 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -628,138 +628,5 @@ export async function testEmailConnection(credentials: EmailCredentials): Promis } } -/** - * Format email for reply/forward - */ -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: ` -
-
-

---------- Forwarded message ---------

-

From: ${fromText}

-

Date: ${formattedDate}

-

Subject: ${email.subject || ''}

-

To: ${toText}

-
-
- ${email.html || email.text ? (email.html || `
${email.text || ''}
`) : '

No content available

'} -
-
- ` - }; - } - - // Format the email body with improved quote styling for replies - const body = ` -
-
-
${quoteHeader}
-
-
- ${quotedContent} -
-
-
`; - - 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 { - // 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 { - // 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 `
On ${formattedDate}, ${fromText} wrote:
`; -} \ No newline at end of file +// Email formatting functions have been moved to lib/utils/email-formatter.ts +// Use those functions instead of the ones previously defined here \ No newline at end of file