Skip to content

feat: implement V1 flow gaps — dispute, invoice, countdown, UI polish - #87

Merged
grunch merged 4 commits into
mainfrom
feat/implement-v1-flow-gaps
Apr 3, 2026
Merged

feat: implement V1 flow gaps — dispute, invoice, countdown, UI polish#87
grunch merged 4 commits into
mainfrom
feat/implement-v1-flow-gaps

Conversation

@grunch

@grunch grunch commented Apr 3, 2026

Copy link
Copy Markdown
Member
  • Wire DISPUTE button to disputes_api.openDispute() in all 3 trade
    status contexts (active buyer, active seller, fiat-sent seller);
    upsert result into DisputeNotifier and navigate to dispute chat
  • Add waitingInvoice/waitingPayment TradeStatus values; map
    waitingBuyerInvoice → ADD INVOICE (buyer) and waitingPayment →
    PAY INVOICE (seller) action buttons in TradeDetailScreen
  • Replace hardcoded hold invoice mock with real TradeInfo.holdInvoice
    and TradeInfo.order.amountSats in PayLightningInvoiceScreen; show
    loading state while invoice is not yet available from the daemon
  • Fetch real expiresAt from getOrder() on screen open instead of
    hardcoded 900s countdown; falls back to default on error
  • Add tradeInfoProvider (FutureProvider.family) to trades_providers
    for looking up TradeInfo by orderId across the app
  • ChatRoomsScreen mobile AppBar: hamburger drawer toggle, Mostro logo,
    notification bell — matching HomeScreen pattern
  • Compact time format in trades list: "4m"/"2h"/"3d" instead of verbose
  • Redirect /relays to /settings (remove _Stub placeholder)

Summary by CodeRabbit

  • New Features
    • Open disputes from trade details; buyers can add invoices and sellers can pay from the trade screen
  • Bug Fixes
    • Trade/payment screen shows proper loading, error and invoice-waiting states; removed dev-only simulate flow
  • UI/UX
    • Mobile chat uses an overlay drawer; chat app bar updated
    • Trade timeline uses compact time format (e.g., "2h", "3d")
  • Navigation
    • Navigating to /relays now redirects to Settings
  • Localization
    • Added new trade/dispute messages and instructions in EN/DE/ES/FR/IT

grunch added 2 commits April 3, 2026 16:08
- Wire DISPUTE button to disputes_api.openDispute() in all 3 trade
  status contexts (active buyer, active seller, fiat-sent seller);
  upsert result into DisputeNotifier and navigate to dispute chat
- Add waitingInvoice/waitingPayment TradeStatus values; map
  waitingBuyerInvoice → ADD INVOICE (buyer) and waitingPayment →
  PAY INVOICE (seller) action buttons in TradeDetailScreen
- Replace hardcoded hold invoice mock with real TradeInfo.holdInvoice
  and TradeInfo.order.amountSats in PayLightningInvoiceScreen; show
  loading state while invoice is not yet available from the daemon
- Fetch real expiresAt from getOrder() on screen open instead of
  hardcoded 900s countdown; falls back to default on error
- Add tradeInfoProvider (FutureProvider.family) to trades_providers
  for looking up TradeInfo by orderId across the app
- ChatRoomsScreen mobile AppBar: hamburger drawer toggle, Mostro logo,
  notification bell — matching HomeScreen pattern
- Compact time format in trades list: "4m"/"2h"/"3d" instead of verbose
- Redirect /relays to /settings (remove _Stub placeholder)
- Hide raw exception details in PayLightningInvoiceScreen error state;
  log via debugPrint and show a generic user-facing message
- Guard NwcPaymentWidget behind amountSats > 0 check so it is only
  instantiated when the hold invoice amount is known
- Localize waiting-invoice/waiting-payment instruction strings in
  TradeDetailScreen (en/es/it/fr/de)
- Localize "Could not open dispute" error snackbar in TradeDetailScreen
- Remove unused flutter/material.dart import from app_routes.dart
@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@grunch has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 23 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 14 minutes and 23 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 495d0257-4043-4f07-9753-4a81a18fda49

