add mutex around _confirmForm to prevent the wallets from breaking (#1602)

* add mutex around _confirmForm to prevent the wallets from breaking * add async * drop mutex for a boolean * don't make the variable global

cyan committed Aug 13, 2024 at 15:47 UTC 4b69277858a75ea3beafb268eb9f4c60cef9f070
2 files changed +99 -75
lib/src/screens/new_wallet/new_wallet_page.dart
+30 -19
@@ -25,6 +25,7 @@ import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
25 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
26 import 'package:cake_wallet/entities/seed_type.dart';
27
28 +
29 class NewWalletPage extends BasePage {
30 NewWalletPage(this._walletNewVM, this._seedTypeViewModel);
31
@@ -74,6 +75,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
75 _walletNewVM.hasWalletPassword ? TextEditingController() : null;
76
77 static const aspectRatioImage = 1.22;
78 + static bool formProcessing = false;
79
80 final GlobalKey<FormState> _formKey;
81 final GlobalKey<SeedLanguageSelectorState> _languageSelectorKey;
@@ -347,26 +349,35 @@ class _WalletNameFormState extends State<WalletNameForm> {
349 );
350 }
351
350 - void _confirmForm() {
351 - if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
352 - return;
353 - }
354 - if (_walletNewVM.nameExists(_walletNewVM.name)) {
355 - showPopUp<void>(
356 - context: context,
357 - builder: (_) {
358 - return AlertWithOneAction(
359 - alertTitle: '',
360 - alertContent: S.of(context).wallet_name_exists,
361 - buttonText: S.of(context).ok,
362 - buttonAction: () => Navigator.of(context).pop());
363 - });
364 - } else {
365 - _walletNewVM.create(
366 - options: _walletNewVM.hasLanguageSelector
367 - ? [_languageSelectorKey.currentState!.selected, isPolyseed]
368 - : null);
352 + void _confirmForm() async {
353 + if (formProcessing) return;
354 + formProcessing = true;
355 + try {
356 + if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
357 + formProcessing = false;
358 + return;
359 + }
360 + if (_walletNewVM.nameExists(_walletNewVM.name)) {
361 + await showPopUp<void>(
362 + context: context,
363 + builder: (_) {
364 + return AlertWithOneAction(
365 + alertTitle: '',
366 + alertContent: S.of(context).wallet_name_exists,
367 + buttonText: S.of(context).ok,
368 + buttonAction: () => Navigator.of(context).pop());
369 + });
370 + } else {
371 + await _walletNewVM.create(
372 + options: _walletNewVM.hasLanguageSelector
373 + ? [_languageSelectorKey.currentState!.selected, isPolyseed]
374 + : null);
375 + }
376 + } catch (e) {
377 + formProcessing = false;
378 + rethrow;
379 }
380 + formProcessing = false;
381 }
382
383 bool get isPolyseed => widget._seedTypeViewModel.moneroSeedType == SeedType.polyseed;
lib/src/screens/restore/wallet_restore_page.dart
+69 -56
@@ -2,6 +2,7 @@ import 'package:cake_wallet/core/execution_state.dart';
2 import 'package:cake_wallet/generated/i18n.dart';
3 import 'package:cake_wallet/routes.dart';
4 import 'package:cake_wallet/src/screens/base_page.dart';
5 +import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
6 import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
7 import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
8 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@@ -80,6 +81,8 @@ class WalletRestorePage extends BasePage {
81 });
82 }
83
84 + static bool formProcessing = false;
85 +
86 @override
87 Widget middle(BuildContext context) => Observer(
88 builder: (_) => Text(
@@ -350,75 +353,85 @@ class WalletRestorePage extends BasePage {
353 }
354
355 Future<void> _confirmForm(BuildContext context) async {
353 - // Dismissing all visible keyboard to provide context for navigation
354 - FocusManager.instance.primaryFocus?.unfocus();
356 + if (formProcessing) return;
357 + formProcessing = true;
358 + try {
359 + // Dismissing all visible keyboard to provide context for navigation
360 + FocusManager.instance.primaryFocus?.unfocus();
361 +
362 + late BuildContext? formContext;
363 + late GlobalKey<FormState>? formKey;
364 + late String name;
365 + if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
366 + formContext = walletRestoreFromSeedFormKey.currentContext;
367 + formKey = walletRestoreFromSeedFormKey.currentState!.formKey;
368 + name = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text;
369 + } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
370 + formContext = walletRestoreFromKeysFormKey.currentContext;
371 + formKey = walletRestoreFromKeysFormKey.currentState!.formKey;
372 + name = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
373 + }
374
356 - late BuildContext? formContext;
357 - late GlobalKey<FormState>? formKey;
358 - late String name;
359 - if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
360 - formContext = walletRestoreFromSeedFormKey.currentContext;
361 - formKey = walletRestoreFromSeedFormKey.currentState!.formKey;
362 - name = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text;
363 - } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
364 - formContext = walletRestoreFromKeysFormKey.currentContext;
365 - formKey = walletRestoreFromKeysFormKey.currentState!.formKey;
366 - name = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
367 - }
375 + if (!formKey!.currentState!.validate()) {
376 + formProcessing = false;
377 + return;
378 + }
379
369 - if (!formKey!.currentState!.validate()) {
370 - return;
371 - }
380 + if (walletRestoreViewModel.nameExists(name)) {
381 + showNameExistsAlert(formContext!);
382 + formProcessing = false;
383 + return;
384 + }
385
373 - if (walletRestoreViewModel.nameExists(name)) {
374 - showNameExistsAlert(formContext!);
375 - return;
376 - }
386 + walletRestoreViewModel.state = IsExecutingState();
387
378 - walletRestoreViewModel.state = IsExecutingState();
388 + DerivationInfo? dInfo;
389
380 - DerivationInfo? dInfo;
390 + // get info about the different derivations:
391 + List<DerivationInfo> derivations =
392 + await walletRestoreViewModel.getDerivationInfo(_credentials());
393
382 - // get info about the different derivations:
383 - List<DerivationInfo> derivations =
384 - await walletRestoreViewModel.getDerivationInfo(_credentials());
394 + int derivationsWithHistory = 0;
395 + int derivationWithHistoryIndex = 0;
396 + for (int i = 0; i < derivations.length; i++) {
397 + if (derivations[i].transactionsCount > 0) {
398 + derivationsWithHistory++;
399 + derivationWithHistoryIndex = i;
400 + }
401 + }
402
386 - int derivationsWithHistory = 0;
387 - int derivationWithHistoryIndex = 0;
388 - for (int i = 0; i < derivations.length; i++) {
389 - if (derivations[i].transactionsCount > 0) {
390 - derivationsWithHistory++;
391 - derivationWithHistoryIndex = i;
403 + if (derivationsWithHistory > 1) {
404 + dInfo = await Navigator.of(context).pushNamed(
405 + Routes.restoreWalletChooseDerivation,
406 + arguments: derivations,
407 + ) as DerivationInfo?;
408 + } else if (derivationsWithHistory == 1) {
409 + dInfo = derivations[derivationWithHistoryIndex];
410 }
393 - }
411
395 - if (derivationsWithHistory > 1) {
396 - dInfo = await Navigator.of(context).pushNamed(
397 - Routes.restoreWalletChooseDerivation,
398 - arguments: derivations,
399 - ) as DerivationInfo?;
400 - } else if (derivationsWithHistory == 1) {
401 - dInfo = derivations[derivationWithHistoryIndex];
402 - }
412 + // get the default derivation for this wallet type:
413 + if (dInfo == null) {
414 + // we only return 1 derivation if we're pretty sure we know which one to use:
415 + if (derivations.length == 1) {
416 + dInfo = derivations.first;
417 + } else {
418 + // if we have multiple possible derivations, and none have histories
419 + // we just default to the most common one:
420 + dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
421 + }
422 + }
423
404 - // get the default derivation for this wallet type:
405 - if (dInfo == null) {
406 - // we only return 1 derivation if we're pretty sure we know which one to use:
407 - if (derivations.length == 1) {
408 - dInfo = derivations.first;
409 - } else {
410 - // if we have multiple possible derivations, and none have histories
411 - // we just default to the most common one:
412 - dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
424 + this.derivationInfo = dInfo;
425 + if (this.derivationInfo == null) {
426 + this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
427 }
414 - }
428
416 - this.derivationInfo = dInfo;
417 - if (this.derivationInfo == null) {
418 - this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
429 + await walletRestoreViewModel.create(options: _credentials());
430 + } catch (e) {
431 + formProcessing = false;
432 + rethrow;
433 }
420 -
421 - walletRestoreViewModel.create(options: _credentials());
434 + formProcessing = false;
435 }
436
437 Future<void> showNameExistsAlert(BuildContext context) {