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}
)
}