Neah/components/email/EmailPanel.tsx
2025-04-30 21:52:21 +02:00

194 lines
5.5 KiB
TypeScript

'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<void>;
}
// 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<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');
// 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);
try {
// Use the adapter utility to convert to the standardized format
return adaptLegacyEmail(email);
} catch (error) {
console.error('EmailPanel: Error adapting email:', error);
return null;
}
}, [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 (
<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 ? (
<ComposeEmailAdapter
initialEmail={standardizedEmail}
type={composeType}
onClose={handleComposeClose}
onSend={handleSendEmail}
/>
) : (
<div className="max-w-4xl mx-auto h-full">
<EmailPreview
email={standardizedEmail}
onReply={handleReply}
/>
</div>
)}
</div>
);
}