Skip to content

Conversation

@untreu2
Copy link
Contributor

@untreu2 untreu2 commented Aug 15, 2025

Description

Removes the CarbonIcons package and implements the direct use of SVG files from Figma for all icons. This change streamlines icon management and improves the workflow between design and development.

Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🛠️ Bug fix (non-breaking change which fixes an issue)
  • ❌ Breaking change (fix or feature that would cause existing functionality to change)
  • 🧹 Code refactor
  • ✅ Build configuration change
  • 📝 Documentation
  • 🗑️ Chore

Checklist

  • Run just precommit to ensure that formatting and linting are correct
  • Updated the CHANGELOG.md file with your changes

Fixes #474

Summary by CodeRabbit

  • Style

    • Migrated app-wide icons to tinted SVG assets for crisper, consistent visuals across chat, contacts, reactions, settings, and headers.
  • Refactor

    • Standardized asset-based icon handling and status indicator rendering throughout the UI.
  • Tests

    • Updated UI tests to assert SVG-based icons.
  • Chores

    • Removed legacy icon-font dependency and related assets; deleted in-chat audio playback component (audio playback UI and controls removed).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 15, 2025

Walkthrough

Replaced CarbonIcons/IconData usage across the app with SVG asset-based icons, added many AssetsPaths constants, updated UI widgets to render SvgPicture with ColorFilter, adjusted APIs (RelayStatus.getIcon→getIconAsset, WnStickyHeadsUp/WnHeadingType icon→iconAsset, MenuItem.icon→assetPath, SettingsListTile icon→assetPath), removed supa_carbon_icons, and updated tests accordingly.

Changes

Cohort / File(s) Summary
Icon API migration: models & enums
lib/models/relay_status.dart
Changed icon accessor from IconData getIcon()String getIconAsset() and mappings now return asset path constants.
Asset constants
lib/ui/core/themes/assets.dart
Added many new SVG asset path constants and renamed icInfoFilledic_information_filled.
Core UI components
lib/ui/core/ui/wn_heads_up.dart, lib/ui/core/ui/wn_callout.dart, lib/ui/core/ui/wn_bottom_sheet.dart
Replaced IconData usage with SvgPicture.asset; constructors/fields updated where applicable (iconiconAsset).
Chat UI & widgets
lib/ui/chat/widgets/*, lib/ui/chat/chat_info/*, lib/ui/chat/chat_management/*
Switched Icon/CarbonIcons to SvgPicture.asset; reaction API changed from icon: IconDataassetPath: String; removed ChatAudioItem and audio notifier files.
Contact list UI
lib/ui/contact_list/*, lib/ui/contact_list/widgets/*
Replaced search/close/trash/qr/user/copy icons with SVG assets; removed CarbonIcons imports and applied SVG tinting.
Settings & Donate
lib/ui/settings/**/*
Migrated settings icons (back, chevrons, trash, list tiles) to assets; SettingsListTile now accepts assetPath instead of icon.
Network / Relay UI
lib/ui/settings/network/*
Replaced Icon widgets with assets; relay status UI now uses relay.status.getIconAsset() rendered via SvgPicture.asset.
Bottom sheets & Misc UI
lib/ui/core/ui/wn_bottom_sheet.dart, various screens
Replaced back/close/chevron/info icons with SVG assets and removed supa_carbon_icons imports.
Dependency cleanup
pubspec.yaml
Removed supa_carbon_icons dependency.
Tests updated
test/ui/**
Updated tests to locate SvgPicture assets instead of CarbonIcons; added flutter_svg imports.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
Migrate all Carbon icons to SVG and remove Carbon icons (issue #474)

Out-of-scope changes

Code Change Explanation
Removal of chat audio playback notifier and state (ChatAudioNotifier, ChatAudioState, chatAudioProvider, currentlyPlayingAudioProvider) (lib/ui/chat/notifiers/chat_audio_notifier.dart) This is a functional subsystem removal unrelated to icon migration objectives.
Deletion of ChatAudioItem widget (lib/ui/chat/widgets/chat_audio_item.dart) UI+logic deletion for audio playback not mentioned in issue #474; not an icon-only change.

Possibly related PRs

Suggested reviewers

  • josefinalliende
  • nextidearly
  • Quwaysim

Poem

"I swapped my fonts for shiny trails,
tiny SVG carrots in my paws,
I hopped through code and fixed the rails,
tests sniffed paths and gave applause.
Hooray — the icons sing because 🥕🐰"

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 377adea and f9eb574.

📒 Files selected for processing (2)
  • lib/ui/chat/notifiers/chat_audio_notifier.dart (0 hunks)
  • lib/ui/chat/widgets/chat_audio_item.dart (0 hunks)
💤 Files with no reviewable changes (2)
  • lib/ui/chat/notifiers/chat_audio_notifier.dart
  • lib/ui/chat/widgets/chat_audio_item.dart
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch carbon-icons-svg

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (4)
lib/ui/settings/nostr_keys/nostr_keys_screen.dart (1)

64-66: Dispose all TextEditingControllers to prevent leaks

_publicKeyController is never disposed. This can leak listeners and resources in a StatefulWidget.

Apply this diff:

   @override
   void dispose() {
+    _publicKeyController.dispose();
     _privateKeyController.dispose();
     super.dispose();
   }
lib/ui/chat/chat_management/add_to_group_screen.dart (1)

41-44: Loading flag remains true on early return in _loadGroups

If groups is null/empty, you return while _isLoading is still true, leaving the UI in a perpetual loading state.

Apply this diff:

-    final groups = ref.read(groupsProvider).groups;
-    if (groups == null || groups.isEmpty) {
-      return;
-    }
+    final groups = ref.read(groupsProvider).groups;
+    if (groups == null || groups.isEmpty) {
+      setState(() {
+        _isLoading = false;
+      });
+      return;
+    }

Alternatively, wrap the method body in try/finally and reset _isLoading in finally.

lib/ui/settings/general_settings_screen.dart (2)

247-251: Logic bug: accounts.length > 2 should be > 1

“Multiple accounts” means two or more. Using > 2 skips the 2-accounts case and alters logout flow.

-    final hasMultipleAccounts = accounts.length > 2;
+    final hasMultipleAccounts = accounts.length > 1;

104-111: Guard setState after async work with mounted check

_loadAccounts awaits multiple futures; the widget may unmount before setState, causing exceptions.

       }
 
-      setState(() {
+      if (!mounted) return;
+      setState(() {
         _accounts = accounts;
         _currentAccount = currentAccount;
         _accountContactModels = contactModels;
       });
🧹 Nitpick comments (43)
lib/ui/core/themes/assets.dart (1)

49-71: Asset constants look good; consider adding a private constructor and documenting name collisions

  • The additions are consistent with existing naming. One exception is icCheckmarkFilledSvg, which is intentionally suffixed to avoid collision with the PNG icCheckmarkFilled—this is fine but worth documenting to avoid confusion later.
  • Prevent accidental instantiation of this static-only class with a private constructor.

Add a private constructor near the top of the file:

class AssetsPaths {
  const AssetsPaths._(); // Prevent instantiation
  static const String _svgsDir = 'assets/svgs';
  // ...
}

Optionally add a short comment above icCheckmarkFilledSvg explaining the PNG/SVG name collision to guide future contributors.

test/ui/contact_list/share_invite_bottom_sheet_test.dart (1)

68-69: Make the finder robust: avoid byType(SvgPicture) which is brittle

Matching by widget type can fail if multiple SvgPictures exist. Prefer a stable Key (or semantics label) for the specific copy button.

Apply this diff:

-      final copyButton = find.byType(SvgPicture);
+      final copyButton = find.byKey(const Key('copyButton'));

Then, in the production widget (ShareInviteBottomSheet), set a key on the copy button (example):

// inside ShareInviteBottomSheet where the copy button is built
CustomIconButton(
  key: const Key('copyButton'),
  onTap: () => /* copy logic */,
  iconPath: AssetsPaths.icCopy,
  // ...
)

Alternatively (if you can’t add a Key), use a predicate on the asset name:

final copyButton = find.byWidgetPredicate(
  (w) => w is SvgPicture &&
         w.bytesLoader is SvgAssetLoader &&
        (w.bytesLoader as SvgAssetLoader).assetName.endsWith('ic_copy.svg'),
);
lib/ui/settings/donate/donate_screen.dart (1)

58-66: Prefer IconButton (with tooltip) for accessibility and better hit testing

Using GestureDetector with a raw SVG loses built-in semantics, focus, and a11y affordances. IconButton also provides a consistent tap target and ripple.

Apply this diff:

-                              GestureDetector(
-                                onTap: () => context.pop(),
-                                child: SvgPicture.asset(
-                                  AssetsPaths.icChevronLeft,
-                                  width: 24.w,
-                                  height: 24.w,
-                                  colorFilter: ColorFilter.mode(
-                                    context.colors.primary,
-                                    BlendMode.srcIn,
-                                  ),
-                                ),
-                              ),
+                              IconButton(
+                                onPressed: () => context.pop(),
+                                tooltip: 'Back',
+                                icon: SvgPicture.asset(
+                                  AssetsPaths.icChevronLeft,
+                                  width: 24.w,
+                                  height: 24.w,
+                                  colorFilter: ColorFilter.mode(
+                                    context.colors.primary,
+                                    BlendMode.srcIn,
+                                  ),
+                                ),
+                              ),
test/ui/contact_list/widgets/user_profile_test.dart (1)

103-105: Stabilize the copy-button finder to avoid false positives

Asserting by SvgPicture type may match unrelated SVGs. Use a Key (preferred) or an asset-based predicate.

Apply this diff:

-      final copyButton = find.byType(SvgPicture);
+      final copyButton = find.byKey(const Key('copyButton'));

Production change (UserProfile) to support the test:

CustomIconButton(
  key: const Key('copyButton'),
  onTap: () => /* copy logic */,
  iconPath: AssetsPaths.icCopy,
  // ...
)

Alternate (no Key):

final copyButton = find.byWidgetPredicate(
  (w) => w is SvgPicture &&
         w.bytesLoader is SvgAssetLoader &&
        (w.bytesLoader as SvgAssetLoader).assetName.endsWith('ic_copy.svg'),
);
test/ui/contact_list/start_chat_bottom_sheet_test.dart (1)

172-176: Use a specific finder for the copy control; tapping byType(SvgPicture) can be ambiguous

If multiple SvgPictures are present, this will fail or tap the wrong widget. Prefer a Key or an asset-based predicate.

Apply this diff:

-      final copyButton = find.byType(SvgPicture);
+      final copyButton = find.byKey(const Key('copyButton'));
       expect(copyButton, findsOneWidget);
 
       await tester.tap(copyButton);

Production change (StartChatBottomSheet) to add the key:

CustomIconButton(
  key: const Key('copyButton'),
  onTap: () => /* copy logic */,
  iconPath: AssetsPaths.icCopy,
  // ...
)

Alternate (no Key):

final copyButton = find.byWidgetPredicate(
  (w) => w is SvgPicture &&
         w.bytesLoader is SvgAssetLoader &&
        (w.bytesLoader as SvgAssetLoader).assetName.endsWith('ic_copy.svg'),
);
lib/ui/settings/nostr_keys/nostr_keys_screen.dart (1)

96-104: Increase back button tap target and add semantics for accessibility

The 24x24 tap area is small for touch. Also, SVGs don’t provide semantics by default. Consider enlarging the tap target and adding a semantics label.

Apply this diff to wrap the icon and add a label:

-                            child: SvgPicture.asset(
-                              AssetsPaths.icChevronLeft,
-                              colorFilter: ColorFilter.mode(
-                                context.colors.primary,
-                                BlendMode.srcIn,
-                              ),
-                              width: 24.w,
-                              height: 24.w,
-                            ),
+                            child: SizedBox(
+                              width: 48.w,
+                              height: 48.w,
+                              child: Center(
+                                child: SvgPicture.asset(
+                                  AssetsPaths.icChevronLeft,
+                                  colorFilter: ColorFilter.mode(
+                                    context.colors.primary,
+                                    BlendMode.srcIn,
+                                  ),
+                                  width: 24.w,
+                                  height: 24.w,
+                                  semanticsLabel: 'Back',
+                                ),
+                              ),
+                            ),

Alternatively, replace GestureDetector with an IconButton to get ripple and built-in a11y.

lib/ui/chat/chat_management/add_to_group_screen.dart (1)

124-132: Use layout units (w/r) instead of sp for icon size; add semantics label

Using .sp for icon dimensions ties icon size to text scale, which is unintended. Prefer .w or .r for icons. Also, add a semantics label for screen readers.

Apply this diff:

-                  icon: SvgPicture.asset(
-                    AssetsPaths.icChevronLeft,
-                    width: 24.sp,
-                    height: 24.sp,
-                    colorFilter: ColorFilter.mode(
-                      context.colors.primary,
-                      BlendMode.srcIn,
-                    ),
-                  ),
+                  icon: SvgPicture.asset(
+                    AssetsPaths.icChevronLeft,
+                    width: 24.w,
+                    height: 24.w,
+                    colorFilter: ColorFilter.mode(
+                      context.colors.primary,
+                      BlendMode.srcIn,
+                    ),
+                    semanticsLabel: 'Back',
+                  ),

Optional: add a tooltip to IconButton for better a11y on web/desktop:

  • tooltip: 'Back',
lib/ui/contact_list/widgets/user_profile.dart (1)

85-93: Use .w/.r for icon size instead of .sp; add semantics label

Icons should not scale with text size. Also, adding a semantics label improves a11y for the copy action.

Apply this diff:

-              icon: SvgPicture.asset(
-                AssetsPaths.icCopy,
-                width: 24.sp,
-                height: 24.sp,
-                colorFilter: ColorFilter.mode(
-                  context.colors.primary,
-                  BlendMode.srcIn,
-                ),
-              ),
+              icon: SvgPicture.asset(
+                AssetsPaths.icCopy,
+                width: 24.w,
+                height: 24.w,
+                colorFilter: ColorFilter.mode(
+                  context.colors.primary,
+                  BlendMode.srcIn,
+                ),
+                semanticsLabel: 'Copy public key',
+              ),

Optional: add tooltip: 'Copy public key', to IconButton.

lib/ui/contact_list/widgets/profile_ready_card.dart (3)

62-70: Enlarge tap target and add semantics for the dismiss (close) affordance

Current GestureDetector wraps a 20x20 icon which is a small hit area. Consider enlarging the tap target and labeling for screen readers.

Apply this diff:

-                child: SvgPicture.asset(
-                  AssetsPaths.icClose,
-                  width: 20.w,
-                  height: 20.w,
-                  colorFilter: ColorFilter.mode(
-                    context.colors.mutedForeground,
-                    BlendMode.srcIn,
-                  ),
-                ),
+                child: SizedBox(
+                  width: 48.w,
+                  height: 48.w,
+                  child: Center(
+                    child: SvgPicture.asset(
+                      AssetsPaths.icClose,
+                      width: 20.w,
+                      height: 20.w,
+                      colorFilter: ColorFilter.mode(
+                        context.colors.mutedForeground,
+                        BlendMode.srcIn,
+                      ),
+                      semanticsLabel: 'Dismiss',
+                    ),
+                  ),
+                ),

Alternatively, switch to an IconButton for built-in semantics and splash.


103-111: Mark decorative icon as excluded from semantics

There is adjacent text; excluding the icon avoids double announcements by screen readers.

Apply this diff:

                 SvgPicture.asset(
                   AssetsPaths.icQrCode,
                   width: 16.w,
                   height: 16.w,
                   colorFilter: ColorFilter.mode(
                     context.colors.primary,
                     BlendMode.srcIn,
                   ),
+                  excludeFromSemantics: true,
                 ),

134-142: Mark decorative icon as excluded from semantics

Same rationale: text already conveys meaning; keep a11y concise.

Apply this diff:

                 SvgPicture.asset(
                   AssetsPaths.icUserFollow,
                   width: 16.w,
                   height: 16.w,
                   colorFilter: ColorFilter.mode(
                     context.colors.primaryForeground,
                     BlendMode.srcIn,
                   ),
+                  excludeFromSemantics: true,
                 ),
lib/ui/chat/chat_info/widgets/group_member_bottom_sheet.dart (1)

122-130: Add semantics label (and optional tooltip) to close button

Improve a11y by labeling the close icon. Tooltips help on web/desktop.

Apply this diff:

               IconButton(
-                icon: SvgPicture.asset(
+                icon: SvgPicture.asset(
                   AssetsPaths.icClose,
                   width: 24.w,
                   height: 24.w,
                   colorFilter: ColorFilter.mode(
                     context.colors.mutedForeground,
                     BlendMode.srcIn,
                   ),
+                  semanticsLabel: 'Close',
                 ),
                 onPressed: () => Navigator.pop(context),
               ),

Optional: add tooltip: 'Close', to the IconButton.

lib/ui/contact_list/widgets/contact_list_tile.dart (1)

180-188: Add accessibility label to the trash icon

Expose a semanticsLabel so TalkBack/VoiceOver can announce the action when swiping.

Apply this diff:

           child: SvgPicture.asset(
             AssetsPaths.icTrashCan,
             colorFilter: const ColorFilter.mode(
               Colors.white,
               BlendMode.srcIn,
             ),
             width: 24.w,
             height: 24.w,
+            semanticsLabel: 'Delete',
           ),
lib/ui/contact_list/chat_list_screen.dart (2)

360-364: Tint search icon to match theme colors

The previous Icon inherited color from the theme. Without a colorFilter, the SVG may not adapt to light/dark themes. Apply a colorFilter consistent with your input styling.

Apply this diff:

-                                prefixIcon: SvgPicture.asset(
-                                  AssetsPaths.icSearch,
-                                  width: 24.w,
-                                  height: 24.w,
-                                ),
+                                prefixIcon: SvgPicture.asset(
+                                  AssetsPaths.icSearch,
+                                  width: 24.w,
+                                  height: 24.w,
+                                  colorFilter: ColorFilter.mode(
+                                    context.colors.mutedForeground,
+                                    BlendMode.srcIn,
+                                  ),
+                                ),

373-377: Also tint the clear (close) icon

Ensure consistent theming for the suffixIcon.

Apply this diff:

-                                  child: SvgPicture.asset(
-                                    AssetsPaths.icClose,
-                                    width: 24.w,
-                                    height: 24.w,
-                                  ),
+                                  child: SvgPicture.asset(
+                                    AssetsPaths.icClose,
+                                    width: 24.w,
+                                    height: 24.w,
+                                    colorFilter: ColorFilter.mode(
+                                      context.colors.mutedForeground,
+                                      BlendMode.srcIn,
+                                    ),
+                                  ),
lib/ui/settings/network/widgets/relay_expansion_tile.dart (3)

99-107: Use width/height scaling (.w/.h) instead of .sp for icons

.sp is intended for text. For icon dimensions, prefer .w/.h to keep responsiveness consistent with other widgets.

Apply this diff:

-              child: SvgPicture.asset(
+              child: SvgPicture.asset(
                 AssetsPaths.icHelp,
                 colorFilter: ColorFilter.mode(
                   context.colors.mutedForeground,
                   BlendMode.srcIn,
                 ),
-                width: 18.sp,
-                height: 18.sp,
+                width: 18.w,
+                height: 18.w,
               ),

99-107: Increase tap target for accessibility

Wrap the icon with padding to approach the 48dp recommended minimum tap area.

Apply this diff:

-              child: SvgPicture.asset(
-                AssetsPaths.icHelp,
-                colorFilter: ColorFilter.mode(
-                  context.colors.mutedForeground,
-                  BlendMode.srcIn,
-                ),
-                width: 18.sp,
-                height: 18.sp,
-              ),
+              child: Padding(
+                padding: EdgeInsets.all(8.w),
+                child: SvgPicture.asset(
+                  AssetsPaths.icHelp,
+                  colorFilter: ColorFilter.mode(
+                    context.colors.mutedForeground,
+                    BlendMode.srcIn,
+                  ),
+                  width: 18.w,
+                  height: 18.w,
+                  semanticsLabel: 'About this section',
+                ),
+              ),

112-120: Align add icon sizing with .w/.h and improve tap target

Same rationale as the help icon—use .w and provide padding for better usability.

Apply this diff:

-            child: SvgPicture.asset(
-              AssetsPaths.icAdd,
-              colorFilter: ColorFilter.mode(
-                context.colors.primary,
-                BlendMode.srcIn,
-              ),
-              width: 23.sp,
-              height: 23.sp,
-            ),
+            child: Padding(
+              padding: EdgeInsets.all(8.w),
+              child: SvgPicture.asset(
+                AssetsPaths.icAdd,
+                colorFilter: ColorFilter.mode(
+                  context.colors.primary,
+                  BlendMode.srcIn,
+                ),
+                width: 23.w,
+                height: 23.w,
+                semanticsLabel: 'Add relay',
+              ),
+            ),
lib/ui/chat/widgets/reaction/reaction_menu_item.dart (1)

6-6: Typo in comment: contsructor → constructor

Minor nit to keep comments clean.

Apply this diff:

-  // contsructor
+  // constructor
lib/ui/contact_list/new_chat_bottom_sheet.dart (1)

385-393: Prefer .w/.h for icon sizing and confirm color choice

  • Use .w/.h instead of .sp for non-text dimensions.
  • If the prefix icon should be less prominent than primary, consider mutedForeground for parity with other search fields.

Apply this diff:

-            prefixIcon: SvgPicture.asset(
-              AssetsPaths.icSearch,
-              colorFilter: ColorFilter.mode(
-                context.colors.primary,
-                BlendMode.srcIn,
-              ),
-              width: 20.sp,
-              height: 20.sp,
-            ),
+            prefixIcon: SvgPicture.asset(
+              AssetsPaths.icSearch,
+              colorFilter: ColorFilter.mode(
+                context.colors.mutedForeground,
+                BlendMode.srcIn,
+              ),
+              width: 20.w,
+              height: 20.w,
+            ),
lib/ui/chat/widgets/chat_contact_avatar.dart (1)

102-109: Fallback avatar: good switch to SVG; mark decorative to improve a11y.

Since this is a non-interactive, purely decorative fallback, exclude it from semantics to avoid screen readers announcing “image”.

-        child: SvgPicture.asset(
+        child: SvgPicture.asset(
           AssetsPaths.icUser,
           width: size * 0.4,
           height: size * 0.4,
+          excludeFromSemantics: true,
           colorFilter: ColorFilter.mode(
             context.colors.primary,
             BlendMode.srcIn,
           ),
         ),
lib/ui/settings/network/add_relay_bottom_sheet.dart (1)

214-221: Warning icon: mark decorative to avoid duplicate screen reader output.

The adjacent text conveys the error; mark the icon decorative to prevent it from being read twice.

-                    SvgPicture.asset(
+                    SvgPicture.asset(
                       AssetsPaths.icWarningFilled,
                       colorFilter: ColorFilter.mode(
                         context.colors.destructive,
                         BlendMode.srcIn,
                       ),
                       width: 16.w,
                       height: 16.w,
+                      excludeFromSemantics: true,
                     ),
lib/ui/chat/widgets/chat_audio_item.dart (2)

62-73: Add tooltip for play/pause button to improve accessibility.

IconButton without a tooltip provides minimal semantics. Adding a tooltip helps screen readers and long-press hints.

           child: IconButton(
             icon: SvgPicture.asset(
               isThisPlaying ? AssetsPaths.icPauseFilled : AssetsPaths.icPlayFilled,
               colorFilter: ColorFilter.mode(
                 isMe ? context.colors.primary : context.colors.primaryForeground,
                 BlendMode.srcIn,
               ),
               width: 20.sp,
               height: 20.sp,
             ),
-            onPressed: () => notifier.togglePlayback(),
+            tooltip: isThisPlaying ? 'Pause' : 'Play',
+            onPressed: () => notifier.togglePlayback(),
           ),

120-130: Microphone icon: mark decorative to avoid noisy semantics.

The icon is purely visual; exclude it from semantics.

-          SvgPicture.asset(
+          SvgPicture.asset(
             AssetsPaths.icMicrophone,
             colorFilter: ColorFilter.mode(
               (isMe ? context.colors.primaryForeground : context.colors.primary).withValues(
                 alpha: 0.7,
               ),
               BlendMode.srcIn,
             ),
             width: 16.sp,
             height: 16.sp,
+            excludeFromSemantics: true,
           ),
lib/ui/chat/widgets/swipe_to_reply_widget.dart (1)

89-96: Reply icon: consider RTL support and mark decorative.

  • Add matchTextDirection so arrows mirror in RTL.
  • Exclude from semantics to avoid redundant announcements.

Please verify the reply asset points to the correct direction in LTR; with matchTextDirection it will auto-mirror for RTL.

-                  child: SvgPicture.asset(
+                  child: SvgPicture.asset(
                     AssetsPaths.icReply,
                     colorFilter: ColorFilter.mode(
                       context.colors.primary,
                       BlendMode.srcIn,
                     ),
                     width: 16.w,
                     height: 16.w,
+                    matchTextDirection: true,
+                    excludeFromSemantics: true,
                   ),
lib/ui/settings/app_settings/app_settings_screen.dart (4)

218-226: Back chevron: add RTL support and semantics label.

  • matchTextDirection ensures the chevron mirrors in RTL.
  • semanticsLabel improves screen reader guidance. For best results, also consider wrapping the GestureDetector with a Semantics(button: true, label: 'Back') widget in a follow-up.
-                                child: SvgPicture.asset(
+                                child: SvgPicture.asset(
                                   AssetsPaths.icChevronLeft,
                                   width: 24.w,
                                   height: 24.w,
                                   colorFilter: ColorFilter.mode(
                                     context.colors.primary,
                                     BlendMode.srcIn,
                                   ),
+                                  matchTextDirection: true,
+                                  semanticsLabel: 'Back',
                                 ),

279-286: Trash icon inside labeled button: exclude from semantics.

The button already has a clear text label; skip announcing the icon.

-                                SvgPicture.asset(
+                                SvgPicture.asset(
                                   AssetsPaths.icTrashCan,
                                   width: 20.w,
                                   height: 20.w,
                                   colorFilter: ColorFilter.mode(
                                     context.colors.solidNeutralWhite,
                                     BlendMode.srcIn,
                                   ),
+                                  excludeFromSemantics: true,
                                 ),

364-371: Theme dropdown chevron: decorative icon should be excluded from semantics.

Prevents screen readers from redundantly announcing non-informative images.

-                SvgPicture.asset(
+                SvgPicture.asset(
                   isExpanded ? AssetsPaths.icChevronUp : AssetsPaths.icChevronDown,
                   width: 20.w,
                   height: 20.w,
                   colorFilter: ColorFilter.mode(
                     context.colors.primary,
                     BlendMode.srcIn,
                   ),
+                  excludeFromSemantics: true,
                 ),

36-36: Typo in dialog title.

“Delete app app data” has a duplicated word.

-            title: 'Delete app app data',
+            title: 'Delete app data',
lib/ui/settings/general_settings_screen.dart (4)

290-301: Add tooltip and semantics label to the back button for accessibility

IconButton doesn’t automatically convey a textual label when the icon is a custom widget. Provide a tooltip and semanticsLabel so screen readers announce “Back”.

Apply this diff:

 IconButton(
   onPressed: () => context.pop(),
+  tooltip: 'Back',
   icon: SvgPicture.asset(
     AssetsPaths.icChevronLeft,
     width: 24.w,
     height: 24.w,
+    semanticsLabel: 'Back',
     colorFilter: ColorFilter.mode(
       context.colors.primarySolid,
       BlendMode.srcIn,
     ),
   ),
 ),

327-335: Mark decorative QR icon as excluded from semantics

This trailing icon is decorative; exclude it from semantics to avoid noisy announcements.

 SvgPicture.asset(
   AssetsPaths.icQrCode,
   width: 20.w,
   height: 20.w,
+  excludeFromSemantics: true,
   colorFilter: ColorFilter.mode(
     context.colors.primary,
     BlendMode.srcIn,
   ),
 ),

357-365: Exclude decorative “switch” icon from semantics

Avoid redundant semantic output; the button’s text already conveys purpose.

 SvgPicture.asset(
   AssetsPaths.icArrowsVertical,
   width: 16.w,
   height: 16.w,
+  excludeFromSemantics: true,
   colorFilter: ColorFilter.mode(
     context.colors.primary,
     BlendMode.srcIn,
   ),
 ),

464-473: Exclude decorative leading icons from semantics in SettingsListTile

The row’s text represents the actionable label; exclude the icon from semantics.

 SvgPicture.asset(
   assetPath,
   width: 24.w,
   height: 24.w,
+  excludeFromSemantics: true,
   colorFilter: ColorFilter.mode(
     foregroundColor ?? context.colors.primary,
     BlendMode.srcIn,
   ),
 ),

Additionally, consider replacing GestureDetector with InkWell to get focus/hover/ink ripple and better built-in semantics.

Outside the selected lines, change the tappable wrapper:

// Before
return GestureDetector(
  onTap: onTap,
  child: Padding( ... ),
);

// After
return InkWell(
  onTap: onTap,
  child: Padding( ... ),
);
lib/ui/core/ui/wn_bottom_sheet.dart (2)

128-136: Add semantics label and tooltip to back button

Improve accessibility with a descriptive label and tooltip.

 IconButton(
   onPressed: () => Navigator.pop(context),
+  tooltip: 'Back',
   icon: SvgPicture.asset(
     AssetsPaths.icChevronLeft,
+    semanticsLabel: 'Back',
     colorFilter: ColorFilter.mode(
       context.colors.primary,
       BlendMode.srcIn,
     ),
     width: 24.w,
     height: 24.w,
   ),
 ),

156-166: Use IconButton for the close action with tooltip and semantics

GestureDetector lacks semantics and focus handling. IconButton improves accessibility.

-        if (showCloseButton)
-          GestureDetector(
-            onTap: () => Navigator.pop(context),
-            child: SvgPicture.asset(
-              AssetsPaths.icClose,
-              colorFilter: ColorFilter.mode(
-                context.colors.primary,
-                BlendMode.srcIn,
-              ),
-              width: 24.w,
-              height: 24.w,
-            ),
-          ),
+        if (showCloseButton)
+          IconButton(
+            onPressed: () => Navigator.pop(context),
+            tooltip: 'Close',
+            icon: SvgPicture.asset(
+              AssetsPaths.icClose,
+              semanticsLabel: 'Close',
+              colorFilter: ColorFilter.mode(
+                context.colors.primary,
+                BlendMode.srcIn,
+              ),
+              width: 24.w,
+              height: 24.w,
+            ),
+          ),
test/ui/core/ui/wn_callout_test.dart (1)

53-60: Avoid brittle assertions on SvgPicture.bytesLoader.toString()

Comparing toString() is fragile. Prefer a semantics-based assertion that doesn’t rely on internal loader details. Pair this with a semanticsLabel on the icon.

Apply this diff to the test:

-      expect(
-        find.byWidgetPredicate(
-          (widget) =>
-              widget is SvgPicture &&
-              widget.bytesLoader.toString().contains('assets/svgs/ic_info_filled.svg'),
-        ),
-        findsOneWidget,
-      );
+      expect(find.bySemanticsLabel('Information'), findsOneWidget);

And in WnCallout (see separate comment), add semanticsLabel: 'Information' to the icon.

lib/ui/settings/network/widgets/network_section.dart (1)

179-187: Exclude tiny status dot from semantics

The 8px status glyph is purely visual; exclude it from semantics to reduce verbosity.

 SvgPicture.asset(
   relay.status.getIconAsset(),
   width: 8.w,
   height: 8.w,
+  excludeFromSemantics: true,
   colorFilter: ColorFilter.mode(
     relay.status.getColor(context),
     BlendMode.srcIn,
   ),
 ),
lib/ui/core/ui/wn_callout.dart (1)

29-36: Add semantics label to the information icon for testability and accessibility

This enables robust tests and improves screen reader support.

 SvgPicture.asset(
   AssetsPaths.icInformation,
+  semanticsLabel: 'Information',
   colorFilter: ColorFilter.mode(
     context.colors.primary,
     BlendMode.srcIn,
   ),
   width: 18.w,
   height: 18.w,
 ),
lib/models/relay_status.dart (1)

73-88: Normalize asset constant naming (avoid mixing “...Svg” suffix with others)

icCheckmarkFilledSvg includes a “Svg” suffix, while others like icInProgress, icIconsTime, etc., do not. Consider standardizing naming in AssetsPaths (e.g., all without file-format suffix) to improve consistency and readability.

lib/ui/chat/widgets/reaction/reaction_default_data.dart (1)

22-41: Localize user-facing labels

Labels like 'Reply', 'Copy', 'Edit', and 'Delete' are user-visible. Consider sourcing them from AppLocalizations instead of hard-coding to keep UI translatable. Since DefaultData is const, one approach is to build these lists where BuildContext is available or provide a factory that returns localized items.

If helpful, I can draft a small helper (e.g., ReactionMenuDefaults.of(context)) that returns the localized lists.

lib/ui/chat/widgets/reaction/reactions_dialog_widget.dart (1)

136-146: Use .w/.h instead of .sp for SVG sizes

Using sp ties icon size to text scaling. For icons, prefer w/h to stay consistent with layout-based scaling.

Apply this diff:

-                            child: SvgPicture.asset(
-                              widget.menuItems[index].assetPath,
-                              width: 20.sp,
-                              height: 20.sp,
+                            child: SvgPicture.asset(
+                              widget.menuItems[index].assetPath,
+                              width: 20.w,
+                              height: 20.w,
                               colorFilter: ColorFilter.mode(
                                 widget.menuItems[index].isDestructive
                                     ? context.colors.destructive
                                     : context.colors.primary,
                                 BlendMode.srcIn,
                               ),
                             ),
lib/ui/settings/network/widgets/relay_tile.dart (1)

139-141: Prefer layout units for SVG icon sizing

Use w/h instead of sp so icon dimensions don’t track text scaling.

Apply this diff:

-            width: 23.sp,
-            height: 23.sp,
+            width: 23.w,
+            height: 23.w,
lib/ui/core/ui/wn_heads_up.dart (1)

39-47: Optional: Introduce a reusable WnSvgIcon to reduce duplication

The same “tinted SVG with width/height” pattern is repeated across the app. A tiny wrapper improves consistency and reduces boilerplate.

Apply this local change:

-          SvgPicture.asset(
-            iconAsset ?? type.iconAsset,
-            width: 24.w,
-            height: 24.w,
-            colorFilter: ColorFilter.mode(
-              color,
-              BlendMode.srcIn,
-            ),
-          ),
+          WnSvgIcon(
+            path: iconAsset ?? type.iconAsset,
+            size: 24.w,
+            color: color,
+          ),

And add a small helper widget somewhere common (e.g., lib/ui/core/ui/wn_svg_icon.dart):

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/flutter_svg.dart';

class WnSvgIcon extends StatelessWidget {
  const WnSvgIcon({
    super.key,
    required this.path,
    this.size,
    this.width,
    this.height,
    this.color,
    this.semanticLabel,
  }) : assert(size != null || (width != null && height != null));

  final String path;
  final double? size;
  final double? width;
  final double? height;
  final Color? color;
  final String? semanticLabel;

  @override
  Widget build(BuildContext context) {
    final double w = size ?? width!;
    final double h = size ?? height!;
    return SvgPicture.asset(
      path,
      width: w,
      height: h,
      colorFilter: color != null ? ColorFilter.mode(color!, BlendMode.srcIn) : null,
      semanticsLabel: semanticLabel,
    );
  }
}

This enables consistent sizing (size/width/height) and color usage across the codebase.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ca6342b and bb9911d.

⛔ Files ignored due to path filters (27)
  • assets/svgs/ic_arrows_vertical.svg is excluded by !**/*.svg
  • assets/svgs/ic_checkmark_filled.svg is excluded by !**/*.svg
  • assets/svgs/ic_chevron_down.svg is excluded by !**/*.svg
  • assets/svgs/ic_chevron_up.svg is excluded by !**/*.svg
  • assets/svgs/ic_close.svg is excluded by !**/*.svg
  • assets/svgs/ic_data_vis3.svg is excluded by !**/*.svg
  • assets/svgs/ic_development.svg is excluded by !**/*.svg
  • assets/svgs/ic_favorite.svg is excluded by !**/*.svg
  • assets/svgs/ic_icons_time.svg is excluded by !**/*.svg
  • assets/svgs/ic_in_progress.svg is excluded by !**/*.svg
  • assets/svgs/ic_information.svg is excluded by !**/*.svg
  • assets/svgs/ic_information_filled.svg is excluded by !**/*.svg
  • assets/svgs/ic_locked.svg is excluded by !**/*.svg
  • assets/svgs/ic_logout.svg is excluded by !**/*.svg
  • assets/svgs/ic_microphone.svg is excluded by !**/*.svg
  • assets/svgs/ic_moon.svg is excluded by !**/*.svg
  • assets/svgs/ic_password.svg is excluded by !**/*.svg
  • assets/svgs/ic_pause_filled.svg is excluded by !**/*.svg
  • assets/svgs/ic_play_filled.svg is excluded by !**/*.svg
  • assets/svgs/ic_radio_button.svg is excluded by !**/*.svg
  • assets/svgs/ic_reply.svg is excluded by !**/*.svg
  • assets/svgs/ic_settings.svg is excluded by !**/*.svg
  • assets/svgs/ic_trash_can.svg is excluded by !**/*.svg
  • assets/svgs/ic_user.svg is excluded by !**/*.svg
  • assets/svgs/ic_user_follow.svg is excluded by !**/*.svg
  • ios/Podfile.lock is excluded by !**/*.lock
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (31)
  • lib/models/relay_status.dart (2 hunks)
  • lib/ui/chat/chat_info/widgets/group_member_bottom_sheet.dart (1 hunks)
  • lib/ui/chat/chat_management/add_to_group_screen.dart (2 hunks)
  • lib/ui/chat/widgets/chat_audio_item.dart (3 hunks)
  • lib/ui/chat/widgets/chat_contact_avatar.dart (2 hunks)
  • lib/ui/chat/widgets/reaction/reaction_default_data.dart (2 hunks)
  • lib/ui/chat/widgets/reaction/reaction_menu_item.dart (1 hunks)
  • lib/ui/chat/widgets/reaction/reactions_dialog_widget.dart (1 hunks)
  • lib/ui/chat/widgets/swipe_to_reply_widget.dart (2 hunks)
  • lib/ui/contact_list/chat_list_screen.dart (2 hunks)
  • lib/ui/contact_list/new_chat_bottom_sheet.dart (1 hunks)
  • lib/ui/contact_list/widgets/contact_list_tile.dart (1 hunks)
  • lib/ui/contact_list/widgets/profile_ready_card.dart (4 hunks)
  • lib/ui/contact_list/widgets/user_profile.dart (2 hunks)
  • lib/ui/core/themes/assets.dart (2 hunks)
  • lib/ui/core/ui/wn_bottom_sheet.dart (3 hunks)
  • lib/ui/core/ui/wn_callout.dart (2 hunks)
  • lib/ui/core/ui/wn_heads_up.dart (4 hunks)
  • lib/ui/settings/app_settings/app_settings_screen.dart (5 hunks)
  • lib/ui/settings/donate/donate_screen.dart (2 hunks)
  • lib/ui/settings/general_settings_screen.dart (10 hunks)
  • lib/ui/settings/network/add_relay_bottom_sheet.dart (2 hunks)
  • lib/ui/settings/network/widgets/network_section.dart (1 hunks)
  • lib/ui/settings/network/widgets/relay_expansion_tile.dart (2 hunks)
  • lib/ui/settings/network/widgets/relay_tile.dart (4 hunks)
  • lib/ui/settings/nostr_keys/nostr_keys_screen.dart (2 hunks)
  • pubspec.yaml (0 hunks)
  • test/ui/contact_list/share_invite_bottom_sheet_test.dart (2 hunks)
  • test/ui/contact_list/start_chat_bottom_sheet_test.dart (3 hunks)
  • test/ui/contact_list/widgets/user_profile_test.dart (2 hunks)
  • test/ui/core/ui/wn_callout_test.dart (2 hunks)
💤 Files with no reviewable changes (1)
  • pubspec.yaml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.dart

📄 CodeRabbit Inference Engine (.cursor/rules/flutter.mdc)

**/*.dart: Always declare the type of each variable and function (parameters and return value). Avoid using 'any'. Create necessary types.
Don't leave blank lines within a function.
One export per file.
Use PascalCase for classes.
Use camelCase for variables, functions, and methods.
Use underscores_case for file and directory names.
Use UPPERCASE for environment variables. Avoid magic numbers and define constants.
Start each function with a verb.
Use verbs for boolean variables. Example: isLoading, hasError, canDelete, etc.
Use complete words instead of abbreviations and correct spelling, except for standard and well-known abbreviations (API, URL, i, j, err, ctx, req, res, next).
Write short functions with a single purpose. Less than 20 instructions.
Name functions with a verb and something else. If it returns a boolean, use isX or hasX, canX, etc. If it doesn't return anything, use executeX or saveX, etc.
Avoid nesting blocks by early checks and returns, or extraction to utility functions.
Use higher-order functions (map, filter, reduce, etc.) to avoid function nesting. Use arrow functions for simple functions (less than 3 instructions). Use named functions for non-simple functions.
Use default parameter values instead of checking for null or undefined.
Reduce function parameters using RO-RO: use an object to pass multiple parameters and to return results. Declare necessary types for input arguments and output.
Use a single level of abstraction in functions.
Don't abuse primitive types and encapsulate data in composite types.
Avoid data validations in functions and use classes with internal validation.
Prefer immutability for data. Use readonly for data that doesn't change. Use 'as const' for literals that don't change.
Declare interfaces to define contracts.
Write small classes with a single purpose. Less than 200 instructions, less than 10 public methods, less than 10 properties.
Use exceptions to handle errors you don't expect. If you catch an exception, it sh...

Files:

  • lib/ui/contact_list/widgets/contact_list_tile.dart
  • lib/ui/chat/widgets/chat_contact_avatar.dart
  • lib/ui/settings/donate/donate_screen.dart
  • lib/ui/settings/network/add_relay_bottom_sheet.dart
  • test/ui/contact_list/share_invite_bottom_sheet_test.dart
  • lib/ui/contact_list/widgets/profile_ready_card.dart
  • lib/ui/settings/nostr_keys/nostr_keys_screen.dart
  • lib/ui/chat/widgets/swipe_to_reply_widget.dart
  • test/ui/contact_list/widgets/user_profile_test.dart
  • lib/ui/settings/network/widgets/relay_expansion_tile.dart
  • test/ui/contact_list/start_chat_bottom_sheet_test.dart
  • lib/ui/contact_list/widgets/user_profile.dart
  • lib/ui/contact_list/new_chat_bottom_sheet.dart
  • lib/ui/contact_list/chat_list_screen.dart
  • lib/ui/chat/chat_info/widgets/group_member_bottom_sheet.dart
  • lib/ui/core/ui/wn_callout.dart
  • lib/ui/core/ui/wn_bottom_sheet.dart
  • lib/ui/chat/chat_management/add_to_group_screen.dart
  • lib/ui/chat/widgets/chat_audio_item.dart
  • lib/models/relay_status.dart
  • lib/ui/settings/general_settings_screen.dart
  • test/ui/core/ui/wn_callout_test.dart
  • lib/ui/chat/widgets/reaction/reaction_menu_item.dart
  • lib/ui/core/themes/assets.dart
  • lib/ui/settings/app_settings/app_settings_screen.dart
  • lib/ui/core/ui/wn_heads_up.dart
  • lib/ui/settings/network/widgets/relay_tile.dart
  • lib/ui/settings/network/widgets/network_section.dart
  • lib/ui/chat/widgets/reaction/reactions_dialog_widget.dart
  • lib/ui/chat/widgets/reaction/reaction_default_data.dart
**/*_test.dart

📄 CodeRabbit Inference Engine (.cursor/rules/flutter.mdc)

**/*_test.dart: Follow the Arrange-Act-Assert convention for tests.
Name test variables clearly. Follow the convention: inputX, mockX, actualX, expectedX, etc.
Write unit tests for each public function. Use test doubles to simulate dependencies, except for third-party dependencies that are not expensive to execute.
Write acceptance tests for each module. Follow the Given-When-Then convention.
Use the standard widget testing for Flutter.

Files:

  • test/ui/contact_list/share_invite_bottom_sheet_test.dart
  • test/ui/contact_list/widgets/user_profile_test.dart
  • test/ui/contact_list/start_chat_bottom_sheet_test.dart
  • test/ui/core/ui/wn_callout_test.dart
🧠 Learnings (6)
📚 Learning: 2025-08-08T13:39:00.500Z
Learnt from: CR
PR: parres-hq/whitenoise_flutter#0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-08-08T13:39:00.500Z
Learning: Applies to **/*.dart : Use flutter_rust_bridge to access core functionality of the app.

Applied to files:

  • lib/ui/contact_list/widgets/contact_list_tile.dart
  • lib/ui/settings/donate/donate_screen.dart
  • lib/ui/contact_list/widgets/profile_ready_card.dart
  • lib/ui/settings/nostr_keys/nostr_keys_screen.dart
  • lib/ui/settings/network/widgets/relay_expansion_tile.dart
  • lib/ui/chat/chat_management/add_to_group_screen.dart
  • lib/ui/settings/general_settings_screen.dart
  • lib/ui/settings/app_settings/app_settings_screen.dart
  • lib/ui/settings/network/widgets/relay_tile.dart
📚 Learning: 2025-08-08T13:39:00.500Z
Learnt from: CR
PR: parres-hq/whitenoise_flutter#0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-08-08T13:39:00.500Z
Learning: Applies to **/*.dart : Use the whitenoise rust crate (via flutter_rust_bridge) as the source of all data and only way to trigger changes in our data model.

Applied to files:

  • lib/ui/contact_list/widgets/contact_list_tile.dart
  • lib/ui/chat/chat_management/add_to_group_screen.dart
  • lib/ui/settings/general_settings_screen.dart
  • lib/ui/settings/app_settings/app_settings_screen.dart
📚 Learning: 2025-08-08T13:39:00.500Z
Learnt from: CR
PR: parres-hq/whitenoise_flutter#0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-08-08T13:39:00.500Z
Learning: Applies to **/*.dart : Use Riverpod to manage state. Use StreamProviders to watch for state changes that come from the rust api. See keepAlive if you need to keep the state alive.

Applied to files:

  • lib/ui/settings/donate/donate_screen.dart
  • lib/ui/settings/nostr_keys/nostr_keys_screen.dart
  • lib/ui/settings/network/widgets/relay_expansion_tile.dart
  • lib/ui/settings/general_settings_screen.dart
  • lib/ui/settings/app_settings/app_settings_screen.dart
  • lib/ui/settings/network/widgets/relay_tile.dart
📚 Learning: 2025-08-08T13:39:00.500Z
Learnt from: CR
PR: parres-hq/whitenoise_flutter#0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-08-08T13:39:00.500Z
Learning: Applies to **/*_test.dart : Use the standard widget testing for Flutter.

Applied to files:

  • test/ui/contact_list/share_invite_bottom_sheet_test.dart
  • test/ui/contact_list/widgets/user_profile_test.dart
  • test/ui/contact_list/start_chat_bottom_sheet_test.dart
  • test/ui/core/ui/wn_callout_test.dart
📚 Learning: 2025-08-08T13:39:00.500Z
Learnt from: CR
PR: parres-hq/whitenoise_flutter#0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-08-08T13:39:00.500Z
Learning: Applies to **/*.dart : Use extensions to manage reusable code.

Applied to files:

  • lib/ui/chat/chat_info/widgets/group_member_bottom_sheet.dart
📚 Learning: 2025-08-08T13:39:00.500Z
Learnt from: CR
PR: parres-hq/whitenoise_flutter#0
File: .cursor/rules/flutter.mdc:0-0
Timestamp: 2025-08-08T13:39:00.500Z
Learning: Applies to **/*.dart : Use ThemeData to manage themes.

Applied to files:

  • lib/ui/settings/app_settings/app_settings_screen.dart
⏰ 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). (1)
  • GitHub Check: Flutter CI
🔇 Additional comments (32)
lib/ui/core/themes/assets.dart (1)

33-33: Asset ‘ic_information_filled.svg’ is present and covered by pubspec.yaml
The file assets/svgs/ic_information_filled.svg exists, and the assets/svgs/ directory is declared in pubspec.yaml, so no runtime errors will occur.

test/ui/contact_list/share_invite_bottom_sheet_test.dart (1)

2-2: LGTM: flutter_svg import aligns tests with SvgPicture usage

lib/ui/settings/donate/donate_screen.dart (1)

5-5: LGTM: flutter_svg import required for SvgPicture.asset

test/ui/contact_list/widgets/user_profile_test.dart (1)

3-3: LGTM: flutter_svg import aligns tests with SvgPicture usage

test/ui/contact_list/start_chat_bottom_sheet_test.dart (1)

2-2: LGTM: flutter_svg import aligns tests with SvgPicture usage

lib/ui/settings/nostr_keys/nostr_keys_screen.dart (1)

5-5: SVG import addition looks good

Importing flutter_svg aligns with the PR objective and enables asset-based icon rendering.

lib/ui/chat/chat_management/add_to_group_screen.dart (1)

3-3: Imports for SVG assets are appropriate

Adding flutter_svg and assets.dart matches the migration away from CarbonIcons.

Also applies to: 9-9

lib/ui/contact_list/widgets/user_profile.dart (1)

4-4: SVG migration imports look good

Importing flutter_svg and assets paths is consistent with the new icon strategy.

Also applies to: 7-7

lib/ui/contact_list/widgets/profile_ready_card.dart (1)

4-4: Imports match the SVG asset migration

Bringing in flutter_svg and assets.dart is correct for the new icon approach.

Also applies to: 10-10

lib/ui/contact_list/widgets/contact_list_tile.dart (1)

180-188: SVG migration for swipe-to-delete looks good

Using SvgPicture with white colorFilter against a red background mirrors the previous Icon behavior and keeps contrast high.

lib/ui/settings/network/widgets/relay_expansion_tile.dart (1)

4-4: Imports for flutter_svg and AssetsPaths are appropriate

These imports align with the SVG asset migration.

Also applies to: 8-8

lib/ui/chat/widgets/reaction/reaction_menu_item.dart (1)

3-3: No remaining MenuItem.icon usages found

A repository-wide search for both MenuItem(icon: ...) and .icon references returned no matches, confirming all call sites have been updated for the constructor change.

lib/ui/chat/widgets/chat_contact_avatar.dart (1)

3-4: Imports updated for SVG assets — LGTM.

Switch to flutter_svg and AssetsPaths aligns with the repo-wide migration.

lib/ui/settings/network/add_relay_bottom_sheet.dart (1)

8-8: SVG import — LGTM.

Consistent with the migration away from CarbonIcons.

lib/ui/chat/widgets/chat_audio_item.dart (1)

4-5: SVG imports — LGTM.

Aligns with asset-based icon usage.

lib/ui/chat/widgets/swipe_to_reply_widget.dart (1)

4-4: SVG import — LGTM.

Migration step looks correct.

lib/ui/settings/app_settings/app_settings_screen.dart (3)

5-5: SVG import — LGTM.

Consistent with SVG assets across the app.


20-20: Assets import — LGTM.

Correctly references AssetsPaths for icon assets.


5-5: All CarbonIcons references successfully removed

The repository scan returned no instances of CarbonIcons or supa_carbon_icons. No further action is needed.

lib/ui/settings/general_settings_screen.dart (3)

4-4: Migration to flutter_svg and centralized AssetsPaths looks good

Using SvgPicture with centralized AssetsPaths is consistent and maintainable. ColorFilter with BlendMode.srcIn preserves theming correctly.

Also applies to: 20-20


378-399: Settings tiles migration looks consistent

All tiles now pass assetPath and use SvgPicture with colorFilter. Consistent with the app-wide migration.


4-4: No remaining CarbonIcons references found—migration complete

Ran a repo-wide search for any lingering CarbonIcons or supa_carbon_icons usages and found none. Verified that:

  • flutter_svg: ^2.1.0 is declared in pubspec.yaml
  • assets/svgs/ (and assets/pngs/) are registered under assets:

All CarbonIcons references have been removed and the SVG assets pipeline is fully configured.

lib/ui/core/ui/wn_bottom_sheet.dart (1)

4-4: Migration to SVG assets is consistent

Imports updated to flutter_svg and asset paths centralized. Header icon rendering matches the rest of the app.

Also applies to: 7-7

lib/ui/settings/network/widgets/network_section.dart (1)

179-187: Nice switch to asset-based relay status icon

Using getIconAsset() with colorFilter keeps theme colors consistent and removes icon font dependency.

lib/ui/core/ui/wn_callout.dart (1)

3-6: Asset-based icon migration looks correct

Imports updated to flutter_svg and AssetsPaths; colorization matches the previous Icon usage.

lib/models/relay_status.dart (2)

70-89: API change to asset-path icons looks consistent

Switching from IconData to asset-paths via getIconAsset() is coherent with the SVG migration. The switch covers all enum cases and keeps color logic separate. LGTM.


70-89: All getIcongetIconAsset usages updated – no residual references found

Ran the scripted searches across the repo and confirmed:

  • No lingering getIcon( calls
  • No WnStickyHeadsUp(icon: …) parameters
  • No leftover CarbonIcons or supa_carbon_icons references
  • All asset constants point to existing files

This public‐API rename is fully propagated and safe to merge.

lib/ui/chat/widgets/reaction/reaction_default_data.dart (1)

22-41: LGTM: MenuItem migration to assetPath

Replacing IconData with assetPath across the default items is clean and matches the new rendering path.

lib/ui/settings/network/widgets/relay_tile.dart (2)

56-64: LGTM: Dialog close icon migrated to SVG

The new SvgPicture.asset(AssetsPaths.icClose) with colorFilter and sizing is consistent with the migration.


114-122: LGTM: Status icon now uses asset path + color

Using status.getIconAsset() with colorFilter preserves the previous color semantics while aligning with SVG assets.

lib/ui/core/ui/wn_heads_up.dart (2)

39-47: LGTM: Switched to SvgPicture with tint

Rendering via SvgPicture.asset(iconAsset ?? type.iconAsset, ...) with ColorFilter.mode(..., BlendMode.srcIn) is consistent with the new asset approach.


13-19: All usages of the old icon API have been updated
No instances of the old WnStickyHeadsUp(icon: …) parameter or WnHeadingType.icon getter remain in the lib directory. The breaking change to iconAsset is fully applied.

codeswot
codeswot previously approved these changes Aug 15, 2025
Copy link
Contributor

@josefinalliende josefinalliende left a comment

Choose a reason for hiding this comment

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

Great job! 🚀 🎉 Thank you for doing this one!! Reviewed it locally and overall it is working really well!

💡 I think that the idea mentioned in the signal chat of having an icon component is great, but definitely would leave that for other issue. It could be mayb e a subissue of the issue you are solving now (and someone else can take it, I am not saying thise for you to take it if you maybe need/have/want to do something else now). If you are ok with this just let me know and I can create the sub-issue.

Last but not least, I caught a little bug in some sizes and colors, left screenshots of the problems I found.

caught-little-bug

Copy link
Contributor

Choose a reason for hiding this comment

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

By reviewing this code, I notice that this file it is used in lots of places as ContactAvatar, I think that maybe (for another issue) we should move this one to the core/ui folder and add the Wn prefix to it. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

Opened issue #489

import 'package:whitenoise/ui/core/themes/assets.dart';
import 'package:whitenoise/ui/core/themes/src/app_theme.dart';

class WnStickyHeadsUp extends StatelessWidget {
Copy link
Contributor

Choose a reason for hiding this comment

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

Oopps probably my bad here when adding Wn prefix to existing components, but this file name does not match the widget name. Can be fixed in other issue, just commenting it here to not forget to fix it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Opened a really tiny PR to fix this one! #490

@untreu2
Copy link
Contributor Author

untreu2 commented Aug 15, 2025

You can create a sub-issue and assign me. And let me fix the bugs that you found so we can merge this first.
@josefinalliende

@untreu2
Copy link
Contributor Author

untreu2 commented Aug 15, 2025

@josefinalliende I fixed the sizing/coloring bugs and removed audio-related files.

image image image

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
lib/ui/settings/network/network_screen.dart (1)

156-157: Add semantics label (and optional tooltip) for accessibility.

Screen readers will announce a meaningful label. Minor but worthwhile.

Apply this diff within the SvgPicture.asset to add a semantics label:

-                            width: 24.w,
-                            height: 24.w,
+                            semanticsLabel: 'Back',
+                            width: 24.w,
+                            height: 24.w,

Optionally, add a tooltip to IconButton (outside the selected lines) for web/desktop hover:

IconButton(
  tooltip: 'Back',
  onPressed: () => Navigator.of(context).pop(),
  icon: SvgPicture.asset(
    // ...
  ),
)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e599b47 and 377adea.

📒 Files selected for processing (3)
  • lib/ui/contact_list/chat_list_screen.dart (2 hunks)
  • lib/ui/contact_list/new_chat_bottom_sheet.dart (1 hunks)
  • lib/ui/settings/network/network_screen.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/ui/contact_list/new_chat_bottom_sheet.dart
  • lib/ui/contact_list/chat_list_screen.dart
🧰 Additional context used
📓 Path-based instructions (1)
**/*.dart

📄 CodeRabbit Inference Engine (.cursor/rules/flutter.mdc)

**/*.dart: Always declare the type of each variable and function (parameters and return value). Avoid using 'any'. Create necessary types.
Don't leave blank lines within a function.
One export per file.
Use PascalCase for classes.
Use camelCase for variables, functions, and methods.
Use underscores_case for file and directory names.
Use UPPERCASE for environment variables. Avoid magic numbers and define constants.
Start each function with a verb.
Use verbs for boolean variables. Example: isLoading, hasError, canDelete, etc.
Use complete words instead of abbreviations and correct spelling, except for standard and well-known abbreviations (API, URL, i, j, err, ctx, req, res, next).
Write short functions with a single purpose. Less than 20 instructions.
Name functions with a verb and something else. If it returns a boolean, use isX or hasX, canX, etc. If it doesn't return anything, use executeX or saveX, etc.
Avoid nesting blocks by early checks and returns, or extraction to utility functions.
Use higher-order functions (map, filter, reduce, etc.) to avoid function nesting. Use arrow functions for simple functions (less than 3 instructions). Use named functions for non-simple functions.
Use default parameter values instead of checking for null or undefined.
Reduce function parameters using RO-RO: use an object to pass multiple parameters and to return results. Declare necessary types for input arguments and output.
Use a single level of abstraction in functions.
Don't abuse primitive types and encapsulate data in composite types.
Avoid data validations in functions and use classes with internal validation.
Prefer immutability for data. Use readonly for data that doesn't change. Use 'as const' for literals that don't change.
Declare interfaces to define contracts.
Write small classes with a single purpose. Less than 200 instructions, less than 10 public methods, less than 10 properties.
Use exceptions to handle errors you don't expect. If you catch an exception, it sh...

Files:

  • lib/ui/settings/network/network_screen.dart
⏰ 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). (1)
  • GitHub Check: Flutter CI
🔇 Additional comments (1)
lib/ui/settings/network/network_screen.dart (1)

156-157: Explicit SVG sizing is correct and consistent with the migration. LGTM.

Adding width and height avoids relying on IconButton.iconSize with a custom child and keeps layout stable. This aligns with the PR’s SVG migration pattern.

Copy link
Contributor

@josefinalliende josefinalliende left a comment

Choose a reason for hiding this comment

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

Amaziiing!! 🎉 ✅ 🎉 Tried it locally again and it looks perfect!

This was referenced Aug 16, 2025
@untreu2 untreu2 merged commit 09e3295 into master Aug 16, 2025
2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Aug 16, 2025
9 tasks
@untreu2 untreu2 deleted the carbon-icons-svg branch August 17, 2025 20:35
@coderabbitai coderabbitai bot mentioned this pull request Aug 21, 2025
11 tasks
@coderabbitai coderabbitai bot mentioned this pull request Oct 10, 2025
11 tasks
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.

Migrate all icons to SVG and remove Carbon icons

4 participants