From a51b844d47cff33701542162743ae7d53b49f4cc Mon Sep 17 00:00:00 2001 From: Catrya <140891948+Catrya@users.noreply.github.com> Date: Mon, 11 May 2026 14:12:26 -0600 Subject: [PATCH 1/7] feat(bond): support Phase 1.5 anti-abuse bond invoice flow - New PayBondInvoiceScreen at /pay_bond/:orderId with explanatory text, invoice QR, copy/share buttons and cancel confirmation dialog - Route handling in AbstractMostroNotifier for the new action - Status label Awaiting deposit payment in trades list - New l10n keys in en/es/it (de/fr use English placeholders) - Notification mapper and restore manager updated to satisfy exhaustive-switch requirements --- lib/core/app_routes.dart | 12 ++ lib/data/models/enums/action.dart | 1 + lib/data/models/enums/status.dart | 1 + .../utils/notification_message_mapper.dart | 2 + .../widgets/notification_item.dart | 3 + lib/features/order/models/order_state.dart | 4 + .../notifiers/abstract_mostro_notifier.dart | 7 + .../screens/pay_bond_invoice_screen.dart | 190 ++++++++++++++++++ lib/features/restore/restore_manager.dart | 3 + .../trades/widgets/trades_list_item.dart | 6 + lib/l10n/intl_de.arb | 7 + lib/l10n/intl_en.arb | 7 + lib/l10n/intl_es.arb | 7 + lib/l10n/intl_fr.arb | 7 + lib/l10n/intl_it.arb | 7 + 15 files changed, 264 insertions(+) create mode 100644 lib/features/order/screens/pay_bond_invoice_screen.dart diff --git a/lib/core/app_routes.dart b/lib/core/app_routes.dart index 3bb156678..bcd0647e6 100644 --- a/lib/core/app_routes.dart +++ b/lib/core/app_routes.dart @@ -16,6 +16,7 @@ import 'package:mostro_mobile/features/trades/screens/trade_detail_screen.dart'; import 'package:mostro_mobile/features/trades/screens/trades_screen.dart'; import 'package:mostro_mobile/features/relays/relays_screen.dart'; import 'package:mostro_mobile/features/order/screens/add_lightning_invoice_screen.dart'; +import 'package:mostro_mobile/features/order/screens/pay_bond_invoice_screen.dart'; import 'package:mostro_mobile/features/order/screens/pay_lightning_invoice_screen.dart'; import 'package:mostro_mobile/features/order/screens/take_order_screen.dart'; import 'package:mostro_mobile/features/walkthrough/screens/walkthrough_screen.dart'; @@ -285,6 +286,17 @@ GoRouter createRouter(WidgetRef ref) { ), ), ), + GoRoute( + path: '/pay_bond/:orderId', + pageBuilder: (context, state) => + buildPageWithDefaultTransition( + context: context, + state: state, + child: PayBondInvoiceScreen( + orderId: state.pathParameters['orderId']!, + ), + ), + ), GoRoute( path: '/add_invoice/:orderId', pageBuilder: (context, state) => diff --git a/lib/data/models/enums/action.dart b/lib/data/models/enums/action.dart index e83df4438..3fda3e1fe 100644 --- a/lib/data/models/enums/action.dart +++ b/lib/data/models/enums/action.dart @@ -3,6 +3,7 @@ enum Action { takeSell('take-sell'), takeBuy('take-buy'), payInvoice('pay-invoice'), + payBondInvoice('pay-bond-invoice'), fiatSent('fiat-sent'), fiatSentOk('fiat-sent-ok'), release('release'), diff --git a/lib/data/models/enums/status.dart b/lib/data/models/enums/status.dart index c87f8ff01..db657a235 100644 --- a/lib/data/models/enums/status.dart +++ b/lib/data/models/enums/status.dart @@ -12,6 +12,7 @@ enum Status { success('success'), waitingBuyerInvoice('waiting-buyer-invoice'), waitingPayment('waiting-payment'), + waitingTakerBond('waiting-taker-bond'), paymentFailed('payment-failed'), cooperativelyCanceled('cooperatively-canceled'), inProgress('in-progress'); diff --git a/lib/features/notifications/utils/notification_message_mapper.dart b/lib/features/notifications/utils/notification_message_mapper.dart index b0878e48a..4e54ae4ef 100644 --- a/lib/features/notifications/utils/notification_message_mapper.dart +++ b/lib/features/notifications/utils/notification_message_mapper.dart @@ -17,6 +17,7 @@ class NotificationMessageMapper { case mostro.Action.takeSell: return 'notification_order_taken_title'; case mostro.Action.payInvoice: + case mostro.Action.payBondInvoice: return 'notification_payment_required_title'; case mostro.Action.fiatSent: return 'notification_fiat_sent_title'; @@ -119,6 +120,7 @@ class NotificationMessageMapper { case mostro.Action.takeSell: return 'notification_buy_order_taken_message'; case mostro.Action.payInvoice: + case mostro.Action.payBondInvoice: return 'notification_payment_required_message'; case mostro.Action.fiatSent: return 'notification_fiat_sent_message'; diff --git a/lib/features/notifications/widgets/notification_item.dart b/lib/features/notifications/widgets/notification_item.dart index c23391847..4707ed891 100644 --- a/lib/features/notifications/widgets/notification_item.dart +++ b/lib/features/notifications/widgets/notification_item.dart @@ -76,6 +76,9 @@ class NotificationItem extends ConsumerWidget { case mostro_action.Action.addInvoice: context.push('/add_invoice/${notification.orderId}'); break; + case mostro_action.Action.payBondInvoice: + context.push('/pay_bond/${notification.orderId}'); + break; case mostro_action.Action.canceled: case mostro_action.Action.adminCanceled: context.push('/order_book'); diff --git a/lib/features/order/models/order_state.dart b/lib/features/order/models/order_state.dart index e06a8e2da..bf90fcc34 100644 --- a/lib/features/order/models/order_state.dart +++ b/lib/features/order/models/order_state.dart @@ -271,6 +271,10 @@ class OrderState { case Action.payInvoice: return Status.waitingPayment; + // Action that should set status to waiting-taker-bond (Phase 1.5 anti-abuse bond) + case Action.payBondInvoice: + return Status.waitingTakerBond; + // Actions that should set status to waiting-buyer-invoice case Action.waitingBuyerInvoice: return Status.waitingBuyerInvoice; diff --git a/lib/features/order/notifiers/abstract_mostro_notifier.dart b/lib/features/order/notifiers/abstract_mostro_notifier.dart index 3ad498b69..d0e80d606 100644 --- a/lib/features/order/notifiers/abstract_mostro_notifier.dart +++ b/lib/features/order/notifiers/abstract_mostro_notifier.dart @@ -252,6 +252,13 @@ class AbstractMostroNotifier extends StateNotifier { ref.read(sessionNotifierProvider.notifier).saveSession(session); break; + case Action.payBondInvoice: + if (event.payload is PaymentRequest) { + navProvider.go('/pay_bond/${event.id!}'); + } + ref.read(sessionNotifierProvider.notifier).saveSession(session); + break; + case Action.addInvoice: final sessionNotifier = ref.read(sessionNotifierProvider.notifier); sessionNotifier.saveSession(session); diff --git a/lib/features/order/screens/pay_bond_invoice_screen.dart b/lib/features/order/screens/pay_bond_invoice_screen.dart new file mode 100644 index 000000000..cfcb2c87d --- /dev/null +++ b/lib/features/order/screens/pay_bond_invoice_screen.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:mostro_mobile/core/app_theme.dart'; +import 'package:mostro_mobile/features/order/providers/order_notifier_provider.dart'; +import 'package:mostro_mobile/features/order/widgets/order_app_bar.dart'; +import 'package:mostro_mobile/generated/l10n.dart'; +import 'package:mostro_mobile/services/logger_service.dart'; +import 'package:mostro_mobile/shared/utils/snack_bar_helper.dart'; +import 'package:mostro_mobile/shared/widgets/custom_card.dart'; + +class PayBondInvoiceScreen extends ConsumerWidget { + final String orderId; + + const PayBondInvoiceScreen({super.key, required this.orderId}); + + Future _confirmAndCancel( + BuildContext context, + WidgetRef ref, + ) async { + final s = S.of(context)!; + final confirmed = await showDialog( + context: context, + builder: (dialogContext) => AlertDialog( + backgroundColor: AppTheme.dark2, + title: Text( + s.bondCancelConfirmTitle, + style: const TextStyle(color: AppTheme.cream1), + ), + content: Text( + s.bondCancelConfirmMessage, + style: const TextStyle(color: AppTheme.cream1), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(dialogContext).pop(false), + child: Text(s.bondCancelConfirmNo), + ), + TextButton( + onPressed: () => Navigator.of(dialogContext).pop(true), + child: Text( + s.bondCancelConfirmYes, + style: const TextStyle(color: Colors.red), + ), + ), + ], + ), + ); + + if (confirmed != true) return; + if (!context.mounted) return; + + final orderNotifier = ref.read(orderNotifierProvider(orderId).notifier); + context.go('/'); + await orderNotifier.cancelOrder(); + } + + Future _shareInvoice(BuildContext context, String lnInvoice) async { + final messenger = ScaffoldMessenger.of(context); + final mediaQuery = MediaQuery.of(context); + final errorMessage = S.of(context)!.failedToShareInvoice; + + try { + final uri = Uri.parse('lightning:$lnInvoice'); + if (await canLaunchUrl(uri)) { + await launchUrl(uri); + logger.i('Launched Lightning wallet with bond invoice'); + } else { + await Share.share(lnInvoice); + logger.i('Shared bond invoice via share sheet'); + } + } catch (e) { + logger.e('Failed to share bond invoice: $e'); + SnackBarHelper.showTopSnackBarAsync( + messenger: messenger, + screenHeight: mediaQuery.size.height, + statusBarHeight: mediaQuery.padding.top, + message: errorMessage, + duration: const Duration(seconds: 3), + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final s = S.of(context)!; + final orderState = ref.watch(orderNotifierProvider(orderId)); + final lnInvoice = orderState.paymentRequest?.lnInvoice ?? ''; + + return Scaffold( + backgroundColor: AppTheme.dark1, + appBar: OrderAppBar(title: s.bondScreenTitle), + body: SingleChildScrollView( + child: CustomCard( + padding: const EdgeInsets.all(16), + child: Material( + color: AppTheme.dark2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + s.bondExplanation, + style: const TextStyle( + color: AppTheme.cream1, + fontSize: 15, + height: 1.4, + ), + ), + const SizedBox(height: 20), + Center( + child: Container( + padding: const EdgeInsets.all(8.0), + color: AppTheme.cream1, + child: QrImageView( + data: lnInvoice, + version: QrVersions.auto, + size: 250.0, + backgroundColor: AppTheme.cream1, + errorStateBuilder: (cxt, err) { + return Center( + child: Text( + s.failedToGenerateQR, + textAlign: TextAlign.center, + ), + ); + }, + ), + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton.icon( + onPressed: lnInvoice.isEmpty + ? null + : () { + Clipboard.setData( + ClipboardData(text: lnInvoice)); + logger.i('Copied bond invoice to clipboard'); + SnackBarHelper.showTopSnackBar( + context, + s.invoiceCopiedToClipboard, + duration: const Duration(seconds: 2), + ); + }, + icon: const Icon(Icons.copy), + label: Text(s.copy), + style: ElevatedButton.styleFrom( + backgroundColor: AppTheme.mostroGreen, + ), + ), + ElevatedButton.icon( + onPressed: lnInvoice.isEmpty + ? null + : () => _shareInvoice(context, lnInvoice), + icon: const Icon(Icons.share), + label: Text(s.share), + style: ElevatedButton.styleFrom( + backgroundColor: AppTheme.mostroGreen, + ), + ), + ], + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () => _confirmAndCancel(context, ref), + style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.red, + ), + child: Text(s.cancel), + ), + ], + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/restore/restore_manager.dart b/lib/features/restore/restore_manager.dart index 2c95478fe..5e815fbb6 100644 --- a/lib/features/restore/restore_manager.dart +++ b/lib/features/restore/restore_manager.dart @@ -489,6 +489,9 @@ class RestoreService { return userRole == Role.seller ? Action.payInvoice : Action.waitingSellerToPay; + case Status.waitingTakerBond: + // Order is in the anti-abuse bond payment window; taker pays the bond + return Action.payBondInvoice; case Status.active: // If user is buyer, they need to confirm fiat sent // If user is seller, buyer took the order and seller waits diff --git a/lib/features/trades/widgets/trades_list_item.dart b/lib/features/trades/widgets/trades_list_item.dart index f41946b61..d1f6b9f1d 100644 --- a/lib/features/trades/widgets/trades_list_item.dart +++ b/lib/features/trades/widgets/trades_list_item.dart @@ -206,6 +206,12 @@ class TradesListItem extends ConsumerWidget { textColor = AppTheme.statusWaitingText; label = S.of(context)!.waitingPayment; break; + case Status.waitingTakerBond: + backgroundColor = + AppTheme.statusWaitingBackground.withValues(alpha: 0.3); + textColor = AppTheme.statusWaitingText; + label = S.of(context)!.statusWaitingTakerBond; + break; case Status.waitingBuyerInvoice: backgroundColor = AppTheme.statusWaitingBackground.withValues(alpha: 0.3); diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index bd73d6ad9..d600b5cc4 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -704,6 +704,13 @@ "copy": "Kopieren", "share": "Teilen", "failedToShareInvoice": "Teilen der Rechnung fehlgeschlagen. Bitte versuche stattdessen, sie zu kopieren.", + "bondScreenTitle": "Anti-abuse deposit", + "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", + "bondCancelConfirmTitle": "Cancel?", + "bondCancelConfirmMessage": "If you cancel, you lose your turn and the order becomes available to other users. Cancel anyway?", + "bondCancelConfirmYes": "Yes, cancel", + "bondCancelConfirmNo": "No, go back", + "statusWaitingTakerBond": "Awaiting deposit payment", "failedToCancelOrder": "Fehler beim Abbrechen der Order: {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 220266fc2..c01b2c587 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -704,6 +704,13 @@ "copy": "Copy", "share": "Share", "failedToShareInvoice": "Failed to share invoice. Please try copying instead.", + "bondScreenTitle": "Anti-abuse deposit", + "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", + "bondCancelConfirmTitle": "Cancel?", + "bondCancelConfirmMessage": "If you cancel, you lose your turn and the order becomes available to other users. Cancel anyway?", + "bondCancelConfirmYes": "Yes, cancel", + "bondCancelConfirmNo": "No, go back", + "statusWaitingTakerBond": "Awaiting deposit payment", "failedToCancelOrder": "Failed to cancel order: {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 1491ccb6b..01bff5db1 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -617,6 +617,13 @@ "copy": "Copiar", "share": "Compartir", "failedToShareInvoice": "Error al compartir factura. Por favor intenta copiarla en su lugar.", + "bondScreenTitle": "Depósito anti-abuso", + "bondExplanation": "Para tomar esta orden debes pagar un depósito anti-abuso. Funciona así:\n\n• Tus sats quedan retenidos en tu wallet, no se gastan.\n• Si el intercambio finaliza correctamente, recuperas el depósito automáticamente.\n• Si tienes una disputa en esta orden y la pierdes, perderás el depósito.\n• Este mecanismo protege a todos los usuarios contra estafadores.\n\nPaga la factura para continuar, o cancela si no estás de acuerdo.", + "bondCancelConfirmTitle": "¿Cancelar?", + "bondCancelConfirmMessage": "Si cancelas, pierdes tu turno y la orden queda disponible para otros usuarios. ¿Cancelar de todos modos?", + "bondCancelConfirmYes": "Sí, cancelar", + "bondCancelConfirmNo": "No, volver", + "statusWaitingTakerBond": "Esperando pago del depósito", "failedToCancelOrder": "Error al cancelar orden: {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 8970a47d6..2b3e03111 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -704,6 +704,13 @@ "copy": "Copier", "share": "Partager", "failedToShareInvoice": "Échec du partage de la facture. Veuillez essayer de copier à la place.", + "bondScreenTitle": "Anti-abuse deposit", + "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", + "bondCancelConfirmTitle": "Cancel?", + "bondCancelConfirmMessage": "If you cancel, you lose your turn and the order becomes available to other users. Cancel anyway?", + "bondCancelConfirmYes": "Yes, cancel", + "bondCancelConfirmNo": "No, go back", + "statusWaitingTakerBond": "Awaiting deposit payment", "failedToCancelOrder": "Échec de l'annulation de la commande : {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 9a77b72c5..ad519e559 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -647,6 +647,13 @@ "copy": "Copia", "share": "Condividi", "failedToShareInvoice": "Errore nel condividere la fattura. Per favore prova a copiarla invece.", + "bondScreenTitle": "Deposito anti-abuso", + "bondExplanation": "Per prendere questo ordine devi pagare un deposito anti-abuso. Funziona così:\n\n• I tuoi sats vengono trattenuti nel tuo wallet, non vengono spesi.\n• Se lo scambio si conclude correttamente, recuperi il deposito automaticamente.\n• Se hai una disputa su questo ordine e la perdi, perderai il deposito.\n• Questo meccanismo protegge tutti gli utenti dai truffatori.\n\nPaga la fattura per continuare, oppure annulla se non sei d'accordo.", + "bondCancelConfirmTitle": "Annullare?", + "bondCancelConfirmMessage": "Se annulli, perdi il tuo turno e l'ordine diventa disponibile ad altri utenti. Annullare comunque?", + "bondCancelConfirmYes": "Sì, annulla", + "bondCancelConfirmNo": "No, indietro", + "statusWaitingTakerBond": "In attesa del pagamento del deposito", "failedToCancelOrder": "Impossibile annullare l'ordine: {error}", "@failedToCancelOrder": { "placeholders": { From a28dfe8a46de41b574180c3a2e15eb7d1c3742b0 Mon Sep 17 00:00:00 2001 From: Catrya <140891948+Catrya@users.noreply.github.com> Date: Mon, 11 May 2026 14:45:19 -0600 Subject: [PATCH 2/7] pay deposit entry point from trade detail - actions map: Status.waitingTakerBond exposes payBondInvoice, cancel, for both buyer and seller roles - MostroMessageDetail: localized message and status label for the bond waiting state - Trade detail switch: render Pay deposit button navigating to pay_bond, and use the bond-specific confirmer dialog when cancelling from the bond waiting state --- lib/features/order/models/order_state.dart | 12 ++++++++++++ .../order/screens/pay_bond_invoice_screen.dart | 2 +- lib/features/trades/screens/trade_detail_screen.dart | 12 ++++++++++++ .../trades/widgets/mostro_message_detail_widget.dart | 4 ++++ lib/l10n/intl_de.arb | 3 ++- lib/l10n/intl_en.arb | 3 ++- lib/l10n/intl_es.arb | 3 ++- lib/l10n/intl_fr.arb | 3 ++- lib/l10n/intl_it.arb | 3 ++- 9 files changed, 39 insertions(+), 6 deletions(-) diff --git a/lib/features/order/models/order_state.dart b/lib/features/order/models/order_state.dart index bf90fcc34..d9076b123 100644 --- a/lib/features/order/models/order_state.dart +++ b/lib/features/order/models/order_state.dart @@ -389,6 +389,12 @@ class OrderState { Action.cancel, ], }, + Status.waitingTakerBond: { + Action.payBondInvoice: [ + Action.payBondInvoice, + Action.cancel, + ], + }, Status.waitingPayment: { Action.payInvoice: [ Action.payInvoice, @@ -533,6 +539,12 @@ class OrderState { Action.cancel, ], }, + Status.waitingTakerBond: { + Action.payBondInvoice: [ + Action.payBondInvoice, + Action.cancel, + ], + }, Status.waitingPayment: { Action.waitingSellerToPay: [ Action.cancel, diff --git a/lib/features/order/screens/pay_bond_invoice_screen.dart b/lib/features/order/screens/pay_bond_invoice_screen.dart index cfcb2c87d..ce3800e57 100644 --- a/lib/features/order/screens/pay_bond_invoice_screen.dart +++ b/lib/features/order/screens/pay_bond_invoice_screen.dart @@ -32,7 +32,7 @@ class PayBondInvoiceScreen extends ConsumerWidget { style: const TextStyle(color: AppTheme.cream1), ), content: Text( - s.bondCancelConfirmMessage, + s.areYouSureCancel, style: const TextStyle(color: AppTheme.cream1), ), actions: [ diff --git a/lib/features/trades/screens/trade_detail_screen.dart b/lib/features/trades/screens/trade_detail_screen.dart index 9a164727c..9d4eb6e8c 100644 --- a/lib/features/trades/screens/trade_detail_screen.dart +++ b/lib/features/trades/screens/trade_detail_screen.dart @@ -332,6 +332,18 @@ class TradeDetailScreen extends ConsumerWidget { } break; + case actions.Action.payBondInvoice: + final hasPaymentRequest = tradeState.paymentRequest != null; + if (hasPaymentRequest) { + widgets.add(_buildNostrButton( + S.of(context)!.payBondButton, + action: actions.Action.payBondInvoice, + backgroundColor: AppTheme.mostroGreen, + onPressed: () => context.push('/pay_bond/$orderId'), + )); + } + break; + case actions.Action.addInvoice: if (userRole == Role.buyer) { widgets.add(_buildNostrButton( diff --git a/lib/features/trades/widgets/mostro_message_detail_widget.dart b/lib/features/trades/widgets/mostro_message_detail_widget.dart index 992ecb320..0f7302a66 100644 --- a/lib/features/trades/widgets/mostro_message_detail_widget.dart +++ b/lib/features/trades/widgets/mostro_message_detail_widget.dart @@ -84,6 +84,8 @@ class MostroMessageDetail extends ConsumerWidget { orderPayload?.fiatAmount.toString() ?? '', orderPayload?.fiatCode ?? '', ); + case actions.Action.payBondInvoice: + return S.of(context)!.payBondMessage; case actions.Action.addInvoice: final expSecs = ref .read(orderRepositoryProvider) @@ -290,6 +292,8 @@ class MostroMessageDetail extends ConsumerWidget { return S.of(context)!.statusDetailPending; case Status.waitingPayment: return S.of(context)!.statusDetailWaitingPayment; + case Status.waitingTakerBond: + return S.of(context)!.statusWaitingTakerBond; case Status.waitingBuyerInvoice: return S.of(context)!.statusDetailWaitingInvoice; case Status.paymentFailed: diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index d600b5cc4..f16be9126 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -707,10 +707,11 @@ "bondScreenTitle": "Anti-abuse deposit", "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", "bondCancelConfirmTitle": "Cancel?", - "bondCancelConfirmMessage": "If you cancel, you lose your turn and the order becomes available to other users. Cancel anyway?", "bondCancelConfirmYes": "Yes, cancel", "bondCancelConfirmNo": "No, go back", "statusWaitingTakerBond": "Awaiting deposit payment", + "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", + "payBondButton": "Pay deposit", "failedToCancelOrder": "Fehler beim Abbrechen der Order: {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index c01b2c587..ccf071cbb 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -707,10 +707,11 @@ "bondScreenTitle": "Anti-abuse deposit", "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", "bondCancelConfirmTitle": "Cancel?", - "bondCancelConfirmMessage": "If you cancel, you lose your turn and the order becomes available to other users. Cancel anyway?", "bondCancelConfirmYes": "Yes, cancel", "bondCancelConfirmNo": "No, go back", "statusWaitingTakerBond": "Awaiting deposit payment", + "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", + "payBondButton": "Pay deposit", "failedToCancelOrder": "Failed to cancel order: {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 01bff5db1..2a204bf39 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -620,10 +620,11 @@ "bondScreenTitle": "Depósito anti-abuso", "bondExplanation": "Para tomar esta orden debes pagar un depósito anti-abuso. Funciona así:\n\n• Tus sats quedan retenidos en tu wallet, no se gastan.\n• Si el intercambio finaliza correctamente, recuperas el depósito automáticamente.\n• Si tienes una disputa en esta orden y la pierdes, perderás el depósito.\n• Este mecanismo protege a todos los usuarios contra estafadores.\n\nPaga la factura para continuar, o cancela si no estás de acuerdo.", "bondCancelConfirmTitle": "¿Cancelar?", - "bondCancelConfirmMessage": "Si cancelas, pierdes tu turno y la orden queda disponible para otros usuarios. ¿Cancelar de todos modos?", "bondCancelConfirmYes": "Sí, cancelar", "bondCancelConfirmNo": "No, volver", "statusWaitingTakerBond": "Esperando pago del depósito", + "payBondMessage": "Por favor paga el depósito anti-abuso para continuar con esta orden.", + "payBondButton": "Pagar depósito", "failedToCancelOrder": "Error al cancelar orden: {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 2b3e03111..13ed753b1 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -707,10 +707,11 @@ "bondScreenTitle": "Anti-abuse deposit", "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", "bondCancelConfirmTitle": "Cancel?", - "bondCancelConfirmMessage": "If you cancel, you lose your turn and the order becomes available to other users. Cancel anyway?", "bondCancelConfirmYes": "Yes, cancel", "bondCancelConfirmNo": "No, go back", "statusWaitingTakerBond": "Awaiting deposit payment", + "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", + "payBondButton": "Pay deposit", "failedToCancelOrder": "Échec de l'annulation de la commande : {error}", "@failedToCancelOrder": { "placeholders": { diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index ad519e559..5bbc5321f 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -650,10 +650,11 @@ "bondScreenTitle": "Deposito anti-abuso", "bondExplanation": "Per prendere questo ordine devi pagare un deposito anti-abuso. Funziona così:\n\n• I tuoi sats vengono trattenuti nel tuo wallet, non vengono spesi.\n• Se lo scambio si conclude correttamente, recuperi il deposito automaticamente.\n• Se hai una disputa su questo ordine e la perdi, perderai il deposito.\n• Questo meccanismo protegge tutti gli utenti dai truffatori.\n\nPaga la fattura per continuare, oppure annulla se non sei d'accordo.", "bondCancelConfirmTitle": "Annullare?", - "bondCancelConfirmMessage": "Se annulli, perdi il tuo turno e l'ordine diventa disponibile ad altri utenti. Annullare comunque?", "bondCancelConfirmYes": "Sì, annulla", "bondCancelConfirmNo": "No, indietro", "statusWaitingTakerBond": "In attesa del pagamento del deposito", + "payBondMessage": "Per favore paga il deposito anti-abuso per continuare questo ordine.", + "payBondButton": "Paga deposito", "failedToCancelOrder": "Impossibile annullare l'ordine: {error}", "@failedToCancelOrder": { "placeholders": { From b969faeb15096127c31023e550601d4878b5a9fd Mon Sep 17 00:00:00 2001 From: Catrya <140891948+Catrya@users.noreply.github.com> Date: Mon, 11 May 2026 15:23:32 -0600 Subject: [PATCH 3/7] fix overlapping --- lib/features/order/screens/pay_bond_invoice_screen.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/features/order/screens/pay_bond_invoice_screen.dart b/lib/features/order/screens/pay_bond_invoice_screen.dart index ce3800e57..7faec961b 100644 --- a/lib/features/order/screens/pay_bond_invoice_screen.dart +++ b/lib/features/order/screens/pay_bond_invoice_screen.dart @@ -95,6 +95,9 @@ class PayBondInvoiceScreen extends ConsumerWidget { backgroundColor: AppTheme.dark1, appBar: OrderAppBar(title: s.bondScreenTitle), body: SingleChildScrollView( + padding: EdgeInsets.only( + bottom: 16 + MediaQuery.of(context).viewPadding.bottom, + ), child: CustomCard( padding: const EdgeInsets.all(16), child: Material( From 0c17e0e1b4e4a57d5ea1cf57266fc4f3d68d4d89 Mon Sep 17 00:00:00 2001 From: Catrya <140891948+Catrya@users.noreply.github.com> Date: Mon, 11 May 2026 15:33:17 -0600 Subject: [PATCH 4/7] flatten pay-bond screen background to match scaffold --- .../screens/pay_bond_invoice_screen.dart | 161 +++++++++--------- 1 file changed, 78 insertions(+), 83 deletions(-) diff --git a/lib/features/order/screens/pay_bond_invoice_screen.dart b/lib/features/order/screens/pay_bond_invoice_screen.dart index 7faec961b..9af1e955b 100644 --- a/lib/features/order/screens/pay_bond_invoice_screen.dart +++ b/lib/features/order/screens/pay_bond_invoice_screen.dart @@ -11,7 +11,6 @@ import 'package:mostro_mobile/features/order/widgets/order_app_bar.dart'; import 'package:mostro_mobile/generated/l10n.dart'; import 'package:mostro_mobile/services/logger_service.dart'; import 'package:mostro_mobile/shared/utils/snack_bar_helper.dart'; -import 'package:mostro_mobile/shared/widgets/custom_card.dart'; class PayBondInvoiceScreen extends ConsumerWidget { final String orderId; @@ -95,97 +94,93 @@ class PayBondInvoiceScreen extends ConsumerWidget { backgroundColor: AppTheme.dark1, appBar: OrderAppBar(title: s.bondScreenTitle), body: SingleChildScrollView( - padding: EdgeInsets.only( - bottom: 16 + MediaQuery.of(context).viewPadding.bottom, + padding: EdgeInsets.fromLTRB( + 16, + 16, + 16, + 16 + MediaQuery.of(context).viewPadding.bottom, ), - child: CustomCard( - padding: const EdgeInsets.all(16), - child: Material( - color: AppTheme.dark2, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + s.bondExplanation, + style: const TextStyle( + color: AppTheme.cream1, + fontSize: 15, + height: 1.4, + ), + ), + const SizedBox(height: 20), + Center( + child: Container( + padding: const EdgeInsets.all(8.0), + color: AppTheme.cream1, + child: QrImageView( + data: lnInvoice, + version: QrVersions.auto, + size: 250.0, + backgroundColor: AppTheme.cream1, + errorStateBuilder: (cxt, err) { + return Center( + child: Text( + s.failedToGenerateQR, + textAlign: TextAlign.center, + ), + ); + }, + ), + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Text( - s.bondExplanation, - style: const TextStyle( - color: AppTheme.cream1, - fontSize: 15, - height: 1.4, + ElevatedButton.icon( + onPressed: lnInvoice.isEmpty + ? null + : () { + Clipboard.setData(ClipboardData(text: lnInvoice)); + logger.i('Copied bond invoice to clipboard'); + SnackBarHelper.showTopSnackBar( + context, + s.invoiceCopiedToClipboard, + duration: const Duration(seconds: 2), + ); + }, + icon: const Icon(Icons.copy), + label: Text(s.copy), + style: ElevatedButton.styleFrom( + backgroundColor: AppTheme.mostroGreen, ), ), - const SizedBox(height: 20), - Center( - child: Container( - padding: const EdgeInsets.all(8.0), - color: AppTheme.cream1, - child: QrImageView( - data: lnInvoice, - version: QrVersions.auto, - size: 250.0, - backgroundColor: AppTheme.cream1, - errorStateBuilder: (cxt, err) { - return Center( - child: Text( - s.failedToGenerateQR, - textAlign: TextAlign.center, - ), - ); - }, - ), + ElevatedButton.icon( + onPressed: lnInvoice.isEmpty + ? null + : () => _shareInvoice(context, lnInvoice), + icon: const Icon(Icons.share), + label: Text(s.share), + style: ElevatedButton.styleFrom( + backgroundColor: AppTheme.mostroGreen, ), ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton.icon( - onPressed: lnInvoice.isEmpty - ? null - : () { - Clipboard.setData( - ClipboardData(text: lnInvoice)); - logger.i('Copied bond invoice to clipboard'); - SnackBarHelper.showTopSnackBar( - context, - s.invoiceCopiedToClipboard, - duration: const Duration(seconds: 2), - ); - }, - icon: const Icon(Icons.copy), - label: Text(s.copy), - style: ElevatedButton.styleFrom( - backgroundColor: AppTheme.mostroGreen, - ), - ), - ElevatedButton.icon( - onPressed: lnInvoice.isEmpty - ? null - : () => _shareInvoice(context, lnInvoice), - icon: const Icon(Icons.share), - label: Text(s.share), - style: ElevatedButton.styleFrom( - backgroundColor: AppTheme.mostroGreen, - ), - ), - ], - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => _confirmAndCancel(context, ref), - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.red, - ), - child: Text(s.cancel), - ), - ], + ], + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () => _confirmAndCancel(context, ref), + style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.red, + ), + child: Text(s.cancel), ), ], ), - ), + ], ), ), ); From 2442580c4726fd55b64464db12b1070500201368 Mon Sep 17 00:00:00 2001 From: Catrya <140891948+Catrya@users.noreply.github.com> Date: Mon, 11 May 2026 15:39:50 -0600 Subject: [PATCH 5/7] align cancel dialog with trade cancel modal UI --- .../screens/pay_bond_invoice_screen.dart | 50 ++++++++++++++++--- lib/l10n/intl_de.arb | 3 -- lib/l10n/intl_en.arb | 3 -- lib/l10n/intl_es.arb | 3 -- lib/l10n/intl_fr.arb | 3 -- lib/l10n/intl_it.arb | 3 -- 6 files changed, 42 insertions(+), 23 deletions(-) diff --git a/lib/features/order/screens/pay_bond_invoice_screen.dart b/lib/features/order/screens/pay_bond_invoice_screen.dart index 9af1e955b..11558bbb7 100644 --- a/lib/features/order/screens/pay_bond_invoice_screen.dart +++ b/lib/features/order/screens/pay_bond_invoice_screen.dart @@ -25,25 +25,59 @@ class PayBondInvoiceScreen extends ConsumerWidget { final confirmed = await showDialog( context: context, builder: (dialogContext) => AlertDialog( - backgroundColor: AppTheme.dark2, + backgroundColor: AppTheme.backgroundCard, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: Colors.white.withValues(alpha: 0.1)), + ), title: Text( - s.bondCancelConfirmTitle, - style: const TextStyle(color: AppTheme.cream1), + s.cancelTradeDialogTitle, + style: const TextStyle( + color: AppTheme.textPrimary, + fontSize: 18, + fontWeight: FontWeight.w600, + ), ), content: Text( s.areYouSureCancel, - style: const TextStyle(color: AppTheme.cream1), + style: const TextStyle( + color: AppTheme.textSecondary, + fontSize: 14, + height: 1.5, + ), ), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(false), - child: Text(s.bondCancelConfirmNo), + child: Text( + s.no, + style: const TextStyle( + color: AppTheme.textSecondary, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ), ), - TextButton( + const SizedBox(width: 12), + ElevatedButton( onPressed: () => Navigator.of(dialogContext).pop(true), + style: ElevatedButton.styleFrom( + backgroundColor: AppTheme.activeColor, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + ), child: Text( - s.bondCancelConfirmYes, - style: const TextStyle(color: Colors.red), + s.yes, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, ), ), ], diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index f16be9126..c41162ca5 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -706,9 +706,6 @@ "failedToShareInvoice": "Teilen der Rechnung fehlgeschlagen. Bitte versuche stattdessen, sie zu kopieren.", "bondScreenTitle": "Anti-abuse deposit", "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", - "bondCancelConfirmTitle": "Cancel?", - "bondCancelConfirmYes": "Yes, cancel", - "bondCancelConfirmNo": "No, go back", "statusWaitingTakerBond": "Awaiting deposit payment", "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", "payBondButton": "Pay deposit", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ccf071cbb..3aa533776 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -706,9 +706,6 @@ "failedToShareInvoice": "Failed to share invoice. Please try copying instead.", "bondScreenTitle": "Anti-abuse deposit", "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", - "bondCancelConfirmTitle": "Cancel?", - "bondCancelConfirmYes": "Yes, cancel", - "bondCancelConfirmNo": "No, go back", "statusWaitingTakerBond": "Awaiting deposit payment", "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", "payBondButton": "Pay deposit", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 2a204bf39..4e6f84224 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -619,9 +619,6 @@ "failedToShareInvoice": "Error al compartir factura. Por favor intenta copiarla en su lugar.", "bondScreenTitle": "Depósito anti-abuso", "bondExplanation": "Para tomar esta orden debes pagar un depósito anti-abuso. Funciona así:\n\n• Tus sats quedan retenidos en tu wallet, no se gastan.\n• Si el intercambio finaliza correctamente, recuperas el depósito automáticamente.\n• Si tienes una disputa en esta orden y la pierdes, perderás el depósito.\n• Este mecanismo protege a todos los usuarios contra estafadores.\n\nPaga la factura para continuar, o cancela si no estás de acuerdo.", - "bondCancelConfirmTitle": "¿Cancelar?", - "bondCancelConfirmYes": "Sí, cancelar", - "bondCancelConfirmNo": "No, volver", "statusWaitingTakerBond": "Esperando pago del depósito", "payBondMessage": "Por favor paga el depósito anti-abuso para continuar con esta orden.", "payBondButton": "Pagar depósito", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 13ed753b1..8d9542ed4 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -706,9 +706,6 @@ "failedToShareInvoice": "Échec du partage de la facture. Veuillez essayer de copier à la place.", "bondScreenTitle": "Anti-abuse deposit", "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", - "bondCancelConfirmTitle": "Cancel?", - "bondCancelConfirmYes": "Yes, cancel", - "bondCancelConfirmNo": "No, go back", "statusWaitingTakerBond": "Awaiting deposit payment", "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", "payBondButton": "Pay deposit", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 5bbc5321f..dd7823997 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -649,9 +649,6 @@ "failedToShareInvoice": "Errore nel condividere la fattura. Per favore prova a copiarla invece.", "bondScreenTitle": "Deposito anti-abuso", "bondExplanation": "Per prendere questo ordine devi pagare un deposito anti-abuso. Funziona così:\n\n• I tuoi sats vengono trattenuti nel tuo wallet, non vengono spesi.\n• Se lo scambio si conclude correttamente, recuperi il deposito automaticamente.\n• Se hai una disputa su questo ordine e la perdi, perderai il deposito.\n• Questo meccanismo protegge tutti gli utenti dai truffatori.\n\nPaga la fattura per continuare, oppure annulla se non sei d'accordo.", - "bondCancelConfirmTitle": "Annullare?", - "bondCancelConfirmYes": "Sì, annulla", - "bondCancelConfirmNo": "No, indietro", "statusWaitingTakerBond": "In attesa del pagamento del deposito", "payBondMessage": "Per favore paga il deposito anti-abuso per continuare questo ordine.", "payBondButton": "Paga deposito", From 7e4b32cbdb476f1b33edf0f7149b5feeb5b27fa1 Mon Sep 17 00:00:00 2001 From: Catrya <140891948+Catrya@users.noreply.github.com> Date: Thu, 14 May 2026 13:18:48 -0600 Subject: [PATCH 6/7] show bond amount on pay-bond screen --- .../order/screens/pay_bond_invoice_screen.dart | 13 +++++++++++++ lib/l10n/intl_de.arb | 3 ++- lib/l10n/intl_en.arb | 3 ++- lib/l10n/intl_es.arb | 3 ++- lib/l10n/intl_fr.arb | 3 ++- lib/l10n/intl_it.arb | 3 ++- 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/features/order/screens/pay_bond_invoice_screen.dart b/lib/features/order/screens/pay_bond_invoice_screen.dart index 11558bbb7..0f594e7bc 100644 --- a/lib/features/order/screens/pay_bond_invoice_screen.dart +++ b/lib/features/order/screens/pay_bond_invoice_screen.dart @@ -123,6 +123,7 @@ class PayBondInvoiceScreen extends ConsumerWidget { final s = S.of(context)!; final orderState = ref.watch(orderNotifierProvider(orderId)); final lnInvoice = orderState.paymentRequest?.lnInvoice ?? ''; + final bondAmount = orderState.paymentRequest?.order?.amount; return Scaffold( backgroundColor: AppTheme.dark1, @@ -145,6 +146,18 @@ class PayBondInvoiceScreen extends ConsumerWidget { height: 1.4, ), ), + if (bondAmount != null) ...[ + const SizedBox(height: 16), + Text( + s.bondPayInvoicePrompt(bondAmount), + style: const TextStyle( + color: AppTheme.cream1, + fontSize: 15, + height: 1.4, + fontWeight: FontWeight.w500, + ), + ), + ], const SizedBox(height: 20), Center( child: Container( diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index c41162ca5..c2cb221c5 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -705,7 +705,8 @@ "share": "Teilen", "failedToShareInvoice": "Teilen der Rechnung fehlgeschlagen. Bitte versuche stattdessen, sie zu kopieren.", "bondScreenTitle": "Anti-abuse deposit", - "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", + "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.", + "bondPayInvoicePrompt": "Pay the following invoice for {amount} sats to continue, or cancel if you don't agree.", "statusWaitingTakerBond": "Awaiting deposit payment", "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", "payBondButton": "Pay deposit", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 3aa533776..8acc81ca8 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -705,7 +705,8 @@ "share": "Share", "failedToShareInvoice": "Failed to share invoice. Please try copying instead.", "bondScreenTitle": "Anti-abuse deposit", - "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", + "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.", + "bondPayInvoicePrompt": "Pay the following invoice for {amount} sats to continue, or cancel if you don't agree.", "statusWaitingTakerBond": "Awaiting deposit payment", "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", "payBondButton": "Pay deposit", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 4e6f84224..0322c51bf 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -618,7 +618,8 @@ "share": "Compartir", "failedToShareInvoice": "Error al compartir factura. Por favor intenta copiarla en su lugar.", "bondScreenTitle": "Depósito anti-abuso", - "bondExplanation": "Para tomar esta orden debes pagar un depósito anti-abuso. Funciona así:\n\n• Tus sats quedan retenidos en tu wallet, no se gastan.\n• Si el intercambio finaliza correctamente, recuperas el depósito automáticamente.\n• Si tienes una disputa en esta orden y la pierdes, perderás el depósito.\n• Este mecanismo protege a todos los usuarios contra estafadores.\n\nPaga la factura para continuar, o cancela si no estás de acuerdo.", + "bondExplanation": "Para tomar esta orden debes pagar un depósito anti-abuso. Funciona así:\n\n• Tus sats quedan retenidos en tu wallet, no se gastan.\n• Si el intercambio finaliza correctamente, recuperas el depósito automáticamente.\n• Si tienes una disputa en esta orden y la pierdes, perderás el depósito.\n• Este mecanismo protege a todos los usuarios contra estafadores.", + "bondPayInvoicePrompt": "Paga la siguiente factura de {amount} sats para continuar, o cancela si no estás de acuerdo.", "statusWaitingTakerBond": "Esperando pago del depósito", "payBondMessage": "Por favor paga el depósito anti-abuso para continuar con esta orden.", "payBondButton": "Pagar depósito", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 8d9542ed4..8a42dc8cc 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -705,7 +705,8 @@ "share": "Partager", "failedToShareInvoice": "Échec du partage de la facture. Veuillez essayer de copier à la place.", "bondScreenTitle": "Anti-abuse deposit", - "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.\n\nPay the invoice to continue, or cancel if you don't agree.", + "bondExplanation": "To take this order you must pay an anti-abuse deposit. Here's how it works:\n\n• Your sats are held in your wallet, they are not spent.\n• If the exchange completes successfully, you get your deposit back automatically.\n• If you have a dispute on this order and lose it, you will lose the deposit.\n• This mechanism protects all users against scammers.", + "bondPayInvoicePrompt": "Pay the following invoice for {amount} sats to continue, or cancel if you don't agree.", "statusWaitingTakerBond": "Awaiting deposit payment", "payBondMessage": "Please pay the anti-abuse deposit to continue this order.", "payBondButton": "Pay deposit", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index dd7823997..89442802e 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -648,7 +648,8 @@ "share": "Condividi", "failedToShareInvoice": "Errore nel condividere la fattura. Per favore prova a copiarla invece.", "bondScreenTitle": "Deposito anti-abuso", - "bondExplanation": "Per prendere questo ordine devi pagare un deposito anti-abuso. Funziona così:\n\n• I tuoi sats vengono trattenuti nel tuo wallet, non vengono spesi.\n• Se lo scambio si conclude correttamente, recuperi il deposito automaticamente.\n• Se hai una disputa su questo ordine e la perdi, perderai il deposito.\n• Questo meccanismo protegge tutti gli utenti dai truffatori.\n\nPaga la fattura per continuare, oppure annulla se non sei d'accordo.", + "bondExplanation": "Per prendere questo ordine devi pagare un deposito anti-abuso. Funziona così:\n\n• I tuoi sats vengono trattenuti nel tuo wallet, non vengono spesi.\n• Se lo scambio si conclude correttamente, recuperi il deposito automaticamente.\n• Se hai una disputa su questo ordine e la perdi, perderai il deposito.\n• Questo meccanismo protegge tutti gli utenti dai truffatori.", + "bondPayInvoicePrompt": "Paga la seguente fattura di {amount} sats per continuare, oppure annulla se non sei d'accordo.", "statusWaitingTakerBond": "In attesa del pagamento del deposito", "payBondMessage": "Per favore paga il deposito anti-abuso per continuare questo ordine.", "payBondButton": "Paga deposito", From fb75c275311175b25020a7e46f28f5fe1ce9d584 Mon Sep 17 00:00:00 2001 From: Catrya <140891948+Catrya@users.noreply.github.com> Date: Thu, 14 May 2026 13:30:08 -0600 Subject: [PATCH 7/7] guard pay-bond prompt against zero amount --- lib/features/order/screens/pay_bond_invoice_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/order/screens/pay_bond_invoice_screen.dart b/lib/features/order/screens/pay_bond_invoice_screen.dart index 0f594e7bc..b1a500c2f 100644 --- a/lib/features/order/screens/pay_bond_invoice_screen.dart +++ b/lib/features/order/screens/pay_bond_invoice_screen.dart @@ -146,7 +146,7 @@ class PayBondInvoiceScreen extends ConsumerWidget { height: 1.4, ), ), - if (bondAmount != null) ...[ + if (bondAmount != null && bondAmount > 0) ...[ const SizedBox(height: 16), Text( s.bondPayInvoicePrompt(bondAmount),