From f117d6662652099577413a68729dc87058bbce6d Mon Sep 17 00:00:00 2001 From: alma Date: Sat, 26 Apr 2025 11:46:03 +0200 Subject: [PATCH] courrier clean 2 --- components/ComposeEmail.tsx | 90 ++++++++-------- lib/email-formatter.ts | 209 ++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+), 42 deletions(-) create mode 100644 lib/email-formatter.ts diff --git a/components/ComposeEmail.tsx b/components/ComposeEmail.tsx index 9139680e..abe0d6ed 100644 --- a/components/ComposeEmail.tsx +++ b/components/ComposeEmail.tsx @@ -6,8 +6,26 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'; import DOMPurify from 'isomorphic-dompurify'; -import { formatEmailForReplyOrForward } from '@/lib/services/email-service'; -import { Email } from '@/app/courrier/page'; +import { formatEmailForReply, formatEmailForForward } from '@/lib/email-formatter'; + +interface EmailObject { + id?: string; + from?: string; + fromName?: string; + to?: string; + subject?: string; + content?: string; + html?: string; + text?: string; + body?: string; + date?: string; + read?: boolean; + starred?: boolean; + attachments?: { name: string; url: string }[]; + folder?: string; + cc?: string; + bcc?: string; +} interface ComposeEmailProps { showCompose: boolean; @@ -35,8 +53,8 @@ interface ComposeEmailProps { }; onSend: (email: any) => Promise; onCancel: () => void; - replyTo?: Email | null; - forwardFrom?: Email | null; + replyTo?: EmailObject | null; + forwardFrom?: EmailObject | null; } export default function ComposeEmail({ @@ -77,10 +95,10 @@ export default function ComposeEmail({ // Initialize content when replying or forwarding useEffect(() => { - // Handle reply or forward initialization + // Initialize reply if replyTo is provided if (replyTo) { // For reply/reply-all - const formattedEmail = formatEmailForReplyOrForward(replyTo as any, 'reply'); + const formattedEmail = formatEmailForReply(replyTo as any, 'reply'); setComposeTo(formattedEmail.to); setComposeSubject(formattedEmail.subject); setComposeBody(formattedEmail.body); @@ -91,55 +109,43 @@ export default function ComposeEmail({ setShowCc(true); } } else if (forwardFrom) { - // For forward + // Initialize forward email if forwardFrom is provided initializeForwardedEmail(forwardFrom); } }, [replyTo, forwardFrom]); // Initialize forwarded email content const initializeForwardedEmail = async (email: any) => { - if (!email) return; + console.log('Initializing forwarded email:', email); + + // Use our client-side formatter + const formattedEmail = formatEmailForForward(email); - // Format subject with Fwd: prefix if needed - const subjectBase = email.subject || '(No subject)'; - const subjectRegex = /^(Fwd|FW|Forward):\s*/i; - const subject = subjectRegex.test(subjectBase) - ? subjectBase - : `Fwd: ${subjectBase}`; + // Set the formatted subject with Fwd: prefix + setComposeSubject(formattedEmail.subject); - setComposeSubject(subject); + // Get the email content and create a forward with proper formatting + let finalContent = ''; - // Create header for forwarded email - const headerHtml = ` -
-
-
---------- Forwarded message ---------
-
From: ${email.fromName || email.from}
-
Date: ${new Date(email.date).toLocaleString()}
-
Subject: ${email.subject || '(No subject)'}
-
To: ${email.to}
-
-
- `; - - // Prepare content - let contentHtml = '
No content available
'; + // Add the email header with proper styling + finalContent += formattedEmail.headerHtml; + // Add the original content if (email.content) { - // Sanitize the content - contentHtml = DOMPurify.sanitize(email.content, { - ADD_TAGS: ['style'], - FORBID_TAGS: ['script', 'iframe'] - }); + finalContent += email.content; + } else if (email.html) { + finalContent += email.html; + } else if (email.text) { + finalContent += `
${email.text}
`; + } else if (email.body) { + finalContent += email.body; + } else { + finalContent += `
+ No content available +
`; } - // Set body with header and content - setComposeBody(` - ${headerHtml} -
- ${contentHtml} -
- `); + setComposeBody(finalContent); }; if (!showCompose) return null; diff --git a/lib/email-formatter.ts b/lib/email-formatter.ts new file mode 100644 index 00000000..9cc17d1f --- /dev/null +++ b/lib/email-formatter.ts @@ -0,0 +1,209 @@ +'use client'; + +/** + * Client-side utilities for formatting email content + * This file contains functions for formatting email content in the browser + * without any server dependencies. + */ + +interface EmailAddress { + name?: string; + address: string; +} + +interface FormattedEmail { + to: string; + cc?: string; + subject: string; + body: string; +} + +/** + * Format an email for replying or forwarding + * Client-side friendly version that doesn't depend on server modules + */ +export function formatEmailForReply( + email: any, + type: 'reply' | 'reply-all' = 'reply' +): FormattedEmail { + // Format the subject with Re: prefix + const subject = formatSubject(email.subject || '(No subject)', type); + + // Format recipients + let to = ''; + let cc = ''; + + // Process 'to' field for reply + if (typeof email.from === 'string') { + to = email.from; + } else if (Array.isArray(email.from)) { + to = email.from.map((addr: EmailAddress) => + addr.name && addr.name !== addr.address + ? `${addr.name} <${addr.address}>` + : addr.address + ).join(', '); + } else if (email.fromName || email.from) { + // Handle cases where from is an object with name and address + if (email.fromName && email.from && email.fromName !== email.from) { + to = `${email.fromName} <${email.from}>`; + } else { + to = email.from; + } + } + + // For reply-all, include other recipients in cc + if (type === 'reply-all' && email.to) { + if (typeof email.to === 'string') { + cc = email.to; + } else if (Array.isArray(email.to)) { + cc = email.to.map((addr: EmailAddress) => + addr.name && addr.name !== addr.address + ? `${addr.name} <${addr.address}>` + : addr.address + ).join(', '); + } + + // Include cc recipients from original email too if available + if (email.cc) { + const ccList = typeof email.cc === 'string' + ? email.cc + : Array.isArray(email.cc) + ? email.cc.map((addr: EmailAddress) => addr.address).join(', ') + : ''; + + if (ccList) { + cc = cc ? `${cc}, ${ccList}` : ccList; + } + } + } + + // Create quote header + const quoteHeader = createQuoteHeader(email); + + // Format body with quote + let body = `

