/** * Mail detail reading pane. * Shows mail headers, sanitized HTML body, attachments, reply/forward/create-event actions. */ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import clsx from 'clsx'; import type { Mail, MailAttachment } from '@/api/mail'; import { Button } from '@/components/ui/Button'; import { Badge } from '@/components/ui/Badge'; import { EmptyState } from '@/components/ui/EmptyState'; export interface MailDetailProps { mail: Mail | null; loading: boolean; onReply: (mail: Mail) => void; onForward: (mail: Mail) => void; onCreateEvent: (mail: Mail) => void; onToggleFlag: (mail: Mail) => void; onDownloadAttachment: (mailId: string, attachment: MailAttachment) => void; downloadingAttachmentId: string | null; } function formatFullDate(dateStr: string): string { const date = new Date(dateStr); return date.toLocaleString([], { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); } function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } function isSanitizedHtmlSafe(html: string | null): boolean { if (!html) return true; const lower = html.toLowerCase(); const dangerous = [' lower.includes(pattern)); } export function MailDetail({ mail, loading, onReply, onForward, onCreateEvent, onToggleFlag, onDownloadAttachment, downloadingAttachmentId, }: MailDetailProps) { const { t } = useTranslation(); const safeHtml = useMemo(() => { if (!mail) return null; return mail.sanitized_html || mail.body_html; }, [mail]); const isSafe = useMemo(() => isSanitizedHtmlSafe(safeHtml), [safeHtml]); if (loading) { return (
{t('common.loading')}
); } if (!mail) { return (
); } return (
{/* Toolbar */}
{/* Headers */}

{mail.subject || t('mail.noSubject')}

{mail.labels && mail.labels.length > 0 && (
{mail.labels.map((label) => ( {label.name} ))}
)}
{t('mail.from')}: {mail.from_name ? `${mail.from_name} <${mail.from_address}>` : mail.from_address}
{t('mail.to')}: {mail.to_addresses.join(', ')}
{mail.cc_addresses.length > 0 && (
{t('mail.cc')}: {mail.cc_addresses.join(', ')}
)}
{t('mail.date')}: {formatFullDate(mail.date)}
{/* Body */}
{safeHtml && isSafe ? (
) : safeHtml && !isSafe ? (

{t('mail.htmlUnsafe')}

{mail.body_text}
) : (
{mail.body_text}
)}
{/* Attachments */} {mail.attachments && mail.attachments.length > 0 && (

{t('mail.attachments')}

    {mail.attachments.map((att) => (
  • {att.filename}

    {formatBytes(att.size)}

  • ))}
)}
); }