Skip to content

Conversation

@chebizarro
Copy link
Contributor

@chebizarro chebizarro commented Jun 26, 2025

This PR addresses several related issues:

Fixes #57: App requiring restart to update order status and chat messages

  • Implemented subscription pooling in SubscriptionManager to prevent hitting relay subscription limits
  • Previously, the app would create separate subscriptions for each order/chat, quickly exhausting relay connection limits
  • New SubscriptionManager consolidates subscriptions by type (chat, orders), preventing lost events and connection issues
  • Made SubscriptionManager reactive to SessionNotifier changes for automatic subscription updates
  • Ensured proper subscription synchronization between foreground and background

Addresses #54: Duplicate notifications for already completed orders

  • Implemented deduplication logic for MostroMessage event handling
  • Fixed provider/emitter logic to prevent duplicate event emissions
  • Synchronized subscription state with SubscriptionManager for all types

Addresses #70: Automatic screen transition after hold invoice payment

  • Re-enabled state update in AddOrderNotifier to properly track order state changes
  • Improved event handling to trigger appropriate UI transitions

Additional improvements:

  • Updated all tests to use NostrUtils static methods directly
  • Fixed mock implementations and mostroPublicKey formats in tests

These changes significantly improve the app's reliability by preventing subscription limit issues, enhancing responsiveness to real-time updates, and providing a more seamless user experience during order flows.

Summary by CodeRabbit

  • New Features

    • Centralized event subscription management added, improving reliability and maintainability.
    • Enhanced session lifecycle management with automated cleanup and expiration.
    • Improved validation and error handling for data models (orders, payments, currencies, peers, sessions, etc.).
    • New localization keys for button labels and improved translations.
    • Added support for new configuration parameters and background service behaviors.
  • Bug Fixes

    • Fixed issues with invalid or malformed data by enforcing stricter validation and error messages.
    • Improved error handling in background event processing and notification retries.
  • Refactor

    • Consolidated and simplified provider dependencies and subscription logic across the app.
    • Removed deprecated or unused classes and methods.
    • Updated button labels and UI text for clarity and localization consistency.
    • Updated dependency versions for better compatibility.
  • Style

    • Improved code formatting and readability in multiple files.
  • Chores

    • Updated .gitignore and analysis options for better development workflow.
    • Removed unnecessary comments and metadata from localization files.
  • Tests

    • Updated and refactored test mocks and test setup to align with new architecture and provider changes.
    • Improved test reliability and maintainability.

@chebizarro chebizarro requested review from Catrya and grunch June 26, 2025 06:28
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 26, 2025

Walkthrough

This update introduces a centralized subscription management system for Nostr events, refactors chat and order state management to use this system, and enhances data model validation and error handling throughout the codebase. It also updates UI localization keys, button logic, and background service behavior, while removing outdated or redundant notifiers and models.

Changes

File(s) / Group Change Summary
lib/features/subscriptions/subscription.dart, lib/features/subscriptions/subscription_manager.dart, lib/features/subscriptions/subscription_manager_provider.dart, lib/features/subscriptions/subscription_type.dart Introduced centralized subscription management classes, enum, and provider for Nostr event streams.
lib/features/chat/notifiers/chat_room_notifier.dart, lib/features/chat/notifiers/chat_rooms_notifier.dart, lib/features/chat/providers/chat_room_providers.dart, lib/features/chat/screens/chat_room_screen.dart, lib/features/chat/screens/chat_rooms_list.dart Refactored chat notifiers and providers to use the new subscription manager and order-based peer lookup.
lib/features/order/notfiers/abstract_mostro_notifier.dart, lib/features/order/notfiers/add_order_notifier.dart, lib/features/order/notfiers/order_notifier.dart Updated order notifiers to use centralized subscription management, improved state updates, and navigation logic.
lib/services/mostro_service.dart, lib/services/lifecycle_manager.dart, lib/shared/providers/mostro_service_provider.dart, lib/shared/providers/session_notifier_provider.dart, lib/shared/providers/app_init_provider.dart Refactored service and provider initialization to use the new subscription manager, improved session cleanup, and streamlined app initialization.
lib/data/models/amount.dart, lib/data/models/cant_do.dart, lib/data/models/chat_room.dart, lib/data/models/currency.dart, lib/data/models/dispute.dart, lib/data/models/order.dart, lib/data/models/payment_request.dart, lib/data/models/peer.dart, lib/data/models/range_amount.dart, lib/data/models/rating_user.dart, lib/data/models/session.dart, lib/data/models/text_message.dart Enhanced validation, error handling, equality, and serialization for all key data models.
lib/data/repositories/mostro_storage.dart, lib/data/repositories/event_storage.dart Improved duplicate prevention, event streaming, and removed direct NostrEvent streaming from event storage.
lib/shared/widgets/mostro_reactive_button.dart, lib/features/trades/screens/trade_detail_screen.dart, lib/shared/widgets/pay_lightning_invoice_widget.dart Updated UI logic for button visibility and labels, reduced timeouts, and aligned with new localization keys.
lib/l10n/intl_en.arb, lib/l10n/intl_es.arb, lib/l10n/intl_it.arb Renamed button and action localization keys, removed unused keys and comments, added new button key.
lib/background/background.dart, lib/background/desktop_background_service.dart, lib/background/mobile_background_service.dart Adjusted background service behavior, removed database operations from desktop, and improved error handling.
lib/features/key_manager/key_manager.dart, lib/core/config.dart Improved key initialization and made config values immutable with new cleanup/session expiration constants.
lib/features/trades/providers/trades_provider.dart Consolidated trade filtering logic with a status set.
lib/data/models/mostro_message.dart Reformatted constructor for consistency.
lib/data/models/chat_model.dart, lib/shared/notifiers/order_action_notifier.dart, test/services/mostro_service_test.mocks.dart Removed unused or obsolete classes and generated mocks.
lib/shared/utils/notification_permission_helper.dart, lib/main.dart Corrected notification permission logic and platform checks.
test/mocks.dart, test/mocks.mocks.dart, test/notifiers/add_order_notifier_test.dart, test/notifiers/take_order_notifier_test.dart, test/services/mostro_service_test.dart Updated and extended test mocks and test setup to support new subscription and session management.
.gitignore, analysis_options.yaml, pubspec.yaml Updated ignore patterns, analysis exclusions, and dependency versions.
lib/notifications/notification_service.dart Cleaned up comments and clarified notification retry logic.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant SubscriptionManager
    participant NostrService
    participant MostroService
    participant ChatRoomNotifier
    participant OrderNotifier

    App->>SubscriptionManager: Initialize (via Provider)
    SubscriptionManager->>NostrService: Subscribe to events (orders/chat)
    NostrService-->>SubscriptionManager: Stream<NostrEvent>
    SubscriptionManager-->>MostroService: Stream orders events
    SubscriptionManager-->>ChatRoomNotifier: Stream chat events
    MostroService->>OrderNotifier: Update order state on event
    ChatRoomNotifier->>ChatRoomNotifier: Update chat state on event
    App->>SubscriptionManager: On app foreground/background, subscribe/unsubscribe all
