Skip to content

Conversation

@BraCR10
Copy link
Contributor

@BraCR10 BraCR10 commented Oct 4, 2025

This PR incorporates the process to restore orders and disputes using the user mnemonic keys

How Orders Are Marked as Owned

The restore session feature works by reconstructing user sessions from backend data, which automatically marks orders and disputes as owned through the existing session-based ownership detection system.

Closes #78

Current Flow:

  1. Request Phase (mobile/lib/services/mostro_service.dart:303-323)
    - User imports mnemonic via import dialog
    - App sends encrypted restore-session action to Mostro daemon
    - Request is encrypted to master key derived from mnemonic
  2. Backend Response
    - Returns array of {id, trade_index, status} for each order
  3. Session Reconstruction (mobile/lib/services/mostro_service.dart:326-386)
    for (final orderInfo in restoreData.orders) {
    // Derive the same trade key that was originally used
    final tradeKey = await keyManager.deriveTradeKeyFromIndex(orderInfo.tradeIndex);
// Create session with original trade_index
final session = Session(
  masterKey: masterKey,
  tradeKey: tradeKey,
  keyIndex: orderInfo.tradeIndex,  // Original index (3, 5, 7, etc.)
  orderId: orderInfo.orderId,
  role: Role.seller
);

await sessionNotifier.saveSession(session);

}
4. Automatic Ownership Detection (mobile/lib/features/home/widgets/order_list_item.dart:44-50)
final session = ref.watch(sessionProvider(order.orderId!));
if (session != null && session.role != null) {
orderLabel = session.role == Role.seller
? S.of(context)!.youAreSelling.toUpperCase()
: S.of(context)!.youAreBuying.toUpperCase();
}

