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 { Input } from '@/components/ui/input';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card';
|
||||||
import DOMPurify from 'isomorphic-dompurify';
|
import DOMPurify from 'isomorphic-dompurify';
|
||||||
import { formatEmailForReplyOrForward } from '@/lib/services/email-service';
|
import { formatEmailForReply, formatEmailForForward } from '@/lib/email-formatter';
|
||||||
import { Email } from '@/app/courrier/page';
|
|
||||||
|
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 {
|
interface ComposeEmailProps {
|
||||||
showCompose: boolean;
|
showCompose: boolean;
|
||||||
@ -35,8 +53,8 @@ interface ComposeEmailProps {
|
|||||||
};
|
};
|
||||||
onSend: (email: any) => Promise<void>;
|
onSend: (email: any) => Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
replyTo?: Email | null;
|
replyTo?: EmailObject | null;
|
||||||
forwardFrom?: Email | null;
|
forwardFrom?: EmailObject | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ComposeEmail({
|
export default function ComposeEmail({
|
||||||
@ -77,10 +95,10 @@ export default function ComposeEmail({
|
|||||||
|
|
||||||
// Initialize content when replying or forwarding
|
// Initialize content when replying or forwarding
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Handle reply or forward initialization
|
// Initialize reply if replyTo is provided
|
||||||
if (replyTo) {
|
if (replyTo) {
|
||||||
// For reply/reply-all
|
// For reply/reply-all
|
||||||
const formattedEmail = formatEmailForReplyOrForward(replyTo as any, 'reply');
|
const formattedEmail = formatEmailForReply(replyTo as any, 'reply');
|
||||||
setComposeTo(formattedEmail.to);
|
setComposeTo(formattedEmail.to);
|
||||||
setComposeSubject(formattedEmail.subject);
|
setComposeSubject(formattedEmail.subject);
|
||||||
setComposeBody(formattedEmail.body);
|
setComposeBody(formattedEmail.body);
|
||||||
@ -91,55 +109,43 @@ export default function ComposeEmail({
|
|||||||
setShowCc(true);
|
setShowCc(true);
|
||||||
}
|
}
|
||||||
} else if (forwardFrom) {
|
} else if (forwardFrom) {
|
||||||
// For forward
|
// Initialize forward email if forwardFrom is provided
|
||||||
initializeForwardedEmail(forwardFrom);
|
initializeForwardedEmail(forwardFrom);
|
||||||
}
|
}
|
||||||
}, [replyTo, forwardFrom]);
|
}, [replyTo, forwardFrom]);
|
||||||
|
|
||||||
// Initialize forwarded email content
|
// Initialize forwarded email content
|
||||||
const initializeForwardedEmail = async (email: any) => {
|
const initializeForwardedEmail = async (email: any) => {
|
||||||
if (!email) return;
|
console.log('Initializing forwarded email:', email);
|
||||||
|
|
||||||
// Format subject with Fwd: prefix if needed
|
// Use our client-side formatter
|
||||||
const subjectBase = email.subject || '(No subject)';
|
const formattedEmail = formatEmailForForward(email);
|
||||||
const subjectRegex = /^(Fwd|FW|Forward):\s*/i;
|
|
||||||
const subject = subjectRegex.test(subjectBase)
|
|
||||||
? subjectBase
|
|
||||||
: `Fwd: ${subjectBase}`;
|
|
||||||
|
|
||||||
setComposeSubject(subject);
|
// Set the formatted subject with Fwd: prefix
|
||||||
|
setComposeSubject(formattedEmail.subject);
|
||||||
|
|
||||||
// Create header for forwarded email
|
// Get the email content and create a forward with proper formatting
|
||||||
const headerHtml = `
|
let finalContent = '';
|
||||||
<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
|
// Add the email header with proper styling
|
||||||
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>';
|
finalContent += formattedEmail.headerHtml;
|
||||||
|
|
||||||
|
// Add the original content
|
||||||
if (email.content) {
|
if (email.content) {
|
||||||
// Sanitize the content
|
finalContent += email.content;
|
||||||
contentHtml = DOMPurify.sanitize(email.content, {
|
} else if (email.html) {
|
||||||
ADD_TAGS: ['style'],
|
finalContent += email.html;
|
||||||
FORBID_TAGS: ['script', 'iframe']
|
} 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(finalContent);
|
||||||
setComposeBody(`
|
|
||||||
${headerHtml}
|
|
||||||
<div style="margin-top: 10px;">
|
|
||||||
${contentHtml}
|
|
||||||
</div>
|
|
||||||
`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!showCompose) return null;
|
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