📥 Commits

Reviewing files that changed from the base of the PR and between 002f3d7 and 85bcee6.

📒 Files selected for processing (14)
  • lib/features/chat/screens/chat_rooms_screen.dart
  • lib/features/order/screens/pay_lightning_invoice_screen.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart

Walkthrough

Refactors chat drawer behavior and pay-invoice flow to load real trade data via a new tradeInfoProvider; adds trade status variants (waitingInvoice, waitingPayment), dispute-opening flow, expiry countdown logic, compact time formatting, route cleanup, and corresponding i18n additions across multiple locales.

Changes

Cohort / File(s) Summary
Core Routing
lib/core/app_routes.dart
Removed unused Material import and _Stub placeholder; changed AppRoute.relays to redirect to /settings.
Chat UI & Drawer
lib/features/chat/screens/chat_rooms_screen.dart
Converted ChatRoomsScreen from ConsumerWidget to StatefulWidget; added _drawerOpen state, replaced mobile appBar with custom _ChatAppBar, and render drawer as overlay Stack on non-desktop.
Invoice Payment Screen
lib/features/order/screens/pay_lightning_invoice_screen.dart
Removed mock invoice and dev simulation; load trade via tradeInfoProvider(widget.orderId) with when() (loading/error/data), gate UI when no holdInvoice/sats, and wire NwcPaymentWidget(onPaymentSuccess: _onPaymentDetected).
Trades Providers
lib/features/trades/providers/trades_providers.dart
Added tradeInfoProvider as FutureProvider.autoDispose.family<rust_types.TradeInfo?, String> that finds a trade by orderId from rawTradesProvider.
Trade Detail & Dispute
lib/features/trades/screens/trade_detail_screen.dart
Added waitingInvoice/waitingPayment statuses and mapping, new _loadExpiresAt() countdown logic, _openDispute() flow calling disputes_api.openDispute and upserting disputeNotifierProvider, and updated role-specific primary actions (e.g., "ADD INVOICE", "PAY INVOICE").
Trades List Item
lib/features/trades/widgets/trades_list_item.dart
Changed _timeAgo() to compact unit-suffixed strings (now, Xm, Xh, Xd) replacing verbose phrases.
Localization API
lib/l10n/app_localizations.dart, lib/l10n/app_localizations_*.dart
Added new getters: openDisputeFailed, tradeWaitingInvoiceBuyerInstruction, tradeWaitingInvoiceSellerInstruction, tradeWaitingPaymentBuyerInstruction, tradeWaitingPaymentSellerInstruction, tradeLoadError.
Locale Files
lib/l10n/app_*.arb (en, de, es, fr, it)
Added corresponding localized strings for the new keys and adjusted punctuation on aboutLndNodeUriExplanation.

Sequence Diagram

sequenceDiagram
    actor User
    participant TradeDetailScreen
    participant disputes_api
    participant DisputeNotifier
    participant Router

    User->>TradeDetailScreen: Tap "Open Dispute"
    TradeDetailScreen->>disputes_api: openDispute(orderId)
    alt Success
        disputes_api-->>TradeDetailScreen: DisputeItem
        TradeDetailScreen->>DisputeNotifier: upsert(disputeItem)
        TradeDetailScreen->>Router: navigate to dispute details
        Router-->>User: Show dispute screen
    else Failure
        disputes_api-->>TradeDetailScreen: Exception
        TradeDetailScreen->>User: show snackbar (openDisputeFailed)
        TradeDetailScreen->>TradeDetailScreen: debugPrint(error)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I nibble code and hop with glee,

Trades bloom, invoices set free,
Drawers slide in a mobile dance,
Disputes now get their chance,
A tiny rabbit cheers the spree!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: implementing dispute functionality, invoice handling, countdown timer, and UI polish across multiple trade flow screens.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/implement-v1-flow-gaps

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
lib/features/trades/screens/trade_detail_screen.dart (1)

