Cw 1117 prevent sending a transaction twice by mistake (#2567)
* Revert "Prevent double send actions in SendPage (#2542)" This reverts commit dd4c941d2f356d7c4f2d39e9744ab43bbe1962c7. * disable send buttons when ExecutedSuccessfully * return value on bottom sheet pop [skip ci]
Serhii committed
Oct 9, 2025 at 15:35 UTC
f7d29c9f389499e74acebaa5fda392961a805b71
4 files changed
+16
-19
lib/cake_pay/src/cards/cake_pay_buy_card_page.dart
+1
-1
@@ -371,7 +371,7 @@ class CakePayBuyCardPage extends BasePage {
371
},
372
text: S.of(context).purchase_gift_card,
373
isDisabled: !cakePayBuyCardViewModel.isAmountSufficient ||
374
- cakePayBuyCardViewModel.isPurchasing,
374
+ cakePayBuyCardViewModel.isPurchasing || _sendViewModel.state is ExecutedSuccessfullyState,
375
isLoading: _sendViewModel.state is IsExecutingState ||
376
cakePayBuyCardViewModel.isPurchasing,
377
color: Theme.of(context).colorScheme.primary,
lib/src/screens/exchange_trade/exchange_trade_page.dart
+8
-4
@@ -194,7 +194,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
194
!(sendingState is TransactionCommitted)),
195
child: LoadingPrimaryButton(
196
key: ValueKey('exchange_trade_page_send_from_cake_button_key'),
197
- isDisabled: trade.inputAddress == null || trade.inputAddress!.isEmpty,
197
+ isDisabled: trade.inputAddress == null || trade.inputAddress!.isEmpty ||
198
+ sendingState is ExecutedSuccessfullyState,
199
isLoading: sendingState is IsExecutingState,
200
onPressed: () => widget.exchangeTradeViewModel.confirmSending(),
201
text: S.current.send_from_cake_wallet,
@@ -272,9 +273,9 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
273
}
274
275
if (state is ExecutedSuccessfullyState) {
275
- WidgetsBinding.instance.addPostFrameCallback((_) {
276
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
277
if (context.mounted) {
277
- showModalBottomSheet<void>(
278
+ final result = await showModalBottomSheet<bool>(
279
context: context,
280
isDismissible: false,
281
isScrollControlled: true,
@@ -304,13 +305,16 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
305
outputs: widget.exchangeTradeViewModel.sendViewModel.outputs,
306
onSlideActionComplete: () async {
307
if (bottomSheetContext.mounted) {
307
- Navigator.of(bottomSheetContext).pop();
308
+ Navigator.of(bottomSheetContext).pop(true);
309
}
310
widget.exchangeTradeViewModel.sendViewModel.commitTransaction(context);
311
},
312
);
313
},
314
);
315
+
316
+ if (result == null) widget.exchangeTradeViewModel.sendViewModel.dismissTransaction();
317
+
318
}
319
});
320
}
lib/src/screens/send/send_page.dart
+1
-12
@@ -64,7 +64,6 @@ class SendPage extends BasePage {
64
final PaymentRequest? initialPaymentRequest;
65
66
bool _effectsInstalled = false;
67
- bool _sendInProgress = false;
67
ContactRecord? newContactAddress;
68
69
@override
@@ -413,9 +412,6 @@ class SendPage extends BasePage {
412
return LoadingPrimaryButton(
413
key: ValueKey('send_page_send_button_key'),
414
onPressed: () async {
416
- // Prevent double taps
417
- if (_sendInProgress) return;
418
-
415
//Request dummy node to get the focus out of the text fields
416
FocusScope.of(context).requestFocus(FocusNode());
417
@@ -474,8 +470,6 @@ class SendPage extends BasePage {
470
}
471
}
472
477
- _sendInProgress = true;
478
-
473
final check = sendViewModel.shouldDisplayTotp();
474
authService.authenticateAction(
475
context,
@@ -483,8 +477,6 @@ class SendPage extends BasePage {
477
onAuthSuccess: (value) async {
478
if (value) {
479
await sendViewModel.createTransaction();
486
- } else {
487
- _sendInProgress = false;
480
}
481
},
482
);
@@ -496,7 +488,7 @@ class SendPage extends BasePage {
488
sendViewModel.state is TransactionCommitting ||
489
sendViewModel.state is IsAwaitingDeviceResponseState ||
490
sendViewModel.state is LoadingTemplateExecutingState,
499
- isDisabled: !sendViewModel.isReadyForSend,
491
+ isDisabled: !sendViewModel.isReadyForSend || sendViewModel.state is ExecutedSuccessfullyState,
492
);
493
},
494
)
@@ -533,7 +525,6 @@ class SendPage extends BasePage {
525
}
526
527
if (state is FailureState) {
536
- _sendInProgress = false;
528
WidgetsBinding.instance.addPostFrameCallback(
529
(_) {
530
showPopUp<void>(
@@ -614,13 +605,11 @@ class SendPage extends BasePage {
605
);
606
607
if (result == null) sendViewModel.dismissTransaction();
617
- _sendInProgress = false;
608
}
609
});
610
}
611
612
if (state is TransactionCommitted) {
623
- _sendInProgress = false;
613
WidgetsBinding.instance.addPostFrameCallback((_) async {
614
if (!context.mounted) {
615
return;
lib/src/screens/transaction_details/rbf_details_page.dart
+6
-2
@@ -105,6 +105,7 @@ class RBFDetailsPage extends BasePage {
105
text: S.of(context).send,
106
isLoading:
107
transactionDetailsViewModel.sendViewModel.state is IsExecutingState,
108
+ isDisabled: transactionDetailsViewModel.sendViewModel.state is ExecutedSuccessfullyState,
109
color: Theme.of(context).colorScheme.primary,
110
textColor: Theme.of(context).colorScheme.onPrimary,
111
))),
@@ -180,9 +181,9 @@ class RBFDetailsPage extends BasePage {
181
}
182
183
if (state is ExecutedSuccessfullyState) {
183
- WidgetsBinding.instance.addPostFrameCallback((_) {
184
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
185
if (context.mounted) {
185
- showModalBottomSheet<void>(
186
+ final result = await showModalBottomSheet<bool>(
187
context: context,
188
isDismissible: false,
189
isScrollControlled: true,
@@ -214,6 +215,9 @@ class RBFDetailsPage extends BasePage {
215
);
216
},
217
);
218
+ if (result == null) {
219
+ transactionDetailsViewModel.sendViewModel.dismissTransaction();
220
+ }
221
}
222
});
223
}