Skip to content

Add Flutter analytics plumbing for Rust-owned product tracking#680

Draft
erskingardner wants to merge 2 commits into
masterfrom
codex/add-flutter-analytics-plumbing
Draft

Add Flutter analytics plumbing for Rust-owned product tracking#680
erskingardner wants to merge 2 commits into
masterfrom
codex/add-flutter-analytics-plumbing

Conversation

@erskingardner

@erskingardner erskingardner commented May 19, 2026

Copy link
Copy Markdown
Member

Summary

  • Wire Flutter into the Rust product analytics API with typed config, consent, tracking, and flush support.
  • Add opt-in onboarding and a privacy/security toggle, with device-local consent and per-account dismissal of the analytics prompt.
  • Update the analytics UI to avoid the janky dismiss animation when swapping from the analytics prompt to the welcome step.
  • Refresh FRB bindings, localization strings, docs, and ignore rules for local review assets and Android credential files.

Testing

  • Added unit and widget coverage for analytics config construction, consent persistence, onboarding dismissal, and notice behavior.
  • Ran Flutter tests and analyzer successfully; the staging iOS simulator was rebuilt and relaunched to verify the updated flow on device.

Summary by CodeRabbit

  • New Features
    • Opt-in product analytics with consent prompts (dismiss/share) and lifecycle tracking; analytics toggle in Privacy & Security; empty-inbox analytics onboarding notice.
  • UI
    • New pill-shaped toggle widget; system notice action layout and placement options.
  • Localization
    • Analytics consent strings added in 10 languages.
  • Documentation
    • README updated with product analytics opt-in and build/run guidance.
  • Chores
    • Updated .gitignore to exclude local analytics env files and Android Google Services.
  • Tests
    • Added tests covering analytics onboarding, settings, lifecycle tracking, and widgets.

Review Change Stack

CleanShot 2026-05-19 at 16 39 19 CleanShot 2026-05-19 at 16 39 07

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds opt-in product analytics: Rust FRB APIs and Dart bridge/types, ProductAnalyticsService and config loader, Riverpod settings/onboarding providers, app init and lifecycle tracking, onboarding UI and privacy toggle, new UI components (WnToggle, notice layout/positioning), Rust media/group/token updates, README/.gitignore/localizations, and extensive tests/mocks.

Changes

Product Analytics Feature