Loading

Estimated code review effort

4 (~90 minutes)

Possibly related PRs

  • Refactor: State Management Overhaul (#77) #110: The main PR and the retrieved PR both involve a comprehensive overhaul of the order and session state management, including replacing raw message-based state with a richer OrderState model, changing the AbstractMostroNotifier to use OrderState, removing separate notifiers for cantDo, payment, and dispute, and centralizing subscription management; these are strongly related changes at the code level affecting the same classes, methods, and providers in the order and session management domain.
  • Implementation of Background Services & Notification System #74: Shares extensive changes related to background services, subscription management, and notification handling, overlapping in architectural refactoring of subscription logic and lifecycle management.
  • fix: comprehensive test suite improvements and fixes #125: Related to test mocks updates and expansions for session notifier and settings notifier, improving mocking infrastructure that aligns with changes in this PR.

Suggested reviewers

  • Catrya
  • grunch

Poem

In burrows deep, a rabbit spies
New streams and chats that synchronize.
With keys and orders, all in sync,
No need to close the app or blink!
Models checked, subscriptions new—
Mostro hops ahead, for you!
🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 82183d5 and 950c6c1.

📒 Files selected for processing (1)
  • lib/shared/widgets/pay_lightning_invoice_widget.dart (0 hunks)
💤 Files with no reviewable changes (1)
  • lib/shared/widgets/pay_lightning_invoice_widget.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

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

@chebizarro chebizarro moved this to Code Review in Mostro tasks Jun 26, 2025
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: 7

🧹 Nitpick comments (6)
lib/shared/notifiers/session_notifier.dart (1)

43-62: Good timer management for periodic cleanup.

The implementation correctly cancels existing timers and handles session expiration. Consider extracting the expiration logic to reduce duplication between init() and _cleanup().

lib/features/subscriptions/subscription_manager.dart (3)

21-21: Consider using late final for _sessionListener

Since _sessionListener is initialized in _initSessionListener which is called from the constructor, it could be declared as late final instead of nullable to avoid null checks.

-  ProviderSubscription? _sessionListener;
+  late final ProviderSubscription _sessionListener;

Then update the dispose method:

-    if (_sessionListener != null) {
-      _sessionListener!.close();
-      _sessionListener = null;
-    }
+    _sessionListener.close();

38-48: Consider using fireImmediately for initial state handling

The session listener doesn't handle the initial state. Consider using fireImmediately: true to process current sessions immediately when the listener is set up.

    _sessionListener = ref.listen<List<Session>>(
      sessionNotifierProvider,
      (previous, current) {
        _logger.i('Sessions changed, updating subscriptions');
        _updateAllSubscriptions(current);
      },
+     fireImmediately: true,
      onError: (error, stackTrace) {

This would eliminate the need for the manual initial update on lines 51-52.


101-101: Improve logging output for subscriptions

Logging the entire _subscriptions map directly may not provide readable output. Consider logging more specific information.

-    _logger.i('Updated $type subscription with $_subscriptions');
+    _logger.i('Updated $type subscription. Active subscriptions: ${_subscriptions.keys.toList()}');
integration_test/test_helpers.dart (1)

320-328: Complete the implementation of unsubscribe and dispose methods

The unsubscribe and dispose methods have TODO comments indicating incomplete implementation. These should be implemented even if they're no-ops for testing.

Would you like me to help implement these methods or create an issue to track this task?

  @override
  void unsubscribe(Session session) {
-    // TODO: implement unsubscribe
+    // No-op for fake service
  }
  
  @override
  void dispose() {
-    // TODO: implement dispose
+    // No-op for fake service
  }
lib/services/mostro_service.dart (1)

40-61: Consider adding @deprecated annotations to backward compatibility methods.

These methods are kept for backward compatibility but don't perform any actions. To make the API clearer, consider adding @deprecated annotations with migration instructions.

+  @Deprecated('SubscriptionManager now automatically handles subscriptions. This method will be removed in a future version.')
   void subscribe(Session session) {
     _logger.i('Manual subscription not needed for: ${session.tradeKey.public}');
     // No action needed - SubscriptionManager handles subscriptions automatically
   }

+  @Deprecated('SubscriptionManager now automatically handles subscriptions. This method will be removed in a future version.')
   void unsubscribe(Session session) {
     _logger
         .i('Manual unsubscription not needed for: ${session.tradeKey.public}');
     // No action needed - SubscriptionManager handles subscriptions automatically
   }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b8a0e5e and 83450c4.

📒 Files selected for processing (35)
  • integration_test/test_helpers.dart (6 hunks)
  • lib/core/config.dart (1 hunks)
  • lib/data/repositories/mostro_storage.dart (2 hunks)
  • lib/features/chat/notifiers/chat_room_notifier.dart (3 hunks)
  • lib/features/chat/notifiers/chat_rooms_notifier.dart (4 hunks)
  • lib/features/chat/providers/chat_room_providers.dart (1 hunks)
  • lib/features/chat/screens/chat_rooms_list.dart (2 hunks)
  • lib/features/order/models/order_state.dart (6 hunks)
  • lib/features/order/notfiers/abstract_mostro_notifier.dart (7 hunks)
  • lib/features/order/notfiers/add_order_notifier.dart (3 hunks)
  • lib/features/order/notfiers/order_notifier.dart (1 hunks)
  • lib/features/order/screens/add_order_screen.dart (1 hunks)
  • lib/features/subscriptions/subscription.dart (1 hunks)
  • lib/features/subscriptions/subscription_manager.dart (1 hunks)
  • lib/features/subscriptions/subscription_manager_provider.dart (1 hunks)
  • lib/features/subscriptions/subscription_type.dart (1 hunks)
  • lib/features/trades/screens/trade_detail_screen.dart (2 hunks)
  • lib/features/trades/widgets/mostro_message_detail_widget.dart (0 hunks)
  • lib/main.dart (1 hunks)
  • lib/services/lifecycle_manager.dart (4 hunks)
  • lib/services/mostro_service.dart (3 hunks)
  • lib/services/nostr_service.dart (1 hunks)
  • lib/shared/notifiers/order_action_notifier.dart (0 hunks)
  • lib/shared/notifiers/session_notifier.dart (2 hunks)
  • lib/shared/providers/app_init_provider.dart (2 hunks)
  • lib/shared/providers/mostro_service_provider.dart (2 hunks)
  • lib/shared/providers/mostro_service_provider.g.dart (1 hunks)
  • lib/shared/providers/session_notifier_provider.dart (1 hunks)
  • lib/shared/utils/notification_permission_helper.dart (1 hunks)
  • test/mocks.dart (5 hunks)
  • test/mocks.mocks.dart (15 hunks)
  • test/notifiers/add_order_notifier_test.dart (2 hunks)
  • test/notifiers/take_order_notifier_test.dart (6 hunks)
  • test/services/mostro_service_test.dart (7 hunks)
  • test/services/mostro_service_test.mocks.dart (26 hunks)
💤 Files with no reviewable changes (2)
  • lib/features/trades/widgets/mostro_message_detail_widget.dart
  • lib/shared/notifiers/order_action_notifier.dart
🧰 Additional context used
🧠 Learnings (3)
lib/shared/providers/mostro_service_provider.g.dart (1)
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.
lib/shared/providers/mostro_service_provider.dart (2)
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.
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.
lib/features/order/notfiers/abstract_mostro_notifier.dart (1)
Learnt from: chebizarro
PR: MostroP2P/mobile#110
File: lib/features/trades/widgets/mostro_message_detail_widget.dart:134-141
Timestamp: 2025-06-08T23:54:09.987Z
Learning: In the OrderState refactor, public keys should be accessed via `tradeState.peer?.publicKey` from the OrderState instance rather than through `session?.peer?.publicKey`, as the OrderState encapsulates peer information directly.
🔇 Additional comments (49)
lib/features/subscriptions/subscription_type.dart (1)

1-4: Clean enum implementation for subscription categorization.

The SubscriptionType enum is well-defined with clear, domain-specific values that align with the app's subscription management needs. This provides type safety for the centralized subscription architecture.

lib/core/config.dart (1)

31-34: Good practice: Converting to immutable constants and adding session management config.

The changes improve configuration management by:

  • Making values immutable with const (good practice)
  • Adding reasonable defaults for cleanup intervals (30 min) and session expiration (36 hours)
  • Supporting centralized session lifecycle management
lib/shared/providers/mostro_service_provider.g.dart (1)

25-25: Expected hash update for generated code.

The hash change reflects the underlying refactoring of mostroServiceProvider implementation. This is expected behavior for generated Riverpod provider code.

lib/main.dart (1)

19-19: Good refactoring: Simplifying main.dart initialization.

Moving the platform and environment checking logic into requestNotificationPermissionIfNeeded() itself is a good architectural decision. This makes the main initialization cleaner and properly separates concerns.

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

1-7: Clean provider implementation for centralized subscription management.

The subscriptionManagerProvider is well-implemented, following Riverpod best practices by passing the ref to enable the SubscriptionManager to access other providers. This supports the architectural goal of centralizing subscription handling.

lib/features/chat/screens/chat_rooms_list.dart (2)

8-8: Good architectural improvement - centralizing data access

The shift from session-based to order-based data access aligns well with the centralized subscription management approach described in the PR objectives.


76-77: Verify null safety for peer access

The change from orderState.session!.peer!.publicKey to orderState.peer!.publicKey simplifies the data access path, but please ensure that orderState.peer cannot be null in this context to avoid potential runtime exceptions.

#!/bin/bash
# Description: Check if OrderState.peer can be null and verify null safety handling

# Search for OrderState model definition and peer field
ast-grep --pattern 'class OrderState {
  $$$
  peer
  $$$
}'

# Search for any null checks on peer in other parts of the codebase
rg -A 3 -B 3 "\.peer(\s*==\s*null|\s*!=\s*null|\?)"
lib/features/order/notfiers/order_notifier.dart (1)

18-20: Verify that removed filtering and sorting logic is not needed

The sync method was significantly simplified by removing message filtering (cantDo actions), sorting by timestamp, and early return for empty messages. Please confirm that:

  1. Filtering out cantDo actions is no longer needed or handled elsewhere
  2. Message ordering doesn't affect state updates
  3. The performance optimization for empty messages is not required
#!/bin/bash
# Description: Verify if message filtering and sorting logic is handled elsewhere

# Check if cantDo action filtering is done in updateWith method or elsewhere
ast-grep --pattern 'updateWith($msg) {
  $$$
}'

# Check for any cantDo action handling
rg -A 5 -B 5 "cantDo|Action\.cantDo"

# Check if message ordering is important in state updates
rg -A 10 -B 5 "timestamp.*sort|sort.*timestamp"
lib/data/repositories/mostro_storage.dart (2)

17-17: Good optimization - prevents duplicate message inserts

Adding the duplicate check prevents unnecessary database operations and potential conflicts when the same message is processed multiple times. This aligns well with the deduplication logic mentioned in the PR objectives.


171-180: Enhanced query with proper sorting and limiting

The improvements to watchByRequestId with sorting by timestamp, limiting to 1 result, and robust stream mapping enhance the reliability of retrieving the latest message. The explicit null handling in the stream mapping is a good defensive programming practice.

lib/features/chat/providers/chat_room_providers.dart (1)

9-9: Approve dependency simplification

Removing the SessionNotifier dependency aligns with the centralized subscription management approach and simplifies the provider construction. This change supports the architectural goal of decoupling session management from chat room state management.

#!/bin/bash
# Description: Verify ChatRoomsNotifier constructor accepts only ref parameter

# Check ChatRoomsNotifier constructor signature
ast-grep --pattern 'class ChatRoomsNotifier {
  $$$
  ChatRoomsNotifier($$$) {
    $$$
  }
  $$$
}'

