Skip to content

Conversation

@josefinalliende
Copy link
Contributor

@josefinalliende josefinalliende commented Sep 9, 2025

Description

To update wn crate, it's necessary to update some rust calls related to group information, that now requiere to receive the account pubkey as a parameter

This PR:

  • Upgrades whitenoise rev and updates bridge methods
  • Updates the use of those methods in the group provider

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 data and types are now scoped to the active account, so group info reflects the selected profile.
  • Bug Fixes

    • Added fallbacks for group-type loading and display-name resolution to avoid errors and blank states when auth is missing.
  • Performance

    • Reduced unnecessary network requests when no account is active.
  • Chores

    • Migrated group image handling to a fixed-size binary format for more robust encoding.
  • Tests

    • Updated tests to cover account-scoped group operations.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

Walkthrough

Account-scoped group data access added across Dart and Rust: group APIs now require an account pubkey; Group image fields changed from URL/bytes to fixed 32-byte hash/key; FFI/wire encoders/decoders and generated Dart types (U8Array32) added; provider logic guards on missing active pubkey; tests updated.

Changes

Cohort / File(s) Summary of changes
Provider account scoping
lib/config/providers/group_provider.dart, test/config/providers/group_provider_test.dart
Provider reads activePubkey, short-circuits when absent, and passes accountPubkey to group fetches; display-name and info maps made account-scoped; tests updated to call groupType(accountPubkey: ...).
Dart API surface & Group model
lib/src/rust/api/groups.dart
getGroupInformation/getGroupsInformations now require accountPubkey; Group.groupType/isDirectMessageType/isGroupType accept accountPubkey; Group replaces imageUrl:String? with imageHash: U8Array32? and changes imageKey type to U8Array32?; equality/hash updated.
Dart FFI / generated bindings
lib/src/rust/frb_generated.dart, lib/src/rust/frb_generated.io.dart, lib/src/rust/lib.dart
Wire methods gain accountPubkey param; added U8Array32 wrapper and new encode/decode helpers for 32-byte arrays; image fields switched to 32-byte arrays across encode/decode paths; added lib.dart import.
Rust API and FRB glue
rust/src/api/groups.rs, rust/src/frb_generated.rs
Public functions and Group methods now accept account_pubkey; Group struct uses image_hash: Option<[u8;32]> and image_key: Option<[u8;32]>; added GroupState/GroupType/GroupInformation types and conversions; SSE handlers decode and forward api_account_pubkey; 32-byte encode/decode implemented.
Dependency revisions
rust/Cargo.toml
Updated git rev SHAs for nostr-mls, nostr-sdk, and whitenoise; no version/feature changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as UI/Widgets
  participant GP as GroupProvider
  participant DA as Dart API (frb_generated)
  participant RS as Rust API (groups)
  participant BE as Backend/Store

  UI->>GP: request groups/types/display names
  GP->>GP: read activePubkey
  alt no activePubkey
    GP-->>UI: return empty / skip API calls
  else activePubkey present
    GP->>DA: getGroupInformation(accountPubkey, groupId)
    DA->>RS: get_group_information(account_pubkey, group_id)
    RS->>BE: fetch scoped info
    BE-->>RS: GroupInformation
    RS-->>DA: GroupInformation
    DA-->>GP: GroupInformation

    GP->>DA: group.groupType(accountPubkey)
    DA->>RS: Group::group_type(account_pubkey)
    RS->>BE: resolve scoped type
    BE-->>RS: GroupType
    RS-->>DA: GroupType
    DA-->>GP: GroupType
    GP-->>UI: update state
  end
  note right of DA: imageHash/imageKey use 32-byte U8Array32
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • erskingardner
  • Quwaysim
  • codeswot
  • untreu2

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.92% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title references the whitenoise revision bump, which is indeed part of the changeset, and although it does not mention the key functional update of requiring an account pubkey parameter for group-related calls, it remains related to the changes and thus meets the criteria for a partially related title.
Description Check ✅ Passed The description clearly explains that updating the wn crate requires changes to Rust calls to accept an account pubkey parameter and enumerates the main adjustments—upgrading the whitenoise revision and updating bridge methods in the group provider—so it is directly related to the changeset.

Poem

I hop between keys in twilight's hush,
Passing pubkeys with a soft tooth-brush.
Thirty-two crumbs, a tidy hash snack,
Groups scoped and snug—no data off track.
Carrots for tests, and a joyful hop-back! 🥕🐇

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0b99d4 and a4710a8.

