import { useMemo } from 'react' import { useMounted } from '#shared/components/ClientTime' import { prepareEmailMessageContent, splitPlainQuotedHistory } from '../lib/email-message-content.js' import { ownMailDraftMarkdown } from '../lib/html-to-markdown.js' import { messageHasHtml } from '../lib/mail-ui-model.js' import { markdownToEmailHtml } from '../lib/markdown-model.js' import type { MailMessage } from '../state/mail-queries.js' import { EmailHtml } from './EmailHtml.js' export function MessageBody({ message, darkenEmail = true, }: { message: MailMessage darkenEmail?: boolean }) { const mounted = useMounted() // Only messages attested by our drafts-endpoint fallback can receive OwnMail's // explicit Markdown storage envelope. Render that exact envelope as the same // final HTML used for sending; never trust provider folder membership or fields. const draftMarkdown = message.ownmailDraft === true && message.body ? ownMailDraftMarkdown(message.body) : undefined if (draftMarkdown !== undefined) { const html = markdownToEmailHtml(draftMarkdown) if (!mounted) return html ? : null return } if (messageHasHtml(message)) { // Keep server and initial hydration output identical without presenting an // HTML message as plaintext. Untrusted markup only reaches EmailHtml's // sanitizer and isolated shadow root after the client has mounted. if (!mounted) return /* v8 ignore next -- `?? ''` is unreachable: this branch only runs when messageHasHtml() confirmed message.body is a non-empty string -- @preserve */ return } const text = plainBodyText(message) if (!text) return null return } function PreparedHtmlBody({ html, message, darkenEmail, }: { html: string message: MailMessage darkenEmail: boolean }) { const prepared = useMemo( () => prepareEmailMessageContent(html, message.id, message.attachments ?? [], message.ownmailImageTokens), [html, message.attachments, message.id, message.ownmailImageTokens], ) return (
) } function HtmlBodyPlaceholder() { return (
) } /** Keep plaintext literal and React-escaped while normalizing platform newlines. */ function plainBodyText(message: MailMessage): string { const body = typeof message.body === 'string' && message.body.trim() ? message.body : undefined const snippet = typeof message.snippet === 'string' ? message.snippet : '' return (body ?? snippet).replace(/\r\n?/g, '\n').trim() } function PlainBody({ text }: { text: string }) { const content = splitPlainQuotedHistory(text) const paragraphs = content.visible ? content.visible.split(/\n{2,}/) : [] return (
{paragraphs.map((paragraph) => (

{paragraph}

))} {content.quoted ? (
Show quoted text

{content.quoted}

) : null}
) }