# Verify no references to sessionNotifier in ChatRoomsNotifier
rg -A 3 -B 3 "sessionNotifier" lib/features/chat/notifiers/chat_rooms_notifier.dart
lib/shared/utils/notification_permission_helper.dart (2)

1-1: Appropriate import for platform detection

Adding the dart:io import enables platform-specific behavior, which is necessary for the Android-only notification permission logic.


6-11: Excellent platform-specific and test-aware implementation

The addition of platform guards (Platform.isAndroid) and test environment detection (!Platform.environment.containsKey('FLUTTER_TEST')) is a best practice that:

  1. Prevents unnecessary permission requests on iOS where they're handled differently
  2. Avoids test failures when running Flutter tests
  3. Maintains clean separation of platform-specific concerns

This change enhances the robustness of the notification permission handling.

test/notifiers/take_order_notifier_test.dart (2)

35-35: LGTM! Test setup correctly adapted to new dependency injection pattern.

The addition of MockRef and its usage in the MockSessionNotifier constructor properly reflects the architectural changes where SessionNotifier now receives dependencies through Riverpod's Ref instead of direct injection.

Also applies to: 47-47, 52-52


131-131: Good practice: Standardized mostroPublicKey across tests.

The consistent mostroPublicKey value across all test configurations improves maintainability and reduces test setup complexity.

