163 lines
5.4 KiB
TypeScript
163 lines
5.4 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import { Loader2, Paperclip, FileDown, Download } from 'lucide-react';
|
|
import { sanitizeHtml } from '@/lib/utils/email-formatter';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Email } from '@/hooks/use-courrier';
|
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
|
|
|
interface EmailContentProps {
|
|
email: Email;
|
|
}
|
|
|
|
export default function EmailContent({ email }: EmailContentProps) {
|
|
const [content, setContent] = useState<React.ReactNode>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!email) return;
|
|
|
|
const renderContent = async () => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
if (!email.content || email.content.length === 0) {
|
|
setContent(<div className="text-gray-500">Email content is empty</div>);
|
|
return;
|
|
}
|
|
|
|
// Use the sanitizer from the centralized formatter
|
|
const sanitizedHtml = sanitizeHtml(email.content);
|
|
|
|
// Look for specific markers that indicate this is a forwarded or replied email
|
|
const isForwarded = sanitizedHtml.includes('---------- Forwarded message ---------');
|
|
const isReply = sanitizedHtml.includes('class="reply-body"') ||
|
|
sanitizedHtml.includes('blockquote style="margin: 0; padding: 10px 0 10px 15px; border-left:');
|
|
|
|
// For forwarded or replied emails, ensure we keep the exact structure
|
|
if (isForwarded || isReply) {
|
|
setContent(
|
|
<div
|
|
className="email-content prose max-w-none"
|
|
dangerouslySetInnerHTML={{ __html: sanitizedHtml }}
|
|
dir="rtl"
|
|
style={{ textAlign: 'right' }}
|
|
/>
|
|
);
|
|
} else {
|
|
// For regular emails, wrap in the same structure used in the compose editor
|
|
setContent(
|
|
<div
|
|
className="email-content prose max-w-none"
|
|
dir="rtl"
|
|
style={{ textAlign: 'right' }}
|
|
>
|
|
<div style={{ minHeight: "20px" }} dir="rtl">
|
|
<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error rendering email content:', err);
|
|
setError('Error rendering email content. Please try again.');
|
|
setContent(null);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
renderContent();
|
|
}, [email]);
|
|
|
|
// Render attachments if they exist
|
|
const renderAttachments = () => {
|
|
if (!email?.attachments || email.attachments.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="mt-6 border-t border-gray-200 pt-6">
|
|
<h3 className="text-sm font-semibold text-gray-900 mb-4">Attachments</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
{email.attachments.map((attachment, index) => (
|
|
<div key={index} className="flex items-center space-x-2 p-2 border rounded hover:bg-gray-50">
|
|
<Paperclip className="h-4 w-4 text-gray-400" />
|
|
<span className="text-sm text-gray-600 truncate flex-1">
|
|
{attachment.filename}
|
|
</span>
|
|
<Download className="h-4 w-4 text-gray-400 hover:text-gray-600 cursor-pointer" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex justify-center items-center h-full p-8">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<Alert variant="destructive" className="m-4">
|
|
<AlertTitle>Error</AlertTitle>
|
|
<AlertDescription>{error}</AlertDescription>
|
|
</Alert>
|
|
);
|
|
}
|
|
|
|
// Format the date for display
|
|
const formatDate = (dateObj: Date) => {
|
|
const now = new Date();
|
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
const yesterday = new Date(today);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
|
|
const date = new Date(dateObj);
|
|
const formattedTime = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
|
|
if (date >= today) {
|
|
return formattedTime;
|
|
} else if (date >= yesterday) {
|
|
return `Yesterday, ${formattedTime}`;
|
|
} else {
|
|
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="email-content p-6">
|
|
<div className="flex items-center gap-4 mb-6">
|
|
<Avatar className="h-10 w-10 bg-gray-100">
|
|
<AvatarFallback className="text-gray-600 font-medium">
|
|
{email.from?.[0]?.name?.[0] || email.from?.[0]?.address?.[0] || '?'}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<p className="font-medium text-gray-900">
|
|
{email.from?.[0]?.name || email.from?.[0]?.address || 'Unknown'}
|
|
</p>
|
|
<p className="text-sm text-gray-500">
|
|
to {email.to?.[0]?.address || 'you'}
|
|
</p>
|
|
</div>
|
|
<div className="ml-auto text-sm text-gray-500">
|
|
{formatDate(new Date(email.date))}
|
|
</div>
|
|
</div>
|
|
|
|
{content}
|
|
{renderAttachments()}
|
|
</div>
|
|
);
|
|
}
|