Layer / File(s) Summary
Rust FRB product_analytics API
rust/src/api/product_analytics.rs, rust/src/api/mod.rs
New FRB-exposed analytics API: config, settings, enable/disable, track/flush, enums and conversions; wired into create_whitenoise_config and re-exported.
Dart FRB bridge types
lib/src/rust/api/product_analytics.dart, lib/src/rust/api.dart
Generated Dart bridge functions and types for product analytics; WhitenoiseConfig updated to accept optional productAnalyticsConfig.
Dart service & config builder
lib/services/product_analytics_service.dart, lib/services/product_analytics_config.dart
ProductAnalyticsService wraps Rust APIs with error fallbacks; build/load product analytics config from package info, locale, platform, device class and dart-define Aptabase settings.
Analytics state & onboarding persistence
lib/providers/product_analytics_provider.dart, lib/providers/analytics_onboarding_provider.dart
AsyncNotifier for settings with stale-response guard; per-pubkey onboarding resolved persistence in secure storage using sha256-derived key.
App initialization & lifecycle tracking
lib/main.dart, lib/services/foreground_service.dart
Load product analytics config during initialization, pass to Rust, initialize settings, emit trackAppStarted; lifecycle transitions emit foreground/background events and flush on background.
Chat list onboarding prompt
lib/screens/chat_list_screen.dart
Empty-inbox analytics onboarding system notice with Not now / Share analytics actions; handles enabling analytics, tracking onboarding events, and per-account resolved persistence.
Privacy/security analytics toggle
lib/screens/privacy_security_screen.dart
Privacy screen adds analytics consent toggle bound to provider, refactored into composable UI blocks including destructive action block.
Widget enhancements & story
lib/widgets/wn_toggle.dart, lib/widgets/wn_system_notice.dart, lib/widgets/wn_slate.dart, widgetbook/lib/components/wn_toggle.dart
New WnToggle widget; WnSystemNotice adds actionLayout/backgroundColor/animateDismiss and _SystemNoticeActions; WnSlate supports systemNoticePosition; Widgetbook story for WnToggle.
Rust support: media/groups/messages
rust/src/api/media_files.rs, rust/src/api/groups.rs, rust/src/api/messages.rs, rust/Cargo.toml
FileMetadata adds duration_ms and waveform; FlutterGroupDataUpdate adds disappearing_message_secs; message token conversion refactored to traverse MarkdownDocument; Cargo.toml updates whitenoise deps.
Tests, mocks, and harness
test/*, test/mocks/*
Extensive unit/widget/integration tests for service, config, providers, screens, and widgets; MockWnApi and MockSecureStorage extended for analytics behavior and failure simulation; test harness overrides analytics service and asserts lifecycle/events/config wiring.
Docs, gitignore, localization
README.md, .gitignore, lib/l10n/app_*.arb
README documents Product Analytics opt-in and dart-define configuration; .gitignore updated for google-services and .env.analytics*; analytics localization keys added across multiple locales.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • marmot-protocol/whitenoise#635: Related changes to app lifecycle handling in lib/main.dart that intersect with analytics lifecycle event wiring.
  • marmot-protocol/whitenoise#649: Related modifications to Rust initialization path and API module (rust/src/api/mod.rs) that may overlap with create_whitenoise_config changes.

Suggested reviewers

  • jgmontoya
  • dannym-arx
  • cypherpinkdev
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/add-flutter-analytics-plumbing

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/screens/chat_list_screen_test.dart (1)

999-1008: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the analytics notice key in archived-view test, not the welcome notice.

This test name says analytics prompt is hidden in archived view, but it only asserts welcome_notice is absent. That can pass even if analytics_onboarding_notice is incorrectly visible.

Suggested fix
-      testWidgets('analytics prompt is not shown in archived view', (tester) async {
+      testWidgets('analytics prompt is not shown in archived view', (tester) async {
         _api.initialChats = [];
         await pumpChatListScreen(tester);
         await revealSearchHeader(tester);

         await tester.tap(find.byKey(const Key('filter_chip_archive')));
         await tester.pumpAndSettle();

-        expect(find.byKey(const Key('welcome_notice')), findsNothing);
+        expect(find.byKey(const Key('analytics_onboarding_notice')), findsNothing);
       });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/screens/chat_list_screen_test.dart` around lines 999 - 1008, The test
"analytics prompt is not shown in archived view" currently asserts that
Key('welcome_notice') is absent; update the assertion to check that
Key('analytics_onboarding_notice') is not found instead (or in addition) so the
test actually verifies the analytics onboarding notice is hidden when tapping
the 'filter_chip_archive' filter; locate the testWidgets block and replace or
add the expect(find.byKey(const Key('welcome_notice')), findsNothing) line with
an assertion using Key('analytics_onboarding_notice') while keeping the existing
setup calls like pumpChatListScreen and revealSearchHeader.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/l10n/app_de.arb`:
- Around line 17-22: The German locale file contains English values for the new
analytics keys; replace the English values for "analyticsConsentTitle",
"analyticsConsentDescription", "analyticsConsentSettingsDescription",
"analyticsPromptTitle", "analyticsPromptNotNow", and "analyticsPromptShare" in
app_de.arb with proper German translations (preserve the exact keys and JSON/ARB
formatting, escaping, and punctuation), ensuring the German text reads naturally
for consent/onboarding flows and follows existing localization style
conventions.

In `@lib/l10n/app_es.arb`:
- Around line 17-22: The new analytics strings in app_es.arb are still in
English; update the values for the keys analyticsConsentTitle,
analyticsConsentDescription, analyticsConsentSettingsDescription,
analyticsPromptTitle, analyticsPromptNotNow, and analyticsPromptShare with
correct Spanish translations so the consent/onboarding flow is fully localized;
ensure translations preserve meaning and tone and follow existing localization
style (no hardcoded English), then run any localization lints or validation to
confirm the .arb file remains well-formed.

In `@lib/l10n/app_fr.arb`:
- Around line 17-22: The ARB file contains English strings for analytics consent
keys—replace the English values for analyticsConsentTitle,
analyticsConsentDescription, analyticsConsentSettingsDescription,
analyticsPromptTitle, analyticsPromptNotNow, and analyticsPromptShare in
lib/l10n/app_fr.arb with proper French translations (keeping the keys intact and
JSON/ARB syntax valid), ensuring wording matches existing app tone and length
constraints and updating any surrounding punctuation/escaping as needed.

In `@lib/l10n/app_it.arb`:
- Around line 17-22: Replace the English values in app_it.arb for the analytics
keys with Italian translations: update "analyticsConsentTitle",
"analyticsConsentDescription", "analyticsConsentSettingsDescription",
"analyticsPromptTitle", "analyticsPromptNotNow", and "analyticsPromptShare" so
the file contains Italian strings (keeping the exact keys unchanged), ensure
valid ARB/JSON quoting and punctuation, and preserve any placeholders or
punctuation from the originals.

In `@lib/l10n/app_pt.arb`:
- Around line 17-22: The Portuguese locale file app_pt.arb is missing
translations for the analytics keys; add Portuguese translations for the keys
analyticsConsentTitle, analyticsConsentDescription,
analyticsConsentSettingsDescription, analyticsPromptTitle,
analyticsPromptNotNow, and analyticsPromptShare (keeping the keys unchanged),
ensure the values are valid JSON strings with proper escaping and punctuation,
preserve existing .arb metadata format, and run the localization linter/tests to
confirm no hardcoded English remains.

In `@lib/l10n/app_ru.arb`:
- Around line 17-22: Replace the English values for the localization keys
analyticsConsentTitle, analyticsConsentDescription,
analyticsConsentSettingsDescription, analyticsPromptTitle,
analyticsPromptNotNow, and analyticsPromptShare in app_ru.arb with proper
Russian translations (keeping the exact keys unchanged), making sure to preserve
ARB/JSON quoting and escaping rules and any placeholders or punctuation; after
updating, run the app/localization checks or linter to verify the file is valid
and strings render correctly in consent/onboarding flows.

In `@lib/l10n/app_tr.arb`:
- Around line 17-22: The ARB contains untranslated English strings for the
analytics consent and prompt keys (analyticsConsentTitle,
analyticsConsentDescription, analyticsConsentSettingsDescription,
analyticsPromptTitle, analyticsPromptNotNow, analyticsPromptShare); replace each
English value with proper Turkish translations preserving JSON/ARB formatting
and escape sequences, ensuring punctuation and placeholders (if any) remain
intact and that the file stays valid ARB for localization.

In `@lib/l10n/app_zh_Hant.arb`:
- Around line 17-22: Replace the English values for the analytics strings with
Traditional Chinese translations for the keys analyticsConsentTitle,
analyticsConsentDescription, analyticsConsentSettingsDescription,
analyticsPromptTitle, analyticsPromptNotNow, and analyticsPromptShare in the
app_zh_Hant.arb file; ensure the translations preserve the original meaning and
tone (privacy reassurance and CTA), keep punctuation/escaping intact, and follow
existing ARB localization formatting so no hardcoded English remains for these
keys.

In `@lib/l10n/app_zh.arb`:
- Around line 17-22: The ARB file contains untranslated analytics keys
(analyticsConsentTitle, analyticsConsentDescription,
analyticsConsentSettingsDescription, analyticsPromptTitle,
analyticsPromptNotNow, analyticsPromptShare); replace the English values in
app_zh.arb with correct Simplified Chinese translations (keeping the exact key
names and JSON/ARB formatting) so the analytics consent flow is localized and no
hardcoded English remains; ensure you preserve quoting, commas, and any
surrounding metadata entries in the .arb file.

In `@lib/providers/product_analytics_provider.dart`:
- Around line 12-28: The setEnabled method can suffer from out-of-order
responses overwriting newer state; add request sequencing by tracking a
monotonically incrementing request id (e.g., a private int _lastRequestId or
_pendingRequestId) and increment it at the start of setEnabled, capture the id
in a local variable before awaiting
ref.read(productAnalyticsServiceProvider).setAnalyticsEnabled(enabled), and only
apply the final state assignment (state = AsyncData(settings)) if the captured
id matches the current _lastRequestId; do the same check before/after the
optimistic state update using the same id so stale responses are ignored and
only the latest setEnabled call updates state.

In `@lib/screens/chat_list_screen.dart`:
- Around line 287-293: The async handlers (e.g., resolveAnalyticsNotice) update
ValueNotifiers and call ref.invalidate after awaits without re-checking widget
mounted state; to fix, after any await in resolveAnalyticsNotice (and the other
async handlers around analyticsNoticeLoading/lines 301-315) immediately check if
(context.mounted) before touching analyticsStepResolved.value,
analyticsNoticeLoading.value or calling
ref.invalidate(analyticsOnboardingResolvedProvider(pubkey)); if not mounted,
return early. Also capture any values you need from ref before awaits (e.g., the
analyticsOnboardingResolutionServiceProvider.read/markResolved call) so you
don’t rely on disposed state after the await.

In `@lib/services/product_analytics_config.dart`:
- Around line 31-49: The loadProductAnalyticsConfig function currently lets
platform calls throw and block startup; wrap the platform-dependent calls
(PackageInfo.fromPlatform(), WidgetsBinding.instance.platformDispatcher and
view/devicePixelRatio access) in a try/catch and on any exception return a safe
“disabled” ProductAnalyticsConfig produced by
buildProductAnalyticsConfig(ProductAnalyticsConfigInput(...)) with analytics
disabled (e.g., isDebug preserved, aptabaseHost/aptabaseAppKey empty/null,
bundleIdentifier/appVersion/locale/platform/logicalSize set to safe defaults or
null), and log the caught error; ensure the function always returns a
ProductAnalyticsConfig even when platform APIs fail so initialization doesn’t
abort.

In `@lib/services/product_analytics_service.dart`:
- Around line 145-149: In _consentVersionOrFallback(), the code trims the
fetched consent version only to check for empty but returns the untrimmed value;
change the logic to capture the trimmed value from _consentVersion() and return
that trimmed string (or _fallbackConsentVersion when the trimmed result is
empty). Update references in the try block around _consentVersionOrFallback and
_consentVersion so the returned consentVersion is always the trimmed
representation.

In `@lib/widgets/wn_toggle.dart`:
- Line 48: Change the thumb height usage from a width-based ScreenUtil call to
the height-based one: replace the `height: 20.w` occurrence in the WnToggle
widget (the thumb/handle size property) with `height: 20.h` so it follows the
repo convention of using `.h` for heights; verify any sibling usages (e.g.,
width: ... or radius properties) remain using `.w`, `.r`, or `.sp` as
appropriate.

---

Outside diff comments:
In `@test/screens/chat_list_screen_test.dart`:
- Around line 999-1008: The test "analytics prompt is not shown in archived
view" currently asserts that Key('welcome_notice') is absent; update the
assertion to check that Key('analytics_onboarding_notice') is not found instead
(or in addition) so the test actually verifies the analytics onboarding notice
is hidden when tapping the 'filter_chip_archive' filter; locate the testWidgets
block and replace or add the expect(find.byKey(const Key('welcome_notice')),
findsNothing) line with an assertion using Key('analytics_onboarding_notice')
while keeping the existing setup calls like pumpChatListScreen and
revealSearchHeader.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 808a4d7f-f7ad-4608-a4d0-bffe27267240

📥 Commits

Reviewing files that changed from the base of the PR and between dba52c1 and b4aa058.

⛔ Files ignored due to path filters (17)
  • lib/l10n/generated/app_localizations.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_de.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_en.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_es.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_fr.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_it.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_pt.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_ru.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_tr.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_zh.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/src/rust/api/product_analytics.freezed.dart is excluded by !**/*.freezed.dart
  • lib/src/rust/frb_generated.dart is excluded by !lib/src/rust/frb_generated.dart
  • lib/src/rust/frb_generated.io.dart is excluded by !lib/src/rust/frb_generated.io.dart
  • pubspec.lock is excluded by !**/*.lock, !pubspec.lock
  • rust/Cargo.lock is excluded by !**/*.lock, !rust/Cargo.lock
  • rust/src/frb_generated.rs is excluded by !rust/src/frb_generated.rs
  • widgetbook/lib/main.directories.g.dart is excluded by !widgetbook/lib/main.directories.g.dart