Also applies to: 195-195, 258-258, 307-307

lib/shared/providers/session_notifier_provider.dart (2)

3-3: Import update aligns with dependency injection refactoring.

The change from importing key_manager_provider.dart to settings.dart correctly reflects the new pattern where dependencies are accessed through the Ref object.


14-24: Excellent reactive pattern implementation.

The SessionNotifier construction now uses ref for dependency access, and the settings listener ensures automatic updates when settings change. This pattern maintains consistency across the application and supports the centralized subscription management architecture.

lib/features/order/notfiers/add_order_notifier.dart (2)

54-54: Improved state management using updateWith method.

The replacement of direct OrderState construction with state.updateWith(message) aligns with the centralized state management improvements and ensures consistent state handling across the application.

Also applies to: 78-78


22-24: Verify request ID uniqueness with timestamp-based generation.

The change from XORing UUID segments to using DateTime.now().microsecondsSinceEpoch could potentially create collisions if multiple orders are created in rapid succession, especially in high-frequency scenarios.

Consider whether microsecond precision provides sufficient uniqueness for your use case. If collision risk is a concern, consider adding additional entropy or reverting to the UUID-based approach.

#!/bin/bash
# Search for other request ID generation patterns in the codebase
ast-grep --pattern '_requestIdFromOrderId($_) {
  $$$
}'

