diff --git a/src/components/EmailBodyView.tsx b/src/components/EmailBodyView.tsx index 2acc95fb..c83946e9 100644 --- a/src/components/EmailBodyView.tsx +++ b/src/components/EmailBodyView.tsx @@ -3,7 +3,7 @@ import { View, StyleSheet, Linking, Platform, Text, Pressable } from 'react-nati import { WebView, type WebViewMessageEvent } from 'react-native-webview'; import { Image as ImageIcon, ShieldCheck } from 'lucide-react-native'; import type { Email } from '../api/types'; -import { wrapEmailHtml, wrapPlainTextEmail, plainTextToSafeHtml, extractCidRefs, hasRemoteContent, hasMeaningfulHtmlBody } from '../lib/email-html'; +import { wrapEmailHtml, wrapPlainTextEmail, plainTextToSafeHtml, extractCidRefs, hasRemoteContent, hasMeaningfulHtmlBody, hasNativeDarkMode } from '../lib/email-html'; import { jmapClient } from '../api/jmap-client'; import { useSettingsStore } from '../stores/settings-store'; import { useContactsStore } from '../stores/contacts-store'; @@ -45,6 +45,106 @@ function extractTextBody(email: Email): string | null { return null; } +// Dark-mode re-inversion, mirroring the webmail's handleIframeLoad pass. The +// body's `filter: invert(1)` flips colored emoji (yellow smiley -> blue, red +// heart -> cyan) and stylesheet-defined background images, which the static CSS +// attribute selectors can't reach. This walks the DOM once and wraps/re-inverts +// those so they keep their original colors. Only injected when inversion is on. +const DARK_REINVERT_SCRIPT = ` +(function () { + if (document.__rnDarkInvertDone) return; + var doc = document; + var win = window; + if (!doc.body) return; + document.__rnDarkInvertDone = true; + + // Re-invert elements with stylesheet-defined background images (CSS attribute + // selectors only catch inline styles, not rules that survive in