'use client';
import { useState, useRef, useEffect } from 'react';
// Remove direct import of server components
import {
X, Paperclip, ChevronDown, ChevronUp, SendHorizontal, Loader2,
AlignLeft, AlignRight
} from 'lucide-react';
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';
// Define EmailMessage interface locally instead of importing from server-only file
interface EmailAddress {
name: string;
address: string;
}
interface EmailMessage {
id: string;
messageId?: string;
subject: string;
from: EmailAddress[];
to: EmailAddress[];
cc?: EmailAddress[];
bcc?: EmailAddress[];
date: Date | string;
flags?: {
seen: boolean;
flagged: boolean;
answered: boolean;
deleted: boolean;
draft: boolean;
};
preview?: string;
content?: string;
html?: string;
text?: string;
hasAttachments?: boolean;
attachments?: any[];
folder?: string;
size?: number;
contentFetched?: boolean;
}
// Simplified formatEmailForReplyOrForward that doesn't rely on server code
function formatEmailForReplyOrForward(
email: EmailMessage,
type: 'reply' | 'reply-all' | 'forward'
): {
to: string;
cc?: string;
subject: string;
body: string;
} {
// Format subject
let subject = email.subject || '';
if (type === 'reply' || type === 'reply-all') {
if (!subject.startsWith('Re:')) {
subject = `Re: ${subject}`;
}
} else if (type === 'forward') {
if (!subject.startsWith('Fwd:')) {
subject = `Fwd: ${subject}`;
}
}
// Create quote header
const date = typeof email.date === 'string'
? new Date(email.date)
: email.date;
const formattedDate = date.toLocaleString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
const sender = email.from[0];
const fromText = sender?.name
? `${sender.name} <${sender.address}>`
: sender?.address || 'Unknown sender';
const quoteHeader = `
On ${formattedDate}, ${fromText} wrote:
`;
// Format content
const quotedContent = email.html || email.content || 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
const allRecipients = [
...(email.to || []),
...(email.cc || [])
];
cc = allRecipients
.map(addr => `${addr.name} <${addr.address}>`)
.join(', ');
} else if (type === 'forward') {
// Forward doesn't set recipients
to = '';
// Format forward differently
const formattedDate = typeof email.date === 'string'
? new Date(email.date).toLocaleString()
: 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 {
to: '',
subject,
body: `
---------- Forwarded message ---------
From: ${fromText}
Date: ${formattedDate}
Subject: ${email.subject || ''}
To: ${toText}
${quotedContent ? quotedContent : '
No content available
'}
`
};
}
// Format body with improved styling for replies
const body = `
${quoteHeader}
${quotedContent}
`;
return {
to,
cc: cc || undefined,
subject,
body
};
}
// Legacy interface for backward compatibility with old ComposeEmail component
interface LegacyComposeEmailProps {
showCompose: boolean;
setShowCompose: (show: boolean) => void;
composeTo: string;
setComposeTo: (to: string) => void;
composeCc: string;
setComposeCc: (cc: string) => void;
composeBcc: string;
setComposeBcc: (bcc: string) => void;
composeSubject: string;
setComposeSubject: (subject: string) => void;
composeBody: string;
setComposeBody: (body: string) => void;
showCc: boolean;
setShowCc: (show: boolean) => void;
showBcc: boolean;
setShowBcc: (show: boolean) => void;
attachments: any[];
setAttachments: (attachments: any[]) => void;
handleSend: () => Promise;
originalEmail?: {
content: string;
type: 'reply' | 'reply-all' | 'forward';
};
onSend: (email: any) => Promise;
onCancel: () => void;
replyTo?: any | null;
forwardFrom?: any | null;
}
// New interface for the modern ComposeEmail component
interface ComposeEmailProps {
initialEmail?: EmailMessage | null;
type?: 'new' | 'reply' | 'reply-all' | 'forward';
onClose: () => void;
onSend: (emailData: {
to: string;
cc?: string;
bcc?: string;
subject: string;
body: string;
attachments?: Array<{
name: string;
content: string;
type: string;
}>;
}) => Promise;
}
// Union type to handle both new and legacy props
type ComposeEmailAllProps = ComposeEmailProps | LegacyComposeEmailProps;
// Type guard to check if props are legacy
function isLegacyProps(props: ComposeEmailAllProps): props is LegacyComposeEmailProps {
return 'showCompose' in props && 'setShowCompose' in props;
}
export default function ComposeEmail(props: ComposeEmailAllProps) {
// Handle legacy props by adapting them to new component
if (isLegacyProps(props)) {
return ;
}
// Continue with modern implementation for new props
const { initialEmail, type = 'new', onClose, onSend } = props;
// Email form state
const [to, setTo] = useState('');
const [cc, setCc] = useState('');
const [bcc, setBcc] = useState('');
const [subject, setSubject] = useState('');
const [body, setBody] = useState('');
const [userMessage, setUserMessage] = useState('');
const [originalContent, setOriginalContent] = useState('');
const [editingOriginalContent, setEditingOriginalContent] = useState(false);
const [showCc, setShowCc] = useState(false);
const [showBcc, setShowBcc] = useState(false);
const [sending, setSending] = useState(false);
const [isRTL, setIsRTL] = useState(false);
const [attachments, setAttachments] = useState>([]);
// Refs
const editorRef = useRef(null);
const originalContentRef = useRef(null);
const attachmentInputRef = useRef(null);
// Initialize the form when replying to or forwarding an email
useEffect(() => {
if (initialEmail && type !== 'new') {
if (type === 'forward') {
initializeForwardedEmail();
} else {
// For reply/reply-all, continue using formatEmailForReplyOrForward
const formattedEmail = formatEmailForReplyOrForward(initialEmail, type as 'reply' | 'reply-all');
setTo(formattedEmail.to);
if (formattedEmail.cc) {
setCc(formattedEmail.cc);
setShowCc(true);
}
setSubject(formattedEmail.subject);
// Make the entire content editable, just like with forwarded emails
const bodyContent = formattedEmail.body;
setUserMessage(bodyContent);
setBody(bodyContent);
}
// Focus editor after initializing
setTimeout(() => {
if (editorRef.current) {
editorRef.current.focus();
// Place cursor at the beginning of the content
const selection = window.getSelection();
const range = document.createRange();
range.setStart(editorRef.current, 0);
range.collapse(true);
selection?.removeAllRanges();
selection?.addRange(range);
}
}, 100);
}
}, [initialEmail, type]);
// Format date for the forwarded message header
const formatDate = (date: Date | null): string => {
if (!date) return '';
try {
return date.toLocaleString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch (e) {
return date.toString();
}
};
// Format sender address in a readable format
const formatSender = (from: Array<{name?: string, address: string}> | undefined): string => {
if (!from || from.length === 0) return 'Unknown';
return from.map(sender =>
sender.name && sender.name !== sender.address
? `${sender.name} <${sender.address}>`
: sender.address
).join(', ');
};
// Format recipient addresses in a readable format
const formatRecipients = (recipients: Array<{name?: string, address: string}> | undefined): string => {
if (!recipients || recipients.length === 0) return '';
return recipients.map(recipient =>
recipient.name && recipient.name !== recipient.address
? `${recipient.name} <${recipient.address}>`
: recipient.address
).join(', ');
};
// Initialize forwarded email with clear structure and style preservation
const initializeForwardedEmail = async () => {
console.log('Starting initializeForwardedEmail');
if (!initialEmail) {
console.error('No email available for forwarding');
setBody('
No email available for forwarding
');
return;
}
try {
// Format subject with Fwd: prefix if needed
const subjectBase = initialEmail.subject || '(No subject)';
const subjectRegex = /^(Fwd|FW|Forward):\s*/i;
const subject = subjectRegex.test(subjectBase)
? subjectBase
: `Fwd: ${subjectBase}`;
setSubject(subject);
// For forwarded emails, we'll use a completely different approach
// Just save the original HTML content and use it directly
// This preserves all formatting without trying to parse it
// Set message parts for the editor
const content = initialEmail.content || initialEmail.html || initialEmail.text || '';
setOriginalContent(content);
setUserMessage(''); // Start with empty user message
setBody(''); // Will be constructed when sending
// Log for debugging
console.log('Set originalContent:', content.substring(0, 100) + '...');
} catch (error) {
console.error('Error formatting forwarded email:', error);
setBody('
Error formatting forwarded email content
');
}
};
// Handle attachment selection
const handleAttachmentClick = () => {
attachmentInputRef.current?.click();
};
// Process selected files
const handleFileSelection = async (e: React.ChangeEvent) => {
const files = e.target.files;
if (!files || files.length === 0) return;
// Convert selected files to attachments
const newAttachments = Array.from(files).map(file => ({
file,
uploading: true
}));
// Read files as data URLs
for (const file of files) {
const reader = new FileReader();
reader.onload = (event) => {
const content = event.target?.result as string;
setAttachments(current => [
...current,
{
name: file.name,
content: content.split(',')[1], // Remove data:mime/type;base64, prefix
type: file.type
}
]);
};
reader.readAsDataURL(file);
}
// Reset file input
if (e.target) {
e.target.value = '';
}
};
// Remove attachment
const removeAttachment = (index: number) => {
setAttachments(current => current.filter((_, i) => i !== index));
};
// Handle editing of original content
const handleOriginalContentInput = (e: React.FormEvent) => {
if (originalContentRef.current) {
const content = originalContentRef.current.innerHTML;
setOriginalContent(content);
}
};
// Toggle original content editing
const toggleEditOriginalContent = () => {
setEditingOriginalContent(!editingOriginalContent);
// If we're starting to edit, make sure the content is ready and focused
if (!editingOriginalContent) {
setTimeout(() => {
if (originalContentRef.current) {
originalContentRef.current.focus();
// Place cursor at the beginning
const selection = window.getSelection();
const range = document.createRange();
if (originalContentRef.current.firstChild) {
range.setStart(originalContentRef.current.firstChild, 0);
} else {
range.setStart(originalContentRef.current, 0);
}
range.collapse(true);
selection?.removeAllRanges();
selection?.addRange(range);
}
}, 100);
}
};
// Handling click on original content even when not in edit mode
const handleOriginalContentClick = () => {
if (!editingOriginalContent) {
toggleEditOriginalContent();
}
};
// Modified send handler to combine user message with forwarded content
const handleSend = async () => {
if (!to) {
alert('Please specify at least one recipient');
return;
}
try {
setSending(true);
// Prepare the complete email body
let finalBody = body;
if (type === 'forward' && originalContent) {
// For forwarded emails, construct the email in a standard format
const fromString = initialEmail?.from?.map(addr =>
addr.name ? `${addr.name} <${addr.address}>` : addr.address
).join(', ') || '';
const toString = initialEmail?.to?.map(addr =>
addr.name ? `${addr.name} <${addr.address}>` : addr.address
).join(', ') || '';
const dateString = initialEmail?.date
? typeof initialEmail.date === 'string'
? new Date(initialEmail.date).toLocaleString()
: initialEmail.date.toLocaleString()
: '';
// Combine user message with forwarded header and content
finalBody = `
${userMessage}