// frontend/components/mail/Reader.tsx — 리딩 패널 (툴바 + 본문 + 첨부 + 대화 + AriPanel) "use client"; import { useEffect, useState } from "react"; import { Icon } from "@/components/Icon"; import { mailApi } from "@/lib/mail/api"; import type { AiReply, Conversation, EmailDetail, MailAccount, Person } from "@/lib/types"; import { AriPanel } from "./AriPanel"; import { MailHtmlBody } from "./MailHtmlBody"; function fmtBytes(n: number): string { if (!n) return ""; if (n < 1024) return `${n} B`; if (n < 1024 * 1024) return `${(n / 1024).toFixed(0)} KB`; return `${(n / 1024 / 1024).toFixed(1)} MB`; } export function Reader({ m, person, account, added, loading, onAdd, onReply, onReplyAll, onForward, onAddAll, onArchive, onStar, onMarkUnread, onDelete, onOpenMsg, }: { m: EmailDetail | null; person?: Person; account?: MailAccount; added: Record; loading?: boolean; onAdd: (key: string, kind: "task" | "event" | "file", index: number) => void; onReply: (m: EmailDetail, replyIndex: number | null, r: AiReply | null) => void; onReplyAll: (m: EmailDetail) => void; onForward: (m: EmailDetail) => void; onAddAll: (m: EmailDetail) => void; onArchive: (id: string) => void; onStar: (id: string) => void; onMarkUnread: (id: string) => void; onDelete: (id: string) => void; onOpenMsg?: (id: string) => void; }) { const [thread, setThread] = useState(null); const threadId = m?.thread_id ?? ""; const mid = m?.id ?? ""; useEffect(() => { setThread(null); if (!mid || !threadId) return; let live = true; mailApi .thread(mid) .then((t) => live && t.count > 1 && setThread(t)) .catch(() => {}); return () => { live = false; }; }, [mid, threadId]); if (!m) { return (

{loading ? "메일을 여는 중…" : "읽을 메일을 선택하세요"}

메일을 열면 아리가 내용을 분석해 할 일·일정·답장을 미리 준비해 드려요.

); } const name = person?.name ?? (m.from_name || m.from); const initial = person?.initial ?? name.slice(0, 1); const color = person?.color ?? "var(--muted)"; const email = person?.email ?? m.from_email; const role = person?.role; const accTone = account?.tone ?? "ink"; const accName = account?.name ?? m.account; const accEmail = account?.email ?? ""; return (

{m.subject}

{initial}
{name} {role && · {role}} {email &&
{email}
}
{m.date}
받는 사람: {m.to}
{m.cc &&
참조: {m.cc}
}
{accName} {accEmail ? ` · ${accEmail}` : ""}
{m.body_html ? ( ) : (
{m.body.map((para, i) => (

{para}

))}
)} {m.attachments && m.attachments.length > 0 && (
{m.attachments.map((at, i) => at.id ? (
{at.name} {fmtBytes(at.size)}
) : (
{at.name} {fmtBytes(at.size)}
), )}
)} {thread && (
대화 {thread.count}통
{thread.messages .filter((tm) => tm.id !== m.id) .map((tm) => ( ))}
)} {m.ai && ( onReply(mail as EmailDetail, idx, r)} onAddAll={(mail) => onAddAll(mail as EmailDetail)} /> )}
); }