From d3c146ec9565d5eeee1859dd69845176db0ff93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Sat, 19 Sep 2026 04:39:13 +0000 Subject: [PATCH] fix(mobile): render security-agent empty-state copy in centered layouts Explorer finding: audit-report-empty: The empty-state title 'No recorded activity' and its description render at near-invisible contrast against the light background, unlike the readable empty state on other screens. The user-agent explorer found this while using the app like a user. One finding per item; the explorer never edits product code. Flow: audit-report-empty Found on revision: fd04ca1ef Repro: 1. set this state first: a wiped install on a brand-new account 2. open the app on emulator-5602 3. reach the screen the capture names (audit-report-empty) 4. the capture shows the defect named below Observed: The empty-state title 'No recorded activity' and its description render at near-invisible contrast against the light background, unlike the readable empty state on other screens. Expected: the screen renders without this defect Evidence (from the device run): - ~/.local/share/kwf/findings/explorer-audit-report-empty-the-empty-state-title-no-reco-05d1597f/audit-report-empty.png --- .../(tabs)/(3_profile)/security-agent/[scope]/filter.tsx | 1 - .../security-agent/audit-report-screen.mounted.test.tsx | 6 ++++++ .../src/components/security-agent/audit-report-screen.tsx | 7 +++++-- .../components/security-agent/dismiss-finding-screen.tsx | 5 ----- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/filter.tsx b/apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/filter.tsx index 306082ff72..2a90954822 100644 --- a/apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/filter.tsx +++ b/apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/filter.tsx @@ -52,7 +52,6 @@ export default function SecurityAgentFilterFindingsRoute() { diff --git a/apps/mobile/src/components/security-agent/audit-report-screen.mounted.test.tsx b/apps/mobile/src/components/security-agent/audit-report-screen.mounted.test.tsx index 4bb9016106..68a33b39b6 100644 --- a/apps/mobile/src/components/security-agent/audit-report-screen.mounted.test.tsx +++ b/apps/mobile/src/components/security-agent/audit-report-screen.mounted.test.tsx @@ -246,6 +246,7 @@ describe('AuditReportScreen states', () => { expect(empty).toHaveLength(1); expect(empty[0]?.props.title).toBe('Audit report unavailable'); expect(findByType(root.root, 'QueryError')).toHaveLength(0); + expect(empty[0]?.props.className ?? '').not.toMatch(/\bflex-1\b/); }); it('treats a personal UNAUTHORIZED as a retryable session error', () => { @@ -259,6 +260,10 @@ describe('AuditReportScreen states', () => { expect(findByType(root.root, 'EmptyState')).toHaveLength(0); }); + // Explorer audit-report-empty: see the Yoga note in audit-report-screen.tsx. + // A `flex-1` child (flexBasis 0%) collapses to zero height inside + // CenteredState's auto-height wrapper, which blanks the title/description, so + // the centered states must reach EmptyState with no flex basis. it('renders EmptyState for an empty period', () => { setQueryState({ data: { @@ -273,6 +278,7 @@ describe('AuditReportScreen states', () => { expect(empty[0]?.props.title).toBe('No recorded activity'); expect(empty[0]?.props.placement).not.toBe('top'); expect(findByType(root.root, 'TabScreenScrollView')).toHaveLength(0); + expect(empty[0]?.props.className ?? '').not.toMatch(/\bflex-1\b/); }); it('retains a cached report with an inline retry after a transient failure', () => { diff --git a/apps/mobile/src/components/security-agent/audit-report-screen.tsx b/apps/mobile/src/components/security-agent/audit-report-screen.tsx index 4dbaac0cf0..a7448497bc 100644 --- a/apps/mobile/src/components/security-agent/audit-report-screen.tsx +++ b/apps/mobile/src/components/security-agent/audit-report-screen.tsx @@ -165,10 +165,14 @@ function AuditReportView({ const end = formatDate(parseTimestamp(report.period.displayEnd), i18n.language, { timeZone: 'UTC', }); + // No `flex-1` here: CenteredState centers its child by measuring the + // child's intrinsic height, and a `flex-1` (flexBasis 0%) child inside its + // auto-height wrapper collapses to zero, which zeroes the height Yoga lets + // the title and description measure into. The screenshot then shows the + // icon bubble with no readable copy (explorer audit-report-empty). return ( @@ -215,7 +219,6 @@ export function AuditReportScreen({ scope }: Readonly<{ scope: string }>) { {forbidden && ( diff --git a/apps/mobile/src/components/security-agent/dismiss-finding-screen.tsx b/apps/mobile/src/components/security-agent/dismiss-finding-screen.tsx index 1c4097016e..d92c9ac757 100644 --- a/apps/mobile/src/components/security-agent/dismiss-finding-screen.tsx +++ b/apps/mobile/src/components/security-agent/dismiss-finding-screen.tsx @@ -148,7 +148,6 @@ export function DismissFindingScreen({ scope, findingId }: Readonly @@ -161,7 +160,6 @@ export function DismissFindingScreen({ scope, findingId }: Readonly void findingQuery.refetch()} /> @@ -174,7 +172,6 @@ export function DismissFindingScreen({ scope, findingId }: Readonly void capability.refetch()} /> @@ -199,7 +196,6 @@ export function DismissFindingScreen({ scope, findingId }: Readonly @@ -213,7 +209,6 @@ export function DismissFindingScreen({ scope, findingId }: Readonly