Neah/components/email/EmailPanel.tsx

218 lines
6.0 KiB
TypeScript

'use client';
import { useState, useEffect, useMemo, useCallback } from 'react';
import EmailPreview from './EmailPreview';
import ComposeEmail from './ComposeEmail';
import { Loader2 } from 'lucide-react';
import { formatReplyEmail, EmailMessage as FormatterEmailMessage } from '@/lib/utils/email-formatter';
import { useEmailFetch } from '@/hooks/use-email-fetch';
import { debounce } from '@/lib/utils/debounce';
// Add local EmailMessage interface
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;
}
interface EmailPanelProps {
selectedEmail: {
emailId: string;
accountId: string;
folder: string;
} | null;
onSendEmail: (email: any) => void;
}
export default function EmailPanel({
selectedEmail,
onSendEmail
}: EmailPanelProps) {
// Use the new email fetch hook
const { email, loading, error, fetchEmail } = useEmailFetch({
onEmailLoaded: (email) => {
// Handle any post-load actions
console.log('Email loaded:', email.id);
},
onError: (error) => {
console.error('Error loading email:', error);
}
});
// Compose mode state
const [isComposing, setIsComposing] = useState<boolean>(false);
const [composeType, setComposeType] = useState<'new' | 'reply' | 'reply-all' | 'forward'>('new');
// Create a formatted version of the email content using the same formatter as ComposeEmail
const formattedEmail = useMemo(() => {
if (!email) return null;
try {
// Convert to the formatter message format - this is what ComposeEmail does
const formatterEmail: FormatterEmailMessage = {
id: email.id,
messageId: email.messageId,
subject: email.subject,
from: email.from || [],
to: email.to || [],
cc: email.cc || [],
bcc: email.bcc || [],
date: email.date,
content: email.content,
html: email.html,
text: email.text,
hasAttachments: email.hasAttachments || false
};
// Try both formatting approaches to match what ComposeEmail would display
// This handles preview, reply and forward cases
let formattedContent: string;
// ComposeEmail switches based on type - we need to do the same
const { content: replyContent } = formatReplyEmail(formatterEmail, 'reply');
// Set the formatted content
formattedContent = replyContent;
console.log("Generated formatted content for email preview");
// Return a new email object with the formatted content
return {
...email,
formattedContent
};
} catch (error) {
console.error('Error formatting email content:', error);
return email;
}
}, [email]);
// Debounced email fetch
const debouncedFetchEmail = useCallback(
debounce((emailId: string, accountId: string, folder: string) => {
fetchEmail(emailId, accountId, folder);
}, 300),
[fetchEmail]
);
// Load email content when selectedEmail changes
useEffect(() => {
if (selectedEmail) {
debouncedFetchEmail(selectedEmail.emailId, selectedEmail.accountId, selectedEmail.folder);
setIsComposing(false);
}
}, [selectedEmail, debouncedFetchEmail]);
// Handle reply/forward actions
const handleReply = (type: 'reply' | 'reply-all' | 'forward') => {
setComposeType(type);
setIsComposing(true);
};
// Handle compose mode close
const handleComposeClose = () => {
setIsComposing(false);
setComposeType('new');
};
// If no email is selected and not composing
if (!selectedEmail && !isComposing) {
return (
<div className="h-full flex items-center justify-center">
<div className="text-center text-muted-foreground">
<p>Select an email to view or</p>
<button
className="text-primary mt-2 hover:underline"
onClick={() => {
setComposeType('new');
setIsComposing(true);
}}
>
Compose a new message
</button>
</div>
</div>
);
}
// Show loading state
if (loading) {
return (
<div className="h-full flex items-center justify-center">
<div className="text-center">
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4 text-primary" />
<p>Loading email...</p>
</div>
</div>
);
}
// Show error state
if (error) {
return (
<div className="h-full flex items-center justify-center">
<div className="text-center text-destructive">
<p>{error}</p>
<button
className="text-primary mt-2 hover:underline"
onClick={() => selectedEmail && fetchEmail(selectedEmail.emailId, selectedEmail.accountId, selectedEmail.folder)}
>
Try again
</button>
</div>
</div>
);
}
// Show compose mode or email preview
return (
<div className="h-full">
{isComposing ? (
<div className="h-full flex items-center justify-center">
<div className="text-center text-muted-foreground">
<p>Compose mode is now handled by the main modal.</p>
<button
className="text-primary mt-2 hover:underline"
onClick={handleComposeClose}
>
Close
</button>
</div>
</div>
) : (
<div className="max-w-4xl mx-auto h-full">
<EmailPreview
email={formattedEmail}
onReply={handleReply}
/>
</div>
)}
</div>
);
}