diff --git a/src/components/SenderAvatar.tsx b/src/components/SenderAvatar.tsx index 618dada8..0bf36f7b 100644 --- a/src/components/SenderAvatar.tsx +++ b/src/components/SenderAvatar.tsx @@ -18,6 +18,7 @@ import { useContactPhotoSource } from '../lib/use-contact-photo'; import { useSettingsStore } from '../stores/settings-store'; import { useContactsStore } from '../stores/contacts-store'; import { useAccountStore } from '../stores/account-store'; +import { useNoThirdPartyTraffic } from '../stores/policy-store'; import { useColors } from '../theme/colors'; import { ShieldAlert } from './icons'; @@ -61,6 +62,10 @@ export default function SenderAvatar({ name, email, size = 40, style, disableIma const c = useColors(); const warned = !!senderWarning; const senderFavicons = useSettingsStore((s) => s.senderFavicons) && !disableImages && !warned; + // Favicons come from an outside host (DuckDuckGo), which the webmail's + // "no third-party traffic" mode rules out (webmail lib/outgoing-hosts.ts). + // BIMI logos come from the webmail itself. + const noThirdParty = useNoThirdPartyTraffic(); // Compared shallowly, so only avatars whose photo changed re-render when the // contacts change; the index behind it is built once per contacts array. const indexedPhoto = useContactsStore( @@ -94,7 +99,7 @@ export default function SenderAvatar({ name, email, size = 40, style, disableIma senderWarning, hasPhoto: !disableImages && !!photoSource, showBrand, - showFavicon: senderFavicons && !!faviconDomain && !imgError && !domainFailed, + showFavicon: senderFavicons && !noThirdParty && !!faviconDomain && !imgError && !domainFailed, }); const handleError = React.useCallback(() => { diff --git a/src/components/__tests__/custom-from-third-party-wiring.test.ts b/src/components/__tests__/custom-from-third-party-wiring.test.ts index 4df4945a..7235a859 100644 --- a/src/components/__tests__/custom-from-third-party-wiring.test.ts +++ b/src/components/__tests__/custom-from-third-party-wiring.test.ts @@ -54,6 +54,12 @@ describe('no third-party traffic', () => { expect(src).toMatch(/t\('third_party\.remote_content_blocked'/); }); + it('shows no sender favicons from the outside host', () => { + const src = read('components/SenderAvatar.tsx'); + expect(src).toMatch(/const noThirdParty = useNoThirdPartyTraffic\(\);/); + expect(src).toMatch(/showFavicon: senderFavicons && !noThirdParty && /); + }); + it('keeps remote images blocked in booking cards and quoted originals', () => { expect(read('components/email/structured-mail/StructuredMailBanner.tsx')).toMatch(/\(\) => !remoteBlockedByOrg && \(policy === 'allow' \|\|/); const compose = read('screens/ComposeScreen.tsx');