import { useEffect, useMemo, useState } from "react"; import { Download, FileText, Loader2, Lock, MailOpen, Shield, Trash2 } from "lucide-react"; import { toast } from "@/client/components/toast"; import { Button, Card, EmptyState } from "@/client/components/ui"; import { prepareEmailHtml } from "@/client/lib/email-html"; import { downloadAttachment, getRawEmailSource, getErrorMessage, type AuthDescriptor, type EmailAttachment, type EmailDetail as EmailDetailType, } from "@/client/lib/api"; import { fullDate } from "@/client/lib/time"; interface EmailDetailProps { address: string; auth: AuthDescriptor; email: EmailDetailType | null; loading: boolean; canDelete: boolean; canViewRaw: boolean; onDelete: (emailId: string) => void; } function formatSize(sizeBytes: number) { if (sizeBytes < 1024) { return `${sizeBytes} B`; } if (sizeBytes < 1024 * 1024) { return `${(sizeBytes / 1024).toFixed(1)} KB`; } return `${(sizeBytes / (1024 * 1024)).toFixed(1)} MB`; } function buildSrcDoc(html: string, bodyAttributes = "", headHtml = "") { return ` ${headHtml} ${html} `; } export function EmailDetail({ address, auth, email, loading, canDelete, canViewRaw, onDelete }: EmailDetailProps) { const [downloadingId, setDownloadingId] = useState(null); const [downloadingRaw, setDownloadingRaw] = useState(false); const [allowRemoteContent, setAllowRemoteContent] = useState(false); useEffect(() => { setAllowRemoteContent(false); }, [email?.id]); const preparedHtml = useMemo( () => (email?.html ? prepareEmailHtml(email.html, allowRemoteContent) : null), [allowRemoteContent, email?.html, email?.id], ); const handleDownload = async (attachment: EmailAttachment) => { if (!email) { return; } setDownloadingId(attachment.id); try { const blob = await downloadAttachment(address, email.id, attachment.id, auth); const fileName = attachment.filename ?? "attachment.bin"; const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = fileName; link.click(); URL.revokeObjectURL(url); } catch (error) { toast.error(getErrorMessage(error)); } finally { setDownloadingId(null); } }; const handleViewRaw = async () => { if (!email) return; const rawWindow = window.open("about:blank", "_blank"); if (!rawWindow) { toast.error("Browser blocked the raw source tab"); return; } rawWindow.document.title = `Raw email ${email.id}`; rawWindow.document.body.textContent = "Loading raw source..."; rawWindow.document.body.style.cssText = "margin:0;padding:16px;background:#0f172a;color:#cbd5e1;font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;"; rawWindow.document.documentElement.style.colorScheme = "dark"; setDownloadingRaw(true); try { const rawSource = await getRawEmailSource(address, email.id, auth); const pre = rawWindow.document.createElement("pre"); pre.style.cssText = "margin:0;white-space:pre-wrap;word-break:break-word;"; pre.textContent = rawSource; rawWindow.document.body.textContent = ""; rawWindow.document.body.appendChild(pre); } catch (error) { rawWindow.close(); toast.error(getErrorMessage(error)); } finally { setDownloadingRaw(false); } }; if (loading && !email) { return (
Loading message...
); } if (!email) { return ( } caption="Viewer" heading="Select an email" description="Pick a message from the list to view its contents." /> ); } return (
{/* Header */}

{email.subject}

{canViewRaw ? ( ) : null} {canDelete ? ( ) : ( Read-only )}

{email.fromName || email.fromAddress} {email.fromName ? <{email.fromAddress}> : null} · {fullDate(email.receivedAt)} · {formatSize(email.sizeBytes)}

{email.recipientAddress ? (

Delivered to {email.recipientAddress}

) : null}
{/* Attachments */} {email.attachments.length > 0 ? (
{email.attachments.map((attachment) => { const busy = downloadingId === attachment.id; return ( ); })}
) : null} {/* Body */} {email.html && preparedHtml ? (
Sandboxed view {preparedHtml.blockedRemoteContent && !allowRemoteContent ? ( ) : preparedHtml.blockedRemoteContent && allowRemoteContent ? ( Remote content loaded ) : null}