417-430: ⚠️ Potential issue | 🟡 Minor

Progress indicator calculation may be inaccurate after loading real expiresAt.

The progress indicator divides _remaining by the hardcoded _kCountdownSeconds (900s), but _loadExpiresAt() may set _remaining to a different total duration from the server. If the real countdown is longer than 900s, the indicator starts at 100%; if shorter, it starts partially filled.

Consider tracking the initial/total duration alongside the remaining time.

Proposed fix
 class _TradeDetailScreenState extends ConsumerState<TradeDetailScreen> {
   Timer? _countdownTimer;
   Duration _remaining = const Duration(seconds: _kCountdownSeconds);
+  Duration _total = const Duration(seconds: _kCountdownSeconds);

   // In _loadExpiresAt():
   setState(() {
-    _remaining = diff > 0 ? Duration(seconds: diff) : Duration.zero;
+    _remaining = diff > 0 ? Duration(seconds: diff) : Duration.zero;
+    _total = _remaining;
   });

   // In build(), update the indicator:
   CircularProgressIndicator(
-    value: (_remaining.inSeconds / _kCountdownSeconds).clamp(0.0, 1.0),
+    value: _total.inSeconds > 0
+        ? (_remaining.inSeconds / _total.inSeconds).clamp(0.0, 1.0)
+        : 0.0,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/trades/screens/trade_detail_screen.dart` around lines 417 - 430,
The progress indicator uses _remaining / _kCountdownSeconds which breaks when
_loadExpiresAt() sets a different total duration; add a new field (e.g.
_totalCountdownSeconds or _initialDurationSeconds) initialized to
_kCountdownSeconds and set it inside _loadExpiresAt() to the server-provided
total seconds, then change the CircularProgressIndicator value to
(_remaining.inSeconds / _totalCountdownSeconds).clamp(0.0,1.0) and guard against
division by zero (fall back to 1.0 or _kCountdownSeconds). Update references to
_kCountdownSeconds in the widget and expiry-loading logic to use the new field
(_remaining, _loadExpiresAt, and the CircularProgressIndicator) so the indicator
reflects the actual total duration.
🧹 Nitpick comments (2)
lib/features/chat/screens/chat_rooms_screen.dart (1)

17-26: Consider using StatefulWidget instead of ConsumerStatefulWidget.

The _ChatRoomsScreenState.build() method doesn't use ref — the Riverpod provider access happens in the child _MessagesTab widget which is already a ConsumerWidget. Using a plain StatefulWidget would be slightly more accurate and avoids the unnecessary WidgetRef overhead.

That said, this is harmless and keeping ConsumerStatefulWidget provides flexibility if provider access is needed in the parent later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/chat/screens/chat_rooms_screen.dart` around lines 17 - 26, The
parent widget ChatRoomsScreen is declared as a ConsumerStatefulWidget but its
state _ChatRoomsScreenState does not use ref; change ChatRoomsScreen to extend
StatefulWidget and change _ChatRoomsScreenState to extend State<ChatRoomsScreen>
(also update the createState signature accordingly) so the parent no longer
carries the unused WidgetRef overhead — leave provider access in the child
_MessagesTab (which is already a ConsumerWidget).
lib/features/order/screens/pay_lightning_invoice_screen.dart (1)

70-88: Consider localizing the "Waiting for hold invoice..." message.

The waiting state text on line 81 is hardcoded. While the PR adds tradeWaitingPaymentSellerInstruction for the seller's instruction, this specific waiting message could benefit from a dedicated localization key for full i18n coverage.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/order/screens/pay_lightning_invoice_screen.dart` around lines 70
- 88, The hardcoded waiting text in PayLightningInvoiceScreen (inside the build
branch guarded by invoice.isEmpty || amountSats <= 0) should be replaced with a
localized string: add a new i18n key (e.g., tradeWaitingForHoldInvoice or
tradeWaitingPaymentInvoice) to your localization resources and use the app's
localization accessor (e.g., AppLocalizations.of(context) or S.of(context)) to
render that key instead of the literal 'Waiting for hold invoice...'; update any
ARB/JSON and generated localization classes accordingly so the widget displays
the localized message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/order/screens/pay_lightning_invoice_screen.dart`:
- Around line 57-64: Replace the hardcoded English error message in the error
handler of PayLightningInvoiceScreen with the localized string for
tradeLoadError: import AppLocalizations (import
'package:mostro/l10n/app_localizations.dart';) and call
AppLocalizations.of(context)!.tradeLoadError when building the Scaffold body
text; ensure the change is made in the error: (e, st) { ... } block that returns
the Scaffold so the displayed message is localized.

---

Outside diff comments:
In `@lib/features/trades/screens/trade_detail_screen.dart`:
- Around line 417-430: The progress indicator uses _remaining /
_kCountdownSeconds which breaks when _loadExpiresAt() sets a different total
duration; add a new field (e.g. _totalCountdownSeconds or
_initialDurationSeconds) initialized to _kCountdownSeconds and set it inside
_loadExpiresAt() to the server-provided total seconds, then change the
CircularProgressIndicator value to (_remaining.inSeconds /
_totalCountdownSeconds).clamp(0.0,1.0) and guard against division by zero (fall
back to 1.0 or _kCountdownSeconds). Update references to _kCountdownSeconds in
the widget and expiry-loading logic to use the new field (_remaining,
_loadExpiresAt, and the CircularProgressIndicator) so the indicator reflects the
actual total duration.

---

Nitpick comments:
In `@lib/features/chat/screens/chat_rooms_screen.dart`:
- Around line 17-26: The parent widget ChatRoomsScreen is declared as a
ConsumerStatefulWidget but its state _ChatRoomsScreenState does not use ref;
change ChatRoomsScreen to extend StatefulWidget and change _ChatRoomsScreenState
to extend State<ChatRoomsScreen> (also update the createState signature
accordingly) so the parent no longer carries the unused WidgetRef overhead —
leave provider access in the child _MessagesTab (which is already a
ConsumerWidget).

In `@lib/features/order/screens/pay_lightning_invoice_screen.dart`:
- Around line 70-88: The hardcoded waiting text in PayLightningInvoiceScreen
(inside the build branch guarded by invoice.isEmpty || amountSats <= 0) should
be replaced with a localized string: add a new i18n key (e.g.,
tradeWaitingForHoldInvoice or tradeWaitingPaymentInvoice) to your localization
resources and use the app's localization accessor (e.g.,
AppLocalizations.of(context) or S.of(context)) to render that key instead of the
literal 'Waiting for hold invoice...'; update any ARB/JSON and generated
localization classes accordingly so the widget displays the localized message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2539fef7-578d-43df-9b7b-4a38a59d3874

📥 Commits

Reviewing files that changed from the base of the PR and between 3512ec1 and 002f3d7.

📒 Files selected for processing (11)
  • lib/core/app_routes.dart
  • lib/features/chat/screens/chat_rooms_screen.dart
  • lib/features/order/screens/pay_lightning_invoice_screen.dart
  • lib/features/trades/providers/trades_providers.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/features/trades/widgets/trades_list_item.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb

Comment thread lib/features/order/screens/pay_lightning_invoice_screen.dart
grunch added 2 commits April 3, 2026 16:25
- Localize error and waiting-for-invoice strings in PayLightningInvoiceScreen
- Add tradeWaitingForHoldInvoice key to all 5 language files
- Fix countdown CircularProgressIndicator ratio: track actual total
  duration in _totalCountdownSeconds (set from server expiresAt) instead
  of the hardcoded constant so the arc is correct after _loadExpiresAt
- Downgrade ChatRoomsScreen from ConsumerStatefulWidget to StatefulWidget
  since the state class does not consume any Riverpod providers directly
@grunch
grunch merged commit a8bebc7 into main Apr 3, 2026
@grunch
grunch deleted the feat/implement-v1-flow-gaps branch April 3, 2026 19:28
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.

1 participant