${quoteHeader}
`; + + // Add quoted content + if (email.content) { + body += email.content; + } else if (email.html) { + body += email.html; + } else if (email.text) { + body += `
${email.text}
`; + } else if (email.body) { + body += email.body; + } else { + body += '
No content available
'; + } + + body += '
'; + + return { + to, + cc, + subject, + body + }; +} + +function formatSubject(subject: string, type: 'reply' | 'reply-all' | 'forward'): string { + // Clean up existing prefixes first + let cleanSubject = subject.replace(/^(Re|Fwd|FW|Forward):\s*/gi, ''); + cleanSubject = cleanSubject.trim() || '(No subject)'; + + // Add appropriate prefix + if (type === 'forward') { + return `Fwd: ${cleanSubject}`; + } else { + // For reply and reply-all + return `Re: ${cleanSubject}`; + } +} + +function createQuoteHeader(email: any): string { + let from = 'Unknown Sender'; + let date = email.date ? new Date(email.date).toLocaleString() : 'Unknown Date'; + let subject = email.subject || '(No subject)'; + let to = ''; + + // Extract from + if (typeof email.from === 'string') { + from = email.from; + } else if (Array.isArray(email.from)) { + from = email.from.map((addr: EmailAddress) => + addr.name ? `${addr.name} <${addr.address}>` : addr.address + ).join(', '); + } else if (email.fromName || email.from) { + from = email.fromName && email.fromName !== email.from + ? `${email.fromName} <${email.from}>` + : email.from; + } + + // Extract to + if (typeof email.to === 'string') { + to = email.to; + } else if (Array.isArray(email.to)) { + to = email.to.map((addr: EmailAddress) => + addr.name ? `${addr.name} <${addr.address}>` : addr.address + ).join(', '); + } + + return ` +
+
On ${date}, ${from} wrote:
+
+ `; +} + +/** + * Format an email for forwarding + */ +export function formatEmailForForward(email: any): { + subject: string; + headerHtml: string; +} { + // Format subject with Fwd: prefix + const subject = formatSubject(email.subject || '(No subject)', 'forward'); + + // Get sender information + let fromString = 'Unknown Sender'; + if (typeof email.from === 'string') { + fromString = email.from; + } else if (Array.isArray(email.from)) { + fromString = email.from.map((addr: EmailAddress) => + addr.name ? `${addr.name} <${addr.address}>` : addr.address + ).join(', '); + } else if (email.fromName && email.from) { + fromString = email.fromName !== email.from + ? `${email.fromName} <${email.from}>` + : email.from; + } + + // Get recipient information + let toString = ''; + if (typeof email.to === 'string') { + toString = email.to; + } else if (Array.isArray(email.to)) { + toString = email.to.map((addr: EmailAddress) => + addr.name ? `${addr.name} <${addr.address}>` : addr.address + ).join(', '); + } + + // Create header for forwarded email + const headerHtml = ` +
+
+
---------- Forwarded message ---------
+
From: ${fromString}
+
Date: ${email.date ? new Date(email.date).toLocaleString() : 'Unknown Date'}
+
Subject: ${email.subject || '(No subject)'}
+
To: ${toString || 'Unknown Recipient'}
+
+
+ `; + + return { + subject, + headerHtml + }; +} \ No newline at end of file