Skip to content

Conversation

@Catrya
Copy link
Member

@Catrya Catrya commented Sep 15, 2025

fix #64

  • Add 10s timer cleanup for phantom sessions when Mostro doesn't respond
  • Handle maker vs taker scenarios differently (session preservation vs cleanup)
  • Add localized timeout notifications and automatic navigation
  • Update documentation with this new behavior

Summary by CodeRabbit

  • New Features
    • Dual orphan-session protection: 10‑second auto-cleanup when taking orders plus real-time public-event timeout detection.
  • UX Improvements
    • Distinct maker/taker outcomes: makers keep pending orders; takers’ sessions removed and orders return to the book. Timeouts/cancellations surface localized notifications and navigate to the main order book.
  • Localization
    • Session timeout message added in English, Spanish, Italian.
  • Documentation
    • Expanded session lifecycle, timer management, cancellation rules; cleanup window extended to 72 hours.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds a per-order orphan-session cleanup timer (10s) in the Mostro notifier, cancels timers on any Mostro response, updates navigation to '/', adds localized session-timeout messages and notification wiring, and expands architecture docs with orphan-prevention, session lifecycle (72h expiration) and timer management; tests/mocks guidance updated.

Changes

Cohort / File(s) Summary
Mostro notifier core
lib/features/order/notfiers/abstract_mostro_notifier.dart
Add static _sessionTimeouts map, startSessionTimeoutCleanup(String orderId, Ref), _cancelSessionTimeoutCleanup, _showTimeoutNotificationAndNavigate, cancel timers on any Mostro response in handleEvent, cancel on dispose(), and logging; navigate to '/' on cancellation/timeouts.
Order flow integration
lib/features/order/notfiers/order_notifier.dart
Invoke AbstractMostroNotifier.startSessionTimeoutCleanup(orderId, ref) immediately after creating sessions in takeSellOrder/takeBuyOrder; update post-cancellation navigation target to '/'.
Localization
lib/l10n/intl_en.arb, lib/l10n/intl_es.arb, lib/l10n/intl_it.arb
Add translator comment and sessionTimeoutMessage entries (EN/ES/IT); minor JSON comma fixes.
Notification wiring
lib/shared/widgets/notification_listener_widget.dart
Map customMessage == 'sessionTimeoutMessage' to S.of(context)!.sessionTimeoutMessage for display in the notification listener.
Architecture docs
docs/architecture/..., CLAUDE.md
Add Orphan Session Prevention section, document 10s timer behavior and real-time detection, session lifecycle/cleanup rules (including 72h expiration), timer management/disposal, and update timestamps/section names.
Tests & mocks (documentation references)
test/, integration_test/, test/mocks.dart
Documentation notes unit/integration tests and Mockito-generated mocks; guidance to regenerate mocks and avoid manual edits.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI
  participant OrderNotifier
  participant MostroNotifier as AbstractMostroNotifier
  participant Timer as "10s Timer"
  participant Mostro
  participant Router
  participant Notifications

  User->>UI: Take order
  UI->>OrderNotifier: takeSellOrder / takeBuyOrder
  OrderNotifier->>MostroNotifier: create session
  OrderNotifier->>MostroNotifier: startSessionTimeoutCleanup(orderId, ref)
  MostroNotifier->>Timer: schedule 10s [orderId]

  par Real-time responses
    Mostro-->>MostroNotifier: any response/event
    MostroNotifier->>Timer: _cancelSessionTimeoutCleanup(orderId)
    MostroNotifier->>OrderNotifier: normal event handling
  end

  alt Timer expires before response
    Timer-->>MostroNotifier: timeout(orderId)
    MostroNotifier->>MostroNotifier: delete session / mark timeout
    MostroNotifier->>Notifications: emit customMessage=sessionTimeoutMessage
    MostroNotifier->>Router: navigate to "/"
  else Response arrives in time
    MostroNotifier->>UI: continue normal flow (session preserved or updated)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • grunch

Poem

