courrier clean 2

This commit is contained in:
alma 2025-04-26 11:46:03 +02:00
parent 7820663c78
commit f117d66626
2 changed files with 257 additions and 42 deletions

View File

@ -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<void>;
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 = `
<div style="border-top: 1px solid #e1e1e1; margin-top: 20px; padding-top: 15px; font-family: Arial, sans-serif;">
<div style="margin-bottom: 15px;">
<div style="font-weight: normal; margin-bottom: 10px;">---------- Forwarded message ---------</div>
<div><b>From:</b> ${email.fromName || email.from}</div>
<div><b>Date:</b> ${new Date(email.date).toLocaleString()}</div>
<div><b>Subject:</b> ${email.subject || '(No subject)'}</div>
<div><b>To:</b> ${email.to}</div>
</div>
</div>
`;
// Prepare content
let contentHtml = '<div style="color: #666; font-style: italic; padding: 15px; border: 1px dashed #ccc; margin: 10px 0; text-align: center; background-color: #f9f9f9;">No content available</div>';
// 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 += `<pre>${email.text}</pre>`;
} else if (email.body) {
finalContent += email.body;
} else {
finalContent += `<div style="border: 1px dashed #ccc; padding: 10px; margin: 10px 0; text-align: center; background-color: #f9f9f9;">
No content available
</div>`;
}
// Set body with header and content
setComposeBody(`
${headerHtml}
<div style="margin-top: 10px;">
${contentHtml}
</div>
`);
setComposeBody(finalContent);
};
if (!showCompose) return null;

209
lib/email-formatter.ts Normal file
View File

@ -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 = `<br/><br/>${quoteHeader}<blockquote style="margin: 0 0 0 0.8ex; border-left: 1px solid #ccc; padding-left: 1ex;">`;
// Add quoted content
if (email.content) {
body += email.content;
} else if (email.html) {
body += email.html;
} else if (email.text) {
body += `<pre>${email.text}</pre>`;
} else if (email.body) {
body += email.body;
} else {
body += '<div style="color: #666; font-style: italic;">No content available</div>';
}
body += '</blockquote>';
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 `
<div style="color: #666; margin-bottom: 10px; font-family: Arial, sans-serif;">
<div>On ${date}, ${from} wrote:</div>
</div>
`;
}
/**
* 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 = `
<div style="border-top: 1px solid #e1e1e1; margin-top: 20px; padding-top: 15px; font-family: Arial, sans-serif;">
<div style="margin-bottom: 15px;">
<div style="font-weight: normal; margin-bottom: 10px;">---------- Forwarded message ---------</div>
<div><b>From:</b> ${fromString}</div>
<div><b>Date:</b> ${email.date ? new Date(email.date).toLocaleString() : 'Unknown Date'}</div>
<div><b>Subject:</b> ${email.subject || '(No subject)'}</div>
<div><b>To:</b> ${toString || 'Unknown Recipient'}</div>
</div>
</div>
`;
return {
subject,
headerHtml
};
}