"use client" import { useEffect, useMemo, useState } from "react" import { cn } from "@/lib/utils" import { splitQuotedHtml } from "@/lib/mail-quoted-content" import { htmlHasRemoteContent } from "@/lib/mail-remote-content" import { findContactByEmail } from "@/lib/contacts/find-contact" import { useContactsList } from "@/lib/contacts/use-contacts-list" import { useSelfMailEmails } from "@/lib/hooks/use-self-mail-emails" import { normalizeMailAddress } from "@/lib/mail-message-participants" import { isMessageRemoteContentAllowed, isTrustedSenderEmail, useTrustedSendersStore, } from "@/lib/stores/trusted-senders-store" import { useMessageAttachmentCidMap } from "@/lib/api/hooks/use-message-attachment-cid-map" import { useReindexMessageAttachments } from "@/lib/api/hooks/use-reindex-message-attachments" import { useInlineCidUrls } from "@/lib/hooks/use-inline-cid-urls" import { SandboxedContent } from "@/components/gmail/email-view/sandboxed-content" import { RemoteContentBanner } from "@/components/gmail/email-view/remote-content-banner" export function MessageBodyContent({ html, isSpam, senderEmail, messageId, collapseQuotedReplies = false, plainTextFallback, }: { html: string isSpam: boolean senderEmail: string messageId: string /** Hide included prior messages when the thread already lists them. */ collapseQuotedReplies?: boolean plainTextFallback?: string }) { const [showQuoted, setShowQuoted] = useState(false) const selfEmails = useSelfMailEmails() const { contacts } = useContactsList() const trustedSenderEmails = useTrustedSendersStore((s) => s.trustedSenderEmails) const allowedMessageIds = useTrustedSendersStore((s) => s.allowedMessageIds) const trustSender = useTrustedSendersStore((s) => s.trustSender) const allowMessageRemoteContent = useTrustedSendersStore( (s) => s.allowMessageRemoteContent ) const isFromSelf = useMemo(() => { const norm = normalizeMailAddress(senderEmail) if (!norm) return false return selfEmails.some((s) => normalizeMailAddress(s) === norm) }, [senderEmail, selfEmails]) const isContact = Boolean(findContactByEmail(contacts, senderEmail)) const isTrusted = isTrustedSenderEmail(trustedSenderEmails, senderEmail) const isMessageAllowed = isMessageRemoteContentAllowed( allowedMessageIds, messageId ) const { mainHtml, quotedHtml } = useMemo(() => { if (!collapseQuotedReplies) { return { mainHtml: html, quotedHtml: null as string | null } } return splitQuotedHtml(html) }, [html, collapseQuotedReplies]) const { data: cidMap } = useMessageAttachmentCidMap(messageId) const { mutate: reindexInlineAttachments, isPending: reindexPending, isSuccess: reindexDone, isError: reindexFailed, } = useReindexMessageAttachments(messageId) const cidUrlMap = useInlineCidUrls(cidMap) useEffect(() => { if (!/cid:/i.test(mainHtml)) return if (Object.keys(cidMap ?? {}).length > 0) return if (reindexPending || reindexDone || reindexFailed) return reindexInlineAttachments() }, [ mainHtml, cidMap, reindexPending, reindexDone, reindexFailed, reindexInlineAttachments, ]) const hasRemoteContent = useMemo( () => htmlHasRemoteContent(mainHtml) || (quotedHtml ? htmlHasRemoteContent(quotedHtml) : false), [mainHtml, quotedHtml] ) const remoteContentAllowed = isFromSelf || isContact || isTrusted || isMessageAllowed const blockRemoteContent = isFromSelf ? false : isSpam || (hasRemoteContent && !remoteContentAllowed) const showRemoteBanner = !isFromSelf && !isSpam && hasRemoteContent && !remoteContentAllowed const hasHiddenQuote = Boolean(quotedHtml) && !showQuoted const sandboxProps = { blockRemoteContent, restrictPopups: isSpam, senderEmail, cidUrlMap, plainTextFallback, messageId, } return (
{showRemoteBanner ? ( allowMessageRemoteContent(messageId)} onAlwaysShow={() => { trustSender(senderEmail) allowMessageRemoteContent(messageId) }} /> ) : null} {hasHiddenQuote ? (
) : null} {quotedHtml && showQuoted ? (
) : null}
) }