From 808927a0d14a98b9dca569a425194e72b1ae1e85 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 1 May 2023 08:51:55 -0600 Subject: [PATCH 01/16] handle change in abi data --- lib/utilities/extensions/impl/contract_abi.dart | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/utilities/extensions/impl/contract_abi.dart b/lib/utilities/extensions/impl/contract_abi.dart index c5a2877d8..eae0f2da4 100644 --- a/lib/utilities/extensions/impl/contract_abi.dart +++ b/lib/utilities/extensions/impl/contract_abi.dart @@ -20,13 +20,15 @@ extension ContractAbiExtensions on ContractAbi { final anonymous = json["anonymous"] as bool? ?? false; final List> components = []; - for (final input in json["inputs"] as List) { - components.add( - EventComponent( - _parseParam(input as Map), - input['indexed'] as bool? ?? false, - ), - ); + if (json["inputs"] is List) { + for (final input in json["inputs"] as List) { + components.add( + EventComponent( + _parseParam(input as Map), + input['indexed'] as bool? ?? false, + ), + ); + } } events.add(ContractEvent(anonymous, name, components)); From 643ef07066fb9662bb34ab613d934e6e9e4bf2ef Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 1 May 2023 09:46:11 -0600 Subject: [PATCH 02/16] add possibility to skip per wallet migration with warning --- lib/pages/wallet_view/wallet_view.dart | 29 +++- .../wallet_view/desktop_wallet_view.dart | 81 ++++++++++ lib/widgets/custom_loading_overlay.dart | 150 +++++++++++------- 3 files changed, 201 insertions(+), 59 deletions(-) diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index b19d291fa..61c4ed415 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -53,6 +53,7 @@ import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart'; @@ -420,6 +421,33 @@ class _WalletViewState extends ConsumerState { eventBus: null, textColor: Theme.of(context).extension()!.textDark, + actionButton: SecondaryButton( + label: "Cancel", + onPressed: () async { + await showDialog( + context: context, + builder: (context) => StackDialog( + title: "Warning!", + message: "Skipping this process can completely" + " break your wallet. It is only meant to be done in" + " emergency situations where the migration fails" + " and will not let you continue. Still skip?", + leftButton: SecondaryButton( + label: "Cancel", + onPressed: + Navigator.of(context, rootNavigator: true).pop, + ), + rightButton: SecondaryButton( + label: "Ok", + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(); + setState(() => _rescanningOnOpen = false); + }, + ), + ), + ); + }, + ), ), ) ], @@ -790,7 +818,6 @@ class _WalletViewState extends ConsumerState { label: "Receive", icon: const ReceiveNavIcon(), onTap: () { - final coin = ref.read(managerProvider).coin; if (mounted) { unawaited( Navigator.of(context).pushNamed( diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart index 512774df8..0d0c408c1 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -31,7 +31,11 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/hover_text_field.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -150,6 +154,83 @@ class _DesktopWalletViewState extends ConsumerState { subMessage: "This only needs to run once per wallet", eventBus: null, textColor: Theme.of(context).extension()!.textDark, + actionButton: SecondaryButton( + label: "Skip", + buttonHeight: ButtonHeight.l, + onPressed: () async { + await showDialog( + context: context, + builder: (context) => DesktopDialog( + maxWidth: 500, + maxHeight: double.infinity, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(left: 32), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Warning!", + style: STextStyles.desktopH3(context), + ), + const DesktopDialogCloseButton(), + ], + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 32), + child: Text( + "Skipping this process can completely" + " break your wallet. It is only meant to be done in" + " emergency situations where the migration fails" + " and will not let you continue. Still skip?", + style: STextStyles.desktopTextSmall(context), + ), + ), + const SizedBox( + height: 32, + ), + Padding( + padding: const EdgeInsets.all(32), + child: Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + buttonHeight: ButtonHeight.l, + onPressed: Navigator.of(context, + rootNavigator: true) + .pop, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Ok", + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context, + rootNavigator: true) + .pop(); + setState( + () => _rescanningOnOpen = false); + }, + ), + ), + ], + ), + ) + ], + ), + ), + ); + }, + ), ), ) ], diff --git a/lib/widgets/custom_loading_overlay.dart b/lib/widgets/custom_loading_overlay.dart index e05158bc4..e2669d054 100644 --- a/lib/widgets/custom_loading_overlay.dart +++ b/lib/widgets/custom_loading_overlay.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; class CustomLoadingOverlay extends ConsumerStatefulWidget { @@ -14,12 +16,14 @@ class CustomLoadingOverlay extends ConsumerStatefulWidget { this.subMessage, required this.eventBus, this.textColor, + this.actionButton, }) : super(key: key); final String message; final String? subMessage; final EventBus? eventBus; final Color? textColor; + final Widget? actionButton; @override ConsumerState createState() => @@ -30,6 +34,7 @@ class _CustomLoadingOverlayState extends ConsumerState { double _percent = 0; late final StreamSubscription? subscription; + final bool isDesktop = Util.isDesktop; @override void initState() { @@ -49,68 +54,97 @@ class _CustomLoadingOverlayState extends ConsumerState { @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Material( - color: Colors.transparent, - child: Center( - child: Column( - children: [ - Text( - widget.message, - textAlign: TextAlign.center, - style: STextStyles.pageTitleH2(context).copyWith( - color: widget.textColor ?? - Theme.of(context) - .extension()! - .loadingOverlayTextColor, + return Material( + color: Colors.transparent, + child: ConditionalParent( + condition: widget.actionButton != null, + builder: (child) => Stack( + children: [ + child, + if (isDesktop) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: SizedBox( + width: 100, + child: widget.actionButton!, + ), + ), + ], + ), + if (!isDesktop) + Positioned( + bottom: 1, + left: 0, + right: 1, + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded(child: widget.actionButton!), + ], ), ), - if (widget.eventBus != null) - const SizedBox( - height: 10, - ), - if (widget.eventBus != null) - Text( - "${(_percent * 100).toStringAsFixed(2)}%", - style: STextStyles.pageTitleH2(context).copyWith( - color: widget.textColor ?? - Theme.of(context) - .extension()! - .loadingOverlayTextColor, - ), - ), - if (widget.subMessage != null) - const SizedBox( - height: 10, - ), - if (widget.subMessage != null) - Text( - widget.subMessage!, - textAlign: TextAlign.center, - style: STextStyles.pageTitleH2(context).copyWith( - fontSize: 14, - color: widget.textColor ?? - Theme.of(context) - .extension()! - .loadingOverlayTextColor, - ), - ) - ], + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.message, + textAlign: TextAlign.center, + style: STextStyles.pageTitleH2(context).copyWith( + color: widget.textColor ?? + Theme.of(context) + .extension()! + .loadingOverlayTextColor, + ), ), - ), + if (widget.eventBus != null) + const SizedBox( + height: 10, + ), + if (widget.eventBus != null) + Text( + "${(_percent * 100).toStringAsFixed(2)}%", + style: STextStyles.pageTitleH2(context).copyWith( + color: widget.textColor ?? + Theme.of(context) + .extension()! + .loadingOverlayTextColor, + ), + ), + if (widget.subMessage != null) + const SizedBox( + height: 10, + ), + if (widget.subMessage != null) + Text( + widget.subMessage!, + textAlign: TextAlign.center, + style: STextStyles.pageTitleH2(context).copyWith( + fontSize: 14, + color: widget.textColor ?? + Theme.of(context) + .extension()! + .loadingOverlayTextColor, + ), + ), + const SizedBox( + height: 64, + ), + const Center( + child: LoadingIndicator( + width: 100, + ), + ), + ], ), - const SizedBox( - height: 64, - ), - const Center( - child: LoadingIndicator( - width: 100, - ), - ), - ], + ), ); } } From dad35470f8e01a64b7070b8912b67fc075bcfbe5 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 1 May 2023 11:18:11 -0600 Subject: [PATCH 03/16] unified desktop/mobile dialog --- lib/widgets/dialogs/basic_dialog.dart | 104 ++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 lib/widgets/dialogs/basic_dialog.dart diff --git a/lib/widgets/dialogs/basic_dialog.dart b/lib/widgets/dialogs/basic_dialog.dart new file mode 100644 index 000000000..f6dab30f0 --- /dev/null +++ b/lib/widgets/dialogs/basic_dialog.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class BasicDialog extends StatelessWidget { + const BasicDialog({ + Key? key, + this.leftButton, + this.rightButton, + this.icon, + required this.title, + this.message, + this.desktopHeight = 474, + this.desktopWidth = 641, + this.canPopWithBackButton = false, + }) : super(key: key); + + final Widget? leftButton; + final Widget? rightButton; + + final Widget? icon; + + final String title; + final String? message; + + final double? desktopHeight; + final double desktopWidth; + + final bool canPopWithBackButton; + + @override + Widget build(BuildContext context) { + final isDesktop = Util.isDesktop; + + if (isDesktop) { + return DesktopDialog( + maxHeight: desktopHeight, + maxWidth: desktopWidth, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(left: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: STextStyles.desktopH3(context), + ), + const DesktopDialogCloseButton(), + ], + ), + ), + if (message != null) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Text( + message!, + style: STextStyles.desktopTextSmall(context), + ), + ), + if (leftButton != null || rightButton != null) + const SizedBox( + height: 32, + ), + if (leftButton != null || rightButton != null) + Padding( + padding: const EdgeInsets.all(32), + child: Row( + children: [ + leftButton != null + ? Expanded(child: leftButton!) + : const Spacer(), + const SizedBox( + width: 16, + ), + rightButton != null + ? Expanded(child: rightButton!) + : const Spacer(), + ], + ), + ) + ], + ), + ); + } else { + return WillPopScope( + onWillPop: () async { + return canPopWithBackButton; + }, + child: StackDialog( + title: title, + leftButton: leftButton, + rightButton: rightButton, + icon: icon, + message: message, + ), + ); + } + } +} From 35ea94a20973719c073f9aa2dac8767b28cdc513 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 1 May 2023 11:18:38 -0600 Subject: [PATCH 04/16] catch abi parsing errors --- .../extensions/impl/contract_abi.dart | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/lib/utilities/extensions/impl/contract_abi.dart b/lib/utilities/extensions/impl/contract_abi.dart index eae0f2da4..61827b32d 100644 --- a/lib/utilities/extensions/impl/contract_abi.dart +++ b/lib/utilities/extensions/impl/contract_abi.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:stackwallet/utilities/logger.dart'; import 'package:web3dart/web3dart.dart'; extension ContractAbiExtensions on ContractAbi { @@ -7,52 +8,61 @@ extension ContractAbiExtensions on ContractAbi { required String name, required String jsonList, }) { - final List functions = []; - final List events = []; + try { + final List functions = []; + final List events = []; - final list = List>.from(jsonDecode(jsonList) as List); + final list = + List>.from(jsonDecode(jsonList) as List); - for (final json in list) { - final type = json["type"] as String; - final name = json["name"] as String? ?? ""; + for (final json in list) { + final type = json["type"] as String; + final name = json["name"] as String? ?? ""; - if (type == "event") { - final anonymous = json["anonymous"] as bool? ?? false; - final List> components = []; + if (type == "event") { + final anonymous = json["anonymous"] as bool? ?? false; + final List> components = []; - if (json["inputs"] is List) { - for (final input in json["inputs"] as List) { - components.add( - EventComponent( - _parseParam(input as Map), - input['indexed'] as bool? ?? false, + if (json["inputs"] is List) { + for (final input in json["inputs"] as List) { + components.add( + EventComponent( + _parseParam(input as Map), + input['indexed'] as bool? ?? false, + ), + ); + } + } + + events.add(ContractEvent(anonymous, name, components)); + } else { + final mutability = _mutabilityNames[json['stateMutability']]; + final parsedType = _functionTypeNames[json['type']]; + if (parsedType != null) { + final inputs = _parseParams(json['inputs'] as List?); + final outputs = _parseParams(json['outputs'] as List?); + + functions.add( + ContractFunction( + name, + inputs, + outputs: outputs, + type: parsedType, + mutability: mutability ?? StateMutability.nonPayable, ), ); } } - - events.add(ContractEvent(anonymous, name, components)); - } else { - final mutability = _mutabilityNames[json['stateMutability']]; - final parsedType = _functionTypeNames[json['type']]; - if (parsedType != null) { - final inputs = _parseParams(json['inputs'] as List?); - final outputs = _parseParams(json['outputs'] as List?); - - functions.add( - ContractFunction( - name, - inputs, - outputs: outputs, - type: parsedType, - mutability: mutability ?? StateMutability.nonPayable, - ), - ); - } } - } - return ContractAbi(name, functions, events); + return ContractAbi(name, functions, events); + } catch (e, s) { + Logging.instance.log( + "Failed to parse ABI for $name: $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } } static const Map _functionTypeNames = { From b9211ad2c3ceecc2bb2eab7db2291052ef3f94dc Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 1 May 2023 11:20:40 -0600 Subject: [PATCH 05/16] pass abi parsing errors (or other token init errors) up and show a simple dialog --- .../sub_widgets/my_token_select_item.dart | 40 ++++++++++++++++- .../ethereum/ethereum_token_service.dart | 36 +++++++++------ lib/widgets/wallet_card.dart | 44 ++++++++++++++++--- 3 files changed, 99 insertions(+), 21 deletions(-) diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index 50b6e9768..42931c354 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -14,6 +14,8 @@ import 'package:stackwallet/utilities/show_loading.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/dialogs/basic_dialog.dart'; import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -36,6 +38,36 @@ class _MyTokenSelectItemState extends ConsumerState { late final CachedEthTokenBalance cachedBalance; + Future _loadTokenWallet( + BuildContext context, + WidgetRef ref, + ) async { + try { + await ref.read(tokenServiceProvider)!.initialize(); + return true; + } catch (_) { + await showDialog( + barrierDismissible: false, + context: context, + builder: (context) => BasicDialog( + title: "Failed to load token data", + desktopHeight: double.infinity, + desktopWidth: 450, + rightButton: PrimaryButton( + label: "OK", + onPressed: () { + Navigator.of(context).pop(); + if (!isDesktop) { + Navigator.of(context).pop(); + } + }, + ), + ), + ); + return false; + } + } + void _onPressed() async { ref.read(tokenServiceStateProvider.state).state = EthTokenWallet( token: widget.token, @@ -49,13 +81,17 @@ class _MyTokenSelectItemState extends ConsumerState { ), ); - await showLoading( - whileFuture: ref.read(tokenServiceProvider)!.initialize(), + final success = await showLoading( + whileFuture: _loadTokenWallet(context, ref), context: context, isDesktop: isDesktop, message: "Loading ${widget.token.name}", ); + if (!success) { + return; + } + if (mounted) { await Navigator.of(context).pushNamed( isDesktop ? DesktopTokenView.routeName : TokenView.routeName, diff --git a/lib/services/ethereum/ethereum_token_service.dart b/lib/services/ethereum/ethereum_token_service.dart index 624b6b59a..c7007c905 100644 --- a/lib/services/ethereum/ethereum_token_service.dart +++ b/lib/services/ethereum/ethereum_token_service.dart @@ -253,13 +253,17 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache { ); _credentials = web3dart.EthPrivateKey.fromHex(privateKey); - _deployedContract = web3dart.DeployedContract( - ContractAbiExtensions.fromJsonList( - jsonList: tokenContract.abi!, - name: tokenContract.name, - ), - contractAddress, - ); + try { + _deployedContract = web3dart.DeployedContract( + ContractAbiExtensions.fromJsonList( + jsonList: tokenContract.abi!, + name: tokenContract.name, + ), + contractAddress, + ); + } catch (_) { + rethrow; + } try { _sendFunction = _deployedContract.function('transfer'); @@ -328,13 +332,17 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache { //==================================================================== } - _deployedContract = web3dart.DeployedContract( - ContractAbiExtensions.fromJsonList( - jsonList: tokenContract.abi!, - name: tokenContract.name, - ), - contractAddress, - ); + try { + _deployedContract = web3dart.DeployedContract( + ContractAbiExtensions.fromJsonList( + jsonList: tokenContract.abi!, + name: tokenContract.name, + ), + contractAddress, + ); + } catch (_) { + rethrow; + } _sendFunction = _deployedContract.function('transfer'); diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index 399ae6211..a5130dd1e 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -19,6 +19,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/show_loading.dart'; import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/dialogs/basic_dialog.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart'; import 'package:tuple/tuple.dart'; @@ -37,7 +39,7 @@ class SimpleWalletCard extends ConsumerWidget { final bool popPrevious; final NavigatorState? desktopNavigatorState; - Future _loadTokenWallet( + Future _loadTokenWallet( BuildContext context, WidgetRef ref, Manager manager, @@ -52,7 +54,31 @@ class SimpleWalletCard extends ConsumerWidget { ), ); - await ref.read(tokenServiceProvider)!.initialize(); + try { + await ref.read(tokenServiceProvider)!.initialize(); + return true; + } catch (_) { + await showDialog( + barrierDismissible: false, + context: context, + builder: (context) => BasicDialog( + title: "Failed to load token data", + desktopHeight: double.infinity, + desktopWidth: 450, + rightButton: PrimaryButton( + label: "OK", + onPressed: () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + if (desktopNavigatorState == null) { + Navigator.of(context).pop(); + } + }, + ), + ), + ); + return false; + } } void _openWallet(BuildContext context, WidgetRef ref) async { @@ -91,13 +117,21 @@ class SimpleWalletCard extends ConsumerWidget { final contract = ref.read(mainDBProvider).getEthContractSync(contractAddress!)!; - await showLoading( - whileFuture: _loadTokenWallet(context, ref, manager, contract), - context: context, + final success = await showLoading( + whileFuture: _loadTokenWallet( + desktopNavigatorState?.context ?? context, + ref, + manager, + contract), + context: desktopNavigatorState?.context ?? context, opaqueBG: true, message: "Loading ${contract.name}", ); + if (!success) { + return; + } + if (desktopNavigatorState == null) { // pop loading nav.pop(); From e05caa589fac685ba11409acf13d5588c90c07e8 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 1 May 2023 13:41:53 -0600 Subject: [PATCH 06/16] bch block height showing as pending in ui fix --- lib/services/coins/bitcoincash/bitcoincash_wallet.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart index 7903927ae..e108352b4 100644 --- a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart +++ b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart @@ -1949,8 +1949,6 @@ class BitcoinCashWallet extends CoinServiceAPI List> allTransactions = []; - final currentHeight = await chainHeight; - for (final txHash in allTxHashes) { final storedTx = await db .getTransactions(walletId) @@ -1958,7 +1956,9 @@ class BitcoinCashWallet extends CoinServiceAPI .txidEqualTo(txHash["tx_hash"] as String) .findFirst(); - if (storedTx == null || storedTx.address.value == null + if (storedTx == null || + storedTx.address.value == null || + storedTx.height == null // zero conf messes this up // !storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS) ) { From 6eea7cd2cfc38c8481a4298eacaeae16589ec102 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 1 May 2023 16:30:43 -0600 Subject: [PATCH 07/16] remove biometric button, move back button, add submit button --- lib/pages/pinpad_views/lock_screen_view.dart | 16 ++++++------- lib/widgets/custom_pin_put/pin_keyboard.dart | 25 +++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index 7e9f9b77f..2f104cc1d 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -22,7 +21,6 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart'; -import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart'; import 'package:stackwallet/widgets/shake/shake.dart'; import 'package:tuple/tuple.dart'; @@ -90,7 +88,7 @@ class _LockscreenViewState extends ConsumerState { final manager = ref.read(walletsChangeNotifierProvider).getManager(walletId); - if (manager.coin == Coin.monero || manager.coin == Coin.wownero) { + if (manager.coin == Coin.monero) { await showLoading( opaqueBG: true, whileFuture: manager.initializeExisting(), @@ -244,12 +242,12 @@ class _LockscreenViewState extends ConsumerState { height: 52, ), CustomPinPut( - customKey: CustomKey( - onPressed: _checkUseBiometrics, - iconAssetName: Platform.isIOS - ? Assets.svg.faceId - : Assets.svg.fingerprint, - ), + // customKey: CustomKey( + // onPressed: _checkUseBiometrics, + // iconAssetName: Platform.isIOS + // ? Assets.svg.faceId + // : Assets.svg.fingerprint, + // ), fieldsCount: Constants.pinLength, eachFieldHeight: 12, eachFieldWidth: 12, diff --git a/lib/widgets/custom_pin_put/pin_keyboard.dart b/lib/widgets/custom_pin_put/pin_keyboard.dart index 1ec611a80..6e108962c 100644 --- a/lib/widgets/custom_pin_put/pin_keyboard.dart +++ b/lib/widgets/custom_pin_put/pin_keyboard.dart @@ -355,12 +355,15 @@ class PinKeyboard extends StatelessWidget { ), Row( children: [ - customKey == null - ? const SizedBox( - height: 72, - width: 72, - ) - : customKey!, + // customKey == null + // ? const SizedBox( + // height: 72, + // width: 72, + // ) + // : customKey!, + BackspaceKey( + onPressed: _backHandler, + ), const SizedBox( width: 24, ), @@ -371,12 +374,12 @@ class PinKeyboard extends StatelessWidget { const SizedBox( width: 24, ), - BackspaceKey( - onPressed: _backHandler, - ), - // SubmitKey( - // onPressed: _submitHandler, + // BackspaceKey( + // onPressed: _backHandler, // ), + SubmitKey( + onPressed: _submitHandler, + ), ], ) ], From 096aab834b57e27436ca4d2739d69600db5d7b00 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 1 May 2023 16:34:16 -0600 Subject: [PATCH 08/16] UseBiometric button + only show if biometrics toggle is on --- lib/pages/pinpad_views/lock_screen_view.dart | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index 2f104cc1d..bf068b140 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -224,6 +224,26 @@ class _LockscreenViewState extends ConsumerState { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + // check prefs and hide if user has biometrics toggle off? + Padding( + padding: const EdgeInsets.only(right: 40.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (ref.read(prefsChangeNotifierProvider).useBiometrics == + true) + CustomTextButton( + text: "Use biometrics", + onTap: () async { + await _checkUseBiometrics(); + }, + ), + ], + ), + ), + const SizedBox( + height: 55, + ), Shake( animationDuration: const Duration(milliseconds: 700), animationRange: 12, From cede4626812b29e644de1c62e6d47780488a78cc Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Fri, 28 Apr 2023 12:03:54 -0600 Subject: [PATCH 09/16] WIP: randomize PIN provider + toggle added --- .../security_views/security_view.dart | 47 +++++++++++++++++++ lib/utilities/prefs.dart | 21 +++++++++ 2 files changed, 68 insertions(+) diff --git a/lib/pages/settings_views/global_settings_view/security_views/security_view.dart b/lib/pages/settings_views/global_settings_view/security_views/security_view.dart index c2a64bb50..74121b39c 100644 --- a/lib/pages/settings_views/global_settings_view/security_views/security_view.dart +++ b/lib/pages/settings_views/global_settings_view/security_views/security_view.dart @@ -146,6 +146,53 @@ class SecurityView extends StatelessWidget { }, ), ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Consumer( + builder: (_, ref, __) { + return RawMaterialButton( + // splashColor: Theme.of(context).extension()!.highlight, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: null, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Randomize PIN Pad", + style: STextStyles.titleBold12(context), + textAlign: TextAlign.left, + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + prefsChangeNotifierProvider + .select((value) => value.randomizePIN), + ), + onValueChanged: (newValue) { + ref + .read(prefsChangeNotifierProvider) + .randomizePIN = newValue; + }, + ), + ), + ], + ), + ), + ); + }, + ), + ), ], ), ), diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index 7dd17e2ad..f3596b55f 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -295,6 +295,27 @@ class Prefs extends ChangeNotifier { // } // } + // randomize PIN + + bool _randomizePIN = false; + + bool get randomizePIN => _randomizePIN; + + set randomizePIN(bool randomizePIN) { + if (_randomizePIN != randomizePIN) { + DB.instance.put( + boxName: DB.boxNamePrefs, key: "randomizePIN", value: randomizePIN); + _randomizePIN = randomizePIN; + notifyListeners(); + } + } + + Future _getRandomizePIN() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, key: "randomizePIN") as bool? ?? + false; + } + // use biometrics bool _useBiometrics = false; From 63277600a60475c88c26ef9cf72382696353513f Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Fri, 28 Apr 2023 12:07:27 -0600 Subject: [PATCH 10/16] unlock wallet using submit button --- lib/widgets/custom_pin_put/custom_pin_put_state.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/widgets/custom_pin_put/custom_pin_put_state.dart b/lib/widgets/custom_pin_put/custom_pin_put_state.dart index 0413aef92..be3249a74 100644 --- a/lib/widgets/custom_pin_put/custom_pin_put_state.dart +++ b/lib/widgets/custom_pin_put/custom_pin_put_state.dart @@ -32,9 +32,9 @@ class CustomPinPutState extends State } catch (e) { _textControllerValue = ValueNotifier(_controller.value.text); } - if (pin.length == widget.fieldsCount) { - widget.onSubmit?.call(pin); - } + // if (pin.length == widget.fieldsCount) { + // widget.onSubmit?.call(pin); + // } } } From e5fe80f057cf55c9a2468cec5fe57054c520cfc0 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Fri, 28 Apr 2023 16:41:53 -0600 Subject: [PATCH 11/16] WIP: randomize PIN pad --- lib/widgets/custom_pin_put/pin_keyboard.dart | 206 +++++++++++++++++-- 1 file changed, 185 insertions(+), 21 deletions(-) diff --git a/lib/widgets/custom_pin_put/pin_keyboard.dart b/lib/widgets/custom_pin_put/pin_keyboard.dart index 6e108962c..372dbb538 100644 --- a/lib/widgets/custom_pin_put/pin_keyboard.dart +++ b/lib/widgets/custom_pin_put/pin_keyboard.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -237,7 +238,7 @@ class CustomKey extends StatelessWidget { } } -class PinKeyboard extends StatelessWidget { +class PinKeyboard extends ConsumerWidget { const PinKeyboard({ Key? key, required this.onNumberKeyPressed, @@ -271,7 +272,23 @@ class PinKeyboard extends StatelessWidget { } @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final list = [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "0", + ]; + + // if (ref.read(prefsChangeNotifierProvider).randomizePIN == true) + list.shuffle(); + return Container( width: width, height: height, @@ -281,21 +298,21 @@ class PinKeyboard extends StatelessWidget { Row( children: [ NumberKey( - number: "1", + number: list[0], onPressed: _numberHandler, ), const SizedBox( width: 24, ), NumberKey( - number: "2", + number: list[1], onPressed: _numberHandler, ), const SizedBox( width: 24, ), NumberKey( - number: "3", + number: list[2], onPressed: _numberHandler, ), ], @@ -306,21 +323,21 @@ class PinKeyboard extends StatelessWidget { Row( children: [ NumberKey( - number: "4", + number: list[3], onPressed: _numberHandler, ), const SizedBox( width: 24, ), NumberKey( - number: "5", + number: list[4], onPressed: _numberHandler, ), const SizedBox( width: 24, ), NumberKey( - number: "6", + number: list[5], onPressed: _numberHandler, ), ], @@ -331,21 +348,21 @@ class PinKeyboard extends StatelessWidget { Row( children: [ NumberKey( - number: "7", + number: list[6], onPressed: _numberHandler, ), const SizedBox( width: 24, ), NumberKey( - number: "8", + number: list[7], onPressed: _numberHandler, ), const SizedBox( width: 24, ), NumberKey( - number: "9", + number: list[8], onPressed: _numberHandler, ), ], @@ -355,12 +372,6 @@ class PinKeyboard extends StatelessWidget { ), Row( children: [ - // customKey == null - // ? const SizedBox( - // height: 72, - // width: 72, - // ) - // : customKey!, BackspaceKey( onPressed: _backHandler, ), @@ -368,15 +379,168 @@ class PinKeyboard extends StatelessWidget { width: 24, ), NumberKey( - number: "0", + number: list[9], + onPressed: _numberHandler, + ), + const SizedBox( + width: 24, + ), + SubmitKey( + onPressed: _submitHandler, + ), + ], + ) + ], + ), + ); + } +} + +class RandomKeyboard extends StatelessWidget { + const RandomKeyboard({ + Key? key, + required this.onNumberKeyPressed, + required this.onBackPressed, + required this.onSubmitPressed, + this.backgroundColor, + this.width = 264, + this.height = 360, + this.customKey, + }) : super(key: key); + + final ValueSetter onNumberKeyPressed; + final VoidCallback onBackPressed; + final VoidCallback onSubmitPressed; + final Color? backgroundColor; + final double? width; + final double? height; + final CustomKey? customKey; + + void _backHandler() { + onBackPressed.call(); + } + + void _submitHandler() { + onSubmitPressed.call(); + } + + void _numberHandler(String number) { + onNumberKeyPressed.call(number); + HapticFeedback.lightImpact(); + debugPrint("NUMBER: $number"); + } + + @override + Widget build(BuildContext context) { + final list = [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "0", + ]; + list.shuffle(); + return Container( + width: width, + height: height, + color: backgroundColor ?? Colors.transparent, + child: Column( + children: [ + Row( + children: [ + NumberKey( + number: list[0], + onPressed: _numberHandler, + ), + const SizedBox( + width: 24, + ), + NumberKey( + number: list[1], + onPressed: _numberHandler, + ), + const SizedBox( + width: 24, + ), + NumberKey( + number: list[2], + onPressed: _numberHandler, + ), + ], + ), + const SizedBox( + height: 24, + ), + Row( + children: [ + NumberKey( + number: list[3], + onPressed: _numberHandler, + ), + const SizedBox( + width: 24, + ), + NumberKey( + number: list[4], + onPressed: _numberHandler, + ), + const SizedBox( + width: 24, + ), + NumberKey( + number: list[5], + onPressed: _numberHandler, + ), + ], + ), + const SizedBox( + height: 24, + ), + Row( + children: [ + NumberKey( + number: list[6], + onPressed: _numberHandler, + ), + const SizedBox( + width: 24, + ), + NumberKey( + number: list[7], + onPressed: _numberHandler, + ), + const SizedBox( + width: 24, + ), + NumberKey( + number: list[8], + onPressed: _numberHandler, + ), + ], + ), + const SizedBox( + height: 24, + ), + Row( + children: [ + BackspaceKey( + onPressed: _backHandler, + ), + const SizedBox( + width: 24, + ), + NumberKey( + number: list[9], onPressed: _numberHandler, ), const SizedBox( width: 24, ), - // BackspaceKey( - // onPressed: _backHandler, - // ), SubmitKey( onPressed: _submitHandler, ), From db9485474e4e3b6d16b7339267add4510f62fa63 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 1 May 2023 16:35:43 -0600 Subject: [PATCH 12/16] import custom button --- lib/pages/pinpad_views/lock_screen_view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index bf068b140..a7a8f83a0 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -20,6 +20,7 @@ import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart'; import 'package:stackwallet/widgets/shake/shake.dart'; import 'package:tuple/tuple.dart'; From 572410db898954b419cffa1fc98ad0b542afa470 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 1 May 2023 16:41:13 -0600 Subject: [PATCH 13/16] resolve merge conflicts --- lib/pages/pinpad_views/create_pin_view.dart | 5 ++++- lib/pages/pinpad_views/lock_screen_view.dart | 3 +++ .../security_views/change_pin_view/change_pin_view.dart | 7 ++++++- lib/utilities/prefs.dart | 1 + lib/widgets/custom_pin_put/custom_pin_put.dart | 3 +++ lib/widgets/custom_pin_put/custom_pin_put_state.dart | 4 ++++ lib/widgets/custom_pin_put/pin_keyboard.dart | 7 +++++-- 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/pages/pinpad_views/create_pin_view.dart b/lib/pages/pinpad_views/create_pin_view.dart index 3180689c2..d2b5a5ce1 100644 --- a/lib/pages/pinpad_views/create_pin_view.dart +++ b/lib/pages/pinpad_views/create_pin_view.dart @@ -9,7 +9,6 @@ import 'package:stackwallet/providers/global/secure_store_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/biometrics.dart'; import 'package:stackwallet/utilities/constants.dart'; -import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -139,6 +138,8 @@ class _CreatePinViewState extends ConsumerState { .background, counterText: "", ), + isRandom: + ref.read(prefsChangeNotifierProvider).randomizePIN, submittedFieldDecoration: _pinPutDecoration.copyWith( color: Theme.of(context) .extension()! @@ -221,6 +222,8 @@ class _CreatePinViewState extends ConsumerState { ), selectedFieldDecoration: _pinPutDecoration, followingFieldDecoration: _pinPutDecoration, + isRandom: + ref.read(prefsChangeNotifierProvider).randomizePIN, onSubmit: (String pin) async { // _onSubmitCount++; // if (_onSubmitCount - _onSubmitFailCount > 1) return; diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index a7a8f83a0..62774e375 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -304,6 +304,9 @@ class _LockscreenViewState extends ConsumerState { ), selectedFieldDecoration: _pinPutDecoration, followingFieldDecoration: _pinPutDecoration, + isRandom: ref + .read(prefsChangeNotifierProvider) + .randomizePIN, onSubmit: (String pin) async { _attempts++; diff --git a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart index fb5722594..384d7852a 100644 --- a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart +++ b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackduo/providers/global/prefs_provider.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/providers/global/secure_store_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; -import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -123,6 +124,8 @@ class _ChangePinViewState extends ConsumerState { .background, counterText: "", ), + isRandom: + ref.read(prefsChangeNotifierProvider).randomizePIN, submittedFieldDecoration: _pinPutDecoration.copyWith( color: Theme.of(context) .extension()! @@ -188,6 +191,8 @@ class _ChangePinViewState extends ConsumerState { .background, counterText: "", ), + isRandom: + ref.read(prefsChangeNotifierProvider).randomizePIN, submittedFieldDecoration: _pinPutDecoration.copyWith( color: Theme.of(context) .extension()! diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index f3596b55f..973541366 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -19,6 +19,7 @@ class Prefs extends ChangeNotifier { if (!_initialized) { _currency = await _getPreferredCurrency(); // _exchangeRateType = await _getExchangeRateType(); + _randomizePIN = await _getRandomizePIN(); _useBiometrics = await _getUseBiometrics(); _hasPin = await _getHasPin(); _language = await _getPreferredLanguage(); diff --git a/lib/widgets/custom_pin_put/custom_pin_put.dart b/lib/widgets/custom_pin_put/custom_pin_put.dart index ca7b28581..4a4e4a4e4 100644 --- a/lib/widgets/custom_pin_put/custom_pin_put.dart +++ b/lib/widgets/custom_pin_put/custom_pin_put.dart @@ -7,6 +7,7 @@ class CustomPinPut extends StatefulWidget { const CustomPinPut({ Key? key, required this.fieldsCount, + required this.isRandom, this.height, this.width, this.onSubmit, @@ -60,6 +61,8 @@ class CustomPinPut extends StatefulWidget { final CustomKey? customKey; + final bool isRandom; + /// Displayed fields count. PIN code length. final int fieldsCount; diff --git a/lib/widgets/custom_pin_put/custom_pin_put_state.dart b/lib/widgets/custom_pin_put/custom_pin_put_state.dart index be3249a74..355656638 100644 --- a/lib/widgets/custom_pin_put/custom_pin_put_state.dart +++ b/lib/widgets/custom_pin_put/custom_pin_put_state.dart @@ -50,6 +50,9 @@ class CustomPinPutState extends State @override Widget build(BuildContext context) { + // final bool randomize = ref + // .read(prefsChangeNotifierProvider) + // .randomizePIN; return SizedBox( width: widget.width, height: widget.height, @@ -69,6 +72,7 @@ class CustomPinPutState extends State ), Center( child: PinKeyboard( + isRandom: widget.isRandom, customKey: widget.customKey, onNumberKeyPressed: (number) { if (_controller.text.length < widget.fieldsCount) { diff --git a/lib/widgets/custom_pin_put/pin_keyboard.dart b/lib/widgets/custom_pin_put/pin_keyboard.dart index 372dbb538..264c04515 100644 --- a/lib/widgets/custom_pin_put/pin_keyboard.dart +++ b/lib/widgets/custom_pin_put/pin_keyboard.dart @@ -244,6 +244,7 @@ class PinKeyboard extends ConsumerWidget { required this.onNumberKeyPressed, required this.onBackPressed, required this.onSubmitPressed, + required this.isRandom, this.backgroundColor, this.width = 264, this.height = 360, @@ -257,6 +258,7 @@ class PinKeyboard extends ConsumerWidget { final double? width; final double? height; final CustomKey? customKey; + final bool isRandom; void _backHandler() { onBackPressed.call(); @@ -286,8 +288,9 @@ class PinKeyboard extends ConsumerWidget { "0", ]; - // if (ref.read(prefsChangeNotifierProvider).randomizePIN == true) - list.shuffle(); + // final isRandom = ref.read(prefsChangeNotifierProvider).randomizePIN; + + if (isRandom) list.shuffle(); return Container( width: width, From b5dff2f88b7bad6dc108f9d19d7e608609324873 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 1 May 2023 16:47:23 -0600 Subject: [PATCH 14/16] fixed an import typo --- .../security_views/change_pin_view/change_pin_view.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart index 384d7852a..cf848739f 100644 --- a/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart +++ b/lib/pages/settings_views/global_settings_view/security_views/change_pin_view/change_pin_view.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackduo/providers/global/prefs_provider.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart'; From dc135597e686d6415ea38e93ddcc9844dbd7de42 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Tue, 2 May 2023 16:45:34 -0600 Subject: [PATCH 15/16] fixed tests for randomized pin --- test/widget_tests/custom_pin_put_test.dart | 227 ++++++++++++++++++++- 1 file changed, 224 insertions(+), 3 deletions(-) diff --git a/test/widget_tests/custom_pin_put_test.dart b/test/widget_tests/custom_pin_put_test.dart index d3a449865..db0403a20 100644 --- a/test/widget_tests/custom_pin_put_test.dart +++ b/test/widget_tests/custom_pin_put_test.dart @@ -7,9 +7,12 @@ import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart'; import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart'; void main() { - group("CustomPinPut tests", () { + group("CustomPinPut tests, non-random PIN", () { testWidgets("CustomPinPut with 4 fields builds correctly", (tester) async { - const pinPut = CustomPinPut(fieldsCount: 4); + const pinPut = CustomPinPut( + fieldsCount: 4, + isRandom: false, + ); await tester.pumpWidget( MaterialApp( @@ -35,8 +38,12 @@ void main() { bool submittedPinMatches = false; final pinPut = CustomPinPut( fieldsCount: 4, - onSubmit: (pin) => submittedPinMatches = pin == "1234", + onSubmit: (pin) { + submittedPinMatches = pin == "1234"; + print("pin entered: $pin"); + }, useNativeKeyboard: false, + isRandom: false, ); await tester.pumpWidget( @@ -69,6 +76,8 @@ void main() { await tester.tap(find.byWidgetPredicate( (widget) => widget is NumberKey && widget.number == "4")); await tester.pumpAndSettle(); + await tester.tap(find.byType(SubmitKey)); + await tester.pumpAndSettle(); expect(submittedPinMatches, true); }); @@ -79,6 +88,7 @@ void main() { fieldsCount: 4, pinAnimationType: PinAnimationType.fade, controller: controller, + isRandom: false, ); await tester.pumpWidget( @@ -110,6 +120,7 @@ void main() { fieldsCount: 4, pinAnimationType: PinAnimationType.scale, controller: controller, + isRandom: false, ); await tester.pumpWidget( @@ -141,6 +152,7 @@ void main() { fieldsCount: 4, pinAnimationType: PinAnimationType.rotation, controller: controller, + isRandom: false, ); await tester.pumpWidget( @@ -172,6 +184,215 @@ void main() { onNumberKeyPressed: (_) {}, onBackPressed: () {}, onSubmitPressed: () {}, + isRandom: false, + ); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: keyboard, + ), + ), + ); + + expect(find.byType(BackspaceKey), findsOneWidget); + expect(find.byType(NumberKey), findsNWidgets(10)); + expect(find.text("0"), findsOneWidget); + expect(find.text("1"), findsOneWidget); + expect(find.text("2"), findsOneWidget); + expect(find.text("3"), findsOneWidget); + expect(find.text("4"), findsOneWidget); + expect(find.text("5"), findsOneWidget); + expect(find.text("6"), findsOneWidget); + expect(find.text("7"), findsOneWidget); + expect(find.text("8"), findsOneWidget); + expect(find.text("9"), findsOneWidget); + expect(find.byType(SvgPicture), findsOneWidget); + }); + + group("CustomPinPut tests, with random PIN", () { + testWidgets("CustomPinPut with 4 fields builds correctly", (tester) async { + const pinPut = CustomPinPut( + fieldsCount: 4, + isRandom: true, + ); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: const Material( + child: pinPut, + ), + ), + ); + + // expects 5 here. Four + the actual text field text + expect(find.text(""), findsNWidgets(5)); + expect(find.byType(PinKeyboard), findsOneWidget); + expect(find.byType(BackspaceKey), findsOneWidget); + expect(find.byType(NumberKey), findsNWidgets(10)); + }); + + testWidgets("CustomPinPut entering a pin successfully", (tester) async { + bool submittedPinMatches = false; + final pinPut = CustomPinPut( + fieldsCount: 4, + onSubmit: (pin) { + submittedPinMatches = pin == "1234"; + print("pin entered: $pin"); + }, + useNativeKeyboard: false, + isRandom: true, + ); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: pinPut, + ), + ), + ); + + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "1")); + await tester.pumpAndSettle(); + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "2")); + await tester.pumpAndSettle(); + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "6")); + await tester.pumpAndSettle(); + await tester.tap(find.byType(BackspaceKey)); + await tester.pumpAndSettle(); + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "3")); + await tester.pumpAndSettle(); + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "4")); + await tester.pumpAndSettle(); + await tester.tap(find.byType(SubmitKey)); + await tester.pumpAndSettle(); + + expect(submittedPinMatches, true); + }); + + testWidgets("CustomPinPut pin enter fade animation", (tester) async { + final controller = TextEditingController(); + final pinPut = CustomPinPut( + fieldsCount: 4, + pinAnimationType: PinAnimationType.fade, + controller: controller, + isRandom: true, + ); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: pinPut, + ), + ), + ); + + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "1")); + await tester.pumpAndSettle(); + expect(controller.text, "1"); + + await tester.tap(find.byType(BackspaceKey)); + await tester.pumpAndSettle(); + expect(controller.text, ""); + }); + + testWidgets("CustomPinPut pin enter scale animation", (tester) async { + final controller = TextEditingController(); + final pinPut = CustomPinPut( + fieldsCount: 4, + pinAnimationType: PinAnimationType.scale, + controller: controller, + isRandom: true, + ); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: pinPut, + ), + ), + ); + + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "1")); + await tester.pumpAndSettle(); + expect(controller.text, "1"); + + await tester.tap(find.byType(BackspaceKey)); + await tester.pumpAndSettle(); + expect(controller.text, ""); + }); + + testWidgets("CustomPinPut pin enter rotate animation", (tester) async { + final controller = TextEditingController(); + final pinPut = CustomPinPut( + fieldsCount: 4, + pinAnimationType: PinAnimationType.rotation, + controller: controller, + isRandom: true, + ); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: pinPut, + ), + ), + ); + + await tester.tap(find.byWidgetPredicate( + (widget) => widget is NumberKey && widget.number == "1")); + await tester.pumpAndSettle(); + expect(controller.text, "1"); + + await tester.tap(find.byType(BackspaceKey)); + await tester.pumpAndSettle(); + expect(controller.text, ""); + }); + }); + + testWidgets("PinKeyboard builds correctly", (tester) async { + final keyboard = PinKeyboard( + onNumberKeyPressed: (_) {}, + onBackPressed: () {}, + onSubmitPressed: () {}, + isRandom: true, ); await tester.pumpWidget( From aaee3f8e684548c70a1cd06b4a569540529fe516 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 1 May 2023 15:31:15 -0600 Subject: [PATCH 16/16] another pin test fix --- test/widget_tests/custom_pin_put_test.dart | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/test/widget_tests/custom_pin_put_test.dart b/test/widget_tests/custom_pin_put_test.dart index db0403a20..ad472707e 100644 --- a/test/widget_tests/custom_pin_put_test.dart +++ b/test/widget_tests/custom_pin_put_test.dart @@ -8,7 +8,8 @@ import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart'; void main() { group("CustomPinPut tests, non-random PIN", () { - testWidgets("CustomPinPut with 4 fields builds correctly", (tester) async { + testWidgets("CustomPinPut with 4 fields builds correctly, non-random PIN", + (tester) async { const pinPut = CustomPinPut( fieldsCount: 4, isRandom: false, @@ -34,7 +35,8 @@ void main() { expect(find.byType(NumberKey), findsNWidgets(10)); }); - testWidgets("CustomPinPut entering a pin successfully", (tester) async { + testWidgets("CustomPinPut entering a pin successfully, non-random PIN", + (tester) async { bool submittedPinMatches = false; final pinPut = CustomPinPut( fieldsCount: 4, @@ -82,7 +84,8 @@ void main() { expect(submittedPinMatches, true); }); - testWidgets("CustomPinPut pin enter fade animation", (tester) async { + testWidgets("CustomPinPut pin enter fade animation, non-random PIN", + (tester) async { final controller = TextEditingController(); final pinPut = CustomPinPut( fieldsCount: 4, @@ -114,7 +117,8 @@ void main() { expect(controller.text, ""); }); - testWidgets("CustomPinPut pin enter scale animation", (tester) async { + testWidgets("CustomPinPut pin enter scale animation, non-random PIN", + (tester) async { final controller = TextEditingController(); final pinPut = CustomPinPut( fieldsCount: 4, @@ -146,7 +150,8 @@ void main() { expect(controller.text, ""); }); - testWidgets("CustomPinPut pin enter rotate animation", (tester) async { + testWidgets("CustomPinPut pin enter rotate animation, non-random PIN", + (tester) async { final controller = TextEditingController(); final pinPut = CustomPinPut( fieldsCount: 4, @@ -179,7 +184,7 @@ void main() { }); }); - testWidgets("PinKeyboard builds correctly", (tester) async { + testWidgets("PinKeyboard builds correctly, non-random PIN", (tester) async { final keyboard = PinKeyboard( onNumberKeyPressed: (_) {}, onBackPressed: () {}, @@ -201,6 +206,7 @@ void main() { ); expect(find.byType(BackspaceKey), findsOneWidget); + expect(find.byType(SubmitKey), findsOneWidget); expect(find.byType(NumberKey), findsNWidgets(10)); expect(find.text("0"), findsOneWidget); expect(find.text("1"), findsOneWidget); @@ -212,11 +218,12 @@ void main() { expect(find.text("7"), findsOneWidget); expect(find.text("8"), findsOneWidget); expect(find.text("9"), findsOneWidget); - expect(find.byType(SvgPicture), findsOneWidget); + expect(find.byType(SvgPicture), findsNWidgets(2)); }); group("CustomPinPut tests, with random PIN", () { - testWidgets("CustomPinPut with 4 fields builds correctly", (tester) async { + testWidgets("CustomPinPut with 4 fields builds correctly, with random PIN", + (tester) async { const pinPut = CustomPinPut( fieldsCount: 4, isRandom: true, @@ -242,7 +249,8 @@ void main() { expect(find.byType(NumberKey), findsNWidgets(10)); }); - testWidgets("CustomPinPut entering a pin successfully", (tester) async { + testWidgets("CustomPinPut entering a pin successfully, with random PIN", + (tester) async { bool submittedPinMatches = false; final pinPut = CustomPinPut( fieldsCount: 4, @@ -290,7 +298,8 @@ void main() { expect(submittedPinMatches, true); }); - testWidgets("CustomPinPut pin enter fade animation", (tester) async { + testWidgets("CustomPinPut pin enter fade animation, with random PIN", + (tester) async { final controller = TextEditingController(); final pinPut = CustomPinPut( fieldsCount: 4, @@ -322,7 +331,8 @@ void main() { expect(controller.text, ""); }); - testWidgets("CustomPinPut pin enter scale animation", (tester) async { + testWidgets("CustomPinPut pin enter scale animation, with random PIN", + (tester) async { final controller = TextEditingController(); final pinPut = CustomPinPut( fieldsCount: 4, @@ -354,7 +364,8 @@ void main() { expect(controller.text, ""); }); - testWidgets("CustomPinPut pin enter rotate animation", (tester) async { + testWidgets("CustomPinPut pin enter rotate animation, with random PIN", + (tester) async { final controller = TextEditingController(); final pinPut = CustomPinPut( fieldsCount: 4, @@ -387,7 +398,7 @@ void main() { }); }); - testWidgets("PinKeyboard builds correctly", (tester) async { + testWidgets("PinKeyboard builds correctly, with random PIN", (tester) async { final keyboard = PinKeyboard( onNumberKeyPressed: (_) {}, onBackPressed: () {}, @@ -409,6 +420,7 @@ void main() { ); expect(find.byType(BackspaceKey), findsOneWidget); + expect(find.byType(SubmitKey), findsOneWidget); expect(find.byType(NumberKey), findsNWidgets(10)); expect(find.text("0"), findsOneWidget); expect(find.text("1"), findsOneWidget); @@ -420,6 +432,6 @@ void main() { expect(find.text("7"), findsOneWidget); expect(find.text("8"), findsOneWidget); expect(find.text("9"), findsOneWidget); - expect(find.byType(SvgPicture), findsOneWidget); + expect(find.byType(SvgPicture), findsNWidgets(2)); }); }