Key Points:

  • Orders already exist in Order Book (they're public events)
  • Creating a session with matching orderId triggers ownership detection
  • Trade keys are deterministically derived using original indices

Additional Features Implemented

  • Notification Cleanup on Key Import/Generation
  • Trade Index Management
  • Mnemonic Validation

Validation rules enforced:

  • - Exactly 12 or 24 words (BIP-39 standard)
  • - Only letters and spaces allowed
  • - Each word must be at least 3 characters
  • - Localized error messages (EN/ES/IT)

Note

Currently, all sessions restored at line 362 in mostro_service.dart are hardcoded with Role.seller. This causes buyer orders to incorrectly display seller information,
affecting:

  • UI labels ("YOU ARE SELLING" instead of "YOU ARE BUYING")
  • FSM state transitions and available actions
  • Buttons visibility

Proposed Solutions:

Option 1 (Recommended): Add a role field to the current restore-session payload response:

  {
    "restore_data": {
      "orders": [
        {
          "id": "abc123",
          "trade_index": 3,
          "status": "active",
          "role": "seller"  // ← NEW FIELD
        }
      ]
    }
  }

Option 2: Wait for the upcoming orders endpoint feature that returns complete order data, then make a second call after restore to fetch full details.
Documentation

Recommendation: If it is possible, Option 1 is more efficient as it avoids double network calls and provides immediate correct role assignment during the restore flow. The role field is minimal data (one string per order) and critical for proper client-side behavior.

Summary by CodeRabbit

Release Notes

  • New Features

    • Session restore capability—recover existing orders and disputes from backup
    • Mnemonic import functionality—import and restore user account data
    • Refresh button to manually sync account data
    • Real-time progress overlay during restoration with status updates
  • Localization

    • Added English, Spanish, and Italian translations for all restore and import features

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 4, 2025

Walkthrough

This pull request implements a complete session restore feature that enables users to import a mnemonic phrase and restore previously active sessions, orders, and disputes. It includes new message models, a service orchestrating the restore workflow, UI components for mnemonic import and progress overlay, state management for tracking restoration steps, and localization strings across multiple languages.

Changes

Cohort / File(s) Summary
Enums & Core Models
lib/data/models/enums/action.dart, lib/data/models/restore_data.dart
Adds restoreSession enum value to Action; introduces RestoreData, RestoredOrder, and RestoredDispute classes for parsing and serializing restore payloads with nested structure support.
Message Models
lib/data/models/restore_message.dart, lib/data/models/last_trade_index_message.dart, lib/data/models/orders_request_message.dart
Adds three new message model classes for constructing and serializing restore-related Nostr payloads (restore-session, last-trade-index, orders requests) with JSON serialization helpers.
Payload & Event Handling
lib/data/models/payload.dart, lib/data/models/mostro_message.dart, lib/data/models/nostr_event.dart
Extends Payload.fromJson to handle RestoreData; adds 'restore' fallback key to MostroMessage.fromJson; introduces mostroWrapWithSeparateKeys method for dual-key Gift Wrap wrapping.
Restore Service & Coordination
lib/services/restore_service.dart, lib/services/last_trade_index_service.dart, lib/features/restore/restore_message_handler.dart, lib/features/restore/order_restorer.dart, lib/features/restore/session_restorer.dart
Implements end-to-end restore orchestration (RestoreService), last-trade-index request handling, message encoding/decoding, order ID extraction, and session restoration with key derivation and role mapping.
Restore UI & Progress
lib/features/restore/restore_overlay.dart, lib/features/restore/restore_progress_notifier.dart, lib/features/restore/restore_progress_state.dart, lib/features/key_manager/import_mnemonic_dialog.dart
Adds RestoreOverlay for full-screen progress display, RestoreProgressNotifier/State for tracking restore lifecycle and UI state transitions, and ImportMnemonicDialog for 12-word mnemonic input with validation.
Key Manager & Settings Integration
lib/features/key_manager/key_management_screen.dart, lib/features/settings/settings_notifier.dart, lib/features/settings/settings_provider.dart, lib/main.dart
Updates KeyManagementScreen with Refresh and Import buttons triggering restore flows; modifies SettingsNotifier constructor to accept and store Riverpod ref; enables session reset and restoration on Mostro key change.
Notifications Integration
lib/features/notifications/utils/notification_message_mapper.dart, lib/features/notifications/utils/notification_data_extractor.dart, lib/features/notifications/widgets/notification_item.dart
Adds restoreSession action mapping in notification message keys and extractor; adds restoreSession case to notification tap handler.
Localization
lib/l10n/intl_en.arb, lib/l10n/intl_es.arb, lib/l10n/intl_it.arb
Adds English, Spanish, and Italian translations for refresh flow, restore overlay states, import mnemonic dialog, and validation error messages.
App Core & Other
lib/core/app.dart, lib/services/mostro_service.dart, lib/features/subscriptions/subscription_manager.dart, lib/data/repositories/event_storage.dart, lib/services/lifecycle_manager.dart, test/mocks.dart
Wraps MaterialApp.router with RestoreOverlay in Stack; minor formatting and mock updates (renaming unsubscribeAll to unsubscribeAllExceptAdmin).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant KeyMgmt as Key Management<br/>Screen
    participant MnemonicDlg as Import Mnemonic<br/>Dialog
    participant RestoreOvly as Restore<br/>Overlay
    participant RestoreSvc as RestoreService
    participant NostrSvc as Nostr Event<br/>Service
    participant MostroSvc as Mostro<br/>Service
    participant Storage as Session &<br/>Message Storage

    User->>KeyMgmt: Tap Import/Refresh Button
    KeyMgmt->>MnemonicDlg: Show dialog (if import)
    MnemonicDlg->>User: Prompt for 12-word mnemonic
    User->>MnemonicDlg: Enter mnemonic
    MnemonicDlg->>MnemonicDlg: Validate (12 words, A-Z only)
    alt Validation Failed
        MnemonicDlg->>User: Show error message
    else Validation Passed
        MnemonicDlg->>RestoreSvc: importMnemonicAndRestore(mnemonic)
    end

    RestoreSvc->>RestoreSvc: Validate master key & Mostro config
    RestoreSvc->>RestoreOvly: Show overlay (requesting state)
    
    rect rgb(200, 220, 255)
    note over RestoreSvc,Storage: Restore Workflow
    
    RestoreSvc->>RestoreSvc: Derive temporary trade key
    RestoreSvc->>NostrSvc: Subscribe to kind 1059 events
    RestoreSvc->>MostroSvc: Send restore-session request<br/>(wrapped with Mostro keys)
    RestoreSvc->>RestoreOvly: Update progress (requestingData)
    
    loop Incoming Events
        NostrSvc->>RestoreSvc: Receive kind 1059 event
        RestoreSvc->>RestoreSvc: Unwrap & decode content
        RestoreSvc->>RestoreSvc: Process action<br/>(restore-session, orders, last-trade-index)
        alt Action: restore-session
            RestoreSvc->>RestoreOvly: Update (receivingOrders)
            RestoreSvc->>RestoreSvc: Extract orders & disputes
            RestoreSvc->>MostroSvc: Request order details
            RestoreSvc->>MostroSvc: Request last-trade-index
        else Action: orders
            RestoreSvc->>RestoreOvly: Update (loadingDetails)
            RestoreSvc->>RestoreSvc: Accumulate order details
        else Action: last-trade-index
            RestoreSvc->>RestoreOvly: Update (processingRoles)
        end
    end
    
    RestoreSvc->>RestoreSvc: All data received?
    RestoreSvc->>Storage: Restore sessions with derived keys
    RestoreSvc->>Storage: Save order details as messages
    RestoreSvc->>RestoreSvc: Update key index
    RestoreSvc->>RestoreOvly: Update (finalizing → completed)
    
    end

    RestoreSvc->>NostrSvc: Unsubscribe & cleanup
    RestoreOvly->>User: Auto-hide after 1s
    User->>KeyMgmt: Sessions/orders restored
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention during review:

  • RestoreService (lib/services/restore_service.dart): Core orchestration logic coordinating event subscription, message decoding, multi-stage data assembly, and finalization; verify error handling paths, timeout logic, and proper cleanup on failure.
  • SessionRestorer (lib/features/restore/session_restorer.dart): Complex logic for deriving trade keys, role determination via public key matching, and session persistence; ensure correctness of key derivation and role mapping against order details.
  • RestoreMessageHandler (lib/features/restore/restore_message_handler.dart): Message wrapping/unwrapping with separate keys; verify NostrEvent handling and error recovery.
  • mostroWrapWithSeparateKeys (lib/data/models/nostr_event.dart): Dual-key wrapping for RUMOR/SEAL/Gift Wrap; ensure cryptographic correctness and compliance with NIP-59.
  • SettingsNotifier changes (lib/features/settings/settings_notifier.dart): Now mutable ref field and restoration on key change; verify session reset/restore sequencing and null ref handling.
  • Data model JSON mapping (lib/data/models/restore_data.dart, related models): Verify snake_case ↔ camelCase conversions match protocol expectations.
  • Localization coverage: Ensure all new string keys are consistently added across all three languages (en, es, it).

Possibly related PRs

Suggested reviewers

  • Catrya
  • grunch

Poem

🐰 A mnemonic whisper, twelve words so fine,
Restores the sessions that once were mine!
Through gift-wrapped events and Nostr's embrace,
Lost orders and disputes find their place.
Progress bars spin as keys align—
Recovery complete, the restore divine! 🔐

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Restore Session Implementation" accurately reflects the primary technical change in this PR, which is the implementation of a comprehensive restore-session flow that reconstructs user sessions from backend data using mnemonic-derived keys. The PR does include supporting features such as the import mnemonic dialog, restore progress overlay, and localization strings, but the core implementation is the restore session functionality and its orchestration through RestoreService, SessionRestorer, and related models, which the title clearly summarizes.
Linked Issues Check ✅ Passed The PR successfully implements the core objectives from issue #78. The implementation includes: an import mnemonic flow via ImportMnemonicDialog [import_mnemonic_dialog.dart], derivation of master keys and per-order trade keys for encryption [restore_service.dart, session_restorer.dart], reconstruction of sessions with original trade_index and orderId values [session_restorer.dart], UI state updates via RestoreOverlay with progress tracking [restore_overlay.dart], session ownership detection [key_management_screen.dart], and supporting infrastructure including notification cleanup, trade index management, mnemonic validation with localized error messages [intl_*.arb], and settings integration [settings_notifier.dart]. All coding requirements for the "Import Mostro User option" feature are addressed.
Out of Scope Changes Check ✅ Passed All code changes in this PR are directly related to implementing the restore-session and import mnemonic functionality specified in issue #78. The changes include: data models for restore flow [restore_data.dart, restore_message.dart, last_trade_index_message.dart, orders_request_message.dart], core service implementations [restore_service.dart, last_trade_index_service.dart, restore_message_handler.dart, order_restorer.dart, session_restorer.dart], UI components [import_mnemonic_dialog.dart, restore_overlay.dart, key_management_screen.dart], state management [restore_progress_notifier.dart, restore_progress_state.dart], settings integration [settings_notifier.dart, settings_provider.dart], action enum extension [action.dart], localization across three languages, and supporting infrastructure. No out-of-scope or unrelated changes were detected.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59836b3 and 4062e25.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • integration_test/test_helpers.dart (1 hunks)
  • lib/data/models.dart (1 hunks)
  • lib/data/models/enums/action.dart (1 hunks)
  • lib/data/models/mostro_message.dart (1 hunks)
  • lib/data/models/payload.dart (2 hunks)
  • lib/data/models/restore_data.dart (1 hunks)
  • lib/features/key_manager/key_management_screen.dart (6 hunks)
  • lib/features/notifications/utils/notification_message_mapper.dart (2 hunks)
  • lib/features/notifications/widgets/notification_item.dart (1 hunks)
  • lib/features/subscriptions/subscription_manager.dart (7 hunks)
  • lib/features/subscriptions/subscription_type.dart (1 hunks)
  • lib/l10n/intl_en.arb (1 hunks)
  • lib/l10n/intl_es.arb (1 hunks)
  • lib/l10n/intl_it.arb (1 hunks)
  • lib/services/mostro_service.dart (4 hunks)
  • test/mocks.mocks.dart (1 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place feature-specific Dart code under lib/features// (screens, providers, widgets)

Files:

  • lib/features/subscriptions/subscription_type.dart
  • lib/features/subscriptions/subscription_manager.dart
  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/features/key_manager/key_management_screen.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Format code with the Dart formatter (two-space indentation, trailing commas where beneficial)
Keep the analyzer (flutter_lints) at zero warnings across the codebase

**/*.dart: All code comments must be in English
Use clear, concise English for variable and function names
Maintain zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., prefer withValues() over withOpacity())
Remove unused imports and dependencies

Files:

  • lib/features/subscriptions/subscription_type.dart
  • integration_test/test_helpers.dart
  • lib/data/models.dart
  • test/mocks.mocks.dart
  • lib/data/models/payload.dart
  • lib/data/models/restore_data.dart
  • lib/services/mostro_service.dart
  • lib/data/models/mostro_message.dart
  • lib/features/subscriptions/subscription_manager.dart
  • lib/data/models/enums/action.dart
  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/features/key_manager/key_management_screen.dart
lib/features/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier within their feature directory

Files:

  • lib/features/subscriptions/subscription_type.dart
  • lib/features/subscriptions/subscription_manager.dart
  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/features/key_manager/key_management_screen.dart
lib/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

lib/**/*.dart: Use post-frame callbacks for UI side effects (e.g., SnackBars/dialogs)
Use S.of(context).keyName for all user-facing strings (no hardcoded text)
Always use localized strings instead of hardcoded text
Pass BuildContext to methods that need localization (use S.of(context))
Check mounted before using BuildContext after async gaps
Use const constructors where possible

Files:

  • lib/features/subscriptions/subscription_type.dart
  • lib/data/models.dart
  • lib/data/models/payload.dart
  • lib/data/models/restore_data.dart
  • lib/services/mostro_service.dart
  • lib/data/models/mostro_message.dart
  • lib/features/subscriptions/subscription_manager.dart
  • lib/data/models/enums/action.dart
  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/features/key_manager/key_management_screen.dart
integration_test/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

integration_test/**/*.dart: Integration scenarios belong in integration_test/ and exercise full user flows
Include end-to-end tests under integration_test/ for flows that touch native bridges

Place integration tests under integration_test/

Files:

  • integration_test/test_helpers.dart
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Place persistence and API abstractions under lib/data/

Files:

  • lib/data/models.dart
  • lib/data/models/payload.dart
  • lib/data/models/restore_data.dart
  • lib/data/models/mostro_message.dart
  • lib/data/models/enums/action.dart
**/*.arb

📄 CodeRabbit inference engine (AGENTS.md)

Keep user-facing strings in localization ARB files; do not hard-code copy in Dart code

Files:

  • lib/l10n/intl_it.arb
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_es.arb
lib/l10n/intl_{en,es,it}.arb

📄 CodeRabbit inference engine (CLAUDE.md)

Add new localization keys to all three ARB files (en, es, it)

Files:

  • lib/l10n/intl_it.arb
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_es.arb
lib/l10n/*.arb

📄 CodeRabbit inference engine (CLAUDE.md)

For parameterized strings, include proper ARB metadata

Files:

  • lib/l10n/intl_it.arb
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_es.arb
test/**

📄 CodeRabbit inference engine (AGENTS.md)

Unit tests should mirror the feature layout under test/

Files:

  • test/mocks.mocks.dart
**/*.{g.dart,mocks.dart}

📄 CodeRabbit inference engine (AGENTS.md)

Do not modify generated files (*.g.dart, *.mocks.dart) by hand

Files:

  • test/mocks.mocks.dart
**/*.mocks.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.mocks.dart: Never manually edit generated *.mocks.dart files
Do not add ignore comments to *.mocks.dart; regenerate instead

Files:

  • test/mocks.mocks.dart
test/mocks.mocks.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Never modify test/mocks.mocks.dart (has file-level ignores; regenerate via build_runner)

Files:

  • test/mocks.mocks.dart
test/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Place unit tests under test/

Files:

  • test/mocks.mocks.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep app-wide services under lib/services/

Files:

  • lib/services/mostro_service.dart
🧠 Learnings (1)
📚 Learning: 2025-10-01T14:10:17.742Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-01T14:10:17.742Z
Learning: Applies to lib/l10n/intl_{en,es,it}.arb : Add new localization keys to all three ARB files (en, es, it)

Applied to files:

  • lib/l10n/intl_it.arb
  • lib/l10n/intl_es.arb
⏰ 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: build
🔇 Additional comments (9)
lib/data/models/restore_data.dart (1)

72-85: LGTM with one note.

The graceful handling of missing orders and disputes arrays with ?? [] is good defensive programming. However, the nested calls to RestoreOrderInfo.fromJson and RestoreDisputeInfo.fromJson on lines 79 and 82 will still throw if individual items have type issues, as noted in previous comments.

Once the type safety improvements are applied to RestoreOrderInfo.fromJson and RestoreDisputeInfo.fromJson, this method will properly handle malformed data.

lib/features/notifications/widgets/notification_item.dart (1)

125-125: LGTM!

The restoreSession case is correctly handled by breaking without navigation, consistent with other action types that don't require user interaction from notifications (like tradePubkey, invoiceUpdated, etc.).

lib/data/models/mostro_message.dart (1)

100-100: LGTM!

The addition of json['restore'] as a fallback source aligns with the existing pattern for order and cant-do. This enables proper extraction of request_id and other fields from restore-related messages.

lib/data/models.dart (1)

16-16: LGTM!

The export of restore_data.dart properly extends the public API surface to include the new restore functionality.

lib/features/subscriptions/subscription_type.dart (1)

5-5: LGTM!

The addition of masterKey to the SubscriptionType enum properly extends the subscription system to support master key event handling for the restore session flow.

lib/data/models/payload.dart (2)

9-9: LGTM!

The import of restore_data.dart enables the new restore payload type integration.


32-33: LGTM!

The addition of restore_data payload handling follows the established pattern for other payload types. The check for json.containsKey('restore_data') and call to RestoreData.fromJson(json) is consistent with the rest of the factory method.

integration_test/test_helpers.dart (1)

310-311: LGTM!

The empty implementation of requestRestoreSession() is appropriate for the test helper mock. This allows integration tests to exercise the UI flow for restore session without requiring actual backend interaction.

lib/l10n/intl_en.arb (1)

862-865: Localization completeness confirmed

All four mnemonicValidation keys appear once in intl_es.arb and intl_it.arb; no further changes needed.

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

♻️ Duplicate comments (1)
lib/services/mostro_service.dart (1)

355-363: Stop hard-coding the restored session role.

Every restored session is saved as Role.seller, so buyer trades will be mislabeled and routed through the wrong FSM/UI paths. Please populate role from the backend payload (or leave it null/unknown until the real role is known) instead of forcing Role.seller.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4062e25 and 078c85f.

📒 Files selected for processing (3)
  • lib/data/models/restore_data.dart (1 hunks)
  • lib/features/key_manager/key_management_screen.dart (6 hunks)
  • lib/services/mostro_service.dart (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/data/models/restore_data.dart
🧰 Additional context used
📓 Path-based instructions (5)
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep app-wide services under lib/services/

Files:

  • lib/services/mostro_service.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Format code with the Dart formatter (two-space indentation, trailing commas where beneficial)
Keep the analyzer (flutter_lints) at zero warnings across the codebase

**/*.dart: All code comments must be in English
Use clear, concise English for variable and function names
Maintain zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., prefer withValues() over withOpacity())
Remove unused imports and dependencies

Files:

  • lib/services/mostro_service.dart
  • lib/features/key_manager/key_management_screen.dart
lib/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

lib/**/*.dart: Use post-frame callbacks for UI side effects (e.g., SnackBars/dialogs)
Use S.of(context).keyName for all user-facing strings (no hardcoded text)
Always use localized strings instead of hardcoded text
Pass BuildContext to methods that need localization (use S.of(context))
Check mounted before using BuildContext after async gaps
Use const constructors where possible

Files:

  • lib/services/mostro_service.dart
  • lib/features/key_manager/key_management_screen.dart
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place feature-specific Dart code under lib/features// (screens, providers, widgets)

Files:

  • lib/features/key_manager/key_management_screen.dart
lib/features/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier within their feature directory

Files:

  • lib/features/key_manager/key_management_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: build

Comment on lines 639 to 648
final validCharacters = RegExp(r'^[a-zA-Z\s]+$');
if (!validCharacters.hasMatch(trimmed)) {
return S.of(context)!.mnemonicValidationInvalidFormat;
}

final words = trimmed.split(RegExp(r'\s+'));
if (words.length != 12 && words.length != 24) {
return S.of(context)!.mnemonicValidationInvalidWordCount;
}

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

Allow non-ASCII BIP-39 characters in mnemonic validation.

The regex only permits [a-zA-Z], so valid Spanish mnemonics (e.g., “ábaco”, “niñez”) fail validation, blocking imports for ES users. Expand the character class to accept all Unicode letters.

-    final validCharacters = RegExp(r'^[a-zA-Z\s]+$');
-    if (!validCharacters.hasMatch(trimmed)) {
+    final validCharacters =
+        RegExp(r'^[\p{L}\s]+$', unicode: true);
+    if (!validCharacters.hasMatch(trimmed)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
final validCharacters = RegExp(r'^[a-zA-Z\s]+$');
if (!validCharacters.hasMatch(trimmed)) {
return S.of(context)!.mnemonicValidationInvalidFormat;
}
final words = trimmed.split(RegExp(r'\s+'));
if (words.length != 12 && words.length != 24) {
return S.of(context)!.mnemonicValidationInvalidWordCount;
}
final validCharacters =
RegExp(r'^[\p{L}\s]+$', unicode: true);
if (!validCharacters.hasMatch(trimmed)) {
return S.of(context)!.mnemonicValidationInvalidFormat;
}
final words = trimmed.split(RegExp(r'\s+'));
if (words.length != 12 && words.length != 24) {
return S.of(context)!.mnemonicValidationInvalidWordCount;
}

Copy link
Member

@Catrya Catrya left a comment

Choose a reason for hiding this comment

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

hola @BraCR10 me está funcionando la importación de las sesiones con el mostro que hay por defecto, pero si cambio de mostro no restaura ninguna de las sesiones que tenía.

aquí tenía varias órdenes usando otro mostro
Image

luego de que regresé al mostro por defecto, y creé un nuevo usuario, sí restauró las sesiones del mostro por defecto, pero al cambiar al otro mostro que estaba usando antes no lo hizo, tampoco veo en los logs del otro mostro nada sobre restauracion de sesiones alli
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: 4

🧹 Nitpick comments (2)
lib/services/mostro_service.dart (2)

343-359: Consider per-batch error handling.

The batching loop sends multiple requests but doesn't handle individual batch failures. If one batch request fails to publish, the completers for those orders will timeout (line 407), which provides a safety net. However, you might want to add error handling within the loop to log which batches failed to send.

Consider wrapping the publish call:

     for (var i = 0; i < orderIds.length; i += maxOrdersPerRequest) {
       final batch = orderIds.skip(i).take(maxOrdersPerRequest).toList();
 
       final message = MostroMessage(
         action: Action.orders,
         payload: OrdersRequest(ids: batch),
       );
 
       final event = await message.wrap(
         tradeKey: masterKey,
         recipientPubKey: _settings.mostroPublicKey,
         masterKey: masterKey,
         keyIndex: null,
       );
 
-      await ref.read(nostrServiceProvider).publishEvent(event);
+      try {
+        await ref.read(nostrServiceProvider).publishEvent(event);
+      } catch (e) {
+        _logger.e('Failed to publish order details batch ${i ~/ maxOrdersPerRequest}', error: e);
+      }
     }

418-420: Handle null buyer/seller pubkeys explicitly
buyerTradePubkey and sellerTradePubkey are nullable in lib/data/models/order.dart, so when both are null the code just logs a warning—consider mapping that to a defined order state (e.g., pending) or handling it explicitly instead of falling through.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 078c85f and a0ec3af.

📒 Files selected for processing (12)
  • integration_test/test_helpers.dart (1 hunks)
  • lib/data/models.dart (2 hunks)
  • lib/data/models/enums/action.dart (1 hunks)
  • lib/data/models/orders_request.dart (1 hunks)
  • lib/data/models/orders_response.dart (1 hunks)
  • lib/data/models/payload.dart (3 hunks)
  • lib/features/notifications/utils/notification_message_mapper.dart (2 hunks)
  • lib/features/notifications/widgets/notification_item.dart (1 hunks)
  • lib/l10n/intl_en.arb (1 hunks)
  • lib/l10n/intl_es.arb (1 hunks)
  • lib/l10n/intl_it.arb (1 hunks)
  • lib/services/mostro_service.dart (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_es.arb
  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/l10n/intl_it.arb
  • lib/data/models/enums/action.dart
🧰 Additional context used
📓 Path-based instructions (6)
lib/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

lib/**/*.dart: Use post-frame callbacks for UI side effects (e.g., SnackBars/dialogs)
Use S.of(context).keyName for all user-facing strings (no hardcoded text)
Always use localized strings instead of hardcoded text
Pass BuildContext to methods that need localization (use S.of(context))
Check mounted before using BuildContext after async gaps
Use const constructors where possible

Name Riverpod providers as Provider or Notifier

Files:

  • lib/data/models/orders_request.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/data/models/orders_response.dart
  • lib/services/mostro_service.dart
  • lib/data/models.dart
  • lib/data/models/payload.dart
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.dart: All code comments must be in English
Use clear, concise English for variable and function names
Maintain zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., prefer withValues() over withOpacity())
Remove unused imports and dependencies

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

Files:

  • lib/data/models/orders_request.dart
  • integration_test/test_helpers.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/data/models/orders_response.dart
  • lib/services/mostro_service.dart
  • lib/data/models.dart
  • lib/data/models/payload.dart
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/models/orders_request.dart
  • lib/data/models/orders_response.dart
  • lib/data/models.dart
  • lib/data/models/payload.dart
integration_test/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Place integration tests under integration_test/

integration_test/**/*.dart: Place end-to-end integration flows under integration_test/
Ensure integration coverage for flows involving networking, background work, or session recovery

Files:

  • integration_test/test_helpers.dart
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/notifications/widgets/notification_item.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/mostro_service.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: build
🔇 Additional comments (11)
lib/data/models/payload.dart (2)

5-11: LGTM! Clean payload model integration.

The new imports for OrdersRequest, OrdersResponse, and RestoreData are properly integrated to support the restore session feature.


20-23: LGTM! Factory pattern extended correctly.

The new conditional branches properly handle the restore and orders payloads by checking for the expected JSON keys and delegating to the appropriate model constructors.

Also applies to: 38-39

lib/services/mostro_service.dart (9)

23-24: LGTM! Proper subscription and coordination infrastructure.

The new _masterKeySubscription field enables processing restore responses, and _orderDetailCompleters provides a clean async coordination mechanism for matching order detail requests with responses.

Also applies to: 25-25


40-49: LGTM! Master key subscription lifecycle managed correctly.

The master key subscription is properly initialized and disposed, mirroring the pattern used for orders subscription.

Also applies to: 54-54


71-87: LGTM! Elegant master key fallback logic.

The code now gracefully handles both session-based messages (using trade keys) and master-key-based messages (for restore/orders actions). The fallback to master key when no session matches is exactly what's needed for the restore flow.


91-100: LGTM! Defensive validation improves robustness.

The added null checks and type validation prevent processing malformed events and provide clear diagnostics via logging.


104-122: LGTM! Clean action routing separates concerns.

Routing restoreSession and orders actions to dedicated handlers before the normal message storage path is correct—these master-key messages don't belong to any specific session.


417-425: Excellent fix! Role now determined from order ownership.

This addresses the critical issue from previous reviews. By comparing the derived trade key against buyerTradePubkey and sellerTradePubkey in the order details, the code correctly identifies whether the user was the buyer or seller—no more hardcoded Role.seller. The logic also safely skips orders that don't match either role, preventing incorrect restoration.


362-465: Well-structured restore flow with good error handling.

The restore response handler properly:

  • Validates payload type and handles empty responses
  • Uses completers with timeouts to coordinate async order detail requests
  • Derives trade keys from indices and determines role from order details
  • Manages key index advancement based on restored count
  • Cleans up completers in all code paths (success, individual failure, and global failure)

The batching of order detail requests (max 20 per batch) is a sensible performance optimization.


454-456: Confirmed fix for key index advancement.

The condition now correctly uses restoredCount > 0 instead of maxTradeIndex > 0, ensuring the key index advances even when the only restored trade had index 0. This addresses the issue flagged in previous reviews.


467-494: LGTM! Clean orders response handler.

The handler properly validates the payload type, iterates through orders, and completes the corresponding completers. Good defensive check for !completer.isCompleted to avoid completing twice.

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 (1)
lib/services/mostro_service.dart (1)

61-69: Don’t mark events as processed before handling; can drop messages.

You write to eventStore before decrypt/routing. If a DM arrives before a session exists, it’s skipped now and won’t be processed later after restore. Mark as processed only after you actually handle it (restore/orders or matched session).

   final eventStore = ref.read(eventStorageProvider);
-  if (await eventStore.hasItem(event.id!)) return;
-  await eventStore.putItem(
-    event.id!,
-    {
-      'id': event.id,
-      'created_at': event.createdAt!.millisecondsSinceEpoch ~/ 1000,
-    },
-  );
+  if (await eventStore.hasItem(event.id!)) return;
+  var handled = false;
   if (msg.action == Action.restoreSession) {
-    await _handleRestoreResponse(msg);
-    return;
+    await _handleRestoreResponse(msg);
+    await eventStore.putItem(event.id!, {
+      'id': event.id,
+      'created_at': event.createdAt!.millisecondsSinceEpoch ~/ 1000,
+    });
+    return;
   }
 
   if (msg.action == Action.orders) {
-    await _handleOrdersResponse(msg);
-    return;
+    await _handleOrdersResponse(msg);
+    await eventStore.putItem(event.id!, {
+      'id': event.id,
+      'created_at': event.createdAt!.millisecondsSinceEpoch ~/ 1000,
+    });
+    return;
   }
 
   if (matchingSession != null) {
     final messageStorage = ref.read(mostroStorageProvider);
     await messageStorage.addMessage(decryptedEvent.id!, msg);
     _logger.i(
-      'Received DM, Event ID: ${decryptedEvent.id} with payload: ${decryptedEvent.content}',
+      'Received DM, Event ID: ${decryptedEvent.id}',
     );
 
     await _maybeLinkChildOrder(msg, matchingSession);
+    handled = true;
   }
 } catch (e, stackTrace) {
   _logger.e('Error processing event', error: e, stackTrace: stackTrace);
 }
+if (handled) {
+  await eventStore.putItem(event.id!, {
+    'id': event.id,
+    'created_at': event.createdAt!.millisecondsSinceEpoch ~/ 1000,
+  });
+}

Also applies to: 104-112, 114-123, 123-126

♻️ Duplicate comments (4)
lib/data/models/orders_request.dart (1)

46-54: Equality/hash now correct and order-aware.

Using ListEquality for == and hash aligns equality with hash. LGTM.

Based on learnings

lib/services/mostro_service.dart (3)

316-321: Master key null-guard added — good.

Prevents crashes when master key is missing.


344-348: Master key null-guard for order details — good.

Fails fast with clear log.


425-433: Correct role derivation — issue resolved.

Derives buyer/seller from order details; no longer hard-coded.

🧹 Nitpick comments (3)
lib/data/models/orders_request.dart (2)

7-11: Make ids immutable to prevent external mutations.

Defensively copy to an unmodifiable list in the constructor.

-  OrdersRequest({required this.ids}) {
+  OrdersRequest({required List<String> ids})
+      : ids = List.unmodifiable(ids) {
     if (ids.isEmpty) {
       throw ArgumentError('Order IDs list cannot be empty');
     }
   }

50-54: Minor nit: add generic parameter to ListEquality.

Be explicit on the type parameter.

-           const ListEquality().equals(other.ids, ids);
+           const ListEquality<String>().equals(other.ids, ids);
-  int get hashCode => const ListEquality().hash(ids);
+  int get hashCode => const ListEquality<String>().hash(ids);

Based on learnings

lib/services/mostro_service.dart (1)

415-421: Use const Duration.

Tiny allocation win.

-          final orderDetails = await completer.future.timeout(
-            Duration(seconds: 10),
+          final orderDetails = await completer.future.timeout(
+            const Duration(seconds: 10),
             onTimeout: () {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0ec3af and 40fbbfb.

📒 Files selected for processing (3)
  • lib/data/models/orders_request.dart (1 hunks)
  • lib/data/models/orders_response.dart (1 hunks)
  • lib/services/mostro_service.dart (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/data/models/orders_response.dart
🧰 Additional context used
📓 Path-based instructions (4)
lib/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

lib/**/*.dart: Use post-frame callbacks for UI side effects (e.g., SnackBars/dialogs)
Use S.of(context).keyName for all user-facing strings (no hardcoded text)
Always use localized strings instead of hardcoded text
Pass BuildContext to methods that need localization (use S.of(context))
Check mounted before using BuildContext after async gaps
Use const constructors where possible

Name Riverpod providers as Provider or Notifier

Files:

  • lib/data/models/orders_request.dart
  • lib/services/mostro_service.dart
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.dart: All code comments must be in English
Use clear, concise English for variable and function names
Maintain zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., prefer withValues() over withOpacity())
Remove unused imports and dependencies

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

Files:

  • lib/data/models/orders_request.dart
  • lib/services/mostro_service.dart
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/models/orders_request.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/mostro_service.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: build
🔇 Additional comments (1)
lib/services/mostro_service.dart (1)

41-49: Master-key subscription wiring looks good.

Subscribes and cancels properly. 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: 0

Caution

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

⚠️ Outside diff range comments (2)
lib/features/settings/settings_notifier.dart (1)

43-46: Likely wrong field in copyWith for privacy mode

copyWith(privacyModeSetting: newValue) looks inconsistent with Settings.default using fullPrivacyMode. If the model doesn’t expose privacyModeSetting, this won’t compile or silently do nothing. Switch to fullPrivacyMode.

-  Future<void> updatePrivacyMode(bool newValue) async {
-    state = state.copyWith(privacyModeSetting: newValue);
+  Future<void> updatePrivacyMode(bool newValue) async {
+    state = state.copyWith(fullPrivacyMode: newValue);
     await _saveToPrefs();
   }
lib/services/mostro_service.dart (1)

100-103: Guard against empty decoded list.

After confirming the decoded result is a List, the code accesses result[0] without checking if the list is empty, which will throw an IndexError.

Apply this defensive check:

  final result = jsonDecode(decryptedEvent.content!);
  if (result is! List) return;
+ if (result.isEmpty) {
+   _logger.w('Decoded result list is empty');
+   return;
+ }

  final msg = MostroMessage.fromJson(result[0]);
🧹 Nitpick comments (10)
lib/features/settings/settings_notifier.dart (1)

65-68: Avoid redundant write when Mostro pubkey is unchanged

If oldPubkey == newValue, we’re persisting identical state. Early-return to reduce needless I/O.

   } else {
-    // Only update pubkey if it's the same (without reset)
-    state = state.copyWith(mostroPublicKey: newValue);
-    await _saveToPrefs();
+    _logger.d('Mostro pubkey unchanged; skipping persistence');
+    return;
   }
lib/features/notifications/widgets/notification_item.dart (2)

74-131: New actions are no-ops on tap; verify intended UX

restoreSession, orders, lastTradeIndex just break. If the intent is to surface the restoration UI (overlay) on tap, consider triggering the relevant flow even when orderId is null, or move these cases outside the orderId guard.

-    if (notification.orderId != null) {
-      switch (notification.action) {
+    if (notification.action == mostro_action.Action.restoreSession ||
+        notification.action == mostro_action.Action.orders ||
+        notification.action == mostro_action.Action.lastTradeIndex) {
+      // Option: trigger restoration overlay or navigate accordingly
+      // ref.read(restorationNotifierProvider.notifier).start();
+      return;
+    }
+    if (notification.orderId != null) {
+      switch (notification.action) {
         // ...
       }
     }

158-199: Dialog side-effect: wrap in post-frame to match UI side-effect guideline

To align with “Use post-frame callbacks for UI side effects,” dispatch showDialog in a post-frame callback.

-  void _showDeleteConfirmationDialog(BuildContext context, WidgetRef ref) {
-    showDialog(
+  void _showDeleteConfirmationDialog(BuildContext context, WidgetRef ref) {
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      if (!context.mounted) return;
+      showDialog(
         context: context,
         builder: (context) => AlertDialog(
           // ...
-    );
+      );
+    });
   }
lib/features/settings/restoration_provider.dart (1)

5-8: Provider wiring looks good

Name matches convention; minimal, clear setup. If RestorationNotifier later needs dependencies, switch to RestorationNotifier(ref) and inject via ref.

lib/core/app.dart (1)

216-220: Localize error message per guidelines

Replace hardcoded 'Initialization Error' with a localized string via S.of(context), and add delegates to this MaterialApp if needed.

-      error: (err, stack) => MaterialApp(
-        home: Scaffold(
-          body: Center(child: Text('Initialization Error: $err')),
-        ),
-      ),
+      error: (err, stack) => MaterialApp(
+        localizationsDelegates: const [
+          S.delegate,
+          GlobalMaterialLocalizations.delegate,
+          GlobalWidgetsLocalizations.delegate,
+          GlobalCupertinoLocalizations.delegate,
+        ],
+        supportedLocales: S.supportedLocales,
+        home: Builder(
+          builder: (context) => Scaffold(
+            body: Center(child: Text(S.of(context)!.initializationError(err.toString()))),
+          ),
+        ),
+      ),

Note: add a new intl key like initializationError(message).

integration_test/test_helpers.dart (1)

310-318: Confirm method signatures against MostroService

requestOrderDetails takes List non-nullable here, but mocks elsewhere use nullable List? If the interface expects nullable, this override won’t compile. Align signature and add a null/empty guard.

-  Future<void> requestOrderDetails(List<String> orderIds) async {}
+  Future<void> requestOrderDetails(List<String>? orderIds) async {
+    if (orderIds == null || orderIds.isEmpty) return;
+    // no-op in fake
+  }
lib/features/settings/restoration_overlay.dart (2)

37-38: Avoid duplicate status icons (Completed/Failed).

Both _buildStatusIcon and _buildProgressIndicator render icons for completed/failed, leading to two icons. Show only one.

Apply this diff to keep the top icon and suppress the second one for terminal states; also show determinate progress when possible:

   Widget _buildProgressIndicator(RestorationState state) {
-    if (state.status == RestorationStatus.completed) {
-      return const Icon(
-        Icons.check_circle,
-        color: Colors.green,
-        size: 48,
-      );
-    }
-
-    if (state.status == RestorationStatus.failed) {
-      return const Icon(
-        Icons.error,
-        color: Colors.red,
-        size: 48,
-      );
-    }
+    // Suppress secondary icon for terminal states; the header icon already conveys status.
+    if (state.status == RestorationStatus.completed ||
+        state.status == RestorationStatus.failed) {
+      return const SizedBox.shrink();
+    }
 
-    // Show generic circular loading indicator for all loading states
-    return const CircularProgressIndicator(
-      color: AppTheme.activeColor,
-    );
+    // Show determinate progress when loading order details
+    if (state.status == RestorationStatus.loadingDetails &&
+        state.totalOrders > 0) {
+      return Column(
+        mainAxisSize: MainAxisSize.min,
+        children: [
+          LinearProgressIndicator(
+            color: AppTheme.activeColor,
+            value: state.progress,
+            backgroundColor: Colors.white.withValues(alpha: 0.08),
+            minHeight: 6,
+          ),
+          const SizedBox(height: 8),
+          Text(
+            '${state.restoredOrders + state.skippedOrders}/${state.totalOrders}',
+            style: const TextStyle(color: AppTheme.textSecondary),
+          ),
+        ],
+      );
+    }
+
+    // Fallback indeterminate loader
+    return const CircularProgressIndicator(color: AppTheme.activeColor);
   }

Also applies to: 91-112


114-129: Drop unnecessary null-assert on S.of(context).

S.of(context) is non-null in generated l10n; the bang operator may trigger analyzer warnings.

-        return S.of(context)!.restorationCleaningTitle;
+        return S.of(context).restorationCleaningTitle;
@@
-        return S.of(context)!.restorationRequestingTitle;
+        return S.of(context).restorationRequestingTitle;
@@
-        return S.of(context)!.restorationLoadingDetailsTitle;
+        return S.of(context).restorationLoadingDetailsTitle;
@@
-        return S.of(context)!.restorationCompletedTitle;
+        return S.of(context).restorationCompletedTitle;
@@
-        return S.of(context)!.restorationFailedTitle;
+        return S.of(context).restorationFailedTitle;
@@
-        return S.of(context)!.restorationCleaningMessage;
+        return S.of(context).restorationCleaningMessage;
@@
-        return S.of(context)!.restorationRequestingMessage;
+        return S.of(context).restorationRequestingMessage;
@@
-        return S.of(context)!.restorationLoadingDetailsMessage;
+        return S.of(context).restorationLoadingDetailsMessage;
@@
-        return S.of(context)!.restorationCompletedMessage;
+        return S.of(context).restorationCompletedMessage;
@@
-        return state.errorMessage ?? S.of(context)!.restorationFailedMessage;
+        return state.errorMessage ?? S.of(context).restorationFailedMessage;

As per coding guidelines.

Also applies to: 131-146

lib/features/settings/restoration_notifier.dart (1)

29-41: DRY the auto-reset logic and make delay explicit.

Reduce duplication and centralize the delayed reset behavior.

 class RestorationNotifier extends StateNotifier<RestorationState> {
-  final _logger = Logger();
+  final _logger = Logger();
+  static const _autoResetDelay = Duration(seconds: 3);
+
+  void _autoResetWhenStable(RestorationStatus expected) {
+    Future.delayed(_autoResetDelay, () {
+      if (!mounted) return;
+      if (state.status == expected) reset();
+    });
+  }
@@
   void complete(int totalOrders, int restoredOrders, int skippedOrders) {
@@
-    Future.delayed(const Duration(seconds: 3), () {
-      if (mounted && state.status == RestorationStatus.completed) {
-        reset();
-      }
-    });
+    _autoResetWhenStable(RestorationStatus.completed);
   }
@@
   void fail(String errorMessage) {
@@
-    Future.delayed(const Duration(seconds: 3), () {
-      if (mounted && state.status == RestorationStatus.failed) {
-        reset();
-      }
-    });
+    _autoResetWhenStable(RestorationStatus.failed);
   }

Also applies to: 43-52, 5-7

lib/features/settings/restoration_state.dart (1)

84-87: Clamp progress to [0,1] and guard zero/negative totals.

Prevents accidental >100% due to race conditions or rounding.

-  double get progress {
-    if (totalOrders == 0) return 0.0;
-    return (restoredOrders + skippedOrders) / totalOrders;
-  }
+  double get progress {
+    if (totalOrders <= 0) return 0.0;
+    return ((restoredOrders + skippedOrders) / totalOrders).clamp(0.0, 1.0);
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40fbbfb and e3c9e83.

📒 Files selected for processing (16)
  • integration_test/test_helpers.dart (1 hunks)
  • lib/core/app.dart (2 hunks)
  • lib/data/models/enums/action.dart (1 hunks)
  • lib/features/notifications/utils/notification_message_mapper.dart (2 hunks)
  • lib/features/notifications/widgets/notification_item.dart (1 hunks)
  • lib/features/settings/restoration_notifier.dart (1 hunks)
  • lib/features/settings/restoration_overlay.dart (1 hunks)
  • lib/features/settings/restoration_provider.dart (1 hunks)
  • lib/features/settings/restoration_state.dart (1 hunks)
  • lib/features/settings/settings_notifier.dart (2 hunks)
  • lib/l10n/intl_en.arb (2 hunks)
  • lib/l10n/intl_es.arb (2 hunks)
  • lib/l10n/intl_it.arb (2 hunks)
  • lib/services/mostro_service.dart (6 hunks)
  • lib/shared/providers/app_init_provider.dart (2 hunks)
  • test/mocks.mocks.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • lib/data/models/enums/action.dart
  • lib/features/notifications/utils/notification_message_mapper.dart
  • test/mocks.mocks.dart
🧰 Additional context used
📓 Path-based instructions (9)
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.dart: All code comments must be in English
Use clear, concise English for variable and function names
Maintain zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., prefer withValues() over withOpacity())
Remove unused imports and dependencies

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

Files:

  • integration_test/test_helpers.dart
  • lib/features/settings/restoration_notifier.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/settings/restoration_overlay.dart
  • lib/services/mostro_service.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/features/settings/restoration_state.dart
  • lib/core/app.dart
  • lib/shared/providers/app_init_provider.dart
  • lib/features/settings/restoration_provider.dart
integration_test/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Place integration tests under integration_test/

integration_test/**/*.dart: Place end-to-end integration flows under integration_test/
Ensure integration coverage for flows involving networking, background work, or session recovery

Files:

  • integration_test/test_helpers.dart
lib/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

lib/**/*.dart: Use post-frame callbacks for UI side effects (e.g., SnackBars/dialogs)
Use S.of(context).keyName for all user-facing strings (no hardcoded text)
Always use localized strings instead of hardcoded text
Pass BuildContext to methods that need localization (use S.of(context))
Check mounted before using BuildContext after async gaps
Use const constructors where possible

Name Riverpod providers as Provider or Notifier

Files:

  • lib/features/settings/restoration_notifier.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/settings/restoration_overlay.dart
  • lib/services/mostro_service.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/features/settings/restoration_state.dart
  • lib/core/app.dart
  • lib/shared/providers/app_init_provider.dart
  • lib/features/settings/restoration_provider.dart
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/settings/restoration_notifier.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/settings/restoration_overlay.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/features/settings/restoration_state.dart
  • lib/features/settings/restoration_provider.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/mostro_service.dart
lib/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep dependency wiring in lib/core/

Files:

  • lib/core/app.dart
lib/l10n/intl_{en,es,it}.arb

📄 CodeRabbit inference engine (CLAUDE.md)

Add new localization keys to all three ARB files (en, es, it)

Files:

  • lib/l10n/intl_es.arb
  • lib/l10n/intl_it.arb
  • lib/l10n/intl_en.arb
lib/l10n/*.arb

📄 CodeRabbit inference engine (CLAUDE.md)

For parameterized strings, include proper ARB metadata

Files:

  • lib/l10n/intl_es.arb
  • lib/l10n/intl_it.arb
  • lib/l10n/intl_en.arb
lib/shared/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared utilities in lib/shared/

Files:

  • lib/shared/providers/app_init_provider.dart
🧠 Learnings (5)
📚 Learning: 2025-06-04T19:35:20.209Z
Learnt from: chebizarro
PR: MostroP2P/mobile#110
File: test/notifiers/take_order_notifier_test.dart:72-74
Timestamp: 2025-06-04T19:35:20.209Z
Learning: MostroService methods like takeBuyOrder() and takeSellOrder() return Future<void> and trigger side effects through other mechanisms rather than direct return values. When testing these methods, focus on verifying method calls and testing state changes through the provider system rather than mocking return values.

Applied to files:

  • integration_test/test_helpers.dart
📚 Learning: 2025-10-21T21:47:03.440Z
Learnt from: Catrya
PR: MostroP2P/mobile#327
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:157-182
Timestamp: 2025-10-21T21:47:03.440Z
Learning: In MostroP2P/mobile, for Action.canceled handling in abstract_mostro_notifier.dart (Riverpod StateNotifier), do not add mounted checks after async sessionNotifier.deleteSession(orderId) as they break order state synchronization during app restart. The Action.canceled flow contains critical business logic that must complete fully; Riverpod handles provider disposal automatically. Mounted checks should only protect UI operations, not business logic in StateNotifiers.

Applied to files:

  • lib/features/settings/restoration_notifier.dart
📚 Learning: 2025-10-01T14:10:17.742Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-01T14:10:17.742Z
Learning: Applies to lib/l10n/intl_{en,es,it}.arb : Add new localization keys to all three ARB files (en, es, it)

Applied to files:

  • lib/l10n/intl_es.arb
  • lib/l10n/intl_it.arb
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, Riverpod code generation is used with `Riverpod` annotations. Providers like `eventStorageProvider` are generated in `.g.dart` files from annotated functions in the main provider files. These providers are accessible by importing the main provider file (e.g., `mostro_service_provider.dart`), not by importing a separate provider file.

Applied to files:

  • lib/shared/providers/app_init_provider.dart
  • lib/features/settings/restoration_provider.dart
📚 Learning: 2025-10-07T17:31:12.117Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T17:31:12.117Z
Learning: Applies to lib/**/*.dart : Name Riverpod providers as <Feature>Provider or <Feature>Notifier

Applied to files:

  • lib/features/settings/restoration_provider.dart
🔇 Additional comments (18)
lib/features/settings/settings_notifier.dart (1)

14-14: Constructor simplification LGTM

Removal of Ref from the constructor narrows responsibilities and matches how it’s instantiated in tests. No issues.

lib/features/notifications/widgets/notification_item.dart (1)

55-58: Good use of withValues(alpha: ...)

Conforms to latest Flutter API preference over withOpacity.

lib/core/app.dart (1)

197-205: Overlay integration LGTM

Stack + builder is a clean way to add RestorationOverlay app‑wide. Ensure the overlay internally uses IgnorePointer/Visibility when idle to avoid intercepting taps.

lib/l10n/intl_it.arb (1)

418-428: LGTM — Italian translations provided for restoration and mnemonic validation.

Also applies to: 929-932

lib/l10n/intl_es.arb (1)

389-399: LGTM — Spanish translations provided for restoration and mnemonic validation.

Also applies to: 873-876

lib/l10n/intl_en.arb (1)

466-477: Verification passed — all keys present across en/es/it locales.

The 12 new localization keys (restoration and mnemonic validation strings) are confirmed present in all three language files. Parity is maintained; no issues found.

lib/shared/providers/app_init_provider.dart (1)

31-69: Await is unnecessary; reinitializeMasterKeySubscription() is void.

The verification confirms that reinitializeMasterKeySubscription() at lib/features/subscriptions/subscription_manager.dart:101 is declared as void and returns synchronously. The diff already comments out the suggested await, which is correct—no awaiting is needed.

The optional guard pattern (checking isRestoring) is technically valid since RestorationState includes an isRestoring getter that returns true for cleaning, requesting, or loadingDetails statuses. However, per learnings on this codebase, Riverpod StateNotifiers should avoid adding extra defensive guards in business logic flows; Riverpod handles concurrent state updates and provider disposal automatically. The guard adds complexity for marginal benefit in an unlikely race scenario.

No further changes needed; the code is correct as-is.

lib/services/mostro_service.dart (11)

24-31: LGTM!

The new fields appropriately support restore orchestration with correct typing and naming conventions.


35-51: LGTM!

The dual subscription setup with distinct error handling and cancelOnError: false ensures resilience.


53-58: LGTM!

Proper resource cleanup with safe null-aware cancellation of subscriptions and timers.


60-66: LGTM!

Event deduplication prevents reprocessing cached events efficiently.


78-94: LGTM!

Private key resolution correctly handles session matching, master key fallback, and graceful skip when neither applies.


319-360: LGTM!

Restore session request properly handles master key validation, state initialization, timeout setup, and failure paths.


362-394: LGTM!

Order details batching (max 20 per request) with proper master key validation and clear progress logging.


396-583: LGTM!

Comprehensive restore handling with proper role detection, dispute restoration via synthetic messages, batched detail loading with timeouts, and deferred trade index updates.


585-646: LGTM!

Last trade index request/response flow with completer-based deduplication, appropriate timeout, and proper key index advancement.


648-669: LGTM!

Batch response handler safely completes pending order detail requests with proper validation and logging.


703-719: LGTM!

Settings update properly clears all restoration state when Mostro instance changes, preventing stale data.

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 (1)
lib/services/mostro_service.dart (1)

119-121: Remove decrypted payload from logs.

Line 120 logs the full decrypted content, which can leak sensitive data like invoices. Log only metadata (event ID, action, order ID) instead.

Apply this fix:

-      _logger.i(
-        'Received DM, Event ID: ${decryptedEvent.id ?? event.id} with payload: ${decryptedEvent.content}',
-      );
+      _logger.i(
+        'Received DM, Event ID: ${decryptedEvent.id ?? event.id}, action: ${msg.action}, orderId: ${msg.id}',
+      );
♻️ Duplicate comments (1)
lib/features/key_manager/key_management_screen.dart (1)

683-686: Allow non-ASCII BIP-39 characters in mnemonic validation.

The regex ^[a-zA-Z\s]+$ only permits ASCII letters, so valid Spanish mnemonics (e.g., "ábaco", "época"), French mnemonics (e.g., "être", "événement"), and Italian mnemonics (e.g., "città", "però") will fail validation, blocking imports for non-English users.

Apply this diff to accept Unicode letters:

-    final validCharacters = RegExp(r'^[a-zA-Z\s]+$');
+    final validCharacters = RegExp(r'^[\p{L}\s]+$', unicode: true);
     if (!validCharacters.hasMatch(trimmed)) {
🧹 Nitpick comments (3)
lib/features/settings/settings_notifier.dart (1)

54-59: Consider removing redundant inline comments.

The inline comments on lines 57-58 repeat information already conveyed by the comprehensive comment on line 54. Consider removing them to reduce redundancy.

     // COMPLETE RESET: Clear blacklist and user relays when changing Mostro
     state = state.copyWith(
       mostroPublicKey: newValue,
-      blacklistedRelays: const [], // Clear blacklist
-      userRelays: const [],         // Clear user relays
+      blacklistedRelays: const [],
+      userRelays: const [],
     );
lib/features/key_manager/key_management_screen.dart (2)

99-155: Consider improving error handling for partial failures.

The current implementation imports the mnemonic before clearing state (which addresses the previous review concern), but if the import succeeds and then a subsequent operation fails (e.g., deleteAll() throws), the system is left in an inconsistent state with a new key but old sessions/storage possibly remaining.

While the try-catch will show an error, users won't know whether the import succeeded or failed, making recovery difficult.

Consider adding more granular error handling:

  Future<void> _importKeyAndRestore() async {
    final importValue = _importController.text.trim();
    if (importValue.isEmpty) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(S.of(context)!.mnemonicValidationEmpty),
            backgroundColor: Colors.red,
            duration: const Duration(seconds: 3),
          ),
        );
      }
      return;
    }

    try {
      final keyManager = ref.read(keyManagerProvider);
      await keyManager.importMnemonic(importValue);
      await _loadKeys();
+   } catch (e) {
+     if (mounted) {
+       ScaffoldMessenger.of(context).showSnackBar(
+         SnackBar(
+           content: Text(S.of(context)!.importFailed(e.toString())),
+           backgroundColor: Colors.red,
+           duration: const Duration(seconds: 5),
+         ),
+       );
+     }
+     return;
+   }
+
+   try {
      final sessionNotifier = ref.read(sessionNotifierProvider.notifier);
      await sessionNotifier.reset();

      final mostroStorage = ref.read(mostroStorageProvider);
      await mostroStorage.deleteAll();

      final eventStorage = ref.read(eventStorageProvider);
      await eventStorage.deleteAll();

      final notificationsRepo = ref.read(notificationsRepositoryProvider);
      await notificationsRepo.clearAll();

      ref.read(subscriptionManagerProvider).reinitializeMasterKeySubscription();

      await ref.read(mostroServiceProvider).requestRestoreSession();

      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(S.of(context)!.keyImportedSuccessfully),
            backgroundColor: Colors.green,
            duration: const Duration(seconds: 3),
          ),
        );
      }
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
-           content: Text(S.of(context)!.importFailed(e.toString())),
+           content: Text(S.of(context)!.restorationFailed(e.toString())),
            backgroundColor: Colors.red,
            duration: const Duration(seconds: 5),
          ),
        );
      }
    }
  }