# Check if there are other timestamp-based ID generators
rg -A 5 "microsecondsSinceEpoch"
lib/features/order/screens/add_order_screen.dart (1)

288-299: Good sanitization implementation for payment method input.

The sanitization logic properly handles problematic characters that could cause issues with NIP-69 formatting. The approach of replacing problematic characters with spaces and normalizing whitespace is sound.

Consider documenting the specific characters being sanitized for future maintenance:

+ // Sanitize payment method to comply with NIP-69 formatting
+ // Remove characters that could break JSON parsing: , " \ [ ] { }
  final problematicChars = RegExp(r'[,"\\\[\]{}]');
lib/features/trades/screens/trade_detail_screen.dart (2)

389-391: Enhanced user communication with sendDm action.

Adding the sendDm action case to display a contact button improves user experience by providing clear communication options during trade interactions.


452-452: Improved navigation determinism.

Changing from context.pop() to context.go('/order_book') provides more predictable navigation behavior, especially useful for deep-linked scenarios and ensures users always land on the expected screen.

test/notifiers/add_order_notifier_test.dart (2)

36-36: LGTM: Proper mock setup for the new Ref dependency.

The addition of MockRef aligns with the architectural change where SessionNotifier now takes a Ref parameter for accessing providers instead of direct dependencies.


50-53: LGTM: Test setup correctly updated for new constructor signature.

The changes properly update the test setup to use MockSettings and include the MockRef as the first parameter in the MockSessionNotifier constructor, matching the refactored SessionNotifier implementation.

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

18-29: LGTM: Excellent lifecycle management and reactive settings handling.

The refactored provider properly implements:

  • Explicit service initialization
  • Reactive settings updates via listener
  • Proper disposal cleanup

This aligns well with the centralized subscription management architecture and follows Riverpod best practices for provider lifecycle management.

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

23-23: LGTM: Proper initialization of the centralized subscription manager.

The subscription manager initialization ensures that the centralized subscription handling is available during app startup.


29-39: LGTM: Simplified and improved session filtering logic.

The changes represent a significant improvement:

  • Uses configurable Config.sessionExpirationHours instead of hardcoded 24-hour cutoff
  • Simplified logic delegates complex subscription management to the centralized SubscriptionManager
  • Cleaner, more maintainable code structure

This aligns well with the architectural goal of centralizing subscription management.

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

4-36: LGTM: Well-designed subscription encapsulation class.

The Subscription class demonstrates excellent design principles:

  • Clean encapsulation of subscription state and lifecycle
  • Proper resource cleanup via cancel() method
  • Immutability support with copyWith()
  • Clear separation of concerns with onCancel callback

This provides a solid foundation for the centralized subscription management system.

lib/features/order/models/order_state.dart (3)

89-89: LGTM: Fixed cantDo action handling.

The fix properly updates the cantDo field instead of returning the unchanged state, ensuring the UI can display cantDo information correctly.


108-122: LGTM: Comprehensive peer extraction logic.

The enhanced peer extraction logic properly handles different payload types:

  • Direct Peer payload extraction
  • Extracting peer from Order payload via trade pubkeys
  • Preserving existing peer when no new peer is found

This ensures accurate peer information for order state management.


236-236: LGTM: Added sendDm action support for enhanced messaging.

The addition of Action.sendDm to the action maps for both buyer and seller roles in active states enables direct messaging functionality, enhancing communication capabilities during active trades.

Also applies to: 303-303, 313-313

lib/services/lifecycle_manager.dart (4)

8-8: Good architectural improvement with centralized subscription management.

The refactoring to use a centralized SubscriptionManager is a solid design decision that improves modularity and reduces code duplication across the application.

Also applies to: 13-13


61-62: LGTM! Clean delegation to SubscriptionManager.

The foreground transition correctly delegates subscription reactivation to the centralized manager.


90-115: Well-implemented background transition with proper filter collection.

The method correctly:

  • Iterates through all subscription types
  • Collects active filters
  • Only transitions to background if there are active subscriptions
  • Provides comprehensive logging

123-127: Good deprecation handling with clear migration path.

The deprecated method properly logs a warning directing users to the new SubscriptionManager approach.

lib/features/chat/notifiers/chat_room_notifier.dart (3)

16-19: Proper subscription lifecycle management in reload.