⛔ Files ignored due to path filters (1)
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • rust/Cargo.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • rust/Cargo.toml
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/update-whitenoise-rev

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.

@josefinalliende josefinalliende force-pushed the chore/update-whitenoise-rev branch from 8391190 to c0b99d4 Compare September 9, 2025 12:33
@josefinalliende josefinalliende marked this pull request as ready for review September 9, 2025 12:35
@josefinalliende josefinalliende marked this pull request as draft September 9, 2025 12:43
@josefinalliende josefinalliende marked this pull request as ready for review September 9, 2025 12:47
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: 2

Caution

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

⚠️ Outside diff range comments (2)
lib/config/providers/group_provider.dart (2)

260-266: Missing await on ApiError.messageText().

This will log a Future instead of the error text.

-      if (e is ApiError) {
-        final errorDetails = e.messageText();
+      if (e is ApiError) {
+        final errorDetails = await e.messageText();
         logMessage += '$errorDetails (Type: ${e.runtimeType})';

833-839: Missing await on ApiError.messageText().

Same issue as above; logs a Future.

-      if (e is ApiError) {
-        final errorDetails = e.messageText();
+      if (e is ApiError) {
+        final errorDetails = await e.messageText();
         logMessage += '$errorDetails (Type: ${e.runtimeType})';
♻️ Duplicate comments (1)
lib/src/rust/api/groups.dart (1)

130-136: Group equality/hashCode now include imageHash — see note on value semantics.

This relies on U8Array32’s equality; apply the value semantics refactor above to avoid identity-based comparisons.

Also applies to: 147-153

🧹 Nitpick comments (17)
test/config/providers/group_provider_test.dart (1)

649-652: Use a realistic accountPubkey format.

Production paths use hex for accountPubkey; the test passes an npub. Even if skipped, keep it consistent to avoid future surprises.

-            final groupType = await group.groupType(accountPubkey: 'npubabc');
+            final groupType = await group.groupType(accountPubkey: 'abcdef1234deadbeef');
lib/config/providers/group_provider.dart (12)

231-239: Avoid logging raw member/admin keys.

Info logs expose full pubkeys. Redact or truncate to reduce sensitive data exposure in logs.

-      _logger.info('  - Member pubkeys (filtered): $filteredMemberHexs');
-      _logger.info('  - Admin pubkeys: $adminPublicKeyHexs');
+      _logger.info('  - Member pubkeys (filtered): ${filteredMemberHexs.length} [redacted]');
+      _logger.info('  - Admin pubkeys: ${adminPublicKeyHexs.length} [redacted]');

251-256: Comment contradicts behavior.

The code calls loadGroups() despite the comment saying “Instead of calling loadGroups()”. Align comment or adjust logic.

-      // Instead of calling loadGroups(), update the group's lastMessageAt to put it at the top
-      await loadGroups();
+      // Reload groups, then bump activity to surface the new group at the top
+      await loadGroups();

518-525: Early-return guard is good; minor polish.

You read activePubkey once and guard. Consider typing the variable explicitly to match guidelines.

-      final activePubkey = ref.read(activePubkeyProvider);
+      final String? activePubkey = ref.read(activePubkeyProvider);

595-601: Wrap getGroupInformation in try/catch and prefer cached type to avoid extra RPCs.

This path calls into Rust per group just to read groupType. You already cache types in state.groupTypes; use that first and only fall back to RPC if missing. Also handle bridge-not-initialized gracefully.

-    final activePubkey = ref.read(activePubkeyProvider);
-    if (activePubkey == null || activePubkey.isEmpty) return '';
-    final groupInformation = await getGroupInformation(
-      accountPubkey: activePubkey,
-      groupId: group.mlsGroupId,
-    );
-    if (groupInformation.groupType == GroupType.directMessage) {
+    final String? activePubkey = ref.read(activePubkeyProvider);
+    if (activePubkey == null || activePubkey.isEmpty) return '';
+    final GroupType? cached = getCachedGroupType(group.mlsGroupId);
+    GroupType effectiveType = cached ?? GroupType.group;
+    if (cached == null) {
+      try {
+        final info = await getGroupInformation(
+          accountPubkey: activePubkey,
+          groupId: group.mlsGroupId,
+        );
+        effectiveType = info.groupType;
+      } catch (e) {
+        _logErrorSync('Failed to fetch group information for ${group.mlsGroupId}', e);
+      }
+    }
+    if (effectiveType == GroupType.directMessage) {

661-661: Remove unnecessary casts in predicate.

groups is List; no need for (group as Group?)?.

-    return groups.where((group) => (group as Group?)?.state == GroupState.active).toList();
+    return groups.where((group) => group.state == GroupState.active).toList();

685-687: Simplify lookup; remove casts.

Cleaner and avoids null-aware on non-nullable Group.

-      return groups.firstWhere(
-        (group) => (group as Group?)?.mlsGroupId == groupId || (group).nostrGroupId == groupId,
-      );
+      return groups.firstWhere(
+        (group) => group.mlsGroupId == groupId || group.nostrGroupId == groupId,
+      );

694-701: Guard empty activePubkey before RPC.

If activePubkey is empty, this will make an invalid call.

-    final activePubkey = ref.read(activePubkeyProvider) ?? '';
-    final groupInformation = await getGroupInformation(
+    final String activePubkey = ref.read(activePubkeyProvider) ?? '';
+    if (activePubkey.isEmpty) {
+      _logger.warning('getGroupType called with no active account');
+      return GroupType.group;
+    }
+    final groupInformation = await getGroupInformation(
       accountPubkey: activePubkey,
       groupId: group.mlsGroupId,
     );

704-711: Same guard for getGroupTypeById.

-    final activePubkey = ref.read(activePubkeyProvider) ?? '';
-    final groupInformation = await getGroupInformation(
+    final String activePubkey = ref.read(activePubkeyProvider) ?? '';
+    if (activePubkey.isEmpty) {
+      _logger.warning('getGroupTypeById called with no active account');
+      return GroupType.group;
+    }
+    final groupInformation = await getGroupInformation(
       accountPubkey: activePubkey,
       groupId: groupId,
     );

789-790: Remove unnecessary casts when building ID set.

-      final currentGroupIds =
-          currentGroups.map((g) => (g as Group?)?.mlsGroupId).whereType<String>().toSet();
+      final currentGroupIds = currentGroups.map((g) => g.mlsGroupId).toSet();

1000-1004: Remove unnecessary casts in update loop.

-          if ((group as Group?)?.mlsGroupId == groupId) {
-            final g = group;
+          if (group.mlsGroupId == groupId) {
+            final g = group;

451-457: Dead stub: either implement or remove.

loadGroupCreator() is a no-op. Add a TODO with owner/date or delete until needed.


510-555: Batch group type resolution to one RPC.

You already have getGroupsInformations(); prefer fetching once and filling the cache instead of per-group groupType(). This will reduce latency and error surface.

rust/src/api/groups.rs (2)

50-60: Deduplicate repeated “group information” lookup.

All three methods parse the same inputs and call the same backend. Factor into a private helper to reduce risk and improve readability.

 impl Group {
-    #[frb]
-    pub async fn group_type(&self, account_pubkey: String) -> Result<GroupType, ApiError> {
-        let whitenoise = Whitenoise::get_instance()?;
-        let mls_group_id = group_id_from_string(&self.mls_group_id)?;
-        let parsed_pubkey = PublicKey::parse(&account_pubkey)?;
-        let group_information = whitenoise
-            .get_group_information_by_mls_group_id(parsed_pubkey, &mls_group_id)
-            .await?;
-        Ok(group_information.group_type.into())
-    }
+    async fn fetch_group_information(
+        &self,
+        account_pubkey: &str,
+    ) -> Result<WhitenoiseGroupInformation, ApiError> {
+        let whitenoise = Whitenoise::get_instance()?;
+        let mls_group_id = group_id_from_string(&self.mls_group_id)?;
+        let parsed_pubkey = PublicKey::parse(account_pubkey)?;
+        let info = whitenoise
+            .get_group_information_by_mls_group_id(parsed_pubkey, &mls_group_id)
+            .await?;
+        Ok(info)
+    }
+
+    #[frb]
+    pub async fn group_type(&self, account_pubkey: String) -> Result<GroupType, ApiError> {
+        Ok(self.fetch_group_information(&account_pubkey).await?.group_type.into())
+    }
 
     #[frb]
-    pub async fn is_direct_message_type(&self, account_pubkey: String) -> Result<bool, ApiError> {
-        let whitenoise = Whitenoise::get_instance()?;
-        let mls_group_id = group_id_from_string(&self.mls_group_id)?;
-        let parsed_pubkey = PublicKey::parse(&account_pubkey)?;
-        let group_information = whitenoise
-            .get_group_information_by_mls_group_id(parsed_pubkey, &mls_group_id)
-            .await?;
-        Ok(group_information.group_type == WhitenoiseGroupType::DirectMessage)
-    }
+    pub async fn is_direct_message_type(&self, account_pubkey: String) -> Result<bool, ApiError> {
+        Ok(
+            self.fetch_group_information(&account_pubkey).await?.group_type
+                == WhitenoiseGroupType::DirectMessage,
+        )
+    }
 
     #[frb]
-    pub async fn is_group_type(&self, account_pubkey: String) -> Result<bool, ApiError> {
-        let whitenoise = Whitenoise::get_instance()?;
-        let mls_group_id = group_id_from_string(&self.mls_group_id)?;
-        let parsed_pubkey = PublicKey::parse(&account_pubkey)?;
-        let group_information = whitenoise
-            .get_group_information_by_mls_group_id(parsed_pubkey, &mls_group_id)
-            .await?;
-        Ok(group_information.group_type == WhitenoiseGroupType::Group)
-    }
+    pub async fn is_group_type(&self, account_pubkey: String) -> Result<bool, ApiError> {
+        Ok(
+            self.fetch_group_information(&account_pubkey).await?.group_type
+                == WhitenoiseGroupType::Group,
+        )
+    }
 }

Also applies to: 62-71, 73-82


275-290: Name nit: “informations”.

If you want cleaner API without breaking callers, add an alias with the corrected name and keep this one for compatibility.

Rust (new alias outside this block):

#[frb]
pub async fn get_groups_information(
    account_pubkey: String,
    group_ids: Vec<String>,
) -> Result<Vec<GroupInformation>, ApiError> {
    get_groups_informations(account_pubkey, group_ids).await
}
lib/src/rust/frb_generated.dart (2)

2732-2735: Add 32-byte length validation for U8Array32.

Defensive check avoids silent corruption if codegen/types drift. Since this file is generated, prefer asserting in U8Array32’s constructor.

Dart (in lib/src/rust/lib.dart):

class U8Array32 {
  final Uint8List inner;
  U8Array32(Uint8List inner)
      : assert(inner.length == 32, 'U8Array32 must be 32 bytes'),
        inner = inner;
}

Also applies to: 2809-2812, 3583-3591, 3661-3667, 4376-4383, 4447-4451


2543-2551: Minor: array length contract in DCO path.

Consider the same 32-byte assert in the DCO decode path (handled by the U8Array32 ctor suggestion).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c82842 and c0b99d4.

⛔ Files ignored due to path filters (1)
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • lib/config/providers/group_provider.dart (4 hunks)
  • lib/src/rust/api/groups.dart (6 hunks)
  • lib/src/rust/frb_generated.dart (21 hunks)
  • lib/src/rust/frb_generated.io.dart (7 hunks)
  • lib/src/rust/lib.dart (1 hunks)
  • rust/Cargo.toml (1 hunks)
  • rust/src/api/groups.rs (5 hunks)
  • rust/src/frb_generated.rs (13 hunks)
  • test/config/providers/group_provider_test.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 '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/src/rust/lib.dart
  • test/config/providers/group_provider_test.dart
  • lib/src/rust/frb_generated.dart
  • lib/src/rust/api/groups.dart
  • lib/config/providers/group_provider.dart
  • lib/src/rust/frb_generated.io.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/config/providers/group_provider_test.dart
🧠 Learnings (4)
📓 Common learnings
Learnt from: josefinalliende
PR: parres-hq/whitenoise_flutter#594
File: lib/src/rust/api/utils.dart:12-16
Timestamp: 2025-09-07T02:15:31.931Z
Learning: The lingering PublicKey references in lib/config/providers/auth_provider.dart, lib/config/providers/group_provider.dart, and rust_builder/cargokit/build_tool/lib/src/options.dart identified during the utils.dart API cleanup were fixed in PR #597 in the whitenoise_flutter codebase.
📚 Learning: 2025-09-07T02:15:31.931Z
Learnt from: josefinalliende
PR: parres-hq/whitenoise_flutter#594
File: lib/src/rust/api/utils.dart:12-16
Timestamp: 2025-09-07T02:15:31.931Z
Learning: The lingering PublicKey references in lib/config/providers/auth_provider.dart, lib/config/providers/group_provider.dart, and rust_builder/cargokit/build_tool/lib/src/options.dart identified during the utils.dart API cleanup were fixed in PR #597 in the whitenoise_flutter codebase.

Applied to files:

  • test/config/providers/group_provider_test.dart
  • lib/src/rust/frb_generated.dart
  • rust/src/api/groups.rs
  • lib/src/rust/api/groups.dart
  • lib/config/providers/group_provider.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 flutter_rust_bridge to access core functionality of the app.

Applied to files:

  • lib/src/rust/frb_generated.dart
  • lib/src/rust/api/groups.dart
  • lib/src/rust/frb_generated.io.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/src/rust/frb_generated.dart
  • lib/src/rust/api/groups.dart
🧬 Code graph analysis (2)
rust/src/api/groups.rs (1)
rust/src/api/utils.rs (1)
  • group_id_from_string (56-59)
rust/src/frb_generated.rs (1)
rust/src/api/groups.rs (5)
  • get_group_information (260-271)
  • get_groups_informations (274-290)
  • group_type (52-60)
  • is_direct_message_type (63-71)
  • is_group_type (74-82)
🔇 Additional comments (31)
rust/Cargo.toml (1)

19-20: Lockfile consistency verified. Cargo.lock pins nostr-mls and nostr-sdk to fadf1a6b3d8c5457e5a66d95d6c1677b4353e653 and whitenoise to c06c4745602c1fffdb77ec0b2330743f171dae7b; no stale SHAs remain.

lib/src/rust/lib.dart (1)

1-4: Fix circular import and missing imports; this won’t compile.

This file imports frb_generated.dart while frb_generated.dart imports this file → cyclic dependency. Also Uint8List and @internal are used without importing dart:typed_data and meta.

Apply:

 // This file is automatically generated, so please do not edit it.
 // @generated by `flutter_rust_bridge`@ 2.11.1.
 
-// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
 
+import 'dart:typed_data';
 import 'package:collection/collection.dart';
 import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
-
-import 'frb_generated.dart';
+import 'package:meta/meta.dart' show internal;
 
 class U8Array32 extends NonGrowableListView<int> {
   static const arraySize = 32;
 
   @internal
   Uint8List get inner => _inner;
   final Uint8List _inner;
 
   U8Array32(this._inner) : assert(_inner.length == arraySize), super(_inner);
 
   U8Array32.init() : this(Uint8List(arraySize));
 }

Also applies to: 6-10, 14-21

⛔ Skipped due to learnings
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.
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.
lib/config/providers/group_provider.dart (1)

635-640: Good: batched fetch for group infos with accountPubkey.

This aligns with the new account-scoped API and reduces round-trips.

lib/src/rust/frb_generated.io.dart (4)

23-23: Import of lib.dart for U8Array32 is correct.

Needed for the new fixed-size byte wrapper. No issues.


273-301: Add DCO decoders for [u8;32] — good alignment with Rust-side changes.

dco_decode_opt_u_8_array_32 and dco_decode_u_8_array_32 declarations look consistent with the 32-byte wire type.


560-590: Add SSE decoders for [u8;32] — OK.

sse_decode_opt_u_8_array_32 and sse_decode_u_8_array_32 complete the decode path.


895-932: Add SSE encoders for [u8;32] — OK.

sse_encode_opt_u_8_array_32 and sse_encode_u_8_array_32 mirror the decode additions.

lib/src/rust/api/groups.dart (4)

9-9: Import U8Array32 source — OK.

The ../lib.dart import is required for the new imageHash/imageKey types.


78-83: Account-scoped getGroupsInformations: OK.

Matches the Rust API and preserves named parameters.


112-123: Group type helpers now require accountPubkey — looks good.

Named param enforces call-site clarity; forwards to the new Rust methods.


69-75: Approve updated getGroupInformation signature
All Dart call sites now pass both accountPubkey and groupId; no legacy invocations detected.

rust/src/frb_generated.rs (12)

981-992: get_group_information now decodes account_pubkey — OK.

Matches Rust API and Dart wrapper.


1022-1033: get_groups_informations adds account_pubkey — OK.

Signature and forwarding are consistent.


1138-1145: Group::group_type requires account_pubkey — OK.

Decoder order and call site are correct.


1264-1272: Group::is_direct_message_type adds account_pubkey — OK.


1305-1312: Group::is_group_type adds account_pubkey — OK.


2540-2555: Group SSE decode switches to Option<[u8;32]> — OK.

Field mapping (image_hash, image_key) aligns with the Rust struct.


2856-2865: Add Option<[u8;32]> decoder — OK.

Handles optional fixed-size arrays; mirrors encode path.


2943-2949: Add [u8;32] decoder — OK.

Uses from_vec_to_array; acceptable given controlled counterpart encoding.


3449-3450: IntoDart for Group now includes image_hash — OK.


3972-3974: SSE encode Option<[u8;32]> for Group fields — OK.


4228-4234: Add Option<[u8;32]> encoder — OK.


4299-4310: Add [u8;32] encoder — OK.

Converts array to Vec efficiently for serialization.

rust/src/api/groups.rs (3)

20-21: Image fields migration looks correct.

Switch to 32-byte hash/key aligns with the crate and FRB expectations; mapping in From preserves order.

Also applies to: 36-38


197-201: Config defaulting to None for image fields is fine.

Matches the new schema; relay/admin wiring unchanged.


260-271: Account-scoped get_group_information looks good.

Correct parse → lookup → convert flow; error propagation is consistent with the rest of the file.

lib/src/rust/frb_generated.dart (5)

23-24: Importing lib.dart for U8Array32 is correct.

Ensures the 32-byte wrapper is in scope.


2543-2551: Group encode/decode updated to imageHash/imageKey — ordering is consistent.

DCO/SSE paths align with Rust struct layout.

Also applies to: 3268-3290, 4105-4118


1139-1170: ConstMeta/argNames updated to include accountPubkey — good.

Keeps task metadata in sync for debugging/tracing.

Also applies to: 1173-1204


3268-3270: Minor: symmetry maintained in SSE decode.

Both imageHash and imageKey use opt_u_8_array_32; mirrors encode side.


178-182: Verify getGroupInformation & wrapper updates

Wrapper in lib/src/rust/api/groups.dart now requires both accountPubkey and groupId, but calls in group_provider.dart (lines 597, 637, 695, 705) pass only groupId. Update each invocation to include accountPubkey:.

Comment on lines +90 to 92
final U8Array32? imageHash;
final U8Array32? imageKey;
final List<String> adminPubkeys;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure U8Array32 has value semantics (== and hashCode) for stable Group equality.

imageHash/imageKey moved from string/bytes to U8Array32?. If U8Array32 doesn’t override ==/hashCode by contents, Group equality and hashCode will become identity-based for these fields, changing behavior vs prior String equality. Implement content-based equality and hashing in U8Array32 to keep semantics predictable.

Example for lib/src/rust/lib.dart (outside this diff):

import 'dart:typed_data';
import 'package:collection/collection.dart';

class U8Array32 {
  final Uint8List bytes; // length == 32, immutable view
  static const _eq = ListEquality<int>();

  const U8Array32(this.bytes);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is U8Array32 && _eq.equals(bytes, other.bytes);

  @override
  int get hashCode => _eq.hash(bytes);
}
🤖 Prompt for AI Agents
In lib/src/rust/api/groups.dart around lines 90 to 92 the fields imageHash and
imageKey were changed to U8Array32?, so ensure U8Array32 implements
content-based equality and hashing (not identity) to preserve previous
String/bytes semantics: in the U8Array32 class (e.g., lib/src/rust/lib.dart)
store an immutable Uint8List (length 32), use package:collection ListEquality to
compare bytes, override operator == to compare contents and hashCode to use the
equality's hash function (and also validate length in constructor or create an
immutable view) so Group equality and hashCode remain stable and
value-semantics-based.

Comment on lines +1 to +3
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Don’t place custom types in generated files.

This header indicates a generated file; custom code here risks being overwritten by the next FRB build. Move U8Array32 to a non-generated module (e.g., lib/src/rust/types/u8_array_32.dart) and import it from frb_generated*.dart.

🤖 Prompt for AI Agents
lib/src/rust/lib.dart lines 1-3: this file is generated by flutter_rust_bridge
and must not contain custom types; move the U8Array32 type definition out of
this generated file into a new non-generated module (for example
lib/src/rust/types/u8_array_32.dart), export or define it there, then remove the
custom type from the generated file and update the generated frb import sites to
import the new module (or import the new type from the new file wherever needed)
so that FRB regeneration won't overwrite your custom type.

@josefinalliende josefinalliende merged commit 3b5504e into master Sep 9, 2025
2 checks passed
@josefinalliende josefinalliende deleted the chore/update-whitenoise-rev branch September 12, 2025 13:46
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