654-671: Add tooltip or label to restore button for clarity.

The restore button only displays an icon without any text label. Users may not understand its purpose, especially since it triggers destructive operations (clearing all sessions, storage, and notifications).

Add a tooltip to improve discoverability:

        Expanded(
          flex: 3,
          child: OutlinedButton(
            onPressed: _restoreCurrentUser,
            style: OutlinedButton.styleFrom(
              side: const BorderSide(color: AppTheme.activeColor),
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(8),
              ),
              padding: const EdgeInsets.symmetric(vertical: 16),
            ),
-           child: const Icon(
-             LucideIcons.refreshCw,
-             size: 20,
-             color: AppTheme.activeColor,
+           child: Tooltip(
+             message: S.of(context)!.restoreSession, // Add to localizations
+             child: const Icon(
+               LucideIcons.refreshCw,
+               size: 20,
+               color: AppTheme.activeColor,
+             ),
            ),
          ),
        ),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3c9e83 and 73e8146.

📒 Files selected for processing (12)
  • lib/data/models/enums/action.dart (1 hunks)
  • lib/data/models/mostro_message.dart (1 hunks)
  • lib/data/models/payload.dart (3 hunks)
  • lib/features/key_manager/key_management_screen.dart (5 hunks)
  • lib/features/notifications/utils/notification_message_mapper.dart (2 hunks)
  • lib/features/notifications/widgets/notification_item.dart (1 hunks)
  • lib/features/settings/settings_notifier.dart (2 hunks)
  • lib/l10n/intl_en.arb (2 hunks)
  • lib/l10n/intl_es.arb (2 hunks)
  • lib/l10n/intl_it.arb (2 hunks)
  • lib/services/mostro_service.dart (8 hunks)
  • test/mocks.mocks.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • lib/l10n/intl_es.arb
  • lib/data/models/payload.dart
  • lib/l10n/intl_it.arb
🧰 Additional context used
📓 Path-based instructions (10)
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/notifications/widgets/notification_item.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/data/models/mostro_message.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/settings/settings_notifier.dart
  • lib/data/models/enums/action.dart
  • lib/features/notifications/widgets/notification_item.dart
  • test/mocks.mocks.dart
  • lib/services/mostro_service.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/data/models/mostro_message.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/settings/settings_notifier.dart
  • lib/data/models/enums/action.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/services/mostro_service.dart
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/models/mostro_message.dart
  • lib/data/models/enums/action.dart
