'use client'; import { useState, useEffect, useMemo, useCallback } from 'react'; import EmailPreview from './EmailPreview'; import ComposeEmailAdapter from './ComposeEmailAdapter'; import { Loader2 } from 'lucide-react'; import { useEmailFetch } from '@/hooks/use-email-fetch'; import { debounce } from '@/lib/utils/debounce'; import { EmailMessage } from '@/types/email'; import { adaptLegacyEmail } from '@/lib/utils/email-adapters'; interface EmailPanelProps { selectedEmail: { emailId: string; accountId: string; folder: string; } | null; onSendEmail: (email: any) => Promise; } // Type for the legacy ComposeEmail component props interface ComposeEmailProps { initialEmail?: any; 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; } 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(false); const [composeType, setComposeType] = useState<'new' | 'reply' | 'reply-all' | 'forward'>('new'); // Convert the email to the standardized format const standardizedEmail = useMemo(() => { if (!email) { console.log('EmailPanel: No email provided'); return null; } console.log('EmailPanel: Raw email:', email); console.log('EmailPanel: Raw email content type:', typeof email.content); if (email.content) { // Log detailed content structure for debugging if (typeof email.content === 'object') { console.log('EmailPanel: Content object keys:', Object.keys(email.content)); console.log('EmailPanel: Content has isHtml?', 'isHtml' in email.content); console.log('EmailPanel: Content has text?', 'text' in email.content); console.log('EmailPanel: Content has direction?', 'direction' in email.content); } else if (typeof email.content === 'string') { console.log('EmailPanel: Content is string, first 100 chars:', email.content.substring(0, 100)); } } try { // Check if it's already in standardized format if (email.content && typeof email.content === 'object' && 'isHtml' in email.content && 'text' in email.content && 'direction' in email.content) { console.log('EmailPanel: Email already in standardized format'); // Normalize address format before returning const normalizedEmail = { ...email, // Ensure from, to, cc are strings as expected by the EmailMessage interface from: normalizeAddress(email.from), to: normalizeAddress(email.to), cc: email.cc ? normalizeAddress(email.cc) : undefined, bcc: email.bcc ? normalizeAddress(email.bcc) : undefined }; return normalizedEmail as EmailMessage; } // Use the adapter utility to convert to the standardized format console.log('EmailPanel: Adapting email to standardized format'); const adapted = adaptLegacyEmail(email); // Log adapted email for debugging console.log('EmailPanel: Adapted email content:', adapted.content); return adapted; } catch (error) { console.error('EmailPanel: Error adapting email:', error); // If adaptation fails, create a minimal valid email for display return { id: email.id || 'unknown', subject: email.subject || 'Error displaying email', from: normalizeAddress(email.from) || '', to: normalizeAddress(email.to) || '', date: email.date || new Date().toISOString(), flags: [], content: { text: `Error processing email: ${error instanceof Error ? error.message : 'Unknown error'}`, html: undefined, isHtml: false, direction: 'ltr' } } as EmailMessage; } }, [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) { // CRITICAL FIX: Store the current account ID to check for changes // This helps prevent race conditions during account switching const currentAccountId = selectedEmail.accountId; console.log(`EmailPanel: Loading email ${selectedEmail.emailId} from account ${currentAccountId}`); debouncedFetchEmail(selectedEmail.emailId, selectedEmail.accountId, selectedEmail.folder); setIsComposing(false); // Return a cleanup function that can detect and handle account changes return () => { console.log(`EmailPanel: Cleaning up email fetch for account ${currentAccountId}`); }; } }, [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'); }; // Wrap the onSendEmail function to ensure it returns a Promise const handleSendEmail = async (emailData: any) => { try { return await onSendEmail(emailData); } catch (error) { console.error('Error sending email:', error); throw error; // Re-throw to let ComposeEmail handle it } }; // If no email is selected and not composing if (!selectedEmail && !isComposing) { return (

Select an email to view or

); } // Show loading state if (loading) { return (

Loading email...

); } // Show error state if (error) { return (

{error}

); } // Show compose mode or email preview return (
{isComposing ? ( ) : (
)}
); } // Helper function to normalize address to string format function normalizeAddress(address: any): string { if (!address) return ''; // If already a string, return as is if (typeof address === 'string') { return address; } // If array of EmailAddress objects if (Array.isArray(address)) { return address.map(addr => { if (typeof addr === 'object' && addr !== null) { // Handle EmailAddress object if ('name' in addr && 'address' in addr) { return addr.name && addr.name !== addr.address ? `${addr.name} <${addr.address}>` : addr.address; } } return String(addr || ''); }).join(', '); } // Handle single EmailAddress object if (typeof address === 'object' && address !== null) { if ('name' in address && 'address' in address) { return address.name && address.name !== address.address ? `${address.name} <${address.address}>` : address.address; } } // Fallback return String(address); }