I set a tiny timer—tick and thump,
If Mostro sleeps, I clear the clump.
I nibble orphans, flash a light,
Send you back to trades at night.
A carrot wink — your sessions right. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning While most code changes are related to session timeout/cleanup, the PR also includes unrelated documentation and policy changes (for example docs/architecture/APP_INITIALIZATION_ANALYSIS.md adds a new initialization rationale and docs/architecture/SESSION_AND_KEY_MANAGEMENT.md changes the session expiration window from 24h to 72h) and a behavioral navigation change (Action.canceled now navigates to '/') that are not required by linked issue #64 and broaden the review surface. These mixed concerns make it unclear which edits are part of the fix versus orthogonal policy or design changes. Split or remove unrelated documentation and policy changes from this branch (or provide explicit justification and test coverage in the PR), and isolate UX/navigation changes or clearly document why they are needed for the timeout fix; once those unrelated changes are separated or justified, re-run review focused on the core session-cleanup changes.
✅ 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 "Fix orphan sessions with timeout detection and cleanup" is a concise, single-sentence summary that directly reflects the primary change set (adding timeout-based detection/cleanup to prevent orphan/phantom sessions) and maps to the notifier, order-taking flow, localization, and docs updates in the diff. It is not vague or misleading and is suitable for a teammate scanning history to understand the primary intent.
Linked Issues Check ✅ Passed The changes implement a per-order timeout started from takeSellOrder/takeBuyOrder, remove taker sessions on timeout via sessionNotifier.deleteSession() and ref.invalidateSelf(), add localized timeout messaging and automatic navigation, and introduce a complementary real-time public-event detection path; these behaviors directly address the core failure mode described in issue #64 (taker-created phantom Pending entries and maker UI). As a result the PR prevents phantom taker sessions and removes the need to restart the app to recover state. There is, however, a mismatch between the PR description (30s) and multiple code/docs references to a 10s timer, and reviewers flagged concerns about hardcoding the timeout and backgrounded-app behavior that should be clarified.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-issue64

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (9)
lib/features/order/notfiers/abstract_mostro_notifier.dart (1)

359-378: Externalize 30s to Config for flexibility.

Hardcoding 30s limits tuning and testing. Move to Config (e.g., Config.orphanSessionCleanupSeconds).