Good use of null-aware cancellation to prevent memory leaks when reloading.


49-82: Well-structured event handling with proper error management.

The _onChatEvent method demonstrates good practices:

  • Comprehensive null safety checks
  • Efficient deduplication using Map
  • Proper error logging with stack traces
  • Chronological message sorting

106-108: Proper resource cleanup in dispose.

Good practice to cancel subscriptions and log disposal for debugging.

lib/features/order/notfiers/abstract_mostro_notifier.dart (2)

97-97: Improved navigation with context-specific routes.

The navigation changes provide better user flow by directing to appropriate trade detail pages based on the order action.

Also applies to: 159-159, 162-162, 174-174, 201-201


142-144: Good null safety improvement for Peer creation.

The conditional check prevents creating invalid Peer objects with null public keys.

lib/shared/notifiers/session_notifier.dart (3)

27-42: Well-implemented session initialization with expiration handling.

The init method properly:

  • Removes expired sessions from storage and memory
  • Uses configurable expiration time
  • Updates state to reflect cleaned sessions
  • Schedules ongoing cleanup

70-72: Clean dependency injection using Ref.

The refactoring to use ref.read(keyManagerProvider) follows Riverpod best practices and improves testability.


157-157: Proper timer cleanup in dispose.

Good practice to cancel the cleanup timer to prevent resource leaks.

test/services/mostro_service_test.dart (2)

125-129: Good test hygiene with tearDown.

Proper resource cleanup prevents test interference and memory leaks.


184-186: Clean mock simplification aligned with architecture.

The reduction to only mocking publishEvent reflects the proper separation of concerns with static methods moved to NostrUtils.

Also applies to: 243-245

lib/features/chat/notifiers/chat_rooms_notifier.dart (1)

119-126: Uncomment the loadChats() call

The loadChats() call is commented out in _refreshAllSubscriptions, which may be needed to refresh the chat list when subscriptions are refreshed.

  void _refreshAllSubscriptions() {
    // No need to manually refresh subscriptions
    // SubscriptionManager now handles this automatically based on SessionNotifier changes
    _logger.i('Subscription management is now handled by SubscriptionManager');
    
    // Just reload the chat rooms from the current sessions
-    //loadChats();
+    loadChats();
  }

Please verify if this call is still needed or if it can be removed entirely along with the method.

lib/services/nostr_service.dart (1)

16-17: Clean refactoring of NostrService.

The removal of cryptographic utility wrapper methods and the simplified constructor align well with the architectural changes to centralize subscription management. The core relay management functionality remains intact.

test/services/mostro_service_test.mocks.dart (1)

1-4: Generated mocks correctly reflect implementation changes.

The Mockito-generated mocks properly align with the refactored NostrService and SessionNotifier implementations, including the removal of cryptographic methods and addition of the ref getter.

test/mocks.mocks.dart (1)

8-8: Comprehensive mock updates support new architecture.

The generated mocks properly include the new SubscriptionManager and updated service interfaces. The consolidated dart_nostr import and new mock classes align perfectly with the centralized subscription management architecture.

Also applies to: 1788-1927

Comment on lines 19 to 22
ChatRoomsNotifier(this.ref) : super(const []) {
loadChats();
//_setupChatSubscription();
}
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: Chat subscription is not initialized

The _setupChatSubscription() method is commented out in the constructor, which means the chat subscription is never set up and chat events won't be received.

  ChatRoomsNotifier(this.ref) : super(const []) {
    loadChats();
-    //_setupChatSubscription();
+    _setupChatSubscription();
  }
📝 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
ChatRoomsNotifier(this.ref) : super(const []) {
loadChats();
//_setupChatSubscription();
}
ChatRoomsNotifier(this.ref) : super(const []) {
loadChats();
_setupChatSubscription();
}
🤖 Prompt for AI Agents
In lib/features/chat/notifiers/chat_rooms_notifier.dart around lines 19 to 22,
the _setupChatSubscription() method call is commented out in the constructor,
preventing chat subscriptions from being initialized and chat events from being
received. Uncomment the _setupChatSubscription() call in the constructor to
ensure the chat subscription is properly set up when the ChatRoomsNotifier is
instantiated.

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 (3)
lib/features/subscriptions/subscription_manager.dart (2)

152-156: Consider adding subscription type validation.

The code cancels existing subscriptions before creating new ones, but there's no validation that the subscription type is being replaced correctly. While this works, adding a debug log could help with troubleshooting.

if (_subscriptions.containsKey(type)) {
+ _logger.d('Replacing existing $type subscription');
  _subscriptions[type]!.cancel();
}

186-188: Remove redundant method delegation.

The unsubscribeSession method is just delegating to unsubscribeByType without adding any value. This creates unnecessary API surface area and potential confusion.

Consider removing this method entirely and using unsubscribeByType directly, or add specific session-based logic if needed:

- void unsubscribeSession(SubscriptionType type) {
-   unsubscribeByType(type);
- }
test/mocks.dart (1)

151-155: Simplify mock dispose method.

The mock calls super.dispose() but this is unnecessary in a mock and could potentially cause issues if the parent dispose method tries to access real dependencies.

