mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 16:05:39 +00:00
feat: show the sender's BIMI logo in the list, the unified inbox and the reader
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
// BIMI sender logos (webmail components/ui/avatar.tsx brandDomain, used by
|
||||
// email-list-item, thread-list-item, email-viewer and thread-conversation-view).
|
||||
// No React Native renderer in the test env, so this checks that every avatar
|
||||
// of a received message hands SenderAvatar its brand domain; the helper has
|
||||
// unit tests in lib/__tests__/avatar-bimi.test.ts.
|
||||
|
||||
const read = (rel: string) => fs.readFileSync(path.resolve(__dirname, '..', '..', rel), 'utf8');
|
||||
|
||||
describe('BIMI logo wiring', () => {
|
||||
it('gives list rows the brand domain, none on rows that show the recipient', () => {
|
||||
const src = read('screens/EmailListScreen.tsx');
|
||||
expect(src).toMatch(/const brandDomain = React\.useMemo\(\(\) => \(showRecipient \? undefined : brandIndicatorDomain\(item\)\), \[showRecipient, item\]\);/);
|
||||
expect(src).toMatch(/senderWarning=\{senderWarning\}\s+brandDomain=\{brandDomain\}/);
|
||||
});
|
||||
|
||||
it('gives unified inbox rows the brand domain', () => {
|
||||
const src = read('screens/UnifiedInboxScreen.tsx');
|
||||
expect(src).toMatch(/brandDomain=\{showRecipient \? undefined : brandIndicatorDomain\(item\)\}/);
|
||||
});
|
||||
|
||||
it('gives the reader header and collapsed thread cards the brand domain', () => {
|
||||
expect(read('components/email/MessageHeader.tsx')).toMatch(/brandDomain=\{brandIndicatorDomain\(email\)\}/);
|
||||
expect(read('components/email/ThreadMessageCard.tsx')).toMatch(
|
||||
/<SenderAvatar [^>]*brandDomain=\{brandIndicatorDomain\(email\)\}/,
|
||||
);
|
||||
});
|
||||
|
||||
it('fetches the Authentication-Results the helper reads', async () => {
|
||||
const { EMAIL_FULL_PROPERTIES, EMAIL_LIST_PROPERTIES, THREAD_HEADER_PROPERTIES } = await import('../../api/email');
|
||||
expect(EMAIL_FULL_PROPERTIES).toContain('headers');
|
||||
expect(EMAIL_LIST_PROPERTIES).toContain('header:Authentication-Results:all');
|
||||
expect(THREAD_HEADER_PROPERTIES).toContain('header:Authentication-Results:all');
|
||||
expect(read('api/unified-inbox.ts')).toMatch(/const EMAIL_LIST_PROPERTIES = \[[^\]]*LIST_AUTH_RESULTS_PROPERTY/);
|
||||
});
|
||||
});
|
||||
@@ -25,6 +25,7 @@ import { resolveEmailTags } from '../../lib/thread-utils';
|
||||
import { findVerificationCode, verificationCodeBodyText } from '../../lib/verification-code';
|
||||
import { VerificationCodeChip } from './VerificationCodeChip';
|
||||
import { useOwnDomainAddress } from '../../lib/use-own-domain-address';
|
||||
import { brandIndicatorDomain } from '../../lib/avatar-bimi';
|
||||
|
||||
interface Props {
|
||||
email: Email;
|
||||
@@ -185,6 +186,7 @@ export function MessageHeader({ email, identities, headerInfo, onToggleStar, onA
|
||||
email={from?.email}
|
||||
size={compact ? componentSizes.avatarSm : componentSizes.avatarMd}
|
||||
senderWarning={verificationText?.label}
|
||||
brandDomain={brandIndicatorDomain(email)}
|
||||
/>
|
||||
</Pressable>
|
||||
<View style={styles.info}>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { emailDisplayDate, formatHeaderDate, formatHeaderTime } from '../../lib/
|
||||
import { singleLine } from '../../lib/single-line';
|
||||
import { cleanPreview } from '../../lib/preview-text';
|
||||
import { useSenderWarning } from './use-sender-warning';
|
||||
import { brandIndicatorDomain } from '../../lib/avatar-bimi';
|
||||
import { useOwnDomainAddress } from '../../lib/use-own-domain-address';
|
||||
import type { CardReplyActions } from '../../lib/thread-card-actions';
|
||||
|
||||
@@ -61,7 +62,7 @@ export function ThreadMessageCard({
|
||||
if (!expanded) {
|
||||
return (
|
||||
<Pressable onPress={onToggleExpanded} style={({ pressed }) => [styles.collapsed, pressed && styles.pressed]}>
|
||||
<SenderAvatar name={from?.name} email={from?.email} size={componentSizes.avatarSm} senderWarning={senderWarning} />
|
||||
<SenderAvatar name={from?.name} email={from?.email} size={componentSizes.avatarSm} senderWarning={senderWarning} brandDomain={brandIndicatorDomain(email)} />
|
||||
<View style={styles.collapsedInfo}>
|
||||
<View style={styles.collapsedTop}>
|
||||
<Text style={[styles.collapsedName, unread && styles.unread]} numberOfLines={1}>
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { showsPaperclip } from '../lib/list-attachments';
|
||||
import { VerificationCodeChip, useListVerificationCode } from '../components/email/VerificationCodeChip';
|
||||
import { useSenderWarning } from '../components/email/use-sender-warning';
|
||||
import { brandIndicatorDomain } from '../lib/avatar-bimi';
|
||||
import type { LoadListAttachments } from '../lib/list-attachments';
|
||||
import { useNetworkStore } from '../stores/network-store';
|
||||
import {
|
||||
@@ -219,6 +220,8 @@ const EmailRow = React.memo(function EmailRow({
|
||||
const attachments = useListAttachments(item, onOpenAttachment ? loadAttachments : undefined);
|
||||
// Sent and Drafts rows show the recipient: the check says nothing about them.
|
||||
const senderWarning = useSenderWarning(showRecipient ? undefined : item);
|
||||
// The sender's BIMI logo, for a message that passed DMARC for its From domain.
|
||||
const brandDomain = React.useMemo(() => (showRecipient ? undefined : brandIndicatorDomain(item)), [showRecipient, item]);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
@@ -272,6 +275,7 @@ const EmailRow = React.memo(function EmailRow({
|
||||
size={componentSizes.avatarMd}
|
||||
disableImages={disableAvatarImages}
|
||||
senderWarning={senderWarning}
|
||||
brandDomain={brandDomain}
|
||||
/>
|
||||
</Pressable>
|
||||
)}
|
||||
|
||||
@@ -33,6 +33,7 @@ import { isPermanentDelete, confirmPermanentDelete } from '../lib/delete-confirm
|
||||
import { confirmMailRemoval } from '../lib/mail-removal-confirm';
|
||||
import { onStateChangeType } from '../lib/state-change-bus';
|
||||
import { describeSenderVerification, senderWarningOf } from '../lib/email-headers';
|
||||
import { brandIndicatorDomain } from '../lib/avatar-bimi';
|
||||
import { createUnifiedReloadTracker } from '../lib/unified-reload';
|
||||
import { spacing, typography, componentSizes, radius, type ThemePalette } from '../theme/tokens';
|
||||
import { useColors } from '../theme/colors';
|
||||
@@ -380,6 +381,7 @@ export default function UnifiedInboxScreen({ navigation, route }: Props) {
|
||||
size={componentSizes.avatarMd}
|
||||
disableImages={role === 'junk' && !showAvatarsInJunk}
|
||||
senderWarning={verification ? describeSenderVerification(verification, t).label : undefined}
|
||||
brandDomain={showRecipient ? undefined : brandIndicatorDomain(item)}
|
||||
/>
|
||||
{acc && accountIds.length > 1 && (
|
||||
<View style={[styles.accountDot, { backgroundColor: acc.avatarColor }]} />
|
||||
|
||||
Reference in New Issue
Block a user