diff --git a/src/app/(inbox)/thread/[threadId]/loading.tsx b/src/app/(inbox)/thread/[threadId]/loading.tsx new file mode 100644 index 0000000..6c4e8a2 --- /dev/null +++ b/src/app/(inbox)/thread/[threadId]/loading.tsx @@ -0,0 +1,25 @@ +export default function Loading() { + return ( +
- {email.preview} + {latestEmail.preview}
)} diff --git a/src/components/ThreadView.tsx b/src/components/ThreadView.tsx new file mode 100644 index 0000000..79c4cad --- /dev/null +++ b/src/components/ThreadView.tsx @@ -0,0 +1,203 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; +import SenderAvatar from "@/components/SenderAvatar"; +import EmailBody from "@/components/EmailBody"; +import { Email } from "@/lib/types"; +import { formatAddressList, formatFullDate } from "@/lib/format"; + +// --------------------------------------------------------------------------- +// Body resolution (mirrors email/[id]/page.tsx logic) +// --------------------------------------------------------------------------- + +function resolveBody(email: Email): { body: string; type: "html" | "text" } | null { + if (email.htmlBody?.length > 0) { + const part = email.htmlBody[0]; + if (part.partId && email.bodyValues?.[part.partId]) { + return { body: email.bodyValues[part.partId].value, type: "html" }; + } + } + if (email.textBody?.length > 0) { + const part = email.textBody[0]; + if (part.partId && email.bodyValues?.[part.partId]) { + const raw = email.bodyValues[part.partId].value; + // Skip text/plain parts that are actually raw HTML source + if (!/^\s* void; +} + +function EmailStackItem({ email, expanded, onToggle }: ItemProps) { + const isUnread = !email.keywords?.["$seen"]; + const resolved = resolveBody(email); + const hasMultipleRecipients = + (email.to?.length ?? 0) + (email.cc?.length ?? 0) > 1; + + return ( ++ No content. +
+ )} +