📒 Files selected for processing (49)
  • .gitignore
  • README.md
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_pt.arb
  • lib/l10n/app_ru.arb
  • lib/l10n/app_tr.arb
  • lib/l10n/app_zh.arb
  • lib/l10n/app_zh_Hant.arb
  • lib/main.dart
  • lib/providers/analytics_onboarding_provider.dart
  • lib/providers/product_analytics_provider.dart
  • lib/screens/chat_list_screen.dart
  • lib/screens/privacy_security_screen.dart
  • lib/services/foreground_service.dart
  • lib/services/product_analytics_config.dart
  • lib/services/product_analytics_service.dart
  • lib/src/rust/api.dart
  • lib/src/rust/api/groups.dart
  • lib/src/rust/api/media_files.dart
  • lib/src/rust/api/messages.dart
  • lib/src/rust/api/product_analytics.dart
  • lib/widgets/wn_slate.dart
  • lib/widgets/wn_system_notice.dart
  • lib/widgets/wn_toggle.dart
  • reviews/PR-517-start-chat-flow-improvements.md
  • rust/Cargo.toml
  • rust/src/api/groups.rs
  • rust/src/api/media_files.rs
  • rust/src/api/messages.rs
  • rust/src/api/mod.rs
  • rust/src/api/product_analytics.rs
  • test/main_test.dart
  • test/mocks/mock_secure_storage.dart
  • test/mocks/mock_wn_api.dart
  • test/providers/analytics_onboarding_provider_test.dart
  • test/screens/chat_list_screen_test.dart
  • test/screens/privacy_security_screen_test.dart
  • test/screens/settings_screen_test.dart
  • test/screens/signup_screen_test.dart
  • test/services/product_analytics_config_test.dart
  • test/services/product_analytics_service_test.dart
  • test/widgets/wn_slate_test.dart
  • test/widgets/wn_system_notice_test.dart
  • test/widgets/wn_toggle_test.dart
  • widgetbook/lib/components/wn_toggle.dart
💤 Files with no reviewable changes (1)
  • reviews/PR-517-start-chat-flow-improvements.md

Comment thread lib/l10n/app_de.arb Outdated
Comment thread lib/l10n/app_es.arb Outdated
Comment thread lib/l10n/app_fr.arb Outdated
Comment thread lib/l10n/app_it.arb Outdated
Comment thread lib/l10n/app_pt.arb Outdated
Comment thread lib/providers/product_analytics_provider.dart
Comment thread lib/screens/chat_list_screen.dart
Comment thread lib/services/product_analytics_config.dart
Comment thread lib/services/product_analytics_service.dart
Comment thread lib/widgets/wn_toggle.dart Outdated
@github-actions

github-actions Bot commented May 19, 2026

Copy link
Copy Markdown

❌ Test Failures

