courrier clean 2
This commit is contained in:
parent
7820663c78
commit
f117d66626
@ -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
209
lib/email-formatter.ts
Normal 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
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user