-    _sessionTimeouts[orderId] = Timer(const Duration(seconds: 30), () {
+    _sessionTimeouts[orderId] = Timer(Duration(seconds: Config.orphanSessionCleanupSeconds), () {

And define:

 class Config {
+  static const int orphanSessionCleanupSeconds = 30;
 }
lib/features/order/notfiers/order_notifier.dart (1)

107-109: Starting the 30s cleanup after session creation is correct.

Timers start only for taker flows; aligns with objectives.

Wrap service calls in try/catch to clean up immediately on synchronous errors and avoid waiting 30s:

-    await mostroService.takeSellOrder(
+    try {
+      await mostroService.takeSellOrder(
         orderId,
         amount,
         lnAddress,
-    );
+      );
+    } catch (e, st) {
+      // Cancel timer and delete session on immediate failure
+      // Consider exposing a public cancel method:
+      // AbstractMostroNotifier.cancelSessionTimeoutCleanup(orderId);
+      await ref.read(sessionNotifierProvider.notifier).deleteSession(orderId);
+      logger.e('takeSellOrder failed', error: e, stackTrace: st);
+      rethrow;
+    }

Same pattern for takeBuyOrder(). Optionally expose a public cancel API on AbstractMostroNotifier to stop the timer here.

Also applies to: 124-126

docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md (1)

970-977: Fix snippet: cancel by orderId, not event.id.

Keep docs aligned with the code fix; event.id ≠ orderId.

-// lib/features/order/notfiers/abstract_mostro_notifier.dart:86-90
-void handleEvent(MostroMessage event) {
-  // Cancel timer on ANY response from Mostro for this orderId
-  if (event.id != null) {
-    _cancelSessionTimeoutCleanup(event.id!);
-  }
+// lib/features/order/notfiers/abstract_mostro_notifier.dart:86-90
+void handleEvent(MostroMessage event) {
+  // Cancel timer on ANY response from Mostro for this order
+  _cancelSessionTimeoutCleanup(orderId);
   // ... rest of event handling
 }
CLAUDE.md (3)

138-141: Fix typo: remove “38383” from public event monitoring.

“38383 public events” reads like a copy/paste from the derivation path and is misleading.

Apply this diff:

- - **Monitors**: 38383 public events for status changes
+ - **Monitors**: public order events for status changes

126-127: Avoid file+line references in docs (they rot quickly).

Replace hardcoded line spans like “abstract_mostro_notifier.dart:359-378” and “order_notifier.dart:172-211” with symbol/path references only.

Suggested edits:

- **Implementation**: `AbstractMostroNotifier.startSessionTimeoutCleanup()` in `abstract_mostro_notifier.dart:359-378`
+ **Implementation**: `AbstractMostroNotifier.startSessionTimeoutCleanup()` (see `lib/features/order/notfiers/abstract_mostro_notifier.dart`)
- **Implementation**: `_checkTimeoutAndCleanup()` method in `order_notifier.dart:172-211`
+ **Implementation**: `_checkTimeoutAndCleanup()` (see `lib/features/order/notfiers/order_notifier.dart`)

Also applies to: 163-164


128-134: Add a code-fence language to satisfy markdownlint MD040.

Label this block as text for better linting and readability.

Apply:

-```
+```text
 English: "No response received, check your connection and try again later"
 Spanish: "No hubo respuesta, verifica tu conexión e inténtalo más tarde"  
 Italian: "Nessuna risposta ricevuta, verifica la tua connessione e riprova più tardi"

</blockquote></details>
<details>
<summary>lib/shared/widgets/notification_listener_widget.dart (1)</summary><blockquote>

`57-74`: **Optional: replace the switch with a map to reduce duplication/typos.**

Mirroring CantDoNotificationMapper, a `Map<String, String Function(BuildContext)>` for customMessage would simplify additions and avoid stringly-typed switches.

</blockquote></details>
<details>
<summary>docs/architecture/SESSION_AND_KEY_MANAGEMENT.md (2)</summary><blockquote>

`195-197`: **Add language to code fence (markdownlint MD040).**

Mark this as Dart.


```diff
-```
+```dart
 // Automatically started when taking orders
 AbstractMostroNotifier.startSessionTimeoutCleanup(orderId, ref);

---

`211-218`: **Tighten the example snippet and label as Dart.**

The line `state = sessions;` may confuse readers unless `sessions` is a known getter. Either show an explicit list/emit or mark as pseudo. If it’s real Dart, prefer a concrete state update and add fence language.


```diff
-```
+```dart
 // lib/shared/notifiers/session_notifier.dart:157-161
 Future<void> deleteSession(String sessionId) async {
   _sessions.remove(sessionId);
   await _storage.deleteSession(sessionId);
-  state = sessions; // Update state to trigger UI updates
+  state = List.unmodifiable(_sessions.values); // Trigger UI updates
 }

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 08383f3bb2d86bc8741ae85e9c19edb90f70d208 and ed48f984ee4f21eca491ed337ffee18291fb0be7.

</details>

<details>
<summary>📒 Files selected for processing (9)</summary>

* `CLAUDE.md` (3 hunks)
* `docs/architecture/SESSION_AND_KEY_MANAGEMENT.md` (2 hunks)
* `docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md` (1 hunks)
* `lib/features/order/notfiers/abstract_mostro_notifier.dart` (5 hunks)
* `lib/features/order/notfiers/order_notifier.dart` (3 hunks)
* `lib/l10n/intl_en.arb` (1 hunks)
* `lib/l10n/intl_es.arb` (1 hunks)
* `lib/l10n/intl_it.arb` (1 hunks)
* `lib/shared/widgets/notification_listener_widget.dart` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (4)</summary>

<details>
<summary>{lib/*.dart,lib/!(generated)/**/*.dart}</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

> `{lib/*.dart,lib/!(generated)/**/*.dart}`: Use localized strings; replace hardcoded user-facing text with `S.of(context).keyName`
> Prefer `const` constructors and `const` where possible
> Use latest Flutter/Dart APIs (e.g., `withValues()` instead of deprecated `withOpacity()`)

Files:
- `lib/shared/widgets/notification_listener_widget.dart`
- `lib/features/order/notfiers/order_notifier.dart`
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>{lib/*.dart,lib/!(generated)/**/*.dart,test/**/*.dart}</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

> `{lib/*.dart,lib/!(generated)/**/*.dart,test/**/*.dart}`: Check `mounted` before using `BuildContext` after `await` (async gaps)
> Remove unused imports and dependencies

Files:
- `lib/shared/widgets/notification_listener_widget.dart`
- `lib/features/order/notfiers/order_notifier.dart`
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>**/*.dart</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

> All code comments, variable names, and function names must be in English

Files:
- `lib/shared/widgets/notification_listener_widget.dart`
- `lib/features/order/notfiers/order_notifier.dart`
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>lib/l10n/intl_{en,es,it}.arb</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

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

Files:
- `lib/l10n/intl_en.arb`
- `lib/l10n/intl_es.arb`
- `lib/l10n/intl_it.arb`

</details>

</details><details>
<summary>🧠 Learnings (9)</summary>

<details>
<summary>📚 Learning: 2025-06-26T15:03:23.529Z</summary>

Learnt from: chebizarro
PR: #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.


**Applied to files:**
- `lib/features/order/notfiers/order_notifier.dart`
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`
- `docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md`
- `CLAUDE.md`
- `docs/architecture/SESSION_AND_KEY_MANAGEMENT.md`

</details>
<details>
<summary>📚 Learning: 2025-05-06T15:49:26.443Z</summary>

Learnt from: chebizarro
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/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>📚 Learning: 2025-08-28T20:32:34.625Z</summary>

Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-28T20:32:34.625Z
Learning: Use Riverpod for state management with Notifiers encapsulating business logic; access data only through repositories


**Applied to files:**
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>📚 Learning: 2025-08-15T01:37:12.243Z</summary>

Learnt from: Catrya
PR: #270
File: lib/shared/widgets/order_filter.dart:133-135
Timestamp: 2025-08-15T01:37:12.243Z
Learning: The MostroP2P/mobile project requires Flutter >=3.27.0 as specified in pubspec.yaml, which supports the Color.withValues() method, so usage of withValues() throughout the codebase is valid and should not be flagged as a compatibility issue.


**Applied to files:**
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>📚 Learning: 2025-07-20T23:33:17.689Z</summary>

Learnt from: Catrya
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. Do not suggest adding await to super.handleEvent() calls as this would cause compilation errors.


**Applied to files:**
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>📚 Learning: 2025-06-04T19:35:20.209Z</summary>

Learnt from: chebizarro
PR: #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 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:**
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>📚 Learning: 2025-08-28T20:32:34.625Z</summary>

Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-28T20:32:34.625Z
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`

</details>
<details>
<summary>📚 Learning: 2025-08-28T20:32:34.625Z</summary>

Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-28T20:32:34.625Z
Learning: Applies to {lib/.dart,lib/!(generated)/**/.dart} : Use latest Flutter/Dart APIs (e.g., withValues() instead of deprecated withOpacity())


**Applied to files:**
- `CLAUDE.md`

</details>
<details>
<summary>📚 Learning: 2025-08-28T20:32:34.625Z</summary>

Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-28T20:32:34.625Z
Learning: Applies to lib/main.dart : Configure timeago locales in main startup (use timeago.setLocaleMessages(...))


**Applied to files:**
- `CLAUDE.md`

</details>

</details><details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>CLAUDE.md</summary>

129-129: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>
<details>
<summary>docs/architecture/SESSION_AND_KEY_MANAGEMENT.md</summary>

189-189: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

199-199: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ 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)</summary>

* GitHub Check: build

</details>

<details>
<summary>🔇 Additional comments (17)</summary><blockquote>

<details>
<summary>lib/l10n/intl_es.arb (2)</summary><blockquote>

`1083-1084`: **Comma fix LGTM.**

Valid JSON separation before the new keys.

---

`1085-1086`: **Resolved — no change required**  
sessionTimeoutMessage is already present in lib/l10n/intl_en.arb, lib/l10n/intl_es.arb and lib/l10n/intl_it.arb.

</blockquote></details>
<details>
<summary>lib/l10n/intl_en.arb (2)</summary><blockquote>

`1105-1106`: **Comma fix LGTM.**

Keeps JSON valid while appending new entries.

---

`1107-1109`: **New timeout message LGTM — ensure Italian added too.**

English key/value look good; mirror in intl_it.arb.


See the shell check in my other comment to confirm presence in all locales.

</blockquote></details>
<details>
<summary>lib/features/order/notfiers/abstract_mostro_notifier.dart (6)</summary><blockquote>

`1-1`: **Import dart:async LGTM.**

Needed for Timer usage.

---

`23-24`: **Static timer map introduced — OK.**

Centralized per-order timers are fine; you already clean up on dispose.

---

`271-273`: **Route change to ‘/’ on cancellation LGTM.**

Matches the documented reset behavior.

---

`395-404`: **Cancel helper LGTM.**

Clear, idempotent cancellation with logging.

---

`408-411`: **Dispose cleanup LGTM.**

Prevents timer leaks when notifier is disposed.

---

`381-393`: **Verify NotificationListenerWidget maps 'sessionTimeoutMessage' to S.of(context).sessionTimeoutMessage**

Confirm NotificationListenerWidget returns the localized string for 'sessionTimeoutMessage' used in lib/features/order/notfiers/abstract_mostro_notifier.dart:381–393; if absent, wire the mapping or switch to an Action-based temporary notification for consistency.

</blockquote></details>
<details>
<summary>docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md (1)</summary><blockquote>

`943-963`: **Doc snippet matches implementation.**

Good example showing timer creation and navigation on expiry.

</blockquote></details>
<details>
<summary>CLAUDE.md (2)</summary><blockquote>

`165-175`: **Verified — referenced flags/utilities exist and are used as described.**

_isProcessingTimeout and _isCreatedByUser(Session, NostrEvent) are in lib/features/order/notfiers/order_notifier.dart; static Map<String, Timer> _sessionTimeouts, startSessionTimeoutCleanup(...), and _cancelSessionTimeoutCleanup(...) are in lib/features/order/notfiers/abstract_mostro_notifier.dart.

---

`122-127`: **Update CLAUDE.md: navigation on session timeout is '/' (main screen)**

abstract_mostro_notifier.startSessionTimeoutCleanup calls navProvider.go('/'), so update the CLAUDE.md bullet (lines 122–127) from "navigates to order book" to "navigates to the main screen ('/')."  
Evidence: lib/features/order/notfiers/abstract_mostro_notifier.dart (startSessionTimeoutCleanup → navProvider.go('/')).

</blockquote></details>
<details>
<summary>docs/architecture/SESSION_AND_KEY_MANAGEMENT.md (2)</summary><blockquote>

`187-188`: **Confirm 72h session retention is implemented**

Found session-cleanup scaffolding in lib/features/order/notfiers/abstract_mostro_notifier.dart (static Map _sessionTimeouts; call to cleanupRequestSession), but I did not find any constant or scheduler set to 72 hours (no "72h", "72 hour", or Duration(hours: 72) matched). Update the enforcement constant/scheduler to use 72 hours (and reference it from the cleanup logic) or point to the exact file/constant that enforces 72h.

---

`220-227`: **Confirmed: static timer storage present and cleaned up on dispose.**

lib/features/order/notfiers/abstract_mostro_notifier.dart declares `static final Map<String, Timer> _sessionTimeouts` and correctly: cancels/replaces existing timers in `startSessionTimeoutCleanup`, removes the entry on timeout, cancels+removes on Mostro response via `_cancelSessionTimeoutCleanup`, and cancels+removes the order's timer in `dispose` (see ~lines 24, 360–375, 396–403, 406–411).

</blockquote></details>
<details>
<summary>lib/l10n/intl_it.arb (1)</summary><blockquote>

`1137-1141`: **LGTM — sessionTimeoutMessage present in intl_en.arb, intl_es.arb, intl_it.arb; regenerate l10n**

Confirmed via ripgrep: lib/l10n/intl_en.arb:1108, lib/l10n/intl_es.arb:1086, lib/l10n/intl_it.arb:1140.

</blockquote></details>
<details>
<summary>lib/shared/widgets/notification_listener_widget.dart (1)</summary><blockquote>

`69-71`: **LGTM — sessionTimeoutMessage key present in ARB files; generated l10n not found.**
- Confirmed in lib/l10n/intl_en.arb:1108, lib/l10n/intl_es.arb:1086, lib/l10n/intl_it.arb:1140.
- No generated localization Dart (no class S or getter found in repo); regenerate l10n (e.g., flutter gen-l10n) and ensure generated files are available/committed.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@AndreaDiazCorreia
Copy link
Member

It looks pretty good. I’m not sure if we should have the timer hardcoded, but other than that it seems fine. One question though: how does the timer work if the user leaves the app in the background?

@Catrya
Copy link
Member Author

Catrya commented Sep 16, 2025

It looks pretty good. I’m not sure if we should have the timer hardcoded, but other than that it seems fine. One question though: how does the timer work if the user leaves the app in the background?

Have you tested it? If the app stays in the background, the counter keeps running, and when the user returns to the app, they see the message that there was no response.

@AndreaDiazCorreia
Copy link
Member

It looks pretty good. I’m not sure if we should have the timer hardcoded, but other than that it seems fine. One question though: how does the timer work if the user leaves the app in the background?

Have you tested it? If the app stays in the background, the counter keeps running, and when the user returns to the app, they see the message that there was no response.

Claro pero no se si deberia ser asi porque una persona puede tomar una orden y recibir una llamada por ejemplo, la app queda en background y le sacas la orden por eso, no se si sea el mejor approach en esos casos

@Catrya
Copy link
Member Author

Catrya commented Sep 16, 2025

It looks pretty good. I’m not sure if we should have the timer hardcoded, but other than that it seems fine. One question though: how does the timer work if the user leaves the app in the background?

Have you tested it? If the app stays in the background, the counter keeps running, and when the user returns to the app, they see the message that there was no response.

Claro pero no se si deberia ser asi porque una persona puede tomar una orden y recibir una llamada por ejemplo, la app queda en background y le sacas la orden por eso, no se si sea el mejor approach en esos casos

El timer corre y la sesión se elimina si no hay respuesta de mostro (si el usiario estaba offline y quizo tomar una orden, o si está online y hay error como unwraping gift u otro que hace que no sea exitoso el take order). Pero si llega cualquier mensaje de mostro, como cant-do o addinvoice o cualquier otro, el timer se cancela y continúa con el mensaje que le llegó

  - Add 30s timer cleanup for phantom sessions when Mostro doesn't respond
  - Handle maker vs taker scenarios differently (session preservation vs cleanup)
  - Add localized timeout notifications and automatic navigation
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/features/order/notfiers/abstract_mostro_notifier.dart (1)

95-97: Fix: cancel timer by orderId (not event.id).

This addresses the earlier bug—now the timer will properly cancel on any response for the order.

🧹 Nitpick comments (4)
lib/features/order/notfiers/abstract_mostro_notifier.dart (2)

309-320: Avoid navigating while app is backgrounded.

Consider gating navigation behind an app‑lifecycle check and show only a notification if not foreground.


286-305: Make timeout configurable (avoid hardcoded 30s).

Add an optional Duration parameter with a default of 30s; change is backward-compatible — callers currently invoke without a custom timeout (lib/features/order/notfiers/order_notifier.dart:108, 125).

- static void startSessionTimeoutCleanup(String orderId, Ref ref) {
+ static void startSessionTimeoutCleanup(String orderId, Ref ref, {Duration? timeout}) {
   // Cancel existing timer if any
   _sessionTimeouts[orderId]?.cancel();
-  _sessionTimeouts[orderId] = Timer(const Duration(seconds: 30), () {
+  _sessionTimeouts[orderId] = Timer(timeout ?? const Duration(seconds: 30), () {
docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md (1)

938-964: Doc consistency: session expiration hours conflict (36h vs 72h).

Unify mentions to reflect the actual Config.sessionExpirationHours (72h per snippet later in doc).

CLAUDE.md (1)

129-135: Add language to fenced code block (markdownlint MD040).

Mark the localized examples as json to satisfy linting.

-```
+```json
 {
   "en": "No response received, check your connection and try again later",
   "es": "No hubo respuesta, verifica tu conexión e inténtalo más tarde",
   "it": "Nessuna risposta ricevuta, verifica la tua connessione e riprova più tardi"
 }

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 68653670856444bf2690de0825bfd06428d11528 and 23533565858aee0a3696b947e3cedff3a46c1c7c.

</details>

<details>
<summary>📒 Files selected for processing (9)</summary>

* `CLAUDE.md` (3 hunks)
* `docs/architecture/SESSION_AND_KEY_MANAGEMENT.md` (2 hunks)
* `docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md` (1 hunks)
* `lib/features/order/notfiers/abstract_mostro_notifier.dart` (4 hunks)
* `lib/features/order/notfiers/order_notifier.dart` (3 hunks)
* `lib/l10n/intl_en.arb` (1 hunks)
* `lib/l10n/intl_es.arb` (1 hunks)
* `lib/l10n/intl_it.arb` (1 hunks)
* `lib/shared/widgets/notification_listener_widget.dart` (1 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (1)</summary>

* docs/architecture/SESSION_AND_KEY_MANAGEMENT.md

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (3)</summary>

* lib/features/order/notfiers/order_notifier.dart
* lib/shared/widgets/notification_listener_widget.dart
* lib/l10n/intl_it.arb

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (3)</summary>

<details>
<summary>lib/l10n/intl_{en,es,it}.arb</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

> When adding a localization key, add it to all three ARB files: intl_en.arb, intl_es.arb, intl_it.arb

Files:
- `lib/l10n/intl_es.arb`
- `lib/l10n/intl_en.arb`

</details>
<details>
<summary>lib/l10n/*.arb</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

> After modifying ARB files, regenerate localization (e.g., `dart run build_runner build -d` or `flutter gen-l10n`)

Files:
- `lib/l10n/intl_es.arb`
- `lib/l10n/intl_en.arb`

</details>
<details>
<summary>**/*.dart</summary>


**📄 CodeRabbit inference engine (CLAUDE.md)**

> Run flutter format and keep code formatted

Files:
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>

</details><details>
<summary>🧠 Learnings (5)</summary>

<details>
<summary>📓 Common learnings</summary>

Learnt from: chebizarro
PR: #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.


</details>
<details>
<summary>📚 Learning: 2025-06-26T15:03:23.529Z</summary>

Learnt from: chebizarro
PR: #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.


**Applied to files:**
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`
- `docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md`
- `CLAUDE.md`

</details>
<details>
<summary>📚 Learning: 2025-05-06T15:49:55.079Z</summary>

Learnt from: chebizarro
PR: #74
File: lib/services/mostro_service.dart:97-103
Timestamp: 2025-05-06T15:49:55.079Z
Learning: In the Mostro protocol, an order cannot be canceled unless it has a valid orderId, so null checks on orderId during cancel operations are unnecessary.


**Applied to files:**
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>📚 Learning: 2025-06-04T19:35:20.209Z</summary>

Learnt from: chebizarro
PR: #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 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:**
- `lib/features/order/notfiers/abstract_mostro_notifier.dart`

</details>
<details>
<summary>📚 Learning: 2025-09-18T17:17:38.944Z</summary>

Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-18T17:17:38.944Z
Learning: Applies to lib/main.dart : Configure timeago locale messages initialization in main.dart


**Applied to files:**
- `CLAUDE.md`

</details>

</details><details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>CLAUDE.md</summary>

130-130: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ 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)</summary>

* GitHub Check: build

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>lib/features/order/notfiers/abstract_mostro_notifier.dart (2)</summary><blockquote>

`23-25`: **Static timer map is fine here.**

Per‑order timers stored statically are acceptable; cleanup is handled elsewhere.

---

`322-330`: **Timer cleanup on cancel/dispose — LGTM.**
_cancelSessionTimeoutCleanup cancels the Timer and removes the map entry; startSessionTimeoutCleanup is invoked from order_notifier — no leak or follow-up required.

</blockquote></details>
<details>
<summary>lib/l10n/intl_en.arb (1)</summary><blockquote>

`1108-1112`: **English locale entry added — intl_it.arb verified.** Confirmed sessionTimeoutMessage exists in lib/l10n/intl_en.arb, lib/l10n/intl_es.arb, and lib/l10n/intl_it.arb; no changes required.

</blockquote></details>
<details>
<summary>lib/l10n/intl_es.arb (1)</summary><blockquote>

`1086-1090`: **Keys present in all locales — regenerate l10n.**
sessionTimeoutMessage exists in lib/l10n/intl_en.arb, lib/l10n/intl_es.arb, and lib/l10n/intl_it.arb; run `flutter gen-l10n` (or `dart run build_runner build -d`) to update generated localization files and confirm the metadata key "@_comment_session_timeout" is present across locales.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

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)
docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md (1)

970-976: LGTM! Documentation now correctly shows timer cancellation.

The code example properly demonstrates cancelling the timer using orderId, which aligns with the implementation.

🧹 Nitpick comments (4)
docs/architecture/APP_INITIALIZATION_ANALYSIS.md (1)

355-366: Consider clarifying the dual-path architecture explanation.

The documentation would benefit from explaining why both paths are necessary rather than using fireImmediately: true with proper state checks. This would help future maintainers understand the architectural decision.

Consider adding a brief explanation like:

**Dual-Path Architecture**:
- **Listener Path**: Handles future session changes (new trades, session deletions)  
- **Manual Path**: Handles existing sessions during app initialization
+- **Why Not fireImmediately: true?**: Using fireImmediately would execute the listener before SessionNotifier completes init, potentially processing empty/incomplete session data
- **Result**: Complete coverage of all session lifecycle scenarios
lib/features/order/notfiers/abstract_mostro_notifier.dart (1)

308-320: Consider consolidating error handling.

Both the timer callback and this method have separate try-catch blocks. The inner catches may prevent proper logging of failures.

Consider simplifying error handling:

   static void _showTimeoutNotificationAndNavigate(Ref ref) {
-    try {
-      // Show snackbar with localized timeout message
-      final notificationNotifier = ref.read(notificationActionsProvider.notifier);
-      notificationNotifier.showCustomMessage('sessionTimeoutMessage');
-      
-      // Navigate to main order book screen (home)
-      final navProvider = ref.read(navigationProvider.notifier);
-      navProvider.go('/');
-    } catch (e) {
-      Logger().e('Failed to show timeout notification or navigate', error: e);
-    }
+    // Show snackbar with localized timeout message
+    final notificationNotifier = ref.read(notificationActionsProvider.notifier);
+    notificationNotifier.showCustomMessage('sessionTimeoutMessage');
+    
+    // Navigate to main order book screen (home)  
+    final navProvider = ref.read(navigationProvider.notifier);
+    navProvider.go('/');
   }

Then let the outer try-catch in the timer callback handle any errors.

docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md (1)

930-963: Documentation inconsistency: 10s vs 30s timer duration.

The documentation correctly shows 10-second timer implementation in the code examples, but line 1038 mentions "30-second cleanup" which contradicts both the code and earlier documentation.

Apply this fix to maintain consistency:

-3. **Dual protection**: Ensures sessions are cleaned up either through real-time detection or automatic timeout
+3. **Dual protection**: Ensures sessions are cleaned up either through real-time detection or automatic 10-second timeout

And update line 1038:

-2. **30-second cleanup**: Acts as a fallback to prevent orphan sessions when Mostro is completely unresponsive
+2. **10-second cleanup**: Acts as a fallback to prevent orphan sessions when Mostro is completely unresponsive
CLAUDE.md (1)

112-135: Documentation inconsistency in timer duration.

Line 142 mentions "30-second cleanup" but the implementation uses 10 seconds. Please update to maintain consistency.

Apply this fix:

-- **Integration**: Works alongside 30-second cleanup for comprehensive protection
+- **Integration**: Works alongside 10-second cleanup for comprehensive protection
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2353356 and 5b630f1.

📒 Files selected for processing (5)
  • CLAUDE.md (3 hunks)
  • docs/architecture/APP_INITIALIZATION_ANALYSIS.md (1 hunks)
  • docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md (1 hunks)
  • lib/features/order/notfiers/abstract_mostro_notifier.dart (4 hunks)
  • lib/features/order/notfiers/order_notifier.dart (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/features/order/notfiers/order_notifier.dart
🧰 Additional context used
📓 Path-based instructions (1)
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Run flutter format and keep code formatted

Files:

  • lib/features/order/notfiers/abstract_mostro_notifier.dart
🧠 Learnings (6)
📓 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.
📚 Learning: 2025-06-26T15:03:23.529Z
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.

Applied to files:

  • lib/features/order/notfiers/abstract_mostro_notifier.dart
  • docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md
📚 Learning: 2025-05-06T15:49:55.079Z
Learnt from: chebizarro
PR: MostroP2P/mobile#74
File: lib/services/mostro_service.dart:97-103
Timestamp: 2025-05-06T15:49:55.079Z
Learning: In the Mostro protocol, an order cannot be canceled unless it has a valid orderId, so null checks on orderId during cancel operations are unnecessary.

Applied to files:

  • lib/features/order/notfiers/abstract_mostro_notifier.dart
  • docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md
📚 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:

  • lib/features/order/notfiers/abstract_mostro_notifier.dart
📚 Learning: 2025-07-20T23:33:17.689Z
Learnt from: Catrya
PR: MostroP2P/mobile#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:

  • docs/architecture/TIMEOUT_DETECTION_AND_SESSION_CLEANUP.md
📚 Learning: 2025-09-18T17:17:38.944Z
Learnt from: CR
PR: MostroP2P/mobile#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-18T17:17:38.944Z
Learning: Applies to lib/main.dart : Configure timeago locale messages initialization in main.dart

Applied to files:

  • CLAUDE.md
⏰ 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 (5)
docs/architecture/APP_INITIALIZATION_ANALYSIS.md (1)

344-349: LGTM!

The documentation clearly explains why fireImmediately: false is used in the SubscriptionManager initialization, aligning well with the PR's goal of preventing orphan sessions.

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

23-24: Good design: Static timer storage prevents memory leaks.

Using a static map for timer storage ensures timers persist across notifier instances and can be properly cleaned up.


95-96: Timer cancellation logic looks correct.

The timer is now properly cancelled using the orderId directly, which matches the key used in the timer map. This fixes the critical issue where timers were never cancelled.


286-305: Verify the timer duration aligns with PR objectives.

The code uses a 10-second timer, but the PR description mentions a "30s timer cleanup". Please confirm which duration is correct and update either the code or documentation accordingly.

Additionally, consider extracting the timer duration as a named constant for better maintainability:

+  static const Duration _orphanSessionTimeout = Duration(seconds: 10);
+  
   static void startSessionTimeoutCleanup(String orderId, Ref ref) {
     // Cancel existing timer if any
     _sessionTimeouts[orderId]?.cancel();
     
-    _sessionTimeouts[orderId] = Timer(const Duration(seconds: 10), () {
+    _sessionTimeouts[orderId] = Timer(_orphanSessionTimeout, () {

335-337: Good cleanup implementation in dispose.

The disposal logic properly cancels and removes the timer for the specific orderId, preventing memory leaks.

@Catrya Catrya requested a review from grunch September 22, 2025 18:24
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.

LGTM

@grunch grunch merged commit 62342ea into main Sep 22, 2025
2 checks passed
@grunch grunch deleted the fix-issue64 branch September 22, 2025 18:47
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.

Bug taking an order: the order is listed as Pending in My Trades for the taker, as if they had created it.

4 participants