Shard 2
Resolving dependencies...
Downloading packages...
  _fe_analyzer_shared 92.0.0 (100.0.0 available)
  analyzer 9.0.0 (13.0.0 available)
  archive 4.0.7 (4.0.9 available)
  async 2.13.0 (2.13.1 available)
  build 4.0.4 (4.0.6 available)
  built_value 8.12.2 (8.12.6 available)
  cross_file 0.3.5+1 (0.3.5+2 available)
  dart_style 3.1.3 (3.1.9 available)
  ffi 2.1.5 (2.2.0 available)
  flutter_plugin_android_lifecycle 2.0.33 (2.0.34 available)
  flutter_secure_storage_darwin 0.3.0 (0.3.1 available)
  flutter_secure_storage_windows 4.1.0 (4.2.1 available)
  image 4.7.2 (4.8.0 available)
  image_picker_android 0.8.13+10 (0.8.13+17 available)
  image_picker_ios 0.8.13+3 (0.8.13+6 available)
  inspector 3.1.0 (4.0.0 available)
  json_annotation 4.9.0 (4.12.0 available)
  lints 6.0.0 (6.1.0 available)
  matcher 0.12.19 (0.12.20 available)
  meta 1.17.0 (1.18.2 available)
  package_info_plus 9.0.0 (10.1.0 available)
  package_info_plus_platform_interface 3.2.1 (4.1.0 available)
  path_provider_android 2.2.22 (2.3.1 available)
  path_provider_foundation 2.5.1 (2.6.0 available)
  petitparser 7.0.1 (7.0.2 available)
  posix 6.0.3 (6.5.0 available)
  qr 3.0.2 (4.0.0 available)
  share_plus 12.0.1 (13.1.0 available)
  share_plus_platform_interface 6.1.0 (7.1.0 available)
  shared_preferences 2.5.4 (2.5.5 available)
  shared_preferences_android 2.4.18 (2.4.23 available)
  shared_preferences_platform_interface 2.4.1 (2.4.2 available)
  source_gen 4.1.1 (4.2.3 available)
  source_span 1.10.1 (1.10.2 available)
  sqflite 2.4.2 (2.4.2+1 available)
  sqflite_android 2.4.2+2 (2.4.2+3 available)
  sqflite_common 2.5.6 (2.5.8 available)
  synchronized 3.4.0 (3.4.0+1 available)
  test 1.30.0 (1.31.1 available)
  test_api 0.7.10 (0.7.12 available)
  test_core 0.6.16 (0.6.18 available)
  url_launcher_android 6.3.28 (6.3.29 available)
  url_launcher_ios 6.3.6 (6.4.1 available)
  url_launcher_web 2.4.2 (2.4.3 available)
  uuid 4.5.2 (4.5.3 available)
  vector_graphics 1.2.0 (1.2.2 available)
  vector_graphics_compiler 1.1.19 (1.2.3 available)
  vector_math 2.2.0 (2.3.0 available)
  video_player_avfoundation 2.9.4 (2.9.7 available)
  vm_service 15.0.2 (15.2.0 available)
  win32 5.15.0 (6.2.0 available)
  xml 6.6.1 (7.0.1 available)
Got dependencies!
52 packages have newer versions incompatible with dependency constraints.
Try `flutter pub outdated` for more information.
+22: /home/runner/work/whitenoise/whitenoise/test/widgets/wn_slate_test.dart: WnSlate systemNotice without shrinkWrapContent child uses all available space left after system notice
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following LateError was thrown running a test:
LateInitializationError: Field '_splitScreenMode@1724084504' has not been initialized.

When the exception was thrown, this was the stack:
#0      ScreenUtil._splitScreenMode (package:flutter_screenutil/src/screen_util.dart)
#1      ScreenUtil.scaleHeight (package:flutter_screenutil/src/screen_util.dart:220:32)
#2      ScreenUtil.setHeight (package:flutter_screenutil/src/screen_util.dart:241:44)
#3      SizeExtension.h (package:flutter_screenutil/src/size_extension.dart:12:32)
#4      main.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (file:///home/runner/work/whitenoise/whitenoise/test/widgets/wn_slate_test.dart:153:27)
#5      testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:192:29)
<asynchronous suspension>
#6      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:1682:5)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)

The test description was:
  child uses all available space left after system notice
════════════════════════════════════════════════════════════════════════════════════════════════════
+22 -1: /home/runner/work/whitenoise/whitenoise/test/widgets/wn_slate_test.dart: WnSlate systemNotice without shrinkWrapContent child uses all available space left after system notice [E]
  Test failed. See exception logs above.
  The test description was: child uses all available space left after system notice
  
+1150 -1: Some tests failed.

@github-actions

github-actions Bot commented May 19, 2026

Copy link
Copy Markdown

❌ Coverage: 99.35% → 99.25% (-0.10%)

