From 5cdce991548d0eb84ec923ccc99a285441887c42 Mon Sep 17 00:00:00 2001 From: Forte11Cuba Date: Fri, 17 Apr 2026 07:52:38 -0600 Subject: [PATCH] fix: address CodeRabbit review on #87 --- lib/data/pending_send.dart | 4 +-- lib/l10n/app_localizations_sw.dart | 3 +-- lib/l10n/app_sw.arb | 2 +- lib/providers/wallet_provider.dart | 18 +++++++++---- lib/screens/5_send/offline_send_screen.dart | 30 +++++++++++++-------- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/lib/data/pending_send.dart b/lib/data/pending_send.dart index c6ec644..d89feae 100644 --- a/lib/data/pending_send.dart +++ b/lib/data/pending_send.dart @@ -38,10 +38,10 @@ class PendingSend { required this.amount, required this.mintUrl, required this.unit, - required this.proofYs, + required List proofYs, required this.createdAt, this.memo, - }); + }) : proofYs = List.unmodifiable(proofYs); PendingSend copyWith({ String? id, diff --git a/lib/l10n/app_localizations_sw.dart b/lib/l10n/app_localizations_sw.dart index 5405365..44faa19 100644 --- a/lib/l10n/app_localizations_sw.dart +++ b/lib/l10n/app_localizations_sw.dart @@ -237,8 +237,7 @@ class L10nSw extends L10n { String get pendingOfflineSend => 'Kutuma nje ya mtandao kunasubiri'; @override - String get pendingOfflineSendsHeader => - 'Matumizi ya nje ya mtandao ambayo hayajadaiwa'; + String get pendingOfflineSendsHeader => 'Kutuma nje ya mtandao bila kudaiwa'; @override String get pendingSendDetailTitle => 'Kutuma kunakusubiri'; diff --git a/lib/l10n/app_sw.arb b/lib/l10n/app_sw.arb index f160bfc..630f4f8 100644 --- a/lib/l10n/app_sw.arb +++ b/lib/l10n/app_sw.arb @@ -99,7 +99,7 @@ }, "cancelSendAlreadyClaimed": "Mpokeaji tayari alitumia tokeni", "pendingOfflineSend": "Kutuma nje ya mtandao kunasubiri", - "pendingOfflineSendsHeader": "Matumizi ya nje ya mtandao ambayo hayajadaiwa", + "pendingOfflineSendsHeader": "Kutuma nje ya mtandao bila kudaiwa", "pendingSendDetailTitle": "Kutuma kunakusubiri", "validToken": "Tokeni halali", "invalidToken": "Tokeni batili au imeharibika", diff --git a/lib/providers/wallet_provider.dart b/lib/providers/wallet_provider.dart index af9c6fa..f136e6e 100644 --- a/lib/providers/wallet_provider.dart +++ b/lib/providers/wallet_provider.dart @@ -183,13 +183,17 @@ class WalletProvider extends ChangeNotifier { Future reclaimPendingSend(PendingSend send) async { final wallet = await getWallet(send.mintUrl, send.unit); final result = await wallet.reclaimProofsByYs(ys: send.proofYs); - // Si recuperamos algo O si no había nada que recuperar (todos los proofs - // ya fueron gastados por el receptor), removemos el registro — la - // transacción está terminada en ambos casos. - await _pendingSendStorage.remove(send.id); + // Solo removemos el registro si el mint confirmó que había proofs + // UNSPENT y las revertimos. Un count=0 es ambiguo: puede significar + // "receptor ya reclamó (SPENT)" o "receptor en medio del swap + // (PENDING)". En el caso PENDING, necesitamos conservar el record + // para poder reintentar después — borrar perdería el retry handle. + // Esto puede dejar records "fantasma" tras un SPENT real; se puede + // agregar dismissal manual como mejora futura. if (result.count > BigInt.zero) { - notifyListeners(); + await _pendingSendStorage.remove(send.id); } + notifyListeners(); return result; } @@ -2031,6 +2035,10 @@ class WalletProvider extends ChangeNotifier { // Limpiar metadata de transacciones await _txMetaStorage.clear(); + // Limpiar envíos offline pendientes (storage separado, sobrevive al + // borrado del wallet.sqlite si no se limpia acá). + await _pendingSendStorage.clear(); + // Borrar archivo final dir = await getApplicationDocumentsDirectory(); final dbPath = '${dir.path}/elcaju_wallet.sqlite'; diff --git a/lib/screens/5_send/offline_send_screen.dart b/lib/screens/5_send/offline_send_screen.dart index 625a9e1..cd0827b 100644 --- a/lib/screens/5_send/offline_send_screen.dart +++ b/lib/screens/5_send/offline_send_screen.dart @@ -35,6 +35,13 @@ class _OfflineSendScreenState extends State { final TextEditingController _memoController = TextEditingController(); final ProofService _proofService = ProofService(); + // Capturado en initState para poder persistir el PendingSend incluso si + // el widget se desmonta durante el await de markProofsPendingSpent. + // Usar `context.read` después del await es inseguro (widget puede no + // existir), y gatear con `mounted` perdería el record — los proofs + // quedan marcados PENDING_SPENT pero sin handle para reclamar. + late final WalletProvider _walletProvider; + List _availableProofs = []; Set _selectedIds = {}; bool _isLoading = true; @@ -44,6 +51,7 @@ class _OfflineSendScreenState extends State { @override void initState() { super.initState(); + _walletProvider = context.read(); _loadProofs(); } @@ -369,17 +377,17 @@ class _OfflineSendScreenState extends State { // Rastrear el envío offline como pendiente para poder cancelarlo // desde el historial. Los offline sends no crean CDK Transaction. - if (mounted) { - final walletProvider = context.read(); - await walletProvider.addPendingSend( - encoded: token, - amount: selectedTotal, - mintUrl: widget.mintUrl, - unit: widget.unit, - proofYs: selectedProofs.map((p) => p.yHex).toList(), - memo: memo, - ); - } + // Usamos el provider capturado en initState (no context.read) para + // que el record se persista aunque el widget se haya desmontado + // mientras esperábamos markProofsPendingSpent. + await _walletProvider.addPendingSend( + encoded: token, + amount: selectedTotal, + mintUrl: widget.mintUrl, + unit: widget.unit, + proofYs: selectedProofs.map((p) => p.yHex).toList(), + memo: memo, + ); if (mounted) { _isCreating = false;