@override
void dispose() {
  _ordersController.close();
  _chatController.close();
- super.dispose();
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 83450c4 and 2ddcdfb.

📒 Files selected for processing (7)
  • lib/features/chat/notifiers/chat_room_notifier.dart (3 hunks)
  • lib/features/order/notfiers/add_order_notifier.dart (3 hunks)
  • lib/features/order/notfiers/order_notifier.dart (1 hunks)
  • lib/features/subscriptions/subscription_manager.dart (1 hunks)
  • lib/services/mostro_service.dart (3 hunks)
  • test/mocks.dart (5 hunks)
  • test/services/mostro_service_test.dart (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • lib/features/order/notfiers/order_notifier.dart
  • lib/features/order/notfiers/add_order_notifier.dart
  • lib/features/chat/notifiers/chat_room_notifier.dart
  • lib/services/mostro_service.dart
  • test/services/mostro_service_test.dart
🔇 Additional comments (2)
lib/features/subscriptions/subscription_manager.dart (2)

1-31: Well-structured class initialization with proper dependencies.

The class setup follows good practices with dependency injection via Ref, proper stream controller initialization, and immediate session listener setup.


33-45: Robust session listener implementation.

The session listener setup includes proper error handling and uses fireImmediately: true to ensure subscriptions are initialized on startup. The error logging provides good debugging capability.

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

♻️ Duplicate comments (1)
test/mocks.dart (1)

85-85: Fix undefined MockRef dependency.

The constructor calls super(MockRef()) but MockRef may not be properly imported or defined, which could cause compilation errors.

Ensure that MockRef is properly generated in the @GenerateMocks annotation (line 32) and imported from mocks.mocks.dart. The mock should be generated automatically when you run dart run build_runner build.

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

185-187: Consider removing redundant wrapper method.

The unsubscribeSession method is just a wrapper around unsubscribeByType without adding any value. This could be confusing as the method name suggests session-specific unsubscription but it unsubscribes the entire type.

Consider removing this method or making it more explicit:

-  void unsubscribeSession(SubscriptionType type) {
-    unsubscribeByType(type);
-  }

54-56: Verify performance impact of subscribing to all types.

The current implementation subscribes to all SubscriptionType.values whenever sessions change, even if some subscription types aren't actively being used. This could create unnecessary network traffic.

Consider implementing on-demand subscription or tracking which types are actually needed:

  void _updateAllSubscriptions(List<Session> sessions) {
    if (sessions.isEmpty) {
      _logger.i('No sessions available, clearing all subscriptions');
      _clearAllSubscriptions();
      return;
    }

-   for (final type in SubscriptionType.values) {
+   for (final type in _getActiveSubscriptionTypes()) {
      _updateSubscription(type, sessions);
    }
  }
test/mocks.dart (1)

104-108: Improve mock subscription for better test control.

The current mock creates a subscription with an empty stream that doesn't allow test control over event simulation.

Consider making the stream subscription more controllable for testing:

  final subscription = Subscription(
    request: request,
-   streamSubscription: const Stream<NostrEvent>.empty().listen((_) {}),
+   streamSubscription: (type == SubscriptionType.orders ? _ordersController.stream : _chatController.stream).listen((_) {}),
    onCancel: () {},
  );

This would allow the mock to properly simulate real subscription behavior where events flow through the same streams that are returned by the subscribe method.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2ddcdfb and 8c2d58a.

📒 Files selected for processing (2)
  • lib/features/subscriptions/subscription_manager.dart (1 hunks)
  • test/mocks.dart (5 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: chebizarro
PR: MostroP2P/mobile#127
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:45-54
Timestamp: 2025-06-26T15:03:23.502Z
Learning: In AbstractMostroNotifier, state updates occur for all messages regardless of timestamp to hydrate the OrderNotifier from MostroStorage during sync, while handleEvent is only called for recent messages (within 60 seconds) to prevent re-triggering side effects like notifications and navigation for previously handled messages. This design prevents displaying stale notifications when the app is reopened or brought to the foreground.
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/background/background.dart:74-77
Timestamp: 2025-05-08T16:29:52.154Z
Learning: In the Mostro Mobile background service architecture, events aren't stored by the background process. Instead, the background service only checks if events exist in the eventStore and sends notifications for new ones, while the foreground process is responsible for storing and processing events. This is an intentional design decision to separate concerns.
lib/features/subscriptions/subscription_manager.dart (5)
Learnt from: chebizarro
PR: MostroP2P/mobile#110
File: lib/features/trades/widgets/mostro_message_detail_widget.dart:134-141
Timestamp: 2025-06-08T23:54:09.987Z
Learning: In the OrderState refactor, public keys should be accessed via `tradeState.peer?.publicKey` from the OrderState instance rather than through `session?.peer?.publicKey`, as the OrderState encapsulates peer information directly.
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/notifications/notification_service.dart:54-59
Timestamp: 2025-05-08T16:31:29.582Z
Learning: In the Nostr protocol, event.id will never be null in events returned by relay subscriptions, so null safety checks for this property are unnecessary.
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.
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.
Learnt from: chebizarro
PR: MostroP2P/mobile#127
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:45-54
Timestamp: 2025-06-26T15:03:23.502Z
Learning: In AbstractMostroNotifier, state updates occur for all messages regardless of timestamp to hydrate the OrderNotifier from MostroStorage during sync, while handleEvent is only called for recent messages (within 60 seconds) to prevent re-triggering side effects like notifications and navigation for previously handled messages. This design prevents displaying stale notifications when the app is reopened or brought to the foreground.
test/mocks.dart (4)
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.
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.
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/notifications/notification_service.dart:54-59
Timestamp: 2025-05-08T16:31:29.582Z
Learning: In the Nostr protocol, event.id will never be null in events returned by relay subscriptions, so null safety checks for this property are unnecessary.
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.
🔇 Additional comments (6)
lib/features/subscriptions/subscription_manager.dart (3)

89-106: LGTM: Chat filter logic has been corrected.

The filter creation properly handles the inconsistency mentioned in previous reviews by filtering sessions where sharedKey?.public != null and then safely mapping to sharedKey!.public.


17-31: Solid centralized subscription management design.

The class structure properly encapsulates subscription state with broadcast streams for orders and chat events, and automatically manages subscriptions based on session changes. The initialization with session listener setup is well-designed.


151-155: Proper subscription lifecycle management.

The code correctly cancels existing subscriptions before creating new ones, preventing subscription leaks and ensuring only one active subscription per type.

test/mocks.dart (3)

96-113: Mock subscribe method signature is now correct.

The method signature properly matches the real implementation without the extra id parameter that was flagged in previous reviews.


135-137: Mock hasActiveSubscription method is now correct.

The method signature and implementation properly match the real implementation without the unused id parameter that was flagged in previous reviews.


140-146: Good test helper method for event simulation.

The addEvent helper method provides a clean way to simulate events in tests, properly routing them to the appropriate stream controllers.

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.

Hi @chebizarro I'm getting tons of notifications at once, but I only posted two orders that haven't been taken.
If I clear the notifications and back into the app, when I exit the app, all 25 notifications come back at once.

-2147483648_-217089
image

…ectly (Fixes: Error displaying the date and time of order creation #83)
@Catrya
Copy link
Member

Catrya commented Jul 3, 2025

Hi @chebizarro please rebase and fix the conflicts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
.gitignore (1)

57-57: Consider unifying the pattern for all platforms

You broadened the ignore rule to generated_plugin_registrant.* for Windows (👍).
For consistency, you might want to:

  1. Apply the same wildcard rule for macOS (macos/Flutter/GeneratedPluginRegistrant.*) so every platform follows the same convention.
  2. Remove the now-redundant platform-specific extension comment to keep the file shorter.

Not critical, but it keeps the ignore rules symmetrical and easier to scan.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 71834ff and 9aeb5f7.

📒 Files selected for processing (3)
  • .gitignore (1 hunks)
  • lib/features/chat/screens/chat_rooms_list.dart (2 hunks)
  • lib/features/order/models/order_state.dart (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/features/chat/screens/chat_rooms_list.dart
  • lib/features/order/models/order_state.dart
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: chebizarro
PR: MostroP2P/mobile#127
File: lib/features/order/notfiers/abstract_mostro_notifier.dart:45-54
Timestamp: 2025-06-26T15:03:23.529Z
Learning: In AbstractMostroNotifier, state updates occur for all messages regardless of timestamp to hydrate the OrderNotifier from MostroStorage during sync, while handleEvent is only called for recent messages (within 60 seconds) to prevent re-triggering side effects like notifications and navigation for previously handled messages. This design prevents displaying stale notifications when the app is reopened or brought to the foreground.
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/background/background.dart:74-77
Timestamp: 2025-05-08T16:29:52.154Z
Learning: In the Mostro Mobile background service architecture, events aren't stored by the background process. Instead, the background service only checks if events exist in the eventStore and sends notifications for new ones, while the foreground process is responsible for storing and processing events. This is an intentional design decision to separate concerns.
🔇 Additional comments (1)
.gitignore (1)

63-63: Double-check that no hand-written source files share this prefix

The new glob will ignore any file named generated_plugin_registrant.* under linux/flutter/.
If you ever check in a custom shim (e.g. generated_plugin_registrant_manual.cc) it will also be ignored. Verify this is acceptable for your workflow.

@Catrya
Copy link
Member

Catrya commented Jul 3, 2025

@chebizarro siguen saliendo el montón de notificaciones a la vez, esta vez salieron 50 notificaciones a los pocos segundos de salir de la app despues de crear una orden (solo fui para otra app, pero no cerré la de mostro)
image

@chebizarro
Copy link
Contributor Author

This works for me, any remaining issues with notifications, which are not the focus of this issue or PR should be handled in a separate issue

@chebizarro chebizarro closed this Jul 21, 2025
@github-project-automation github-project-automation bot moved this from Code Review to Done in Mostro tasks Jul 21, 2025
@chebizarro chebizarro reopened this Jul 21, 2025
Copy link
Member

@grunch grunch left a comment

Choose a reason for hiding this comment

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

utACK

@grunch grunch merged commit 49f109b into main Jul 21, 2025
2 checks passed
@grunch grunch deleted the chebizarro/issue57 branch July 21, 2025 17:45
@coderabbitai coderabbitai bot mentioned this pull request Sep 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Reopen the app to update the status of an order and chat messages.

4 participants