History
  • 17e8b27 2026-05-19 14:42:39 UTC — 99.28% (-0.07% vs base)
  • 87d0599 2026-05-19 15:09:46 UTC — 99.25% (-0.10% vs base)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/screens/chat_list_screen.dart`:
- Around line 425-426: The unawaited calls for onAnalyticsDismiss and
onAnalyticsShare can let exceptions from resolveAnalyticsNotice() or
shareAnalytics bubble as unhandled async errors; wrap these calls with explicit
error handling. Replace the current arrow-call patterns with short async
closures that await the call inside try/catch (e.g. onAnalyticsDismiss: () async
{ try { await resolveAnalyticsNotice(); } catch (e, s) { /* log or handle error
*/ } }) or append .catchError(...) to the futures (e.g.
unawaited(resolveAnalyticsNotice().catchError((e, s) { /* log */ }))), and do
the same for shareAnalytics to ensure failures are caught and logged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 48a0b211-8875-4355-a94c-0808af3c046e

📥 Commits

Reviewing files that changed from the base of the PR and between b4aa058 and 0e204c4.

⛔ Files ignored due to path filters (8)
  • lib/l10n/generated/app_localizations_de.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_es.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_fr.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_it.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_pt.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_ru.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_tr.dart is excluded by !**/generated/**, !lib/l10n/generated/**
  • lib/l10n/generated/app_localizations_zh.dart is excluded by !**/generated/**, !lib/l10n/generated/**
📒 Files selected for processing (17)
  • lib/l10n/app_de.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_pt.arb
  • lib/l10n/app_ru.arb
  • lib/l10n/app_tr.arb
  • lib/l10n/app_zh.arb
  • lib/l10n/app_zh_Hant.arb
  • lib/providers/product_analytics_provider.dart
  • lib/screens/chat_list_screen.dart
  • lib/services/product_analytics_config.dart
  • lib/services/product_analytics_service.dart
  • lib/widgets/wn_toggle.dart
  • test/providers/product_analytics_provider_test.dart
  • test/screens/chat_list_screen_test.dart
  • test/services/product_analytics_service_test.dart
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test (3/4)
  • GitHub Check: Test (4/4)
  • GitHub Check: Test (2/4)
  • GitHub Check: Check
  • GitHub Check: Test (1/4)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.arb

⚙️ CodeRabbit configuration file

**/*.arb: These are localization files. Check for:

  • Consistent key naming across all locale files
  • Proper ICU message format for plurals/gender
  • No hardcoded strings that should be localized

Files:

  • lib/l10n/app_zh.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_de.arb
  • lib/l10n/app_ru.arb
  • lib/l10n/app_zh_Hant.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_tr.arb
  • lib/l10n/app_pt.arb
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use single quotes for strings in Dart/Flutter code
Enable and respect prefer_const_constructors lint rule in Dart/Flutter code
Enable and respect prefer_final_locals lint rule in Dart/Flutter code
Set line width to 100 characters in Dart/Flutter code
Preserve trailing commas in Dart/Flutter code
Maintain minimum test coverage of 99%; never submit a PR that reduces test coverage
Avoid using // coverage:ignore, // coverage:ignore-line, // coverage:ignore-start, or // coverage:ignore-end to bypass coverage requirements; write tests instead (only exception: truly unreachable code)

Files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
**/*.{dart,rs}

📄 CodeRabbit inference engine (AGENTS.md)

Do not add comments except for code that is really complex or hard to understand

Files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
test/**/*_test.dart

📄 CodeRabbit inference engine (AGENTS.md)

test/**/*_test.dart: Test files should mirror source structure with _test.dart suffix
Use helpers from test/test_helpers.dart in tests: setUpTestView(), mountTestApp(), mountHook(), mountWidget(), mountStackedWidget()
Mock Rust API by extending MockWnApi from test/mocks/mock_wn_api.dart instead of implementing RustLibApi directly
Prefer find.byKey() over find.byIcon() in tests; add keys to icons in widgets and use find.byKey(const Key('icon_name')) in tests
Use valid 64-character hex strings for pubkeys in tests, not dummy values like 'abc' or 'test-pubkey'

Files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
test/**

⚙️ CodeRabbit configuration file

test/**: IMPORTANT: CI enforces coverage regression (coverage must never decrease). It does not enforce a fixed 95% minimum threshold.
Rules:

  • Test files mirror source structure with _test.dart suffix
  • Use helpers from test/test_helpers.dart (setUpTestView, mountTestApp, etc.)
  • Mock Rust API using RustLib.initMock(api: mockApi)
  • Always extend MockWnApi from test/mocks/mock_wn_api.dart
  • Prefer find.byKey() over find.byIcon() for widget testing
  • Use valid 64-char hex strings for pubkeys, not dummy values like 'abc'
  • Tests must be deterministic — no external service dependencies

Files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
lib/widgets/wn_*.dart

📄 CodeRabbit inference engine (AGENTS.md)

lib/widgets/wn_*.dart: Design system widgets should be prefixed with wn_ in filename and Wn in class name (e.g., wn_filled_button.dart / WnFilledButton)
Design system widgets must have Widgetbook stories and should not contain translations or Rust API calls

Files:

  • lib/widgets/wn_toggle.dart
lib/widgets/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Screen-scoped widgets extracted from a single screen should be prefixed with the screen name and only used in that one screen

Files:

  • lib/widgets/wn_toggle.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

lib/**/*.dart: Use flutter_screenutil for all size values (padding, margins, gaps, icon sizes, font sizes, border radius, container dimensions) to ensure responsive layouts. Use .w for width, .h for height, .sp for font size/letter spacing, and .r for radius
Avoid StatefulWidget; prefer providers (for shared app-wide state) or hooks (for widget-local state) instead

Files:

  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • lib/services/product_analytics_config.dart
lib/widgets/**

⚙️ CodeRabbit configuration file

lib/widgets/**: This is a reusable widget.
There are two kinds of reusable widgets:

  1. Design system widgets — simple, presentational widgets that match
    the Figma design system in name and structure. They have Widgetbook
    stories, contain only presentational logic, and do NOT have
    translations or Rust API calls.

    • File MUST be prefixed with wn_ (e.g. wn_filled_button.dart)
    • Class MUST be prefixed with Wn (e.g. WnFilledButton)
  2. Complex reusable widgets — used across multiple screens but contain
    translations, hooks with Rust API calls, or other complex logic
    that makes them harder to display in Widgetbook.

    • These do NOT use the wn_/Wn prefix
    • Example: OnboardingCarousel (used in multiple screens, has
      translations and a page controller inside)

General rules for all widgets in this directory:

  • Use const constructors where possible
  • Use flutter_screenutil (.w, .h, .sp, .r) for all dimensions
  • Avoid StatefulWidget — prefer hooks for local state
  • No comments except for truly complex logic

Files:

  • lib/widgets/wn_toggle.dart
lib/screens/**

⚙️ CodeRabbit configuration file

lib/screens/**: This is a Flutter screen (full-page widget).
Architecture rules:

  • Screens should WATCH Riverpod providers for shared state
  • Use flutter_hooks for ephemeral/local state (NOT StatefulWidget)
  • Pass data to hooks, not refs
  • Use flutter_screenutil (.w, .h, .sp, .r) for all size values
  • Widgets should use const constructors where possible
  • No comments except for truly complex logic
  • When a widget is extracted from a screen and only used in that one
    screen, it should be prefixed with the screen name (e.g.
    ChatListTile for a widget only used in the chat list screen).
    These are screen-scoped widgets and do NOT use the Wn prefix.

Files:

  • lib/screens/chat_list_screen.dart
lib/providers/*_provider.dart

📄 CodeRabbit inference engine (AGENTS.md)

lib/providers/*_provider.dart: Provider files should end with _provider.dart (e.g., auth_provider.dart)
Provider variables should end with Provider (e.g., authProvider)

Files:

  • lib/providers/product_analytics_provider.dart
lib/providers/**

⚙️ CodeRabbit configuration file

lib/providers/**: This is a Riverpod provider (shared app-wide state).
Rules:

  • Files must end with _provider.dart
  • Provider variables must end with Provider (e.g. authProvider)
  • Don't duplicate logic from the Rust crate — whitenoise is source of truth
  • Don't cache data that whitenoise already persists in its local DB

Files:

  • lib/providers/product_analytics_provider.dart
lib/services/**

⚙️ CodeRabbit configuration file

lib/services/**: Services are stateless operations (API calls, etc.).
They should not hold state — that belongs in providers.
Check that they don't duplicate logic from whitenoise-rs.

Files:

  • lib/services/product_analytics_service.dart
  • lib/services/product_analytics_config.dart
🧠 Learnings (31)
📚 Learning: 2026-02-02T10:05:10.264Z
Learnt from: erskingardner
Repo: marmot-protocol/sloth PR: 128
File: lib/l10n/app_it.arb:53-53
Timestamp: 2026-02-02T10:05:10.264Z
Learning: For the Sloth app localization (lib/l10n/app_it.arb), ensure the Italian translations for the keys 'cancel' and 'discard' are both set to 'Annulla'. This should be consistently used across UI labels, dialogs, and tooltips. If there are multiple occurrences of these keys, verify all use the same translation to avoid inconsistencies.

Applied to files:

  • lib/l10n/app_it.arb
📚 Learning: 2026-03-04T16:40:26.588Z
Learnt from: josefinalliende
Repo: marmot-protocol/whitenoise PR: 408
File: lib/l10n/app_ru.arb:107-107
Timestamp: 2026-03-04T16:40:26.588Z
Learning: In the whitenoise Flutter project lib/l10n/app_ru.arb, the invitedYouToChatSuffix uses masculine past-tense (пригласил) as the default form. Since Nostr identities have no gender metadata, ICU gender-select is not feasible. Do not flag masculine default as a missing gender-agreement issue for Russian or other locales in this project.

Applied to files:

  • lib/l10n/app_ru.arb
📚 Learning: 2026-01-05T21:28:05.652Z
Learnt from: untreu2
Repo: marmot-protocol/sloth PR: 22
File: test/screens/profile_keys_screen_test.dart:14-23
Timestamp: 2026-01-05T21:28:05.652Z
Learning: In test mocks for the marmot-protocol/sloth project, allow skipping bounds checking for substring operations when test inputs are controlled and have proven sufficient length (e.g., 'test_pubkey' is always 12 characters). Favor simpler, deterministic mock implementations in such controlled test contexts, but avoid relaxing bounds checks in production code or tests with variable inputs. Apply this guideline primarily to test files under the test/ directory.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-07T16:49:18.694Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 33
File: test/screens/chat_screen_test.dart:245-282
Timestamp: 2026-01-07T16:49:18.694Z
Learning: In the marmot-protocol/sloth repository, it is acceptable to use force-unwrapping (!) in Dart test files as an implicit assertion: if a value is unexpectedly null, the test will fail. This guideline applies only to test code under the test/ directory and should not be used in production code.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-11T22:43:09.610Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 36
File: test/screens/chat_list_screen_test.dart:74-76
Timestamp: 2026-01-11T22:43:09.610Z
Learning: In Dart tests (e.g., test/screens/chat_list_screen_test.dart and related files), use setUp() to call _api.reset() to clean up StreamController resources. Do not suggest or rely on tearDownAll for this cleanup.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-04-14T03:52:37.716Z
Learnt from: josefinalliende
Repo: marmot-protocol/whitenoise PR: 565
File: test/providers/offline_provider_test.dart:63-177
Timestamp: 2026-04-14T03:52:37.716Z
Learning: In this repository, it’s acceptable to use fixed-time waits for test timing using `Future.delayed` (e.g., `await Future.delayed(const Duration(milliseconds: 10))`) inside test files. During code reviews, don’t recommend replacing these fixed sleeps with event-based or poll-based synchronization when the change is localized to Dart test files under the `test/` directory.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-06T01:08:41.552Z
Learnt from: untreu2
Repo: marmot-protocol/sloth PR: 22
File: lib/widgets/wn_warning_box.dart:28-79
Timestamp: 2026-01-06T01:08:41.552Z
Learning: In reviews for the marmot-protocol/sloth repository, avoid suggesting accessibility, design, or general improvements unless they are strictly relevant to the PR description and its stated goals. Focus feedback on the specific PR objectives and requirements; ignore unrelated stylistic or broad improvement suggestions.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-15T14:42:54.111Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 39
File: lib/hooks/use_user_search.dart:47-53
Timestamp: 2026-01-15T14:42:54.111Z
Learning: When using npubFromHex(String hexPubkey) from lib/utils/formatting.dart, it already handles errors internally and returns null on failure. Do not wrap calls to this function in try/catch blocks. Instead, check for a null return and handle accordingly (e.g., treat as invalid hex). This guideline applies to all Dart files in the repository that may call this function.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-16T17:35:32.431Z
Learnt from: untreu2
Repo: marmot-protocol/sloth PR: 43
File: lib/hooks/use_network_relays.dart:155-184
Timestamp: 2026-01-16T17:35:32.431Z
Learning: In Dart/Flutter code, prefer void as the return type for fire-and-forget async functions that are intended to run in the background without blocking the caller. Using Future or Future<void> would allow callers to accidentally await, which could block for long periods. This pattern makes the call fire-and-forget by design. Apply to functions that intentionally should not be awaited; document the intent where appropriate. (Example context: pollRelayStatus in lib/hooks/use_network_relays.dart uses void to prevent callers from awaiting a potentially long-running background task.)

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-19T15:50:56.684Z
Learnt from: erskingardner
Repo: marmot-protocol/sloth PR: 52
File: lib/screens/app_settings_screen.dart:30-40
Timestamp: 2026-01-19T15:50:56.684Z
Learning: Target the Flutter SDK version >= 3.27 across the repo. Since features like Column.spacing and Row.spacing were added in Flutter 3.27, you can safely use them in Dart files (e.g., lib/screens/app_settings_screen.dart) as long as the pubspec.yaml environment specifies Flutter >= 3.27. If needed, enforce this by validating the environment constraint (e.g., flutter, sdk: flutter) in pubspec.yaml.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-29T03:02:34.290Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 100
File: lib/widgets/wn_dropdown_selector.dart:126-139
Timestamp: 2026-01-29T03:02:34.290Z
Learning: Global font-family usage guideline: Since Manrope is configured in lib/theme.dart for both light and dark themes, individual TextStyle declarations should not specify fontFamily: 'Manrope'. During reviews, verify that no TextStyle instances override fontFamily unnecessarily; rely on theme inheritance instead. If a TextStyle must specify a font for a specific case, ensure it is clearly justified and localized, and document why the override is needed. This guideline applies across Dart files in the project (e.g., lib/widgets/wn_dropdown_selector.dart and related components).

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-29T16:02:52.588Z
Learnt from: erskingardner
Repo: marmot-protocol/sloth PR: 129
File: test/widgets/wn_overlay_test.dart:84-91
Timestamp: 2026-01-29T16:02:52.588Z
Learning: In Dart/Flutter code, when constructing widgets inside an already-const context (for example, within a const Scaffold with a child that is a collection of widgets), avoid adding explicit const keywords to the children. The Dart analyzer treats these as implicitly const, and adding explicit const can trigger the unnecessary_const lint. Review test files and general Dart files to ensure you do not redundantly prefix children with const in const contexts; rely on the implicit const behavior instead.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-02-07T03:58:22.587Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 193
File: lib/services/android_signer_service.dart:13-13
Timestamp: 2026-02-07T03:58:22.587Z
Learning: In Dart files across the repository, retain NIP (Nostr Implementation Possibilities) and MIP (Marmot Implementation Possibilities) protocol reference comments that link code to their specifications. While general guidance discourages extraneous comments, these references improve traceability and maintainability by documenting the protocol context for related implementation details. Keep them in Dart sources (e.g., lib/services/android_signer_service.dart) and ensure they are kept up-to-date.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-02-11T17:29:43.985Z
Learnt from: untreu2
Repo: marmot-protocol/whitenoise PR: 225
File: lib/screens/app_settings_screen.dart:64-69
Timestamp: 2026-02-11T17:29:43.985Z
Learning: In the whitenoise Flutter app, after deleting all data and resetting auth, use Routes.goToHome(context) instead of Routes.goToLogin(context) because the home screen is the app entry point and will manage authentication routing. Apply this change to navigation calls that should land on the home screen after a data reset or auth reset. Update lib/screens/app_settings_screen.dart and any similar navigation points accordingly.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-02-18T18:36:13.394Z
Learnt from: untreu2
Repo: marmot-protocol/whitenoise PR: 315
File: lib/screens/edit_group_screen.dart:45-47
Timestamp: 2026-02-18T18:36:13.394Z
Learning: Whitenoise uses automated code formatting through the precommit workflow (just precommit). Do not tweak line wrapping or formatting manually; follow the formatter's output. Run the precommit formatter locally and ensure the code matches its styling decisions, and only deviate if the formatter cannot express the intended style (in which case adjust the code to satisfy the formatter).

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-04-06T09:36:06.726Z
Learnt from: untreu2
Repo: marmot-protocol/whitenoise PR: 553
File: lib/widgets/wn_copyable_field.dart:38-40
Timestamp: 2026-04-06T09:36:06.726Z
Learning: In Dart, `String.operator*` is a valid built-in operator for repeating strings (e.g., `'⬤' * 16`). In code reviews of Dart (`.dart`) files, do not treat expressions like `'text' * n` as invalid syntax and do not recommend replacing them with alternatives such as `List.filled(...).join()`; the operator is supported by `dart:core`.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-05-13T13:46:32.612Z
Learnt from: dannym-arx
Repo: marmot-protocol/whitenoise PR: 656
File: lib/hooks/use_block_actions.dart:3-3
Timestamp: 2026-05-13T13:46:32.612Z
Learning: In marmot-protocol/whitenoise code reviews, do not flag imports that reference FRB-generated bindings under `package:rust_lib_whitenoise/src/rust/...` as private-internals violations. In `rust_lib_whitenoise`, Dart bindings are intentionally generated into `lib/src/rust/`, and `analysis_options.yaml` sets `implementation_imports: false` to suppress the related Dart lint—so `package:rust_lib_whitenoise/src/rust/...` imports are the correct/only way to consume that FRB-generated API.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-05-13T13:46:39.250Z
Learnt from: dannym-arx
Repo: marmot-protocol/whitenoise PR: 656
File: lib/services/foreground_service.dart:13-16
Timestamp: 2026-05-13T13:46:39.250Z
Learning: In this repo (marmot-protocol/whitenoise), `rust_lib_whitenoise` is an FRB-generated Flutter package where the generated Dart bindings are intentionally exposed under `lib/src/rust/` as the public API surface. Do not treat imports like `package:rust_lib_whitenoise/src/rust/...` as breaking package API boundaries in code reviews (the project also sets `implementation_imports: false` in `analysis_options.yaml` to opt into this). Never raise concerns about `src/` imports from `rust_lib_whitenoise`.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • test/services/product_analytics_service_test.dart
  • lib/services/product_analytics_config.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-02-05T11:59:09.400Z
Learnt from: erskingardner
Repo: marmot-protocol/sloth PR: 189
File: test/widgets/wn_filter_chip_test.dart:1-3
Timestamp: 2026-02-05T11:59:09.400Z
Learning: In Dart/Flutter test files, Offset is exported by package:flutter_test/flutter_test.dart. If you already import flutter_test, you do not need to import dart:ui for Offset. Ensure there are no conflicting Offset symbols when both imports occur.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-04-28T17:39:39.941Z
Learnt from: pepina-dev
Repo: marmot-protocol/whitenoise PR: 600
File: test/hooks/use_leave_group_test.dart:67-78
Timestamp: 2026-04-28T17:39:39.941Z
Learning: In this repo’s Dart test files under `test/` (e.g., `test/hooks/use_leave_group_test.dart`), it’s acceptable to avoid calling `mockApi.reset()` at the start of `setUp()` when the test suite already manually resets all relevant individual mock fields in `setUp()`. Reviewers should not flag the absence of a `reset()` call as long as every field that could leak state between tests is explicitly reinitialized and the tests are passing.

Applied to files:

  • test/providers/product_analytics_provider_test.dart
  • test/services/product_analytics_service_test.dart
  • test/screens/chat_list_screen_test.dart
📚 Learning: 2026-01-05T20:05:32.918Z
Learnt from: untreu2
Repo: marmot-protocol/sloth PR: 22
File: lib/screens/settings_screen.dart:76-88
Timestamp: 2026-01-05T20:05:32.918Z
Learning: In the Sloth project, tooltips should not be used for UI elements. During code reviews, verify that UI components do not include Tooltip widgets and replace them with accessible alternatives (e.g., long-press hints, labels, or interactive guidance). Apply this guidance across Dart UI files under lib (not just settings_screen.dart) to maintain a consistent design approach.

Applied to files:

  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • lib/services/product_analytics_config.dart
📚 Learning: 2026-01-09T13:25:18.531Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 33
File: lib/services/message_service.dart:14-26
Timestamp: 2026-01-09T13:25:18.531Z
Learning: When errors are presented to users in the Sloth codebase (UI layers such as snackbars, dialogs, toasts), show friendly, user-facing messages instead of raw Rust error messages. Implement a mapping from internal error types or codes to clear, non-technical messages, and surface actionable guidance for end users where appropriate. Avoid exposing internal stack traces or language runtime details; centralize common user-facing error wording in a dedicated utility or service and translate backend errors into concise, helpful UI messages.

Applied to files:

  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • lib/services/product_analytics_config.dart
📚 Learning: 2026-04-06T09:40:41.044Z
Learnt from: untreu2
Repo: marmot-protocol/whitenoise PR: 552
File: lib/screens/profile_keys_screen.dart:71-71
Timestamp: 2026-04-06T09:40:41.044Z
Learning: In this Flutter/Dart codebase, when using flutter_screenutil (e.g., ScreenUtil or ScreenUtil-based sizing like `w`, `h`), do not require scaled units for literal zero values. Specifically, in EdgeInsets (and similar sizing/padding/margin/gap APIs), bare numeric `0` should be allowed (e.g., `EdgeInsets.all(0)`, `EdgeInsets.symmetric(vertical: 0)`, `SizedBox(width: 0)`), because scaling `0` remains `0`. Only flag ScreenUtil violations when a non-zero literal needs to be expressed via `0.w`/`0.h` equivalents; do not flag bare `0`.

Applied to files:

  • lib/widgets/wn_toggle.dart
  • lib/screens/chat_list_screen.dart
  • lib/providers/product_analytics_provider.dart
  • lib/services/product_analytics_service.dart
  • lib/services/product_analytics_config.dart
📚 Learning: 2026-01-05T22:40:27.888Z
Learnt from: untreu2
Repo: marmot-protocol/sloth PR: 22
File: lib/widgets/wn_image_picker.dart:127-143
Timestamp: 2026-01-05T22:40:27.888Z
Learning: In marmot-protocol's Sloth project (lib/widgets/...): image hosting URLs returned by blossom servers are always lowercase (http:// or https://). Do not implement or rely on case-insensitive URL protocol detection in WN image picker code; assume the protocol is lowercase. If URL protocol checks are needed, compare against the exact literals 'http://' and 'https://'. Add tests that assert URLs use lowercase protocols. This guideline applies to all Dart files under lib/widgets that handle image URLs.

Applied to files:

  • lib/widgets/wn_toggle.dart
📚 Learning: 2026-02-04T10:35:43.112Z
Learnt from: erskingardner
Repo: marmot-protocol/sloth PR: 170
File: lib/widgets/wn_list_item.dart:232-234
Timestamp: 2026-02-04T10:35:43.112Z
Learning: In this repository ( marmot-protocol/sloth ), when writing widget build methods in Dart files under lib/widgets, only extract a local typography variable if context.typographyScaled or context.typography is used multiple times within the same build method. If typography is used only once, keep the inline access to avoid an unnecessary extra line. This guideline helps maintain readability and avoids premature refactoring.

Applied to files:

  • lib/widgets/wn_toggle.dart
📚 Learning: 2026-02-14T05:09:20.007Z
Learnt from: josefinalliende
Repo: marmot-protocol/whitenoise PR: 260
File: lib/widgets/wn_message_quote.dart:32-88
Timestamp: 2026-02-14T05:09:20.007Z
Learning: In Dart widget files under lib/widgets (matching lib/widgets/**/*.dart), spacing literals such as padding, gap values, and border radii should not be extracted into constants based on josefinalliende's preference. Apply this rule consistently across widget files to improve readability and avoid over-abstracting simple literals.

Applied to files:

  • lib/widgets/wn_toggle.dart
📚 Learning: 2026-01-15T15:00:17.356Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 39
File: lib/screens/user_search_screen.dart:50-75
Timestamp: 2026-01-15T15:00:17.356Z
Learning: In lib/screens/user_search_screen.dart and similar screen widgets in the sloth repository, prefer using inline ternary expressions for conditional rendering (e.g., isLoading → empty state → list) over extracting into separate widget classes when the conditional UI is simple. This improves readability by keeping the render logic in a single place. Reserve extracting to a separate widget only when the conditional UI becomes sufficiently complex or is reused across multiple screens.

Applied to files:

  • lib/screens/chat_list_screen.dart
📚 Learning: 2026-02-10T04:57:31.475Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 208
File: lib/screens/signup_screen.dart:66-70
Timestamp: 2026-02-10T04:57:31.475Z
Learning: In Dart/Flutter using flutter_hooks, a ValueNotifier (from useState) is disposed when the widget unmounts. Async callbacks (e.g., .then() on animations) may still run after disposal and throw 'A ValueNotifier was used after being disposed'. Reviewers should ensure that state updates in async callbacks are guarded by a mounted check (e.g., if (mounted) { notifier.value = ... } or cancel/avoid updating after disposal). This guideline applies to screen widgets under lib/screens and similar Dart files using hooks that manage widget lifecycle. Update code patterns to prevent post-disposal state updates, and document the rationale in comments where necessary.

Applied to files:

  • lib/screens/chat_list_screen.dart
📚 Learning: 2026-02-19T10:33:16.889Z
Learnt from: untreu2
Repo: marmot-protocol/whitenoise PR: 315
File: lib/screens/group_info_screen.dart:146-149
Timestamp: 2026-02-19T10:33:16.889Z
Learning: Pubkey validation (length/format) is performed by the Rust crate before data reaches Dart. Do not add defensive substring guards in Dart for pubkeys in lib/screens/**/*.dart or other Flutter code consuming group member pubkeys, as this would duplicate validation and be unnecessary. If any validation is needed in Dart, keep it to lightweight sanity checks only after data enters Dart and ensure it aligns with the Rust validation rules.

Applied to files:

  • lib/screens/chat_list_screen.dart
📚 Learning: 2026-04-06T09:40:51.965Z
Learnt from: untreu2
Repo: marmot-protocol/whitenoise PR: 552
File: lib/screens/sign_out_screen.dart:86-86
Timestamp: 2026-04-06T09:40:51.965Z
Learning: When reviewing Flutter/Dart code that uses ScreenUtil (e.g., ScreenUtil `.h`/`.w`/`.sp`), do not require converting a literal `0` used for padding/margin/sizing to a ScreenUtil expression. In sizing contexts like `EdgeInsets` (or other `EdgeInsets`/layout parameters), raw `0` is mathematically invariant (`0 * scale = 0`) and should not be flagged as a ScreenUtil violation. Only non-zero literal size values should be checked for ScreenUtil usage.

Applied to files:

  • lib/screens/chat_list_screen.dart
📚 Learning: 2025-12-23T14:37:11.241Z
Learnt from: josefinalliende
Repo: marmot-protocol/sloth PR: 9
File: test/screens/error_screen_test.dart:60-71
Timestamp: 2025-12-23T14:37:11.241Z
Learning: In tests for screen widgets that are not wired through the app's Routes.build (e.g., ErrorScreen under lib/screens/error_screen.dart), avoid mounting the full app with mountTestApp. instead, configure a minimal test harness with a custom router or navigator setup (e.g., a MaterialApp with a mock router) to render the screen in isolation. This ensures the test focuses on the screen's behavior without depending on the app's global routing configuration.

Applied to files:

  • test/screens/chat_list_screen_test.dart
🔇 Additional comments (17)
lib/l10n/app_zh.arb (1)

17-22: LGTM!

lib/l10n/app_es.arb (1)

17-22: LGTM!

lib/l10n/app_it.arb (1)

17-22: LGTM!

lib/l10n/app_de.arb (1)

17-22: LGTM!

lib/l10n/app_ru.arb (1)

17-22: LGTM!

test/providers/product_analytics_provider_test.dart (1)

1-64: LGTM!

lib/l10n/app_zh_Hant.arb (1)

17-22: LGTM!

lib/l10n/app_fr.arb (1)

17-22: LGTM!

lib/l10n/app_tr.arb (1)

17-22: LGTM!

lib/l10n/app_pt.arb (1)

17-22: LGTM!

lib/widgets/wn_toggle.dart (1)

5-59: LGTM!

lib/screens/chat_list_screen.dart (1)

102-202: LGTM!

Also applies to: 239-283, 287-324, 419-433

lib/providers/product_analytics_provider.dart (1)

5-42: LGTM!

lib/services/product_analytics_service.dart (1)

51-168: LGTM!

test/services/product_analytics_service_test.dart (1)

5-147: LGTM!

lib/services/product_analytics_config.dart (1)

33-147: LGTM!

test/screens/chat_list_screen_test.dart (1)

1-1069: LGTM!

Comment on lines +425 to +426
onAnalyticsDismiss: () => unawaited(resolveAnalyticsNotice()),
onAnalyticsShare: () => unawaited(shareAnalytics()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle errors for unawaited analytics callbacks.

Line 425 and Line 426 launch async work with unawaited(...), but failures from resolveAnalyticsNotice() (notably markResolved) can bubble as unhandled async errors. Wrap these paths with explicit error handling (either inside the async methods or via .catchError(...)) so notice actions cannot emit uncaught exceptions.

🔧 Suggested fix
 Future<void> resolveAnalyticsNotice() async {
   final resolutionService = ref.read(analyticsOnboardingResolutionServiceProvider);
   final resolvedProvider = analyticsOnboardingResolvedProvider(pubkey);
   if (!context.mounted) return;
   analyticsStepResolved.value = true;
-  await resolutionService.markResolved(pubkey);
+  try {
+    await resolutionService.markResolved(pubkey);
+  } catch (_) {
+    return;
+  }
   if (!context.mounted) return;
   ref.invalidate(resolvedProvider);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/screens/chat_list_screen.dart` around lines 425 - 426, The unawaited
calls for onAnalyticsDismiss and onAnalyticsShare can let exceptions from
resolveAnalyticsNotice() or shareAnalytics bubble as unhandled async errors;
wrap these calls with explicit error handling. Replace the current arrow-call
patterns with short async closures that await the call inside try/catch (e.g.
onAnalyticsDismiss: () async { try { await resolveAnalyticsNotice(); } catch (e,
s) { /* log or handle error */ } }) or append .catchError(...) to the futures
(e.g. unawaited(resolveAnalyticsNotice().catchError((e, s) { /* log */ }))), and
do the same for shareAnalytics to ensure failures are caught and logged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant