Skip to content

Conversation

@codeswot
Copy link
Contributor

@codeswot codeswot commented Oct 17, 2025

Description

Add group profile image support

Implements automatic loading and display of group profile images across the app.

What's Changed

Backend Integration

  • Added groupImagePaths map to GroupsState to cache image file paths for all groups
  • Implemented _loadGroupImagePaths() method that fetches images using getGroupImagePath API
  • Images load in parallel during group initialization for optimal performance
  • Added getCachedGroupImagePath() getter for synchronous access to cached image paths

UI Updates

  • Chat screen: Group avatars now display in chat headers (previously only showed for direct messages)
  • Group info screen: Group profile images now appear on the group info page
  • Chat header widget: Updated to show group images where applicable

Technical Details

  • Image paths are loaded when groups are first loaded and when new groups are detected
  • The getGroupDisplayImage() helper now returns cached group images for regular groups
  • All changes include proper error handling with graceful fallbacks to prevent loading failures from blocking the UI

Files Changed

  • lib/config/providers/group_provider.dart - Added image loading logic
  • lib/config/states/group_state.dart - Added groupImagePaths field
  • lib/ui/chat/chat_screen.dart - Display group images in chat header
  • lib/ui/chat/chat_info/group_chat_info.dart - Display group images on info page
  • lib/ui/chat/widgets/chat_header_widget.dart - Updated to support group images

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
  • 🧪 Tests

Checklist

  • Run just precommit to ensure that formatting and linting are correct
  • Run just check-flutter-coverage to ensure that flutter coverage rules are passing
  • Updated the CHANGELOG.md file with your changes (if they affect the user experience)

Summary by CodeRabbit

  • New Features

    • Group images now display in chat headers and conversation screens for easier identification.
    • Previously loaded group images are cached for faster display.
  • Bug Fixes / Improvements

    • Avatar rendering now reliably uses cached group images with a safe fallback when unavailable.
    • Background subscription handling improved to update images and avoid memory leaks.

@codeswot codeswot self-assigned this Oct 17, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2025

Walkthrough

Adds cached group image path support: state gains a groupImagePaths map, GroupsNotifier loads image paths in parallel with error handling and exposes getCachedGroupImagePath; UI components read the cached path to display group avatars instead of empty placeholders.

Changes

Cohort / File(s) Summary
State Definition
lib/config/states/group_state.dart, lib/config/states/group_state.freezed.dart
Added optional groupImagePaths: Map<String, String>? to GroupsState; integrated into the freezed implementation (getter, copyWith, equality, hashCode, constructor).
Provider Logic
lib/config/providers/group_provider.dart
Added _loadGroupImagePaths(List<Group> groups) to load and cache image paths in parallel with error handling/logging; added synchronous String? getCachedGroupImagePath(String groupId) on GroupsNotifier and same accessor on GroupMemberUtils extension.
UI Components
lib/ui/chat/chat_info/group_chat_info.dart, lib/ui/chat/chat_screen.dart, lib/ui/chat/widgets/chat_header_widget.dart, lib/ui/chat/chat_info/chat_info_screen.dart
Replaced hard-coded empty avatar URLs with dynamic cached group image paths via groupsNotifier.getCachedGroupImagePath(groupId); added manual provider subscription in group_chat_info.dart and disposed subscription on widget teardown.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Provider as GroupsNotifier
    participant State as GroupsState
    participant UI as UI Components

    App->>Provider: setGroups(groups)
    activate Provider
    Provider->>Provider: _loadGroupImagePaths(groups) (parallel)
    Note over Provider: fetch image path per group<br/>catch/log errors, continue
    Provider->>State: update groupImagePaths map
    deactivate Provider

    UI->>Provider: getCachedGroupImagePath(groupId)
    activate Provider
    Provider-->>UI: return path or null
    deactivate Provider

    UI->>UI: render avatar with path (or fallback empty)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • erskingardner
  • untreu2
  • josefinalliende
  • Quwaysim

Poem

🐰 I hopped through maps and code so spry,

Cached group faces under sky,
Parallel fetch, no error scold,
Avatars bloom from paths I hold,
A joyful hop — the UI smiles!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Patch group profile image" is concise, clear, and directly aligned with the main objective of the changeset. The title accurately captures the primary change: implementing automatic loading and display of group profile images throughout the app. The term "patch" appropriately conveys the addition and update of this functionality. A teammate reviewing the commit history would immediately understand that this PR addresses group profile image handling without confusion.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch patch-group-profile-image

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 1

🧹 Nitpick comments (1)
lib/ui/chat/chat_info/group_chat_info.dart (1)

130-130: Consider using ref.read instead of ref.watch for notifier access.

Since you only need the notifier's methods and not reactive updates (the ref.listen on line 131 handles updates), you could optimize this line to use ref.read(groupsProvider.notifier) instead of ref.watch(groupsProvider.notifier).

-    final groupsNotifier = ref.watch(groupsProvider.notifier);
+    final groupsNotifier = ref.read(groupsProvider.notifier);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61d76d4 and 3cf0bb3.

⛔ Files ignored due to path filters (1)
  • ios/Podfile.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • lib/config/providers/group_provider.dart (6 hunks)
  • lib/config/states/group_state.dart (1 hunks)
  • lib/config/states/group_state.freezed.dart (14 hunks)
  • lib/ui/chat/chat_info/group_chat_info.dart (3 hunks)
  • lib/ui/chat/chat_screen.dart (1 hunks)
  • lib/ui/chat/widgets/chat_header_widget.dart (1 hunks)
🧰 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 dynamic and Object without justification
Create necessary types instead of overusing primitives or dynamic
One export per file
Use PascalCase for classes
Use camelCase for variables, functions, and methods
Avoid magic numbers and define constants
Start each function name with a verb
Use verbs for boolean variables (e.g., isLoading, hasError, canDelete)
Write short functions with a single purpose (under ~20 instructions)
Name functions with a verb plus context; for booleans use isX/hasX/canX; for void use executeX/saveX
Avoid deep nesting via early returns and extraction to utility functions
Use higher-order functions (map, where/filter, reduce) to avoid nesting
Use arrow functions for simple functions (under ~3 statements); use named functions otherwise
Use default parameter values instead of null checks
Reduce function parameters using RO-RO: pass/return parameter objects with declared types
Maintain a single level of abstraction within functions
Encapsulate data in composite types; avoid overusing primitives
Prefer validating data within classes rather than in functions
Prefer immutability; use final for runtime constants and const for compile-time constants
Use const constructors and const literals where possible
Follow SOLID principles
Prefer composition over inheritance
Declare interfaces (abstract classes) to define contracts
Write small classes with a single purpose (under ~200 instructions, <10 public methods, <10 properties)
Use exceptions for unexpected errors
Only catch exceptions to fix expected problems or add context; otherwise use a global handler

Files:

  • lib/config/states/group_state.freezed.dart
  • lib/config/states/group_state.dart
  • lib/config/providers/group_provider.dart
  • lib/ui/chat/widgets/chat_header_widget.dart
  • lib/ui/chat/chat_info/group_chat_info.dart
  • lib/ui/chat/chat_screen.dart
lib/**/*.dart

📄 CodeRabbit inference engine (.cursor/rules/flutter.mdc)

lib/**/*.dart: Use flutter_rust_bridge to access core app functionality
Use Riverpod for state management; prefer StreamProviders for Rust API streams; use keepAlive if needed
Use freezed to model/manage UI states
Controllers should expose methods as inputs and update UI state that drives the UI
Use AutoRoute for navigation and use extras to pass data between pages
Use Dart extensions to manage reusable code
Use ThemeData to manage themes
Use AppLocalizations for translations
Use constants to manage constant values
Avoid deeply nested widget trees; aim for a flatter widget structure for performance and readability
Break down large widgets into smaller, focused, reusable components
Keep the widget tree shallow to simplify state management and data flow
Utilize const constructors and const widgets wherever possible to reduce rebuilds

Files:

  • lib/config/states/group_state.freezed.dart
  • lib/config/states/group_state.dart
  • lib/config/providers/group_provider.dart
  • lib/ui/chat/widgets/chat_header_widget.dart
  • lib/ui/chat/chat_info/group_chat_info.dart
  • lib/ui/chat/chat_screen.dart
🧠 Learnings (1)
📚 Learning: 2025-09-14T21:22:00.962Z
Learnt from: Quwaysim
PR: parres-hq/whitenoise_flutter#634
File: lib/config/providers/group_provider.dart:1130-1132
Timestamp: 2025-09-14T21:22:00.962Z
Learning: In the whitenoise_flutter codebase, the _updateGroupInfo method in GroupsNotifier performs optimistic updates that directly modify Group objects in the provider state (groups list and groupsMap). For non-DM groups, the display name comes from group.name, so updating the Group object directly is sufficient to reflect name changes in the UI without needing to refresh the separate display name cache.

Applied to files:

  • lib/ui/chat/chat_info/group_chat_info.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 (11)
lib/config/states/group_state.dart (1)

16-16: LGTM!

The new groupImagePaths field follows the established pattern for caching group-related data. The type annotation and inline comment are clear and consistent with other similar fields in the state.

lib/ui/chat/chat_screen.dart (1)

385-388: LGTM!

The change correctly retrieves the cached group image path for non-DM groups with an appropriate fallback to an empty string when the path is null.

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

73-73: LGTM!

The changes correctly retrieve and use the cached group image path. The groupsNotifier is properly obtained via ref.watch, and the avatar uses the cached path with an appropriate fallback to an empty string.

Also applies to: 80-84

lib/ui/chat/chat_info/group_chat_info.dart (2)

18-18: LGTM!

The groupImagePath field is properly declared as a nullable String to hold the cached image path.


151-151: LGTM!

The avatar correctly uses groupImagePath with an appropriate fallback. Once the setState issue on line 132 is fixed, this will properly display the cached group image.

lib/config/providers/group_provider.dart (5)

167-168: LGTM!

The call to _loadGroupImagePaths is properly placed in the initialization sequence, parallel to other group data loading operations.


628-676: LGTM!

The _loadGroupImagePaths method follows the established pattern from _loadGroupTypesForAllGroups. It correctly:

  • Loads image paths in parallel for better performance
  • Handles errors gracefully without blocking the UI
  • Updates state only after all loads complete
  • Logs appropriate info and error messages

861-865: LGTM!

The getCachedGroupImagePath getter is well-implemented with clear documentation. It follows the same pattern as getCachedGroupType and properly handles null cases.


957-958: LGTM!

The call to _loadGroupImagePaths for newly detected groups is properly placed and consistent with the initial loading pattern.


1313-1335: LGTM!

The getGroupDisplayImage method is correctly updated to return the cached group image path for regular groups. The comment on line 1313 accurately describes the new behavior.

lib/config/states/group_state.freezed.dart (1)

30-31: LGTM!

This is generated code from freezed that properly includes the new groupImagePaths field in all necessary locations: mixin getter, copyWith interfaces/implementations, constructor, private storage, public accessor, toString, equality, and hashCode.

Also applies to: 55-55, 82-82, 118-122, 154-154, 180-180, 216-220, 246-246, 254-255, 326-335, 346-346, 375-378, 392-392, 414-414, 432-432

Copy link
Member

@erskingardner erskingardner left a comment

Choose a reason for hiding this comment

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

LGTM

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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cf0bb3 and 6c747bc.

📒 Files selected for processing (2)
  • lib/ui/chat/chat_info/chat_info_screen.dart (1 hunks)
  • lib/ui/chat/chat_info/group_chat_info.dart (3 hunks)
🧰 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 dynamic and Object without justification
Create necessary types instead of overusing primitives or dynamic
One export per file
Use PascalCase for classes
Use camelCase for variables, functions, and methods
Avoid magic numbers and define constants
Start each function name with a verb
Use verbs for boolean variables (e.g., isLoading, hasError, canDelete)
Write short functions with a single purpose (under ~20 instructions)
Name functions with a verb plus context; for booleans use isX/hasX/canX; for void use executeX/saveX
Avoid deep nesting via early returns and extraction to utility functions
Use higher-order functions (map, where/filter, reduce) to avoid nesting
Use arrow functions for simple functions (under ~3 statements); use named functions otherwise
Use default parameter values instead of null checks
Reduce function parameters using RO-RO: pass/return parameter objects with declared types
Maintain a single level of abstraction within functions
Encapsulate data in composite types; avoid overusing primitives
Prefer validating data within classes rather than in functions
Prefer immutability; use final for runtime constants and const for compile-time constants
Use const constructors and const literals where possible
Follow SOLID principles
Prefer composition over inheritance
Declare interfaces (abstract classes) to define contracts
Write small classes with a single purpose (under ~200 instructions, <10 public methods, <10 properties)
Use exceptions for unexpected errors
Only catch exceptions to fix expected problems or add context; otherwise use a global handler

Files:

  • lib/ui/chat/chat_info/group_chat_info.dart
  • lib/ui/chat/chat_info/chat_info_screen.dart
lib/**/*.dart

📄 CodeRabbit inference engine (.cursor/rules/flutter.mdc)

lib/**/*.dart: Use flutter_rust_bridge to access core app functionality
Use Riverpod for state management; prefer StreamProviders for Rust API streams; use keepAlive if needed
Use freezed to model/manage UI states
Controllers should expose methods as inputs and update UI state that drives the UI
Use AutoRoute for navigation and use extras to pass data between pages
Use Dart extensions to manage reusable code
Use ThemeData to manage themes
Use AppLocalizations for translations
Use constants to manage constant values
Avoid deeply nested widget trees; aim for a flatter widget structure for performance and readability
Break down large widgets into smaller, focused, reusable components
Keep the widget tree shallow to simplify state management and data flow
Utilize const constructors and const widgets wherever possible to reduce rebuilds

Files:

  • lib/ui/chat/chat_info/group_chat_info.dart
  • lib/ui/chat/chat_info/chat_info_screen.dart
🧠 Learnings (2)
📚 Learning: 2025-09-14T21:22:00.962Z
Learnt from: Quwaysim
PR: parres-hq/whitenoise_flutter#634
File: lib/config/providers/group_provider.dart:1130-1132
Timestamp: 2025-09-14T21:22:00.962Z
Learning: In the whitenoise_flutter codebase, the _updateGroupInfo method in GroupsNotifier performs optimistic updates that directly modify Group objects in the provider state (groups list and groupsMap). For non-DM groups, the display name comes from group.name, so updating the Group object directly is sufficient to reflect name changes in the UI without needing to refresh the separate display name cache.

Applied to files:

  • lib/ui/chat/chat_info/group_chat_info.dart
  • lib/ui/chat/chat_info/chat_info_screen.dart
📚 Learning: 2025-09-07T13:10:16.542Z
Learnt from: josefinalliende
PR: parres-hq/whitenoise_flutter#597
File: lib/config/providers/group_provider.dart:311-314
Timestamp: 2025-09-07T13:10:16.542Z
Learning: In the whitenoise_flutter codebase, the User class used in group_provider.dart (and similar contexts) is presentational only, not the actual user class from the Rust API. There are plans to remove this User class and replace it with UserProfileData, similar to the planned consolidation with ContactModel.

Applied to files:

  • lib/ui/chat/chat_info/chat_info_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 (5)
lib/ui/chat/chat_info/chat_info_screen.dart (1)

12-12: LGTM!

The import is necessary for the part file group_chat_info.dart to access the GroupsState type used in the ProviderSubscription<GroupsState> declaration.

lib/ui/chat/chat_info/group_chat_info.dart (4)

18-19: LGTM!

The state field declarations follow proper naming conventions and use appropriate nullable types.


29-31: LGTM!

The initial load of groupImagePath is correctly wrapped in setState and uses ref.read appropriately within initState.


41-45: LGTM!

The dispose method properly closes the subscription before calling super.dispose(), ensuring correct cleanup of the manual listener.


164-164: LGTM!

The avatar correctly uses the dynamic groupImagePath with a safe fallback to an empty string when the path is null.

Comment on lines +33 to +37
_groupsSubscription = ref.listenManual(groupsProvider, (previous, next) {
if (mounted) {
_loadMembers();
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Update groupImagePath when the provider changes.

The listener only calls _loadMembers() but doesn't update groupImagePath when the groups provider changes. Since group images are loaded in parallel (per the PR description), the cached image path may become available after initialization, and the UI won't reflect the updated image until another rebuild occurs.

Apply this diff to update the image path when the provider changes:

     _groupsSubscription = ref.listenManual(groupsProvider, (previous, next) {
       if (mounted) {
+        setState(() {
+          groupImagePath = ref.read(groupsProvider.notifier).getCachedGroupImagePath(widget.groupId);
+        });
         _loadMembers();
       }
     });
🤖 Prompt for AI Agents
In lib/ui/chat/chat_info/group_chat_info.dart around lines 33 to 37, the
groupsProvider listener only calls _loadMembers() and does not refresh
groupImagePath when provider data changes; update the listener to also refresh
the cached image path by retrieving the current group's image path from the
provider and assigning it to groupImagePath inside a mounted setState (or call
an existing helper that updates the image), ensuring the UI re-renders when the
provider supplies a newly available image path.

@codeswot codeswot merged commit 700b89c into master Oct 17, 2025
2 checks passed
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.

3 participants