lib/l10n/*.arb

📄 CodeRabbit inference engine (CLAUDE.md)

lib/l10n/*.arb: Maintain ARB files for en/es/it in lib/l10n/ (intl_en.arb, intl_es.arb, intl_it.arb)
When adding a new localization key, update all three ARB files (en, es, it)
Use proper ARB metadata for parameterized strings
After modifying ARB files, run dart run build_runner build -d to regenerate localization

Files:

  • lib/l10n/intl_en.arb
test/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

test/**/*.dart: Mirror feature layout for tests under test/
Prefer Mockito fakes generated via build_runner in tests

test/**/*.dart: Unit tests belong in test/; integration tests in integration_test/
Mocks are generated via Mockito; regenerate with dart run build_runner build -d after mock changes

Files:

  • test/mocks.mocks.dart
test/mocks.mocks.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Do not manually edit test/mocks.mocks.dart (Mockito-generated)

Files:

  • test/mocks.mocks.dart
**/*.mocks.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Do not manually edit any *.mocks.dart generated Mockito files

Files:

  • test/mocks.mocks.dart
**/*.{g.dart,mocks.dart}

📄 CodeRabbit inference engine (CLAUDE.md)

Never add per-item ignore comments to generated files; rely on file-level ignores or regenerate

Files:

  • test/mocks.mocks.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/mostro_service.dart
🧠 Learnings (2)
📚 Learning: 2025-10-22T12:29:26.958Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.958Z
Learning: Applies to lib/features/relays/relays_notifier.dart : Implement relay operations: syncWithMostroInstance, removeRelayWithBlacklist, addRelayWithSmartValidation, _handleMostroRelayListUpdate

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.958Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.958Z
Learning: Applies to test/mocks.mocks.dart : Do not manually edit test/mocks.mocks.dart (Mockito-generated)

Applied to files:

  • test/mocks.mocks.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: build
🔇 Additional comments (19)
lib/l10n/intl_en.arb (1)

467-478: LGTM! Restoration and validation strings are clear and well-structured.

The new localization keys for the restoration flow and mnemonic validation follow ARB conventions and provide clear, user-friendly messages in proper English.

Also applies to: 895-898

lib/features/notifications/widgets/notification_item.dart (1)

124-127: LGTM! Internal restoration actions handled correctly.

The three new restoration-related actions (restoreSession, orders, lastTradeIndex) intentionally break without navigation, which is appropriate since these are internal system actions rather than user-actionable notifications.

lib/data/models/mostro_message.dart (1)

45-45: LGTM! Restore payload support added correctly.

The additional json['restore'] fallback extends parsing to handle restoration payloads while preserving the existing fallback chain for order and cant-do keys.

lib/features/notifications/utils/notification_message_mapper.dart (1)

86-89: LGTM! Consistent notification mapping for restoration actions.

The three new restoration-related actions are consistently mapped to generic order update title and message keys, which is appropriate for these internal system actions.

Also applies to: 181-184

lib/data/models/enums/action.dart (1)

41-44: LGTM! New action enum values follow established conventions.

The three new restoration-related actions (restoreSession, orders, lastTradeIndex) follow the existing kebab-case string value pattern and integrate cleanly into the enum.

test/mocks.mocks.dart (1)

737-766: Generated mock methods for restoration flow.

These three new mock method overrides (requestRestoreSession, requestOrderDetails, requestLastTradeIndex) were auto-generated by Mockito to support the new restoration methods in MostroService. As this is a generated file, no manual edits should be made here.

Based on learnings

lib/services/mostro_service.dart (10)

24-31: Good state management structure for restoration.

The new fields properly track restoration state with clear purposes: event deduplication, order detail coordination, timeout management, and progress tracking.


36-50: Dual subscription setup looks solid.

Subscribing to both orders and masterKey streams with separate error handlers and proper cleanup in dispose() provides the coverage needed for both regular trading and restoration flows.

Also applies to: 55-56


61-92: Event deduplication and private key resolution improved.

The addition of _processedEventIds prevents duplicate processing, and the refactored private key resolution correctly tries matching session first, then falls back to master key when appropriate.


101-104: Empty list guard properly implemented.

Checking both result is! List and result.isEmpty before accessing result[0] prevents potential index-out-of-bounds crashes.


334-374: Restoration request method is well-guarded.

Good defensive checks: clears stale state, validates master key availability, and sets up a 60-second timeout with proper failure handling.


376-408: Batch request logic is sound.

Guarding against empty lists and null master key, then batching up to 20 order IDs per request, is a sensible approach to avoid overwhelming the server or hitting message size limits.


514-523: Role determination now correctly matches trade keys.

The role is properly derived by comparing the order's buyerTradePubkey and sellerTradePubkey against the derived trade key, addressing the previous issue where all restored sessions were hardcoded to Role.seller.


410-608: Complex restoration flow—ensure thorough testing.

The _handleRestoreResponse method orchestrates many async operations: completers, timeouts, role derivation, synthetic message creation, and progress updates. While the logic appears sound, this complexity increases the risk of subtle bugs under edge cases (e.g., partial failures, rapid reconnections, concurrent restores).

Ensure integration tests cover:

  • Partial restore failures (some orders succeed, others timeout)
  • Concurrent restore attempts
  • Role mismatch scenarios
  • Timeout and completer cleanup under various failure modes

610-671: Last trade index request and response handling look solid.

Proper null guards, completer lifecycle management, and timeout handling ensure the key index is updated reliably after restoration completes.


729-743: Good instance-change cleanup.

Clearing restoration state and resetting the UI when the Mostro public key changes prevents stale data from persisting across instance switches.

lib/features/settings/settings_notifier.dart (3)

51-52: LGTM: Improved log message clarity.

The updated log message "Mostro instance change detected" is clearer and more specific than the previous wording.


61-63: LGTM: Proper async handling added.

Good addition of await for _saveToPrefs() and the success log message. This ensures the save operation completes before the method returns and provides better observability.


14-14: Constructor simplification verified—no breaking changes detected.

The removal of the optional ref parameter is safe. The class fields confirm no ref field exists, and the codebase instantiation in integration_test/test_helpers.dart only passes the _prefs parameter, matching the updated constructor signature. The ref appearing in provider overrides is a standard Riverpod callback parameter, unrelated to the removed constructor parameter.

@BraCR10
Copy link
Contributor Author

BraCR10 commented Oct 23, 2025

Saludos @Catrya Cambios listos para revisión

Nuevas funcionalidades

  • Overlay de restauración:
    Indica el proceso de restauración con diferentes mensajes dependiendo del estado de carga y posibles errores.
    Posee un timeout de 60 segundos; en caso de no recibir respuesta o presentarse algún problema, el overlay desaparece automáticamente.
    image

  • Botón para restablecer órdenes:
    Permite solicitar la actualizacion de las órdenes del usuario actual, en caso de algún fallo o inconsistencia.
    image

  • Trigger de sincronización:
    Se ejecuta automáticamente al cambiar de mostro.

  • Request de índice de trade actual:
    Se implementó la consulta de current index trade para evitar problemas, utilizando:
    https://mostro.network/protocol/last_trade_index.html


Consideraciones en el backend detectadas al depurar

1. Órdenes con estado success no se restauran

Después de que una orden se marca como success, al solicitar la restauración al backend, este no devuelve las órdenes con ese estado, por lo que no es posible restablecerlas en la seccion de my trades en la UI.

Antes de la confirmación:
WhatsApp Image 2025-10-22 at 14 34 54_721c50c5

Momento del settled:
WhatsApp Image 2025-10-22 at 14 35 43_729dfdf6

Después de restaurar nuevamente:
Como se puede ver, se devuelven solo 4 órdenes, cuando antes eran 5. Falta la orden con estado success.
WhatsApp Image 2025-10-22 at 14 36 11_1534402c


2. Órdenes en disputa continúan marcadas como tal tras el release

Al tener una disputa y posteriormente liberar el pago (released), si se solicitan los datos para la restauración, la orden sigue apareciendo como “en disputa”, aunque ya se haya confirmado.

Orden después del settled:
WhatsApp Image 2025-10-22 at 18 37 20_898e5412

Resultado tras la restauración:
La orden aún se muestra como “en disputa”:
WhatsApp Image 2025-10-22 at 18 38 25_8d40823a


Quedo atento a cualquier ajuste o solución que sea necesario.
Gracias.

Copy link
Member

@Catrya Catrya left a comment

Choose a reason for hiding this comment

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

@BraCR10 cuando restauro un usuario que tiene ordenes active o fiatsent, no las muestra en el status correcto, sino que, o no las muestra, o lo hace en el status anterior. Para que las muestre debo dar al botón de reintentar restaurar órdenes, pero debería hacerlo bien a la primera.

  • Tenía 1 orden en Active y 2 Pending
  • Creé un nuevo usuario
  • Restauré el anterior
  • En My Trades solo aperecían las 2 Pending
  • Luego le di al boton de reintentar restauracion y la orden en Active apareció en Active.

Después de eso continué el intercambio y la orden fue a fiat sent

  • volví a crear un nuevo usuario y a restaurar el anterior
  • la orden ahora aparecía en Active
  • tuve que volver a dar al boton de reintentar para que se pusiera en Fiat-sent

Además de eso, como en 30 min me han salido 2 veces las mismas notificaciones en el movil con la última sesión restaurada, de que contacte a la contraparte, y de que ya está en fiat-sent

image image image

@Catrya
Copy link
Member

Catrya commented Oct 23, 2025

Además a @grunch al restaurar el usuario, una orden que tenia en status active le salio en status waiting-buyer-invoice

@BraCR10 BraCR10 force-pushed the feat/br/restoredata branch from 3661b6b to 0d595c0 Compare October 25, 2025 07:43
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: 8

Caution

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

⚠️ Outside diff range comments (1)
lib/features/settings/settings_notifier.dart (1)

58-62: Use English-only comments

Replace Spanish inline comments with English per guidelines.

-        blacklistedRelays: const [], // Blacklist vacío
-        userRelays: const [],         // User relays vacíos
+        blacklistedRelays: const [], // Clear blacklist
+        userRelays: const [],        // Clear user relays

As per coding guidelines.

♻️ Duplicate comments (2)
lib/services/mostro_service.dart (1)

124-126: Stop logging decrypted payloads (sensitive data leak)

Logging decryptedEvent.content may expose invoices/PII. Log metadata only.

Apply this diff:

-      _logger.i(
-        'Received DM, Event ID: ${decryptedEvent.id ?? event.id} with payload: ${decryptedEvent.content}',
-      );
+      final type = (result[0] is Map && (result[0] as Map).isNotEmpty)
+          ? (result[0] as Map).keys.first
+          : 'unknown';
+      _logger.i('Received DM, Event ID: ${decryptedEvent.id ?? event.id}, type: $type');

Run to find any remaining sensitive logs:

#!/bin/bash
rg -n -C2 -e 'decryptedEvent\.content' -e 'with payload:' --type=dart lib/
lib/features/settings/settings_notifier.dart (1)

85-88: Remove no-op else branch to avoid unnecessary writes/notifications

When oldPubkey == newValue, copying the same value and saving prefs is unnecessary.

-    } else {
-      state = state.copyWith(mostroPublicKey: newValue);
-      await _saveToPrefs();
-    }
+    }

As per coding guidelines.

🧹 Nitpick comments (10)
lib/data/models/restore_message.dart (1)

3-21: Use Action enum constant instead of hardcoded string.

The action string 'restore-session' is hardcoded, but based on the changes in notification_message_mapper.dart, there's a corresponding Action.restoreSession enum value. Consider using a constant from the Action enum or defining a string constant to ensure consistency across the codebase and reduce the risk of typos.

+import 'package:mostro_mobile/data/models/enums/action.dart';
+
 class RestoreMessage {
   final int version;
   final String action;
 
   RestoreMessage({
     this.version = 1,
-    this.action = 'restore-session',
+    this.action = 'restore-session', // TODO: Consider using Action.restoreSession.value or a constant
   });
lib/data/models/last_trade_index_message.dart (1)

3-24: Use Action enum constant instead of hardcoded string.

Similar to RestoreMessage, the action 'last-trade-index' is hardcoded. For consistency and maintainability, consider using a constant or enum value to define this action string.

 class LastTradeIndexRequest {
   final int version;
   final String action;
 
   LastTradeIndexRequest({
     this.version = 1,
-    this.action = 'last-trade-index',
+    this.action = 'last-trade-index', // TODO: Consider using a constant
   });
lib/services/mostro_service.dart (1)

328-329: Avoid logging full outgoing payloads

Logging order.toJson() can leak invoices or amounts. Log action/id only.

-    _logger.i('Sending DM, Event ID: ${event.id} with payload: ${order.toJson()}');
+    _logger.i('Sending DM, Event ID: ${event.id}, action: ${order.action}, id: ${order.id}, requestId: ${order.requestId}');
lib/features/restore/restore_overlay.dart (1)

30-72: Improve accessibility and insets handling (optional)

Wrap content in SafeArea and announce live status for screen readers.

-    return Material(
+    return Material(
       color: Colors.black.withValues(alpha: 0.85),
-      child: Center(
-        child: Container(
+      child: Center(
+        child: SafeArea(
+          child: Container(
           ...
-          child: Column(
+          child: Semantics(
+            liveRegion: true,
+            label: S.of(context)!.restoringOrders,
+            child: Column(
               mainAxisSize: MainAxisSize.min,
               children: [
                 _buildStatusIcon(state.step, iconSize),
                 ...
-            ],
-          ),
-        ),
+            ],
+            ),
+          ),
+        ),
       ),
     );
lib/features/key_manager/import_mnemonic_dialog.dart (1)

111-165: Harden TextField for secrets

Disable autocorrect/suggestions and capitalization to reduce leakage risk.

               TextField(
                 controller: _mnemonicController,
                 minLines: 4,
                 maxLines: 6,
+                enableSuggestions: false,
+                autocorrect: false,
+                textCapitalization: TextCapitalization.none,
+                keyboardType: TextInputType.visiblePassword,
                 style: const TextStyle(
lib/features/key_manager/key_management_screen.dart (2)

79-100: Remove unused private method

_cleanup: _importKey is unused and annotated to ignore; delete to keep analyzer clean.

-  // ignore: unused_element
-  Future<void> _importKey() async {
-    final keyManager = ref.read(keyManagerProvider);
-    final importValue = _importController.text.trim();
-    if (importValue.isNotEmpty) {
-      try {
-        await keyManager.importMnemonic(importValue);
-        await _loadKeys();
-        if (mounted) {
-          ScaffoldMessenger.of(context).showSnackBar(
-            SnackBar(content: Text(S.of(context)!.keyImportedSuccessfully)),
-          );
-        }
-      } catch (e) {
-        if (mounted) {
-          ScaffoldMessenger.of(context).showSnackBar(
-            SnackBar(content: Text(S.of(context)!.importFailed(e.toString()))),
-          );
-        }
-      }
-    }
-  }

315-322: Add explicit type for settings parameter (style nit)

Annotate _buildPrivacyCard’s second parameter as Settings for clarity.

-  Widget _buildPrivacyCard(BuildContext context, settings) {
+  Widget _buildPrivacyCard(BuildContext context, Settings settings) {
lib/services/restore_service.dart (1)

57-63: Avoid hardcoded user-facing error strings in services

Service writes English strings to UI via overlay. Prefer localized mapping (e.g., pass error codes or use a notifier API that the UI localizes).

As per coding guidelines.

lib/features/restore/restore_progress_notifier.dart (1)

62-66: Consider using cancellable timers for auto-hide delays.

The Future.delayed callback will execute even if the notifier is disposed before the delay completes. While the mounted check prevents issues, using a cancellable Timer (like _timeoutTimer) would provide more consistent lifecycle management and allow immediate cleanup on dispose.

Example approach:

Timer? _autoHideTimer;

void completeRestore() {
  _logger.i('Restore completed successfully');
  _cancelTimeoutTimer();
  _cancelAutoHideTimer();

  state = state.copyWith(
    step: RestoreStep.completed,
  );

  _autoHideTimer = Timer(const Duration(seconds: 1), () {
    if (mounted) {
      hide();
    }
  });
}

void _cancelAutoHideTimer() {
  _autoHideTimer?.cancel();
  _autoHideTimer = null;
}

@override
void dispose() {
  _cancelTimeoutTimer();
  _cancelAutoHideTimer();
  super.dispose();
}
lib/features/restore/restore_progress_state.dart (1)

42-45: Consider clamping progress percentage.

If currentProgress exceeds totalProgress due to a bug, progressPercentage will return a value greater than 1.0, which might cause UI issues depending on how the progress indicator is implemented.

Apply this diff to clamp the value:

 double get progressPercentage {
   if (totalProgress == 0) return 0.0;
-  return currentProgress / totalProgress;
+  return (currentProgress / totalProgress).clamp(0.0, 1.0);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3661b6b and 77dc943.

📒 Files selected for processing (24)
  • lib/core/app.dart (2 hunks)
  • lib/data/models/enums/action.dart (1 hunks)
  • lib/data/models/last_trade_index_message.dart (1 hunks)
  • lib/data/models/mostro_message.dart (1 hunks)
  • lib/data/models/payload.dart (2 hunks)
  • lib/data/models/restore_data.dart (1 hunks)
  • lib/data/models/restore_message.dart (1 hunks)
  • lib/features/key_manager/import_mnemonic_dialog.dart (1 hunks)
  • lib/features/key_manager/key_management_screen.dart (5 hunks)
  • lib/features/notifications/utils/notification_data_extractor.dart (1 hunks)
  • lib/features/notifications/utils/notification_message_mapper.dart (2 hunks)
  • lib/features/notifications/widgets/notification_item.dart (1 hunks)
  • lib/features/restore/restore_overlay.dart (1 hunks)
  • lib/features/restore/restore_progress_notifier.dart (1 hunks)
  • lib/features/restore/restore_progress_state.dart (1 hunks)
  • lib/features/settings/settings_notifier.dart (3 hunks)
  • lib/features/settings/settings_provider.dart (1 hunks)
  • lib/l10n/intl_en.arb (3 hunks)
  • lib/l10n/intl_es.arb (3 hunks)
  • lib/l10n/intl_it.arb (3 hunks)
  • lib/main.dart (1 hunks)
  • lib/services/last_trade_index_service.dart (1 hunks)
  • lib/services/mostro_service.dart (2 hunks)
  • lib/services/restore_service.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • lib/data/models/enums/action.dart
  • lib/features/notifications/widgets/notification_item.dart
  • lib/core/app.dart
  • lib/data/models/mostro_message.dart
🧰 Additional context used
📓 Path-based instructions (7)
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/restore_progress_state.dart
  • lib/features/restore/restore_progress_notifier.dart
  • lib/features/settings/settings_provider.dart
  • lib/features/restore/restore_overlay.dart
  • lib/features/notifications/utils/notification_data_extractor.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/key_manager/import_mnemonic_dialog.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/main.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/data/models/payload.dart
  • lib/services/last_trade_index_service.dart
  • lib/features/restore/restore_progress_state.dart
  • lib/features/restore/restore_progress_notifier.dart
  • lib/services/mostro_service.dart
  • lib/features/settings/settings_provider.dart
  • lib/data/models/restore_message.dart
  • lib/features/restore/restore_overlay.dart
  • lib/features/notifications/utils/notification_data_extractor.dart
  • lib/data/models/last_trade_index_message.dart
  • lib/services/restore_service.dart
  • lib/data/models/restore_data.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/key_manager/import_mnemonic_dialog.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/features/notifications/utils/notification_message_mapper.dart
  • lib/main.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/data/models/payload.dart
  • lib/services/last_trade_index_service.dart
  • lib/features/restore/restore_progress_state.dart
  • lib/features/restore/restore_progress_notifier.dart
  • lib/services/mostro_service.dart
  • lib/features/settings/settings_provider.dart
  • lib/data/models/restore_message.dart
  • lib/features/restore/restore_overlay.dart
  • lib/features/notifications/utils/notification_data_extractor.dart
  • lib/data/models/last_trade_index_message.dart
  • lib/services/restore_service.dart
  • lib/data/models/restore_data.dart
  • lib/features/settings/settings_notifier.dart
  • lib/features/key_manager/import_mnemonic_dialog.dart
lib/main.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Configure timeago locales and app initialization in main.dart

Files:

  • lib/main.dart
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/models/payload.dart
  • lib/data/models/restore_message.dart
  • lib/data/models/last_trade_index_message.dart
  • lib/data/models/restore_data.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/last_trade_index_service.dart
  • lib/services/mostro_service.dart
  • lib/services/restore_service.dart
lib/l10n/*.arb

📄 CodeRabbit inference engine (CLAUDE.md)

lib/l10n/*.arb: Maintain ARB files for en/es/it in lib/l10n/ (intl_en.arb, intl_es.arb, intl_it.arb)
When adding a new localization key, update all three ARB files (en, es, it)
Use proper ARB metadata for parameterized strings
After modifying ARB files, run dart run build_runner build -d to regenerate localization

Files:

  • lib/l10n/intl_es.arb
  • lib/l10n/intl_en.arb
  • lib/l10n/intl_it.arb
🧠 Learnings (6)
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/core/app_routes.dart : Use GoRouter for navigation configured in core/app_routes.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/data/models.dart : Export models through lib/data/models.dart

Applied to files:

  • lib/data/models/payload.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/shared/providers/mostro_database_provider.dart : Initialize Sembast database in shared/providers/mostro_database_provider.dart

Applied to files:

  • lib/services/mostro_service.dart
📚 Learning: 2025-10-07T17:31:12.117Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T17:31:12.117Z
Learning: Applies to lib/**/*.dart : Name Riverpod providers as <Feature>Provider or <Feature>Notifier

Applied to files:

  • lib/features/settings/settings_provider.dart
📚 Learning: 2025-10-21T21:47:03.451Z
Learnt from: Catrya
PR: MostroP2P/mobile#327
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:157-182
Timestamp: 2025-10-21T21:47:03.451Z
Learning: In MostroP2P/mobile, for Action.canceled handling in abstract_mostro_notifier.dart (Riverpod StateNotifier), do not add mounted checks after async sessionNotifier.deleteSession(orderId) as they break order state synchronization during app restart. The Action.canceled flow contains critical business logic that must complete fully; Riverpod handles provider disposal automatically. Mounted checks should only protect UI operations, not business logic in StateNotifiers.

Applied to files:

  • lib/features/notifications/utils/notification_data_extractor.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/relays/relays_notifier.dart : Implement relay operations: syncWithMostroInstance, removeRelayWithBlacklist, addRelayWithSmartValidation, _handleMostroRelayListUpdate

Applied to files:

  • lib/features/settings/settings_notifier.dart
🔇 Additional comments (27)
lib/features/settings/settings_provider.dart (1)

9-9: LGTM!

The addition of the ref parameter correctly passes the Riverpod reference to the SettingsNotifier, enabling it to interact with other providers as needed for the restoration flow.

lib/l10n/intl_en.arb (4)

463-467: LGTM!

The refresh-related localization keys are properly formatted and provide clear user-facing messages for the user data refresh feature.


891-893: LGTM!

The refresh dialog localization keys are well-structured and provide clear context for the user about what the refresh operation does.


902-910: LGTM!

The restore overlay localization keys provide comprehensive status messages for each stage of the restore process, enhancing user experience by keeping them informed of progress.


1163-1174: LGTM!

The import mnemonic dialog localization keys are well-organized and provide clear guidance and validation messages for the mnemonic import flow.

lib/l10n/intl_it.arb (4)

414-418: LGTM!

The Italian translations for the refresh feature are accurate and follow proper localization conventions.


926-928: LGTM!

The Italian translations for the refresh dialog are clear and properly convey the purpose of the operation to Italian-speaking users.


936-945: LGTM!

The Italian translations for the restore overlay provide comprehensive status updates throughout the restoration process.


1196-1207: LGTM!

The Italian translations for the import mnemonic dialog are accurate and provide clear guidance and error messages for Italian-speaking users.

lib/features/notifications/utils/notification_message_mapper.dart (2)

85-86: LGTM!

Correctly handles the new restoreSession action by returning an empty string, which is appropriate since restore operations are internal and don't require user-facing notification titles.


179-180: LGTM!

Consistent with the title handling, returning an empty string for the message key is appropriate for internal restore operations.

lib/features/notifications/utils/notification_data_extractor.dart (1)

22-24: LGTM!

Correctly prevents notification generation for the internal restoreSession action by returning null. The comment clearly explains the rationale, and this is consistent with the handling in notification_message_mapper.dart.

lib/data/models/payload.dart (1)

21-22: ****

The payload extraction pattern is correct. RestoreData.fromJson(json) properly receives the full JSON object and internally extracts json['restore_data'] (as seen in restore_data.dart line 15), matching the implementation pattern used by CantDo, Dispute, and TextMessage. The pattern is consistent and appropriate.

lib/main.dart (1)

42-45: Provider override correctly injects Ref into SettingsNotifier

This fixes the previous inability to access ref inside SettingsNotifier and keeps a single instance in the container. LGTM.

lib/services/mostro_service.dart (3)

74-77: Good defensive check for empty/invalid payloads

Guarding against non-List and empty results prevents index access errors. LGTM.


84-101: Restore and last-trade-index dispatch looks correct

Branching to RestoreService and LastTradeIndexService based on action keeps _onData cohesive. LGTM.


102-115: Orders details routing is clear

Extracting payload.orders and delegating to RestoreService is the right split. LGTM.

lib/features/key_manager/key_management_screen.dart (3)

174-187: Import and Refresh controls: good UX addition

Clear split between import and refresh. LGTM.


739-824: Refresh flow looks safe

Confirmation + restore call with snackbars and logging reads well. LGTM.


315-431: No SDK compatibility issues—WidgetStateProperty is fully supported

Your project's minimum Flutter version is 3.27.0 (per pubspec.yaml), which exceeds Flutter 3.19.0 where WidgetStateProperty was introduced. The code at lines 415–423 is compatible with your SDK constraints. The implementation is correct as-is.

lib/services/last_trade_index_service.dart (1)

45-52: Advancing key index and cleaning temp session is correct

Setting index to tradeIndex+1 and deleting 'restore' aligns with restore flow. LGTM.

lib/features/restore/restore_progress_notifier.dart (3)

102-106: LGTM!

The conditional timer reset ensures timers are only active when the overlay is visible, preventing unnecessary resource usage.


113-118: LGTM!

The dispose method properly cancels the timeout timer before calling super.dispose(), ensuring clean resource management.


9-9: The review comment is unsupported by available project information.

The claim that "the PR description states a 60-second timeout" cannot be verified. The git commit message is "fix: resolve BuildContext async gaps in key management" and contains no mention of timeout changes. The code consistently implements a 30-second timeout throughout RestoreProgressNotifier, and it is properly managed (cancelled and nullified). Without concrete evidence from the PR description or codebase requirements documenting a 60-second timeout expectation, the suggested diff should not be applied.

Likely an incorrect or invalid review comment.

lib/features/restore/restore_progress_state.dart (1)

47-52: LGTM!

The initial state factory provides a clean, const-optimized starting point for the restore progress flow.

lib/l10n/intl_es.arb (2)

1141-1152: LGTM!

The Spanish translations for the import mnemonic dialog and validation errors are clear, grammatically correct, and maintain a consistent tone with the rest of the application.


385-385: No action required—all localization files have been properly updated.

All 25 new localization keys have been confirmed in all three ARB files (intl_en.arb, intl_es.arb, intl_it.arb). The coding guidelines have been followed correctly.

Likely an incorrect or invalid review comment.

@BraCR10 BraCR10 force-pushed the feat/br/restoredata branch from 77dc943 to e83c6dd Compare October 26, 2025 04:57
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 (1)
lib/features/subscriptions/subscription_manager.dart (1)

300-304: Admin subscription is dropped by subscribeAll() and never re-initialized.

unsubscribeAll() cancels admin, but subscribeAll() only re-subscribes session-based types. Admin channel dies until app restart, breaking restore/last-trade-index flows.

Apply this minimal fix to re-init admin after resubscribing sessions:

 void subscribeAll() {
   unsubscribeAll();
   final currentSessions = ref.read(sessionNotifierProvider);
   _updateAllSubscriptions(currentSessions);
+  // Re-create admin subscription (was canceled above)
+  _initializeAdminSubscription();
 }

Based on learnings.

Also applies to: 306-309

♻️ Duplicate comments (1)
lib/services/restore_service.dart (1)

107-107: Null-guard master key before use.

Line 107 force-unwraps masterKeyPair without checking for null. If the master key is unavailable, this will crash the restore flow.

Apply this fix:

+      final masterKey = keyManager.masterKeyPair;
+      if (masterKey == null) {
+        _logger.w('Cannot process restore data: master key not available');
+        progressNotifier.showError('Master key not available');
+        return;
+      }
+
       final List<String> orderIds = [];
-      final masterKey = keyManager.masterKeyPair!;
🧹 Nitpick comments (3)
lib/features/subscriptions/subscription_manager.dart (3)

80-104: Make admin subscription reactive to master key changes (import/generate).

Currently initialized once. If the master key becomes available later or changes, admin won’t update.

Consider listening to keyManagerProvider and re-initializing admin when masterKeyPair transitions from null→non-null or changes:

+  late final ProviderSubscription _keyManagerListener;
   SubscriptionManager(this.ref) {
     _initSessionListener();
     ref.onDispose(dispose);
     _initializeExistingSessions();
+    _keyManagerListener = ref.listen(
+      keyManagerProvider,
+      (prev, next) {
+        final prevPub = prev?.masterKeyPair?.public;
+        final currPub = next.masterKeyPair?.public;
+        if (currPub != null && currPub != prevPub) {
+          unsubscribeByType(SubscriptionType.admin);
+          _initializeAdminSubscription();
+        }
+      },
+      fireImmediately: false,
+    );
   }
@@
   void dispose() {
     _sessionListener.close();
     unsubscribeAll();
     _ordersController.close();
     _chatController.close();
     _relayListController.close();
+    _keyManagerListener.close();
     _adminController.close();
   }

Based on learnings.


220-246: Guard against null subscriptionId in onCancel.

request.subscriptionId! can be null if the ID wasn’t set; avoid a crash during unsubscribe.

   final subscription = Subscription(
     request: request,
     streamSubscription: streamSubscription,
     onCancel: () {
-      ref.read(nostrServiceProvider).unsubscribe(request.subscriptionId!);
+      final id = request.subscriptionId;
+      if (id != null) {
+        ref.read(nostrServiceProvider).unsubscribe(id);
+      } else {
+        _logger.w('Unsubscribe called without subscriptionId for $type');
+      }
     },
   );

180-189: Avoid duplicating admin filter construction.

Admin filter logic exists both in _initializeAdminSubscription and _createFilterForType. Prefer a single source of truth to prevent drift.

Refactor _initializeAdminSubscription to reuse _createFilterForType(SubscriptionType.admin, ...) and only proceed when a filter is returned.

Also applies to: 80-104

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77dc943 and e83c6dd.

📒 Files selected for processing (12)
  • lib/core/app.dart (2 hunks)
  • lib/features/restore/restore_overlay.dart (1 hunks)
  • lib/features/restore/restore_progress_notifier.dart (1 hunks)
  • lib/features/restore/restore_progress_state.dart (1 hunks)
  • lib/features/subscriptions/subscription_manager.dart (8 hunks)
  • lib/features/subscriptions/subscription_type.dart (1 hunks)
  • lib/l10n/intl_en.arb (3 hunks)
  • lib/l10n/intl_es.arb (3 hunks)
  • lib/l10n/intl_it.arb (3 hunks)
  • lib/services/last_trade_index_service.dart (1 hunks)
  • lib/services/mostro_service.dart (3 hunks)
  • lib/services/restore_service.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/features/restore/restore_progress_notifier.dart
  • lib/services/last_trade_index_service.dart
🧰 Additional context used
📓 Path-based instructions (7)
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/mostro_service.dart
  • lib/services/restore_service.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • lib/services/mostro_service.dart
  • lib/core/app.dart
  • lib/features/subscriptions/subscription_type.dart
  • lib/features/restore/restore_overlay.dart
  • lib/services/restore_service.dart
  • lib/features/subscriptions/subscription_manager.dart
  • lib/features/restore/restore_progress_state.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/services/mostro_service.dart
  • lib/core/app.dart
  • lib/features/subscriptions/subscription_type.dart
  • lib/features/restore/restore_overlay.dart
  • lib/services/restore_service.dart
  • lib/features/subscriptions/subscription_manager.dart
  • lib/features/restore/restore_progress_state.dart
lib/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep dependency wiring in lib/core/

Files:

  • lib/core/app.dart
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/subscriptions/subscription_type.dart
  • lib/features/restore/restore_overlay.dart
  • lib/features/subscriptions/subscription_manager.dart
  • lib/features/restore/restore_progress_state.dart
lib/features/subscriptions/subscription_manager.dart

📄 CodeRabbit inference engine (CLAUDE.md)

SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Files:

  • lib/features/subscriptions/subscription_manager.dart
lib/l10n/*.arb

📄 CodeRabbit inference engine (CLAUDE.md)

lib/l10n/*.arb: Maintain ARB files for en/es/it in lib/l10n/ (intl_en.arb, intl_es.arb, intl_it.arb)
When adding a new localization key, update all three ARB files (en, es, it)
Use proper ARB metadata for parameterized strings
After modifying ARB files, run dart run build_runner build -d to regenerate localization

Files:

  • lib/l10n/intl_it.arb
  • lib/l10n/intl_es.arb
  • lib/l10n/intl_en.arb
🧠 Learnings (2)
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/subscriptions/subscription_manager.dart : SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Applied to files:

  • lib/features/subscriptions/subscription_manager.dart
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, `eventStorageProvider` is exported from `package:mostro_mobile/shared/providers/mostro_service_provider.dart` and not from a separate `event_storage_provider.dart` file.

Applied to files:

  • lib/features/subscriptions/subscription_manager.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: build
🔇 Additional comments (13)
lib/services/mostro_service.dart (2)

25-126: LGTM! Admin stream handling looks solid.

The new admin subscription and message routing are well-implemented:

  • Master key null guard prevents crashes
  • Empty/invalid payload guard added
  • Admin actions properly routed to corresponding services (restore, last-trade-index)
  • Batch orders response correctly delegated to RestoreService

128-179: LGTM! Order data handling improvements applied.

The renaming to _onOrderData improves clarity, and the empty list guard (line 156) addresses previous review feedback.

lib/features/subscriptions/subscription_type.dart (1)

5-5: LGTM! Clear enum addition.

The admin enum member is well-documented and aligns with the new admin subscription flow.

lib/core/app.dart (1)

167-174: LGTM! RestoreOverlay integration is clean.

The Stack-based overlay approach ensures the restore progress UI appears above all screens. The overlay conditionally renders via isVisible, so performance impact is minimal when not in use.

lib/features/restore/restore_overlay.dart (1)

1-172: LGTM! Well-structured restore overlay UI.

The implementation follows Flutter best practices:

  • Uses latest Color.withValues() API
  • All user-facing text properly localized via S.of(context)
  • Responsive design with screen-width-based sizing
  • Clean separation of concerns with helper methods
lib/services/restore_service.dart (3)

30-37: Verify the clearing order for importMnemonicAndRestore.

The current implementation calls restore() (which calls _clearAll() at line 40) after importing the mnemonic. However, this means:

  1. Import mnemonic (line 34)
  2. Clear all data (via restore → _clearAll at line 40)

This order seems counterintuitive. If the import fails, we've already cleared everything. Consider moving the clearing step inside importMnemonicAndRestore before the import to match the typical pattern:

Future<void> importMnemonicAndRestore(String mnemonic) async {
  _logger.i('Importing mnemonic and restoring session');
  
  await _clearAll(); // Clear first
  
  final keyManager = ref.read(keyManagerProvider);
  await keyManager.importMnemonic(mnemonic); // Then import
  
  await restore(); // Then restore (which won't clear again)
}

And update restore() to optionally skip clearing when called from import flow.


163-220: LGTM! Order details handling is well-structured.

The _requestOrderDetails method properly constructs and publishes the request, and processOrderDetails correctly assigns session roles based on trade pubkeys.


222-244: LGTM! Comprehensive cleanup with proper error handling.

The _clearAll method clears all relevant stores (sessions, storage, events, notifications) with appropriate error handling.

lib/features/restore/restore_progress_state.dart (1)

1-53: LGTM! Clean and well-structured state model.

The RestoreStep enum and RestoreProgressState class follow Flutter/Riverpod best practices:

  • Immutable state with const constructor
  • Proper copyWith for state updates
  • Computed getter for progress percentage
  • Factory method for initial state
lib/l10n/intl_en.arb (1)

463-468: LGTM! New localization keys properly defined.

All new keys for refresh, import mnemonic, and restore overlay flows are well-structured with appropriate placeholders. The English strings are clear and concise.

Also applies to: 891-893, 1153-1174

lib/l10n/intl_it.arb (1)

414-418: LGTM! Italian translations complete.

All new localization keys from the English file are present with Italian translations, satisfying the guideline to update all three ARB files (en, es, it).

Also applies to: 926-928, 1186-1207

lib/l10n/intl_es.arb (2)

870-873: LGTM for new ES strings (refresh dialog, restore overlay, timeout, refreshUser).

Also applies to: 1145-1153, 1131-1132, 385-386


385-386: Fix missing metadata for placeholder keys in ARB files.

All 25 target keys exist across en/es/it locales. However, refreshFailed contains the {error} placeholder but is missing its @refreshFailed metadata entry in intl_en.arb. Per ARB specification, any key with placeholders must have corresponding metadata defining the placeholder types and descriptions.

After adding the metadata, regenerate the localization files:

dart run build_runner build -d
⛔ Skipped due to learnings
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/l10n/*.arb : After modifying ARB files, run dart run build_runner build -d to regenerate localization
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/l10n/*.arb : When adding a new localization key, update all three ARB files (en, es, it)
Learnt from: CR
PR: MostroP2P/mobile#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T17:31:12.117Z
Learning: Run dart run build_runner build -d when ARB or annotated files change to regenerate localization, mocks, and models

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

Caution

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

⚠️ Outside diff range comments (1)
lib/features/key_manager/key_management_screen.dart (1)

30-30: Remove unused import field and method.

The _importController field (line 30) and _importKey method (lines 80-100) are no longer used after refactoring the import flow to use ImportMnemonicDialog. This creates unnecessary dead code and a potential resource leak since _importController is never disposed.

Apply this diff to remove the unused code:

-  final TextEditingController _importController = TextEditingController();
-
   @override
   void initState() {

...

-  // ignore: unused_element
-  Future<void> _importKey() async {
-    final keyManager = ref.read(keyManagerProvider);
-    final importValue = _importController.text.trim();
-    if (importValue.isNotEmpty) {
-      try {
-        await keyManager.importMnemonic(importValue);
-        await _loadKeys();
-        if (mounted) {
-          ScaffoldMessenger.of(context).showSnackBar(
-            SnackBar(content: Text(S.of(context)!.keyImportedSuccessfully)),
-          );
-        }
-      } catch (e) {
-        if (mounted) {
-          ScaffoldMessenger.of(context).showSnackBar(
-            SnackBar(content: Text(S.of(context)!.importFailed(e.toString()))),
-          );
-        }
-      }
-    }
-  }
-

Also applies to: 80-100

🧹 Nitpick comments (2)
lib/features/key_manager/key_management_screen.dart (1)

790-795: Consider sanitizing error messages for better UX.

Both error handlers expose raw exception details via e.toString() to users, which may include technical stack traces or implementation details. While some error context is helpful, consider showing user-friendly messages while logging full details via the logger.

For example:

  } catch (e) {
    _logger.e('Refresh failed: $e');
    if (!mounted || !context.mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
-     SnackBar(content: Text(S.of(context)!.refreshFailed(e.toString()))),
+     SnackBar(content: Text(S.of(context)!.refreshFailed(
+       e is Exception ? e.toString() : S.of(context)!.unexpectedError
+     ))),
    );
  }

Apply similar logic to the import error handler.

Also applies to: 838-843

lib/data/models/orders_request_message.dart (1)

3-28: Consider adding deserialization and documentation.

The model currently supports only serialization (toJson). Consider these optional enhancements:

  1. Add fromJson factory: If order responses need to be parsed, a factory OrdersRequestMessage.fromJson(Map<String, dynamic> json) would be useful.

  2. Add documentation: Brief class-level and method-level doc comments would improve maintainability.

  3. Input validation: Consider validating that orderIds is non-empty and requestId is non-negative, either in the constructor or via an assertion.

Example documentation:

/// Represents a request message to fetch multiple orders from the Mostro daemon.
///
/// Constructs a nested JSON structure with order metadata and a payload
/// containing the list of order IDs to retrieve.
class OrdersRequestMessage {
  // ... existing code ...
  
  /// Converts this message to a JSON map structure.
  ///
  /// Returns a nested map with 'order' as the root key containing
  /// version, request_id, action, and a payload with order IDs.
  Map<String, dynamic> toJson() => {
    // ... existing code ...
  };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e83c6dd and 90fb352.

📒 Files selected for processing (3)
  • lib/data/models/orders_request_message.dart (1 hunks)
  • lib/features/key_manager/key_management_screen.dart (5 hunks)
  • lib/services/restore_service.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/services/restore_service.dart
🧰 Additional context used
📓 Path-based instructions (4)
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/models/orders_request_message.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • lib/data/models/orders_request_message.dart
  • lib/features/key_manager/key_management_screen.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/data/models/orders_request_message.dart
  • lib/features/key_manager/key_management_screen.dart
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/key_manager/key_management_screen.dart
🧠 Learnings (1)
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/core/app_routes.dart : Use GoRouter for navigation configured in core/app_routes.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
🔇 Additional comments (3)
lib/features/key_manager/key_management_screen.dart (2)

174-187: LGTM!

The button layout effectively balances the Import button (with text) and the Refresh button (icon-only) using appropriate flex ratios and spacing.


739-818: LGTM!

Both dialog methods follow Flutter best practices:

  • Proper mounted checks after async operations (lines 786, 792, 834, 840)
  • Try-catch error handling with user feedback
  • Consistent use of localized strings
  • Appropriate logger usage for debugging

The refresh confirmation dialog prevents accidental data loss, and the import flow delegates validation to ImportMnemonicDialog.

Also applies to: 820-846

lib/data/models/orders_request_message.dart (1)

27-27: No changes needed—implementation correctly follows the Mostro protocol.

The [toJson(), null] array format matches the Mostro protocol specification, where messages are JSON-serialized arrays with two elements: [wrapper-object, signature]. The null signature is valid in full-privacy mode, and this pattern is correctly applied across RestoreMessage, OrdersRequestMessage, and LastTradeIndexMessage.

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

♻️ Duplicate comments (1)
lib/l10n/intl_es.arb (1)

1160-1161: Update text to reflect support for 12 or 24 word mnemonics.

The UI strings still reference only "12 palabras" but the validation logic accepts both 12 and 24 word BIP-39 mnemonics (as stated in PR objectives). This misleads users and may prevent valid 24-word mnemonic imports.

Apply this diff:

-  "secretWordsPlaceholder": "Ingresa tus 12 palabras secretas separadas por espacios",
-  "errorNotTwelveWords": "Debes ingresar exactamente 12 palabras",
+  "secretWordsPlaceholder": "Ingresa tus 12 o 24 palabras secretas separadas por espacios",
+  "errorNotTwelveWords": "Debes ingresar exactamente 12 o 24 palabras",

As per PR objectives.

🧹 Nitpick comments (1)
lib/features/subscriptions/subscription_manager.dart (1)

80-104: Eliminate code duplication in admin filter creation.

The admin filter creation logic (lines 89-92) is duplicated in _createFilterForType() at lines 187-190. Consider refactoring to use _createFilterForType() directly.

Apply this diff to eliminate duplication:

  void _initializeAdminSubscription() {
    try {
-      final masterKey = ref.read(keyManagerProvider).masterKeyPair;
-      if (masterKey == null) {
+      final filter = _createFilterForType(SubscriptionType.admin, []);
+      if (filter == null) {
        _logger.i('No master key available, skipping admin subscription');
        return;
      }

-      final filter = NostrFilter(
-        kinds: [1059],
-        p: [masterKey.public],
-      );
-
      subscribe(
        type: SubscriptionType.admin,
        filter: filter,
      );

      _logger.i('Admin subscription initialized for master key');
    } catch (e, stackTrace) {
      _logger.e('Failed to initialize admin subscription',
          error: e, stackTrace: stackTrace);
    }
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90fb352 and fdc9288.

📒 Files selected for processing (4)
  • lib/features/subscriptions/subscription_manager.dart (9 hunks)
  • lib/l10n/intl_en.arb (3 hunks)
  • lib/l10n/intl_es.arb (3 hunks)
  • lib/l10n/intl_it.arb (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/l10n/intl_it.arb
🧰 Additional context used
📓 Path-based instructions (5)
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/subscriptions/subscription_manager.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • lib/features/subscriptions/subscription_manager.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/features/subscriptions/subscription_manager.dart
lib/features/subscriptions/subscription_manager.dart

📄 CodeRabbit inference engine (CLAUDE.md)

SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Files:

  • lib/features/subscriptions/subscription_manager.dart
lib/l10n/*.arb

📄 CodeRabbit inference engine (CLAUDE.md)

lib/l10n/*.arb: Maintain ARB files for en/es/it in lib/l10n/ (intl_en.arb, intl_es.arb, intl_it.arb)
When adding a new localization key, update all three ARB files (en, es, it)
Use proper ARB metadata for parameterized strings
After modifying ARB files, run dart run build_runner build -d to regenerate localization

Files:

  • lib/l10n/intl_es.arb
  • lib/l10n/intl_en.arb
🧠 Learnings (2)
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/subscriptions/subscription_manager.dart : SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Applied to files:

  • lib/features/subscriptions/subscription_manager.dart
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, `eventStorageProvider` is exported from `package:mostro_mobile/shared/providers/mostro_service_provider.dart` and not from a separate `event_storage_provider.dart` file.

Applied to files:

  • lib/features/subscriptions/subscription_manager.dart
🔇 Additional comments (9)
lib/features/subscriptions/subscription_manager.dart (6)

8-8: LGTM: Admin infrastructure properly added.

The import of key_manager_provider, _adminController declaration, and admin stream getter follow established patterns for orders/chat/relayList subscriptions.

Also applies to: 28-28, 33-33


58-78: LGTM: Correct initialization order.

Initializing the admin subscription before session-based subscriptions is appropriate since admin functionality is independent of user sessions.


107-127: LGTM: Admin properly excluded from session-based flows.

The admin subscription is correctly kept independent of session lifecycle by skipping it in _updateAllSubscriptions() and _clearAllSubscriptions().


197-219: LGTM: Admin event routing implemented correctly.

The admin case in _handleEvent() follows the same pattern as orders and chat subscriptions.


221-266: LGTM: Admin stream properly returned.

The admin case in subscribe() correctly returns the admin stream, consistent with other subscription types.


307-313: LGTM: Proper admin lifecycle management.

Admin subscription is correctly preserved in unsubscribeAll() and _adminController is properly closed in dispose().

Also applies to: 380-387

lib/l10n/intl_es.arb (1)

397-403: LGTM! Metadata issue resolved.

The ARB metadata for the refreshFailed placeholder has been correctly added as requested in the previous review.

lib/l10n/intl_en.arb (2)

475-481: LGTM! Metadata correctly defined.

The ARB metadata for the refreshFailed placeholder is properly structured with the error placeholder.


1176-1196: No issues found—Italian localization file was properly updated.

Verification confirms that lib/l10n/intl_it.arb includes all new localization keys added in this PR, complying with the coding guidelines requiring all three ARB files (en, es, it) to be updated.

Comment on lines +1182 to +1183
"secretWordsPlaceholder": "Enter your 12 secret words separated by spaces",
"errorNotTwelveWords": "You must enter exactly 12 words",
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 text to reflect support for 12 or 24 word mnemonics.

The UI strings still reference only "12 words" but the validation logic accepts both 12 and 24 word BIP-39 mnemonics (as stated in PR objectives). This misleads users and may prevent valid 24-word mnemonic imports.

Apply this diff:

-  "secretWordsPlaceholder": "Enter your 12 secret words separated by spaces",
-  "errorNotTwelveWords": "You must enter exactly 12 words",
+  "secretWordsPlaceholder": "Enter your 12 or 24 secret words separated by spaces",
+  "errorNotTwelveWords": "You must enter exactly 12 or 24 words",

As per PR objectives.

🤖 Prompt for AI Agents
In lib/l10n/intl_en.arb around lines 1182 to 1183, the strings currently
reference only "12 words" while the app accepts 12 or 24-word BIP-39 mnemonics;
update the two entries so the placeholder reads something like "Enter your 12 or
24 secret words separated by spaces" and the error reads "You must enter exactly
12 or 24 words" (or equivalent phrasing that clearly indicates support for both
lengths).

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

Caution

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

⚠️ Outside diff range comments (1)
lib/services/mostro_service.dart (1)

182-183: Avoid logging decrypted payloads in production.

Logging ${decryptedEvent.content} exposes decrypted order data (potentially including invoices, amounts, and other sensitive information) to logs. Consider logging only metadata (event ID, action, order ID) instead.

Apply this diff to reduce sensitive data exposure:

-      _logger.i(
-        'Received DM, Event ID: ${decryptedEvent.id ?? event.id} with payload: ${decryptedEvent.content}',
-      );
+      _logger.i(
+        'Received DM, Event ID: ${decryptedEvent.id ?? event.id}, action: ${msg.action}, order: ${msg.id}',
+      );
♻️ Duplicate comments (1)
lib/services/restore_service.dart (1)

106-177: Add null check for master key at line 123 in processRestoreData().

The force unwrap is inconsistent with the defensive pattern used 10 lines earlier in the same method (line 55: null check before access). More importantly, processRestoreData() is invoked asynchronously as an admin message callback (mostro_service.dart:102), not within the restore() flow that validates the master key. Between the initial validation and the async callback execution, the master key could theoretically become unavailable.

The codebase establishes a defensive null-check pattern in the service layer (mostro_service.dart:77-79, last_trade_index_service.dart:19-21, subscription_manager.dart:83-85), so the suggested fix aligns with existing conventions.

       final keyManager = ref.read(keyManagerProvider);
       final settings = ref.read(settingsProvider);
 
       final List<String> orderIds = [];
-      final masterKey = keyManager.masterKeyPair!;
+      final masterKey = keyManager.masterKeyPair;
+      if (masterKey == null) {
+        _logger.e('Cannot process restore data: master key not available');
+        progressNotifier.showError('Master key not available');
+        return;
+      }
🧹 Nitpick comments (2)
lib/data/repositories/event_storage.dart (2)

6-6: Consider using a named logger for better log filtering.

Creating a named logger helps filter logs by component in production environments.

Apply this diff to use a named logger:

-  final _logger = Logger();
+  final _logger = Logger(printer: PrettyPrinter(methodCount: 0));

Or create a named logger:

  final _logger = Logger(
    printer: SimplePrinter(),
    filter: ProductionFilter(),
  );

49-85: Excellent documentation and logging; consider adding error handling.

The implementation correctly deletes all events and provides clear warnings about the implications of deleting admin events. The logging is informative and helpful for debugging.

Recommended: Add error handling for robustness.

Apply this diff to add error handling around the transaction:

   @override
   Future<void> deleteAll() async {
     final allRecords = await store.find(db);
 
     int adminEventsDeleted = 0;
     int totalDeleted = 0;
 
-    await db.transaction((txn) async {
-      for (final record in allRecords) {
-        final eventType = record.value['type'] as String?;
-
-        if (eventType == 'admin') {
-          adminEventsDeleted++;
-        }
-
-        await store.record(record.key).delete(txn);
-        totalDeleted++;
-      }
-    });
+    try {
+      await db.transaction((txn) async {
+        for (final record in allRecords) {
+          final eventType = record.value['type'] as String?;
+
+          if (eventType == 'admin') {
+            adminEventsDeleted++;
+          }
+
+          await store.record(record.key).delete(txn);
+          totalDeleted++;
+        }
+      });
+    } catch (e, stackTrace) {
+      _logger.e('Failed to delete all events', error: e, stackTrace: stackTrace);
+      rethrow;
+    }
 
     if (adminEventsDeleted > 0) {
       _logger.w('WARNING: deleteAll deleted $adminEventsDeleted admin events! '
           'This may cause restore message reprocessing on next startup. '
           'Total events deleted: $totalDeleted');
     } else {
       _logger.i('deleteAll: deleted $totalDeleted events (no admin events present)');
     }
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fdc9288 and 4200881.

📒 Files selected for processing (7)
  • lib/data/repositories/event_storage.dart (2 hunks)
  • lib/features/key_manager/key_management_screen.dart (6 hunks)
  • lib/features/subscriptions/subscription_manager.dart (9 hunks)
  • lib/services/lifecycle_manager.dart (1 hunks)
  • lib/services/mostro_service.dart (3 hunks)
  • lib/services/restore_service.dart (1 hunks)
  • test/mocks.dart (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
test/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

test/**/*.dart: Mirror feature layout for tests under test/
Prefer Mockito fakes generated via build_runner in tests

test/**/*.dart: Unit tests belong in test/; integration tests in integration_test/
Mocks are generated via Mockito; regenerate with dart run build_runner build -d after mock changes

Files:

  • test/mocks.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • test/mocks.dart
  • lib/services/lifecycle_manager.dart
  • lib/data/repositories/event_storage.dart
  • lib/services/mostro_service.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/services/restore_service.dart
  • lib/features/subscriptions/subscription_manager.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/lifecycle_manager.dart
  • lib/services/mostro_service.dart
  • lib/services/restore_service.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/services/lifecycle_manager.dart
  • lib/data/repositories/event_storage.dart
  • lib/services/mostro_service.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/services/restore_service.dart
  • lib/features/subscriptions/subscription_manager.dart
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/repositories/event_storage.dart
lib/data/repositories/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Use the Repository pattern for all data access (repositories under data/repositories)

Files:

  • lib/data/repositories/event_storage.dart
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/key_manager/key_management_screen.dart
  • lib/features/subscriptions/subscription_manager.dart
lib/features/subscriptions/subscription_manager.dart

📄 CodeRabbit inference engine (CLAUDE.md)

SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Files:

  • lib/features/subscriptions/subscription_manager.dart
🧠 Learnings (3)
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to test/mocks.mocks.dart : Do not manually edit test/mocks.mocks.dart (Mockito-generated)

Applied to files:

  • test/mocks.dart
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, `eventStorageProvider` is exported from `package:mostro_mobile/shared/providers/mostro_service_provider.dart` and not from a separate `event_storage_provider.dart` file.

Applied to files:

  • lib/data/repositories/event_storage.dart
  • lib/features/subscriptions/subscription_manager.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/subscriptions/subscription_manager.dart : SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Applied to files:

  • lib/features/subscriptions/subscription_manager.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: build
🔇 Additional comments (13)
lib/data/repositories/event_storage.dart (1)

25-47: Add error handling and clarify null type field behavior.

The two concerns in the original review are substantiated:

  1. Events without a type field are deleted silently. The code casts eventType as String? and checks if (eventType != 'admin'). Null values match this condition and are deleted. While other repositories in the codebase (e.g., notifications_history_repository.dart) defensively handle null type fields with defaults, this code lacks such protection. If events are guaranteed to have a type field, this is acceptable but should be documented. If malformed events might exist, they'll be silently removed without logging.

  2. No error handling around the transaction. If the transaction fails, the method throws without context.

Recommended changes:

Add a try-catch block and document the null handling behavior:

   Future<void> deleteAllExceptAdmin() async {
     final allRecords = await store.find(db);
 
     int deletedCount = 0;
     int preservedCount = 0;
 
-    await db.transaction((txn) async {
-      for (final record in allRecords) {
-        final eventType = record.value['type'] as String?;
-
-        if (eventType != 'admin') {
-          await store.record(record.key).delete(txn);
-          deletedCount++;
-        } else {
-          preservedCount++;
-        }
-      }
-    });
+    try {
+      await db.transaction((txn) async {
+        for (final record in allRecords) {
+          final eventType = record.value['type'] as String?;
+
+          // Events without a type or with non-admin type are deleted
+          if (eventType != 'admin') {
+            await store.record(record.key).delete(txn);
+            deletedCount++;
+          } else {
+            preservedCount++;
+          }
+        }
+      });
+    } catch (e, stackTrace) {
+      _logger.e('Failed to delete non-admin events', error: e, stackTrace: stackTrace);
+      rethrow;
+    }
 
     _logger.i('deleteAllExceptAdmin: deleted $deletedCount events, preserved $preservedCount admin events');
   }
lib/services/lifecycle_manager.dart (1)

106-106: LGTM! Admin subscriptions preserved during background transitions.

The change from unsubscribeAll() to unsubscribeAllExceptAdmin() correctly preserves admin subscriptions when the app moves to background, allowing administrative messages (restore-session, last-trade-index) to continue being received.

test/mocks.dart (1)

149-151: LGTM! Mock updated to align with production API.

The method rename from unsubscribeAll() to unsubscribeAllExceptAdmin() keeps the mock aligned with the production SubscriptionManager interface. The implementation (clearing all subscriptions) is appropriate for test mocks.

lib/features/key_manager/key_management_screen.dart (4)

71-71: LGTM! Admin events preserved during cleanup.

Calling deleteAllExceptAdmin() instead of deleteAll() ensures that admin event history is preserved, preventing reprocessing of historical restore-session messages when generating a new master key.


76-78: LGTM! Admin subscription refreshed after key generation.

Updating the admin subscription after generating a new master key ensures the subscription filter is correctly configured with the new master key's public key, enabling proper receipt of admin messages.


720-799: LGTM! Refresh flow includes proper confirmation and error handling.

The refresh user dialog includes:

  • Confirmation dialog to prevent accidental triggers
  • Try-catch for error handling
  • Mounted checks before showing snackbars
  • User-facing success/failure feedback
  • Logging for debugging

801-827: LGTM! Import flow includes proper error handling.

The import mnemonic dialog flow includes:

  • Validation via ImportMnemonicDialog
  • Try-catch for error handling
  • Mounted checks before showing snackbars
  • Key reload after successful import
  • User-facing success/failure feedback
  • Logging for debugging
lib/services/mostro_service.dart (2)

25-25: LGTM! Admin subscription lifecycle properly managed.

The admin subscription is correctly initialized in init() alongside the orders subscription, with proper error handling and cancelOnError: false to ensure resilience. Cleanup in dispose() cancels both subscriptions.

Also applies to: 32-49, 53-54


58-131: LGTM! Admin event processing includes proper validation and routing.

The _onAdminData method includes:

  • Duplicate event detection (lines 62-65)
  • Master key null guard (lines 76-81)
  • Empty/invalid payload checks (lines 89-92)
  • Proper routing to restore and last-trade-index services
  • Comprehensive error logging
lib/features/subscriptions/subscription_manager.dart (2)

28-33: LGTM! Admin subscription architecture is well-designed.

The admin subscription implementation demonstrates good architectural decisions:

  • Independent lifecycle: Admin subscriptions are managed separately from session-based subscriptions, initialized unconditionally during startup (lines 64-66)
  • Master key integration: Admin filter correctly uses the master key's public key (lines 196-204)
  • Graceful degradation: Returns early with logging when master key is unavailable (lines 84-87)
  • Refresh mechanism: updateAdminSubscription() allows re-initialization after key import/generation (lines 113-119)
  • Proper cleanup: Admin controller closed in dispose (line 400)
  • Event routing: Admin events correctly routed to dedicated stream (lines 220-222)

Also applies to: 64-111, 195-204, 220-222, 396-400


316-327: LGTM! Method renamed to reflect admin subscription preservation.

Renaming unsubscribeAll() to unsubscribeAllExceptAdmin() with admin subscription skip (lines 323-324) correctly reflects the behavior and prevents accidental cancellation of the admin channel during routine subscription management.

lib/services/restore_service.dart (2)

31-42: LGTM! Restore flow architecture and cleanup logic are well-structured.

The restore workflow demonstrates good practices:

  • Clear separation: importMnemonicAndRestore delegates to restore after import, enabling reuse
  • Admin subscription refresh: Line 39 updates admin subscription after key import
  • Progress tracking: Restore progress notifier provides user feedback at each step (lines 50-51, 101)
  • Validation: Master key and Mostro config validated before sending request (lines 55-68)
  • Error handling: Comprehensive try-catch blocks with progress notifications on failure (lines 99-102)
  • State cleanup: _clearAll removes sessions, storage, and notifications while preserving admin event history via deleteAllExceptAdmin() (line 240), preventing reprocessing of historical admin messages

Also applies to: 44-104, 229-252


206-227: LGTM! Role assignment logic correctly matches trade keys.

processOrderDetails assigns session roles by comparing the session's trade key public key against the order's buyer/seller trade pubkeys from the backend response. This ensures restored sessions reflect the user's actual role in each order.

@BraCR10
Copy link
Contributor Author

BraCR10 commented Oct 26, 2025

@Catrya saludos, tengo el siguiente problema:

Estado actual del restablecimiento

  • Se creó un nuevo tipo de subscription en lib/features/subscriptions/subscription_type.dart llamado Admin, ya que, si no estoy equivocado, las respuestas de los eventos de restore, get orders y get current index son dirigidas a la master pub key. Actualmente, el proyecto solo contaba con suscripciones para chat, order y relay list.

  • La idea es manejar una sola suscripción a la master pub key.
    Esta suscripción se crea en el constructor de lib/features/subscriptions/subscription_manager.dart, dentro de la función _initializeAdminSubscription().
    Esta función elimina la suscripción actual de la master key y genera una nueva con la master pub key activa.

  • Se creó lib/services/restore_service.dart, donde reside la lógica para crear y procesar las solicitudes y respuestas de mostrod.
    En la función restore() está la lógica encargada de limpiar la app, similar a lo que ocurre cuando se crean nuevas llaves.

  • En lib/services/mostro_service.dart se implementó _onAdminData(event), que replica el comportamiento del anterior _onData().
    Este método recibe un Nostr Event, verifica si ya está almacenado en Sembast (para evitar duplicaciones), y si no existe, lo guarda y lo procesa usando la lógica de lib/services/restore_service.dart.

  • Se ajustó el código para no eliminar los admin events, con el fin de evitar duplicaciones incluso cuando se borren los demás eventos.

  • Cada vez que se cambia de usuario (ya sea por import o create new), se elimina la suscripción actual de la master pub key y se crea una nueva para el usuario correspondiente.


Problema actual

A pesar de que el flujo funciona correctamente en algunos casos, en otras ocasiones se procesan eventos duplicados después de enviar la petición de restore.
Lo extraño es que, en teoría, las duplicaciones ya se manejan en mostro_service de la misma forma que con los eventos de órdenes.
Sin embargo, al seguir procesando duplicados, esto dispara múltiples requests de order details y trade index.


Dudas

Estoy pegado con ese problema, necesito saber si estoy previniendo la duplicación de eventos o saber si el enfoque actual no esta bien o si las duplicaciones se manejan diferente. La verificación de duplicación en Sembast es lo correcto?

Cualquier orientación o mejora sobre este enfoque lo agradezco.

@Catrya
Copy link
Member

Catrya commented Oct 27, 2025

Hola @BraCR10

las respuestas de los eventos de restore, get orders y get current index son dirigidas a la master pub key.

antes sí, pero era un error que se corrigió ahora mostrod envía la respuesta a la trade key que envíe el mensaje de solicitud

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

🧹 Nitpick comments (4)
lib/features/restore/order_restorer.dart (1)

6-47: Deduplicate order IDs before requesting details

When an order also has a dispute entry, this helper now returns duplicate IDs, so downstream detail fetches/logs trigger twice for the same order. Gathering into an ordered set fixes that without changing call sites.

-    final orderIds = <String>[];
+    final orderIds = <String>{};
...
-          orderIds.add(orderId);
+          orderIds.add(orderId);
...
-    if (orderIds.isNotEmpty) {
-      _logger.d('Extracted ${orderIds.length} order IDs for details request');
-    }
-
-    return orderIds;
+    final ordered = orderIds.toList();
+    if (ordered.isNotEmpty) {
+      _logger.d('Extracted ${ordered.length} order IDs for details request');
+    }
+
+    return ordered;
lib/features/restore/session_restorer.dart (2)

31-185: Avoid double-processing order details

Because the dispute list can echo entries already covered in restoreOrders, we currently emit duplicate sessions/messages and repeatedly walk the full orderDetails list. Tracking processed IDs and caching the detail map once keeps things linear and prevents duplicate storage writes.

-    if (restoreOrders != null) {
+    final processedIds = <String>{};
+    final orderDetailsById = <String, Map<String, dynamic>>{};
+    if (orderDetails != null) {
+      for (final detail in orderDetails) {
+        if (detail is Map<String, dynamic>) {
+          final id = detail['id'] as String?;
+          if (id != null) {
+            orderDetailsById[id] = detail;
+          }
+        }
+      }
+    }
+
+    Future<Role?> resolveRole(String orderId, int tradeIndex) =>
+        _determineRole(orderId, tradeIndex, orderDetailsById[orderId]);
+
+    if (restoreOrders != null) {
       for (final order in restoreOrders) {
-        final orderId = order['order_id'] as String?;
+        final orderId = order['order_id'] as String?;
...
-        if (orderId != null && tradeIndex != null) {
+        if (orderId != null && tradeIndex != null && processedIds.add(orderId)) {
           final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
-          final role = await _determineRole(orderId, tradeIndex, orderDetails);
+          final role = await resolveRole(orderId, tradeIndex);
...
-          await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);
+          await _saveOrderDetailsAsMessage(orderId, orderDetailsById[orderId], storage);
...
     if (restoreDisputes != null) {
       for (final dispute in restoreDisputes) {
-        final orderId = dispute['order_id'] as String?;
+        final orderId = dispute['order_id'] as String?;
...
-        if (orderId != null && tradeIndex != null) {
+        if (orderId != null && tradeIndex != null && processedIds.add(orderId)) {
           final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
-          final role = await _determineRole(orderId, tradeIndex, orderDetails);
+          final role = await resolveRole(orderId, tradeIndex);
...
-          await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);
+          await _saveOrderDetailsAsMessage(orderId, orderDetailsById[orderId], storage);

And adjust _saveOrderDetailsAsMessage to accept a single detail map instead of the entire list.

-  Future<void> _saveOrderDetailsAsMessage(
-    String orderId,
-    List<dynamic>? orderDetails,
-    dynamic storage,
-  ) async {
-    if (orderDetails == null || orderDetails.isEmpty) {
+  Future<void> _saveOrderDetailsAsMessage(
+    String orderId,
+    Map<String, dynamic>? orderDetail,
+    dynamic storage,
+  ) async {
+    if (orderDetail == null) {
...
-      Map<String, dynamic>? orderDetail;
-
-      for (final detail in orderDetails) {
-        if (detail is Map<String, dynamic> && detail['id'] == orderId) {
-          orderDetail = detail;
-          break;
-        }
-      }
-
-      if (orderDetail == null) {
-        _logger.w('Order detail not found for order $orderId');
-        return;
-      }
-
       final order = Order.fromJson(orderDetail);

172-181: Use microsecond precision for storage keys

Two restore writes for the same order can happen in the same millisecond (order loop + dispute loop), which risks identical keys. Bumping to microseconds avoids that without extra state.

-      final messageKey = '$orderId-restore-${DateTime.now().millisecondsSinceEpoch}';
+      final messageKey = '$orderId-restore-${DateTime.now().microsecondsSinceEpoch}';
lib/features/restore/restore_service.dart (1)

101-113: Safe force unwrap after validation, but consider defensive guard.

The force unwrap of keyManager.masterKeyPair! at line 107 is currently safe because _validatePrerequisites validates it beforehand. However, this creates a hidden dependency on call order that could break during refactoring.

For defensive coding, consider adding a runtime assertion or null-guard:

  Future<void> _sendRestoreRequest() async {
    final keyManager = ref.read(keyManagerProvider);
    final settings = ref.read(settingsProvider);
+   
+   assert(keyManager.masterKeyPair != null, 'Master key must be validated before sending restore request');

    final request = await _messageHandler.createRestoreRequest(
      tradeKey: _tempTradeKey!,
      masterKey: keyManager.masterKeyPair!,
      mostroPubkey: settings.mostroPublicKey,
    );

    await ref.read(nostrServiceProvider).publishEvent(request);
    _logger.d('Restore: request sent');
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4200881 and d15ab0d.

📒 Files selected for processing (13)
  • lib/data/models/nostr_event.dart (1 hunks)
  • lib/data/repositories/event_storage.dart (1 hunks)
  • lib/features/key_manager/key_management_screen.dart (5 hunks)
  • lib/features/restore/order_restorer.dart (1 hunks)
  • lib/features/restore/restore_message_handler.dart (1 hunks)
  • lib/features/restore/restore_service.dart (1 hunks)
  • lib/features/restore/session_restorer.dart (1 hunks)
  • lib/features/settings/settings_notifier.dart (3 hunks)
  • lib/features/subscriptions/subscription_manager.dart (1 hunks)
  • lib/services/lifecycle_manager.dart (1 hunks)
  • lib/services/mostro_service.dart (1 hunks)
  • lib/services/restore_service.dart (1 hunks)
  • test/mocks.dart (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • lib/features/subscriptions/subscription_manager.dart
  • lib/services/lifecycle_manager.dart
  • lib/services/mostro_service.dart
  • lib/data/repositories/event_storage.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/mocks.dart
🧰 Additional context used
📓 Path-based instructions (5)
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/models/nostr_event.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • lib/data/models/nostr_event.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/order_restorer.dart
  • lib/services/restore_service.dart
  • lib/features/restore/restore_message_handler.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/data/models/nostr_event.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/order_restorer.dart
  • lib/services/restore_service.dart
  • lib/features/restore/restore_message_handler.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/order_restorer.dart
  • lib/features/restore/restore_message_handler.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/restore_service.dart
🧠 Learnings (20)
📚 Learning: 2025-07-20T23:33:17.689Z
Learnt from: Catrya
Repo: MostroP2P/mobile PR: 215
File: lib/features/order/notfiers/order_notifier.dart:0-0
Timestamp: 2025-07-20T23:33:17.689Z
Learning: In AbstractMostroNotifier, the handleEvent method is synchronous and returns void, not Future<void>. Do not suggest adding await to super.handleEvent() calls as this would cause compilation errors.

Applied to files:

  • lib/data/models/nostr_event.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/**/screens/**/*.dart : Pass BuildContext to methods that need localization (S.of(context)!.keyName usage)

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-21T21:47:03.451Z
Learnt from: Catrya
Repo: MostroP2P/mobile PR: 327
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:157-182
Timestamp: 2025-10-21T21:47:03.451Z
Learning: In MostroP2P/mobile, for Action.canceled handling in abstract_mostro_notifier.dart (Riverpod StateNotifier), do not add mounted checks after async sessionNotifier.deleteSession(orderId) as they break order state synchronization during app restart. The Action.canceled flow contains critical business logic that must complete fully; Riverpod handles provider disposal automatically. Mounted checks should only protect UI operations, not business logic in StateNotifiers.

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
  • lib/services/restore_service.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/core/app_routes.dart : Use GoRouter for navigation configured in core/app_routes.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
Repo: MostroP2P/mobile PR: 74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, Riverpod code generation is used with `Riverpod` annotations. Providers like `eventStorageProvider` are generated in `.g.dart` files from annotated functions in the main provider files. These providers are accessible by importing the main provider file (e.g., `mostro_service_provider.dart`), not by importing a separate provider file.

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/*/providers/**/*.dart : Organize Riverpod providers by feature under features/{feature}/providers/

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-07T17:31:12.117Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T17:31:12.117Z
Learning: Applies to lib/**/*.dart : Name Riverpod providers as <Feature>Provider or <Feature>Notifier

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
Repo: MostroP2P/mobile PR: 74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, `eventStorageProvider` is exported from `package:mostro_mobile/shared/providers/mostro_service_provider.dart` and not from a separate `event_storage_provider.dart` file.

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/generated/l10n.dart : Generated localization entrypoint file is lib/generated/l10n.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/main.dart : Configure timeago locales and app initialization in main.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/data/models.dart : Export models through lib/data/models.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/shared/providers/mostro_database_provider.dart : Initialize Sembast database in shared/providers/mostro_database_provider.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
  • lib/services/restore_service.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/services/nostr_service.dart : All Nostr protocol interactions must go through NostrService (services/nostr_service.dart)

Applied to files:

  • lib/services/restore_service.dart
  • lib/features/restore/restore_message_handler.dart
  • lib/features/restore/restore_service.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/subscriptions/subscription_manager.dart : SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Applied to files:

  • lib/features/restore/restore_service.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/relays/relays_notifier.dart : Implement relay operations: syncWithMostroInstance, removeRelayWithBlacklist, addRelayWithSmartValidation, _handleMostroRelayListUpdate

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-08-21T14:45:43.974Z
Learnt from: Catrya
Repo: MostroP2P/mobile PR: 272
File: lib/features/relays/widgets/relay_selector.dart:13-15
Timestamp: 2025-08-21T14:45:43.974Z
Learning: In the Mostro mobile app's RelaySelector widget (lib/features/relays/widgets/relay_selector.dart), watching relaysProvider.notifier correctly triggers rebuilds because the relaysProvider itself depends on settingsProvider (line 8 in relays_provider.dart). When blacklist changes via toggleMostroRelayBlacklist(), the settingsProvider updates, causing relaysProvider to rebuild, which then notifies widgets watching the notifier. The UI correctly reflects active/inactive states in real-time through this dependency chain.

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/settings/settings.dart : Settings model must include blacklistedRelays and maintain backward-compatible serialization

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/notifications/notification_service.dart : Notification service configuration and custom icons should be defined in notification_service.dart

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/core/mostro_fsm.dart : MostroFSM manages order state transitions (core/mostro_fsm.dart)

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/relays/relay.dart : Relay model must track source (user, mostro, defaultConfig) and expose canDelete/canBlacklist semantics

Applied to files:

  • lib/features/settings/settings_notifier.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: build
🔇 Additional comments (4)
lib/features/key_manager/key_management_screen.dart (1)

739-844: Solid restore trigger with UI safeguards

The confirmation dialog plus mounted guards around the snackbars keep this refresh flow safe, and the added logging will help diagnose restore failures quickly.

lib/data/models/nostr_event.dart (1)

268-352: Extension matches separated-key wrap flow

The stepwise build of rumor → seal → gift wrap with distinct key pairs is spot on and keeps the restore payload aligned with the daemon’s expectations.

lib/features/settings/settings_notifier.dart (1)

51-88: Mostro switch rehydrate flow looks good

Nice to see the blacklist/user relay reset, prefs flush, and automatic restore chained together—the guard logs on null ref will be handy if the notifier is reused elsewhere.

lib/services/restore_service.dart (1)

1-377: Remove duplicate restore service file.

This file at lib/services/restore_service.dart appears to be superseded by the refactored version at lib/features/restore/restore_service.dart. Having both files creates confusion, maintenance burden, and violates the coding guideline that application feature code should be placed under lib/features/<feature>/.

The lib/features/restore/ version:

  • Uses RestoreMessageHandler for message creation (eliminating duplication)
  • Has cleaner structure with _RestoreData class
  • Uses helper classes SessionRestorer and OrderRestorer
  • Better separation of concerns

Remove this file and ensure all references point to lib/features/restore/restore_service.dart.

#!/bin/bash
# Verify no imports reference the services/ version
rg -n "import.*services/restore_service" --type=dart
⛔ Skipped due to learnings
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/services/nostr_service.dart : All Nostr protocol interactions must go through NostrService (services/nostr_service.dart)
Learnt from: Catrya
Repo: MostroP2P/mobile PR: 327
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:157-182
Timestamp: 2025-10-21T21:47:03.451Z
Learning: In MostroP2P/mobile, for Action.canceled handling in abstract_mostro_notifier.dart (Riverpod StateNotifier), do not add mounted checks after async sessionNotifier.deleteSession(orderId) as they break order state synchronization during app restart. The Action.canceled flow contains critical business logic that must complete fully; Riverpod handles provider disposal automatically. Mounted checks should only protect UI operations, not business logic in StateNotifiers.
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/shared/providers/mostro_database_provider.dart : Initialize Sembast database in shared/providers/mostro_database_provider.dart

Comment on lines +170 to +177
void _handleCantDo(dynamic payload) {
// Check if it's a not_found error (user has no data to restore)
if (payload != null && payload.toString().toLowerCase().contains('not_found')) {
_logger.i('Restore: no data found for user (cant-do: not_found)');
// Clean up and close the restore process
_cleanup();
}
}
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 | 🟡 Minor

Fragile error detection using string matching.

Line 172 uses payload.toString().toLowerCase().contains('not_found') to detect if the user has no data to restore. This string matching is brittle and may break if the backend changes error message format or if the payload structure changes.

Consider checking for structured error codes or specific payload fields instead:

  void _handleCantDo(dynamic payload) {
-   // Check if it's a not_found error (user has no data to restore)
-   if (payload != null && payload.toString().toLowerCase().contains('not_found')) {
+   // Check if it's a not_found error (user has no data to restore)
+   if (payload is Map && payload['error_code'] == 'not_found') {
      _logger.i('Restore: no data found for user (cant-do: not_found)');
-     // Clean up and close the restore process
      _cleanup();
    }
  }

If the backend doesn't provide structured error codes, document the expected error format and add a more robust check.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +271 to +278
// Invalidate and rebuild SubscriptionManager and MostroService to ensure fresh state
ref.invalidate(subscriptionManagerProvider);
ref.invalidate(mostroServiceProvider);

// Force rebuild by reading them immediately
ref.read(subscriptionManagerProvider);
final mostroService = ref.read(mostroServiceProvider);
mostroService.init();
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 | 🔴 Critical

🧩 Analysis chain

Verify provider invalidation pattern aligns with Riverpod best practices.

The code invalidates subscriptionManagerProvider and mostroServiceProvider, then immediately reads them and calls init(). This pattern forces a rebuild of these providers.

Please verify this pattern is the intended way to reinitialize services after restore in this codebase. Check if there's a standard service reinitialization pattern documented elsewhere:


🏁 Script executed:

#!/bin/bash
# Search for similar provider invalidation patterns in the codebase
rg -n -A3 -B3 "invalidate.*Provider" --type=dart -g '!**/restore_service.dart'

Length of output: 2996


I need to examine the provider definitions and understand the actual pattern better.

Now let me search for the provider definitions and init() method:

I need to examine the codebase directly to understand these providers. Let me search for the provider definitions and the init() method:


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for subscriptionManagerProvider and mostroServiceProvider definitions
echo "=== Searching for provider definitions ==="
rg -n "subscriptionManagerProvider|mostroServiceProvider" --type=dart -g '!**/restore_service.dart' -A 2

echo ""
echo "=== Searching for init() method definitions on services ==="
rg -n "\.init\(\)" --type=dart lib/services lib/features -B 2 -A 2

Length of output: 7524


Let me search for the MostroService class definition and init() method to understand what this pattern does:





🏁 Script executed:

#!/bin/bash
# Find MostroService class definition and init() method
echo "=== MostroService class definition and init() method ==="
fd -e dart -x grep -l "class MostroService" {} \;

echo ""
echo "=== Search for init() method in MostroService ==="
rg -n "class MostroService" --type=dart -A 50 | head -100

Length of output: 3515


🏁 Script executed:

#!/bin/bash
# Find MostroService init method more specifically
rg -n "void init\(\)|Future.*init\(\)" --type=dart lib/services/ -B 5 -A 10 | grep -A 15 "MostroService\|mostro_service"

Length of output: 1181


Remove unnecessary provider invalidation pattern; call init() directly like in lifecycle_manager.dart.

The invalidate-then-read pattern differs from how service reinitialization is done elsewhere in the codebase. In lib/services/lifecycle_manager.dart (lines 61-66), services are reinitialized by directly reading the provider and calling init() without invalidation first:

ref.read(subscriptionManagerProvider).subscribeAll();
ref.read(mostroServiceProvider).init();

Replace lines 272-278 in restore_service.dart to follow the same pattern:

ref.read(subscriptionManagerProvider).subscribeAll();
ref.read(mostroServiceProvider).init();

Invalidating providers before immediately reading them is not a standard Riverpod pattern and can introduce timing issues. The init() method already handles subscription setup on the existing instance.

🤖 Prompt for AI Agents
In lib/features/restore/restore_service.dart around lines 271-278, remove the
invalidate-then-read pattern and instead directly read the existing providers
and call their reinit methods; replace the ref.invalidate(...) + ref.read(...)
sequence with a direct ref.read(subscriptionManagerProvider).subscribeAll() and
a direct ref.read(mostroServiceProvider).init() so the existing instances are
reused and initialized consistently with lifecycle_manager.dart.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4200881 and dde3538.

📒 Files selected for processing (13)
  • lib/data/models/nostr_event.dart (1 hunks)
  • lib/data/repositories/event_storage.dart (1 hunks)
  • lib/features/key_manager/key_management_screen.dart (5 hunks)
  • lib/features/restore/order_restorer.dart (1 hunks)
  • lib/features/restore/restore_message_handler.dart (1 hunks)
  • lib/features/restore/restore_service.dart (1 hunks)
  • lib/features/restore/session_restorer.dart (1 hunks)
  • lib/features/settings/settings_notifier.dart (3 hunks)
  • lib/features/subscriptions/subscription_manager.dart (1 hunks)
  • lib/services/lifecycle_manager.dart (1 hunks)
  • lib/services/mostro_service.dart (1 hunks)
  • lib/services/restore_service.dart (1 hunks)
  • test/mocks.dart (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • lib/features/subscriptions/subscription_manager.dart
  • lib/data/repositories/event_storage.dart
🚧 Files skipped from review as they are similar to previous changes (3)
  • lib/services/mostro_service.dart
  • test/mocks.dart
  • lib/services/lifecycle_manager.dart
🧰 Additional context used
📓 Path-based instructions (5)
lib/features/**

📄 CodeRabbit inference engine (AGENTS.md)

Place application feature code under lib/features//, grouped by domain

Files:

  • lib/features/restore/restore_message_handler.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/order_restorer.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

**/*.dart: Use Dart formatter defaults (two-space indentation, trailing commas) and run flutter format before committing
Resolve all Flutter analyzer warnings (flutter analyze must be clean)

**/*.dart: Remove unused imports and unused dependencies
Do not add // ignore: must_be_immutable to classes within generated files; rely on regeneration and existing file-level ignores

Files:

  • lib/features/restore/restore_message_handler.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/order_restorer.dart
  • lib/services/restore_service.dart
  • lib/data/models/nostr_event.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
lib/**/*.dart

📄 CodeRabbit inference engine (AGENTS.md)

Name Riverpod providers as Provider or Notifier

lib/**/*.dart: Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text
Use clear, concise English for variable names, function names, and code comments; all comments must be in English
Target zero flutter analyze issues
Use latest Flutter/Dart APIs (e.g., Color.withValues() instead of withOpacity())
After async operations, check mounted before using BuildContext
Use const constructors where possible

Files:

  • lib/features/restore/restore_message_handler.dart
  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/order_restorer.dart
  • lib/services/restore_service.dart
  • lib/data/models/nostr_event.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
lib/services/**

📄 CodeRabbit inference engine (AGENTS.md)

Place service-layer code in lib/services/

Files:

  • lib/services/restore_service.dart
lib/data/**

📄 CodeRabbit inference engine (AGENTS.md)

Store persistence and API code under lib/data/

Files:

  • lib/data/models/nostr_event.dart
🧠 Learnings (22)
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/services/nostr_service.dart : All Nostr protocol interactions must go through NostrService (services/nostr_service.dart)

Applied to files:

  • lib/features/restore/restore_message_handler.dart
  • lib/services/restore_service.dart
  • lib/features/restore/restore_service.dart
📚 Learning: 2025-10-21T21:47:03.451Z
Learnt from: Catrya
Repo: MostroP2P/mobile PR: 327
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:157-182
Timestamp: 2025-10-21T21:47:03.451Z
Learning: In MostroP2P/mobile, for Action.canceled handling in abstract_mostro_notifier.dart (Riverpod StateNotifier), do not add mounted checks after async sessionNotifier.deleteSession(orderId) as they break order state synchronization during app restart. The Action.canceled flow contains critical business logic that must complete fully; Riverpod handles provider disposal automatically. Mounted checks should only protect UI operations, not business logic in StateNotifiers.

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
  • lib/services/restore_service.dart
  • lib/features/restore/session_restorer.dart
  • lib/features/restore/restore_service.dart
  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/core/app_routes.dart : Use GoRouter for navigation configured in core/app_routes.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
Repo: MostroP2P/mobile PR: 74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, Riverpod code generation is used with `Riverpod` annotations. Providers like `eventStorageProvider` are generated in `.g.dart` files from annotated functions in the main provider files. These providers are accessible by importing the main provider file (e.g., `mostro_service_provider.dart`), not by importing a separate provider file.

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/*/providers/**/*.dart : Organize Riverpod providers by feature under features/{feature}/providers/

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
  • lib/features/restore/session_restorer.dart
📚 Learning: 2025-10-07T17:31:12.117Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T17:31:12.117Z
Learning: Applies to lib/**/*.dart : Name Riverpod providers as <Feature>Provider or <Feature>Notifier

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-05-06T15:49:26.443Z
Learnt from: chebizarro
Repo: MostroP2P/mobile PR: 74
File: lib/services/mostro_service.dart:70-76
Timestamp: 2025-05-06T15:49:26.443Z
Learning: In the Mostro Mobile codebase, `eventStorageProvider` is exported from `package:mostro_mobile/shared/providers/mostro_service_provider.dart` and not from a separate `event_storage_provider.dart` file.

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/generated/l10n.dart : Generated localization entrypoint file is lib/generated/l10n.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/main.dart : Configure timeago locales and app initialization in main.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/data/models.dart : Export models through lib/data/models.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/**/screens/**/*.dart : Pass BuildContext to methods that need localization (S.of(context)!.keyName usage)

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/shared/providers/mostro_database_provider.dart : Initialize Sembast database in shared/providers/mostro_database_provider.dart

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
  • lib/services/restore_service.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/**/*.dart : Always use localized strings via S.of(context)!.keyName; avoid hardcoded user-facing text

Applied to files:

  • lib/features/key_manager/key_management_screen.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/core/models/relay_list_event.dart : Parse NIP-65 kind 10002 events, validate WS URLs, and handle timestamps/null-safety

Applied to files:

  • lib/data/models/nostr_event.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/subscriptions/subscription_manager.dart : SubscriptionManager manages subscriptions, including relay list stream; ensure proper initialization and cleanup

Applied to files:

  • lib/features/restore/restore_service.dart
📚 Learning: 2025-08-21T14:45:43.974Z
Learnt from: Catrya
Repo: MostroP2P/mobile PR: 272
File: lib/features/relays/widgets/relay_selector.dart:13-15
Timestamp: 2025-08-21T14:45:43.974Z
Learning: In the Mostro mobile app's RelaySelector widget (lib/features/relays/widgets/relay_selector.dart), watching relaysProvider.notifier correctly triggers rebuilds because the relaysProvider itself depends on settingsProvider (line 8 in relays_provider.dart). When blacklist changes via toggleMostroRelayBlacklist(), the settingsProvider updates, causing relaysProvider to rebuild, which then notifies widgets watching the notifier. The UI correctly reflects active/inactive states in real-time through this dependency chain.

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/settings/settings.dart : Settings model must include blacklistedRelays and maintain backward-compatible serialization

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/relays/relays_notifier.dart : Implement relay operations: syncWithMostroInstance, removeRelayWithBlacklist, addRelayWithSmartValidation, _handleMostroRelayListUpdate

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/**/notifiers/**/*.dart : Encapsulate business logic in Notifiers (use Notifier pattern for complex state logic)

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/notifications/notification_service.dart : Notification service configuration and custom icons should be defined in notification_service.dart

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/core/mostro_fsm.dart : MostroFSM manages order state transitions (core/mostro_fsm.dart)

Applied to files:

  • lib/features/settings/settings_notifier.dart
📚 Learning: 2025-10-22T12:29:26.971Z
Learnt from: CR
Repo: MostroP2P/mobile PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T12:29:26.971Z
Learning: Applies to lib/features/relays/relay.dart : Relay model must track source (user, mostro, defaultConfig) and expose canDelete/canBlacklist semantics

Applied to files:

  • lib/features/settings/settings_notifier.dart
🔇 Additional comments (4)
lib/data/models/nostr_event.dart (1)

284-349: Dual-key wrap flow looks solid

The separation between rumor and seal key material is implemented cleanly here; the serialization and encryption steps line up with the new restore flow. Nicely done.

lib/features/settings/settings_notifier.dart (1)

85-88: Skip rewriting identical Mostro key state

When oldPubkey == newValue, we still clone state and persist to prefs even though nothing changed. That means we notify listeners and hit disk for no benefit. Dropping the else branch keeps the hot path quiet while preserving the new restore workflow.

[ suggest_optional_refactor ]

-    } else {
-      state = state.copyWith(mostroPublicKey: newValue);
-      await _saveToPrefs();
-    }
+    }
lib/features/restore/order_restorer.dart (1)

6-46: Good extraction/logging helpers

Appreciate the focused helpers here—collecting IDs and logging detail counts keeps the restore pipeline tidy.

lib/features/key_manager/key_management_screen.dart (1)

788-855: Thanks for capturing context before awaits

Nice touch precomputing the scaffold messenger and localized strings before awaiting restore flows—prevents mounted checks from tripping over a stale BuildContext.

Comment on lines +47 to +66
Future<void> restore() async {
_logger.i('Restore: initiated');

await _clearAll();
_reset();

final progress = ref.read(restoreProgressProvider.notifier);
progress.startRestore();

try {
await _validatePrerequisites();
await _setupSubscription();
await _sendRestoreRequest();
} catch (e, stackTrace) {
_logger.e('Restore: failed', error: e, stackTrace: stackTrace);
progress.showError(e.toString());
rethrow;
}
}

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 | 🔴 Critical

Don’t wipe local state before we know restore will succeed

restore() calls _clearAll() up front, so we delete every session, notification, and cached message before verifying prerequisites, subscribing, or receiving any restore data. Any exception (network failure, malformed response, timeout, etc.) leaves the app in a wall‑clock empty state with no way to operate until the user retries, effectively losing all locally restored context. Please postpone _clearAll() until finalization—after we have restore data and just before we persist the new state—so a failed attempt doesn’t brick the client.

One possible fix:

-    await _clearAll();
-    _reset();
+    _reset();

and then invoke _clearAll() at the start of _finalize() (before calling _sessionRestorer.restoreSessions) so we only wipe once success is imminent.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In lib/features/restore/restore_service.dart around lines 47 to 66, the code
calls _clearAll() immediately at the start of restore(), which wipes local
sessions/notifications/caches before prerequisites, subscription, or restore
data are confirmed; move the deletion so failed restores do not leave the app
empty: remove the early _clearAll() call from restore(), and instead invoke
_clearAll() at the start of _finalize() (immediately before persisting/restoring
new state and before calling _sessionRestorer.restoreSessions), keeping _reset()
and progress handling as appropriate; ensure error paths still rethrow and do
not perform any clearing when exceptions occur prior to _finalize().

Comment on lines +32 to +87
for (final order in restoreOrders) {
final orderId = order['order_id'] as String?;
final tradeIndex = order['trade_index'] as int?;

if (orderId != null && tradeIndex != null) {
final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
final role = await _determineRole(orderId, tradeIndex, orderDetails);

final session = Session(
masterKey: masterKey,
tradeKey: tradeKey,
keyIndex: tradeIndex,
fullPrivacy: settings.fullPrivacyMode,
startTime: DateTime.now(),
orderId: orderId,
role: role,
);

await sessionNotifier.saveSession(session);

// Save order details as MostroMessage
await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);

restoredCount++;
progress.incrementProgress();
}
}
}

if (restoreDisputes != null) {
for (final dispute in restoreDisputes) {
final orderId = dispute['order_id'] as String?;
final tradeIndex = dispute['trade_index'] as int?;

if (orderId != null && tradeIndex != null) {
final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
final role = await _determineRole(orderId, tradeIndex, orderDetails);

final session = Session(
masterKey: masterKey,
tradeKey: tradeKey,
keyIndex: tradeIndex,
fullPrivacy: settings.fullPrivacyMode,
startTime: DateTime.now(),
orderId: orderId,
role: role,
);

await sessionNotifier.saveSession(session);

// Save order details as MostroMessage
await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);

restoredCount++;
progress.incrementProgress();
}
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 | 🔴 Critical

Restored orders never persist because we read the wrong field

The restore payload for orders/disputes comes back as {id, trade_index, status} (see PR summary), so order['order_id'] is always null. As a result every iteration bails out and no sessions are recreated. The same mistake is repeated for disputes. Please read the correct id field (with an optional fallback if the backend ever sends order_id).

Apply this diff to stop skipping every restored entry:

-        final orderId = order['order_id'] as String?;
+        final orderId = (order['id'] ?? order['order_id']) as String?;
...
-        final orderId = dispute['order_id'] as String?;
+        final orderId = (dispute['id'] ?? dispute['order_id']) as String?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (final order in restoreOrders) {
final orderId = order['order_id'] as String?;
final tradeIndex = order['trade_index'] as int?;
if (orderId != null && tradeIndex != null) {
final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
final role = await _determineRole(orderId, tradeIndex, orderDetails);
final session = Session(
masterKey: masterKey,
tradeKey: tradeKey,
keyIndex: tradeIndex,
fullPrivacy: settings.fullPrivacyMode,
startTime: DateTime.now(),
orderId: orderId,
role: role,
);
await sessionNotifier.saveSession(session);
// Save order details as MostroMessage
await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);
restoredCount++;
progress.incrementProgress();
}
}
}
if (restoreDisputes != null) {
for (final dispute in restoreDisputes) {
final orderId = dispute['order_id'] as String?;
final tradeIndex = dispute['trade_index'] as int?;
if (orderId != null && tradeIndex != null) {
final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
final role = await _determineRole(orderId, tradeIndex, orderDetails);
final session = Session(
masterKey: masterKey,
tradeKey: tradeKey,
keyIndex: tradeIndex,
fullPrivacy: settings.fullPrivacyMode,
startTime: DateTime.now(),
orderId: orderId,
role: role,
);
await sessionNotifier.saveSession(session);
// Save order details as MostroMessage
await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);
restoredCount++;
progress.incrementProgress();
}
for (final order in restoreOrders) {
final orderId = (order['id'] ?? order['order_id']) as String?;
final tradeIndex = order['trade_index'] as int?;
if (orderId != null && tradeIndex != null) {
final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
final role = await _determineRole(orderId, tradeIndex, orderDetails);
final session = Session(
masterKey: masterKey,
tradeKey: tradeKey,
keyIndex: tradeIndex,
fullPrivacy: settings.fullPrivacyMode,
startTime: DateTime.now(),
orderId: orderId,
role: role,
);
await sessionNotifier.saveSession(session);
// Save order details as MostroMessage
await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);
restoredCount++;
progress.incrementProgress();
}
}
}
if (restoreDisputes != null) {
for (final dispute in restoreDisputes) {
final orderId = (dispute['id'] ?? dispute['order_id']) as String?;
final tradeIndex = dispute['trade_index'] as int?;
if (orderId != null && tradeIndex != null) {
final tradeKey = await keyManager.deriveTradeKeyFromIndex(tradeIndex);
final role = await _determineRole(orderId, tradeIndex, orderDetails);
final session = Session(
masterKey: masterKey,
tradeKey: tradeKey,
keyIndex: tradeIndex,
fullPrivacy: settings.fullPrivacyMode,
startTime: DateTime.now(),
orderId: orderId,
role: role,
);
await sessionNotifier.saveSession(session);
// Save order details as MostroMessage
await _saveOrderDetailsAsMessage(orderId, orderDetails, storage);
restoredCount++;
progress.incrementProgress();
}
🤖 Prompt for AI Agents
In lib/features/restore/session_restorer.dart around lines 32 to 87, the code
reads order['order_id'] which is null because the payload uses id; change the
extraction to prefer order['id'] and fall back to order['order_id'] (e.g. final
orderId = (order['id'] ?? order['order_id']) as String?); apply the same change
for disputes, keeping the rest of the logic (deriveTradeKeyFromIndex,
_determineRole, creating and saving Session, saving message, incrementing
counters) unchanged so restored entries are not skipped.

Comment on lines +329 to +353
} catch (e) {
_logger.e('Error processing all data: $e');
_cleanupTempSession();
}
}

void _cleanupTempSession() {
_tempSubscription?.cancel();
_tempSubscription = null;
_tempTradeKey1 = null;
_processedEventIds.clear();

_receivedRestoreData = false;
_receivedOrdersInfo = false;
_receivedLastTradeIndex = false;

_tempRestoreOrders = null;
_tempRestoreDisputes = null;
_tempOrdersDetails = null;
_tempLastTradeIndex = null;

final progressNotifier = ref.read(restoreProgressProvider.notifier);
progressNotifier.completeRestore();

_logger.i('Restore completed');
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

Do not report restore success after an exception

If _processAllData() throws, we catch it, call _cleanupTempSession(), and that method unconditionally fires progressNotifier.completeRestore(). The overlay therefore reports success even though the restore failed, and we never surface an error to the user.

Guard the cleanup routine so only the happy path calls completeRestore(), and route failures to showError(...) (or similar). For example:

-    } catch (e) {
-      _logger.e('Error processing all data: $e');
-      _cleanupTempSession();
-    }
+    } catch (e) {
+      _logger.e('Error processing all data: $e');
+      _cleanupTempSession(success: false, errorMessage: e.toString());
+    }
   }

-  void _cleanupTempSession() {
+  void _cleanupTempSession({bool success = true, String? errorMessage}) {
     _tempSubscription?.cancel();
     _tempSubscription = null;
     _tempTradeKey1 = null;
     _processedEventIds.clear();
@@
-    final progressNotifier = ref.read(restoreProgressProvider.notifier);
-    progressNotifier.completeRestore();
-
-    _logger.i('Restore completed');
+    final progressNotifier = ref.read(restoreProgressProvider.notifier);
+    if (success) {
+      progressNotifier.completeRestore();
+      _logger.i('Restore completed');
+    } else {
+      progressNotifier.showError(errorMessage ?? 'Restore failed');
+      _logger.w('Restore aborted due to errors');
+    }
   }
🤖 Prompt for AI Agents
In lib/services/restore_service.dart around lines 329 to 353, the cleanup
routine unconditionally calls progressNotifier.completeRestore() which reports
success even when _processAllData() throws; change the flow so
_cleanupTempSession() only clears subscriptions/state and DOES NOT call
completeRestore(), and instead call completeRestore() from the successful/happy
path after _processAllData() finishes; in the catch block, after cleaning up,
call the error surface method (e.g. showError(...) or a
progressNotifier.fail/error method) to inform the user and log the exception,
ensuring no success signal is emitted on failure.

/// - rumorKeys: The keys used to sign the rumor (trade key)
/// - sealKeys: The keys used to sign the seal (master/identity key)
/// - receiverPubkey: The receiver's public key (Mostro pubkey)
Future<NostrEvent> mostroWrapWithSeparateKeys({
Copy link
Member

Choose a reason for hiding this comment

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

why create a new function to build gift wrap event there is mostroWrap() in this same file?

@BraCR10
Copy link
Contributor Author

BraCR10 commented Nov 26, 2025

solved in #355

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.

implement the Import Mostro User option

3 participants