From 6522d36c7bfd7f5c793513c629ee368ab2dc24b3 Mon Sep 17 00:00:00 2001 From: detherminal Date: Thu, 27 Apr 2023 21:14:04 +0300 Subject: [PATCH 01/20] feat: wrap with semantic widget for talkback --- .../add_wallet_view/add_wallet_view.dart | 92 ++++++++-------- .../name_your_wallet_view.dart | 18 +++- .../new_wallet_recovery_phrase_view.dart | 1 + ...w_wallet_recovery_phrase_warning_view.dart | 1 + .../restore_wallet_view.dart | 2 + lib/pages/exchange_view/exchange_form.dart | 48 +++++---- lib/pages/home_view/home_view.dart | 2 + lib/pages/pinpad_views/lock_screen_view.dart | 7 ++ lib/pages/receive_view/receive_view.dart | 1 + lib/pages/send_view/send_view.dart | 4 + .../sub_widgets/wallet_refresh_button.dart | 102 +++++++++--------- lib/pages/wallet_view/wallet_view.dart | 3 + .../custom_buttons/app_bar_icon_button.dart | 63 ++++++----- lib/widgets/custom_pin_put/pin_keyboard.dart | 64 ++++++----- lib/widgets/textfield_icon_button.dart | 32 +++--- 15 files changed, 253 insertions(+), 187 deletions(-) diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart index 3f78bda33..5c8c800be 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart @@ -333,53 +333,57 @@ class _AddWalletViewState extends ConsumerState { borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), - child: TextField( - autofocus: isDesktop, - autocorrect: !isDesktop, - enableSuggestions: !isDesktop, - controller: _searchFieldController, - focusNode: _searchFocusNode, - onChanged: (value) => setState(() => _searchTerm = value), - style: STextStyles.field(context), - decoration: standardInputDecoration( - "Search", - _searchFocusNode, - context, - desktopMed: isDesktop, - ).copyWith( - prefixIcon: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 16, + child: Semantics( + label: "Search Text Field. Inputs Text To Search In Wallets.", + excludeSemantics: true, + child: TextField( + autofocus: isDesktop, + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: _searchFieldController, + focusNode: _searchFocusNode, + onChanged: (value) => setState(() => _searchTerm = value), + style: STextStyles.field(context), + decoration: standardInputDecoration( + "Search", + _searchFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + prefixIcon: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 16, + ), + child: SvgPicture.asset( + Assets.svg.search, + width: 16, + height: 16, + ), ), - child: SvgPicture.asset( - Assets.svg.search, - width: 16, - height: 16, - ), - ), - suffixIcon: _searchFieldController.text.isNotEmpty - ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - _searchFieldController.text = ""; - _searchTerm = ""; - }); - }, - ), - ], + suffixIcon: _searchFieldController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _searchFieldController.text = ""; + _searchTerm = ""; + }); + }, ), - ), - ) - : null, + ], + ), + ), + ) + : null, + ), ), - ), + ) ), const SizedBox( height: 10, diff --git a/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart b/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart index 6a3f28318..805355071 100644 --- a/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart +++ b/lib/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart @@ -238,14 +238,22 @@ class _NameYourWalletViewState extends ConsumerState { TextFieldIconButton( key: const Key("genRandomWalletNameButtonKey"), child: _showDiceIcon - ? DiceIcon( - width: isDesktop ? 20 : 17, - height: isDesktop ? 20 : 17, - ) - : XIcon( + ? Semantics( + label: "Generate Random Wallet Name Button. Generates A Random Name For Wallet.", + excludeSemantics: true, + child: DiceIcon( + width: isDesktop ? 20 : 17, + height: isDesktop ? 20 : 17, + ), + ) + : Semantics( + label: "Generate Random Wallet Name Button. Generates A Random Name For Wallet.", + excludeSemantics: true, + child: XIcon( width: isDesktop ? 21 : 18, height: isDesktop ? 21 : 18, ), + ), onTap: () async { if (_showDiceIcon) { textEditingController.text = diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart index 0b358753d..f6a3c74a2 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart @@ -140,6 +140,7 @@ class _NewWalletRecoveryPhraseViewState child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Copy Button. Copies The Recovery Phrase To Clipboard.", color: Theme.of(context) .extension()! .background, diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart index 262d882d4..85e1c1063 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart @@ -87,6 +87,7 @@ class _NewWalletRecoveryPhraseWarningViewState right: 10, ), child: AppBarIconButton( + label: "Question Button. Openes A Dialog For Recovery Phrase Explanation.", icon: SvgPicture.asset( Assets.svg.circleQuestion, width: 20, diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index ec0d5bc2c..ca3a4f394 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -636,6 +636,7 @@ class _RestoreWalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "View QR Code Button. Opens Camera To Scan QR Code For Restoring Wallet.", key: const Key("restoreWalletViewQrCodeButton"), size: 36, shadows: const [], @@ -662,6 +663,7 @@ class _RestoreWalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Paste Button. Pastes From Clipboard For Restoring Wallet.", key: const Key("restoreWalletPasteButton"), size: 36, shadows: const [], diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index 11b4f4e51..389bedd05 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -781,31 +781,35 @@ class _ExchangeFormState extends ConsumerState { cursor: SystemMouseCursors.click, child: child, ), - child: RoundedContainer( - padding: isDesktop - ? const EdgeInsets.all(6) - : const EdgeInsets.all(2), - color: Theme.of(context) - .extension()! - .buttonBackSecondary, - radiusMultiplier: 0.75, - child: GestureDetector( - onTap: () async { - await _swap(); - }, - child: Padding( - padding: const EdgeInsets.all(4), - child: SvgPicture.asset( - Assets.svg.swap, - width: 20, - height: 20, - color: Theme.of(context) - .extension()! - .accentColorDark, + child: Semantics( + label: "Swap Button. Reverse The Exchange Currencies.", + excludeSemantics: true, + child: RoundedContainer( + padding: isDesktop + ? const EdgeInsets.all(6) + : const EdgeInsets.all(2), + color: Theme.of(context) + .extension()! + .buttonBackSecondary, + radiusMultiplier: 0.75, + child: GestureDetector( + onTap: () async { + await _swap(); + }, + child: Padding( + padding: const EdgeInsets.all(4), + child: SvgPicture.asset( + Assets.svg.swap, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .accentColorDark, + ), ), ), ), - ), + ) ), ], ), diff --git a/lib/pages/home_view/home_view.dart b/lib/pages/home_view/home_view.dart index ea9b29558..e9c839452 100644 --- a/lib/pages/home_view/home_view.dart +++ b/lib/pages/home_view/home_view.dart @@ -191,6 +191,7 @@ class _HomeViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Notifications Button. Takes To Notifications Page.", key: const Key("walletsViewAlertsButton"), size: 36, shadows: const [], @@ -254,6 +255,7 @@ class _HomeViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Settings Button. Takes To Settings Page.", key: const Key("walletsViewSettingsButton"), size: 36, shadows: const [], diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index 7e9f9b77f..f99f3ddfc 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -39,6 +39,7 @@ class LockscreenView extends ConsumerStatefulWidget { this.routeOnSuccessArguments, this.biometrics = const Biometrics(), this.onSuccess, + this.customKeyLabel = "Button", }) : super(key: key); static const String routeName = "/lockscreen"; @@ -53,6 +54,8 @@ class LockscreenView extends ConsumerStatefulWidget { final String biometricsCancelButtonString; final Biometrics biometrics; final VoidCallback? onSuccess; + final String customKeyLabel; + @override ConsumerState createState() => _LockscreenViewState(); @@ -245,6 +248,10 @@ class _LockscreenViewState extends ConsumerState { ), CustomPinPut( customKey: CustomKey( + customKeyLabel: Platform.isIOS + ? "Face ID Button. Checks Face ID." + : "Fingerprint Button. Checks Fingerprint." + , onPressed: _checkUseBiometrics, iconAssetName: Platform.isIOS ? Assets.svg.faceId diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart index 87587aa46..e46badfd3 100644 --- a/lib/pages/receive_view/receive_view.dart +++ b/lib/pages/receive_view/receive_view.dart @@ -143,6 +143,7 @@ class _ReceiveViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Adress List Pop-up Button. Opens A Pop-up For Adress List Button.", key: const Key("walletNetworkSettingsAddNewNodeViewButton"), size: 36, shadows: const [], diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 83c40943f..12896b87a 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -980,6 +980,7 @@ class _SendViewState extends ConsumerState { children: [ _addressToggleFlag ? TextFieldIconButton( + label: "Clear Button. Clears The Address Field Input.", key: const Key( "sendViewClearAddressFieldButtonKey"), onTap: () { @@ -996,6 +997,7 @@ class _SendViewState extends ConsumerState { child: const XIcon(), ) : TextFieldIconButton( + label: "Paste Button. Pastes From Clipboard To Address Field Input.", key: const Key( "sendViewPasteAddressFieldButtonKey"), onTap: () async { @@ -1045,6 +1047,7 @@ class _SendViewState extends ConsumerState { ), if (sendToController.text.isEmpty) TextFieldIconButton( + label: "Address Book Button. Opens Address Book For Address Field.", key: const Key( "sendViewAddressBookButtonKey"), onTap: () { @@ -1057,6 +1060,7 @@ class _SendViewState extends ConsumerState { ), if (sendToController.text.isEmpty) TextFieldIconButton( + label: "Scan QR Button. Opens Camera For Scanning QR Code.", key: const Key( "sendViewScanQrButtonKey"), onTap: () async { diff --git a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart index ac82fc98c..b1541617c 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart @@ -117,58 +117,62 @@ class _RefreshButtonState extends ConsumerState return SizedBox( height: isDesktop ? 22 : 36, width: isDesktop ? 22 : 36, - child: MaterialButton( - color: isDesktop - ? Theme.of(context).extension()!.buttonBackSecondary - : null, - splashColor: Theme.of(context).extension()!.highlight, - onPressed: () { - if (widget.tokenContractAddress == null) { - final managerProvider = ref - .read(walletsChangeNotifierProvider) - .getManagerProvider(widget.walletId); - final isRefreshing = ref.read(managerProvider).isRefreshing; - if (!isRefreshing) { - _spinController?.repeat(); - ref - .read(managerProvider) - .refresh() - .then((_) => _spinController?.stop()); + child: Semantics( + label: "Refresh Button. Refreshes The Values In Page.", + excludeSemantics: true, + child: MaterialButton( + color: isDesktop + ? Theme.of(context).extension()!.buttonBackSecondary + : null, + splashColor: Theme.of(context).extension()!.highlight, + onPressed: () { + if (widget.tokenContractAddress == null) { + final managerProvider = ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(widget.walletId); + final isRefreshing = ref.read(managerProvider).isRefreshing; + if (!isRefreshing) { + _spinController?.repeat(); + ref + .read(managerProvider) + .refresh() + .then((_) => _spinController?.stop()); + } + } else { + if (!ref.read(tokenServiceProvider)!.isRefreshing) { + ref.read(tokenServiceProvider)!.refresh(); + } } - } else { - if (!ref.read(tokenServiceProvider)!.isRefreshing) { - ref.read(tokenServiceProvider)!.refresh(); - } - } - }, - elevation: 0, - highlightElevation: 0, - hoverElevation: 0, - padding: EdgeInsets.zero, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, + }, + elevation: 0, + highlightElevation: 0, + hoverElevation: 0, + padding: EdgeInsets.zero, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: RotationTransition( + turns: _spinAnimation, + child: SvgPicture.asset( + Assets.svg.arrowRotate, + width: isDesktop ? 12 : 24, + height: isDesktop ? 12 : 24, + color: widget.overrideIconColor != null + ? widget.overrideIconColor! + : isDesktop + ? Theme.of(context) + .extension()! + .textFieldDefaultSearchIconRight + : Theme.of(context) + .extension()! + .textFavoriteCard, + ), ), ), - child: RotationTransition( - turns: _spinAnimation, - child: SvgPicture.asset( - Assets.svg.arrowRotate, - width: isDesktop ? 12 : 24, - height: isDesktop ? 12 : 24, - color: widget.overrideIconColor != null - ? widget.overrideIconColor! - : isDesktop - ? Theme.of(context) - .extension()! - .textFieldDefaultSearchIconRight - : Theme.of(context) - .extension()! - .textFavoriteCard, - ), - ), - ), + ) ); } } diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 6e39edd9b..7d43acfbd 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -474,6 +474,7 @@ class _WalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Network Button. Takes To Network Status Page.", key: const Key("walletViewRadioButton"), size: 36, shadows: const [], @@ -503,6 +504,7 @@ class _WalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Notifications Button. Takes To Notifications Page.", key: const Key("walletViewAlertsButton"), size: 36, shadows: const [], @@ -570,6 +572,7 @@ class _WalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( + label: "Settings Button. Takes To Wallet Settings Page.", key: const Key("walletViewSettingsButton"), size: 36, shadows: const [], diff --git a/lib/widgets/custom_buttons/app_bar_icon_button.dart b/lib/widgets/custom_buttons/app_bar_icon_button.dart index 9edc1ca5f..7c3980bb8 100644 --- a/lib/widgets/custom_buttons/app_bar_icon_button.dart +++ b/lib/widgets/custom_buttons/app_bar_icon_button.dart @@ -13,6 +13,7 @@ class AppBarIconButton extends StatelessWidget { // this.circularBorderRadius = 10.0, this.size = 36.0, this.shadows = const [], + this.label = "Button", }) : super(key: key); final Widget icon; @@ -21,6 +22,7 @@ class AppBarIconButton extends StatelessWidget { // final double circularBorderRadius; final double size; final List shadows; + final String label; @override Widget build(BuildContext context) { @@ -32,16 +34,20 @@ class AppBarIconButton extends StatelessWidget { color: color ?? Theme.of(context).extension()!.background, boxShadow: shadows, ), - child: MaterialButton( - splashColor: Theme.of(context).extension()!.highlight, - padding: EdgeInsets.zero, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(1000), + child: Semantics( + excludeSemantics: true, + label: label, + child: MaterialButton( + splashColor: Theme.of(context).extension()!.highlight, + padding: EdgeInsets.zero, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(1000), + ), + onPressed: onPressed, + child: icon, ), - onPressed: onPressed, - child: icon, - ), + ) ); } } @@ -53,12 +59,14 @@ class AppBarBackButton extends StatelessWidget { this.isCompact = false, this.size, this.iconSize, + this.label = "Back Button. Takes Back To Previous Page.", }) : super(key: key); final VoidCallback? onPressed; final bool isCompact; final double? size; final double? iconSize; + final String label; @override Widget build(BuildContext context) { @@ -71,24 +79,25 @@ class AppBarBackButton extends StatelessWidget { ) : const EdgeInsets.all(10), child: AppBarIconButton( - size: size ?? - (isDesktop - ? isCompact - ? 42 - : 56 - : 32), - color: isDesktop - ? Theme.of(context).extension()!.textFieldDefaultBG - : Theme.of(context).extension()!.background, - shadows: const [], - icon: SvgPicture.asset( - Assets.svg.arrowLeft, - width: iconSize ?? (isCompact ? 18 : 24), - height: iconSize ?? (isCompact ? 18 : 24), - color: Theme.of(context).extension()!.topNavIconPrimary, - ), - onPressed: onPressed ?? Navigator.of(context).pop, - ), + label: label, + size: size ?? + (isDesktop + ? isCompact + ? 42 + : 56 + : 32), + color: isDesktop + ? Theme.of(context).extension()!.textFieldDefaultBG + : Theme.of(context).extension()!.background, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: iconSize ?? (isCompact ? 18 : 24), + height: iconSize ?? (isCompact ? 18 : 24), + color: Theme.of(context).extension()!.topNavIconPrimary, + ), + onPressed: onPressed ?? Navigator.of(context).pop, + ) ); } } diff --git a/lib/widgets/custom_pin_put/pin_keyboard.dart b/lib/widgets/custom_pin_put/pin_keyboard.dart index a578f9c17..86c5bddd3 100644 --- a/lib/widgets/custom_pin_put/pin_keyboard.dart +++ b/lib/widgets/custom_pin_put/pin_keyboard.dart @@ -139,15 +139,19 @@ class _BackspaceKeyState extends State { } }); }, - child: Center( - child: SvgPicture.asset( - Assets.svg.delete, - width: 20, - height: 20, - color: - Theme.of(context).extension()!.numpadTextDefault, + child: Semantics( + label: "Backspace Button. Deletes The Last Digit.", + excludeSemantics: true, + child: Center( + child: SvgPicture.asset( + Assets.svg.delete, + width: 20, + height: 20, + color: + Theme.of(context).extension()!.numpadTextDefault, + ), ), - ), + ) ), ); } @@ -197,10 +201,12 @@ class CustomKey extends StatelessWidget { Key? key, required this.onPressed, this.iconAssetName, + this.customKeyLabel = "Button", }) : super(key: key); final VoidCallback onPressed; final String? iconAssetName; + final String customKeyLabel; @override Widget build(BuildContext context) { @@ -212,26 +218,30 @@ class CustomKey extends StatelessWidget { color: Theme.of(context).extension()!.numpadBackDefault, shadows: const [], ), - child: MaterialButton( - // splashColor: Theme.of(context).extension()!.highlight, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const StadiumBorder(), - onPressed: () { - onPressed.call(); - }, - child: Center( - child: iconAssetName == null - ? null - : SvgPicture.asset( - iconAssetName!, - width: 20, - height: 20, - color: Theme.of(context) - .extension()! - .numpadTextDefault, - ), + child: Semantics( + label: customKeyLabel, + excludeSemantics: true, + child: MaterialButton( + // splashColor: Theme.of(context).extension()!.highlight, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const StadiumBorder(), + onPressed: () { + onPressed.call(); + }, + child: Center( + child: iconAssetName == null + ? null + : SvgPicture.asset( + iconAssetName!, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .numpadTextDefault, + ), + ), ), - ), + ) ); } } diff --git a/lib/widgets/textfield_icon_button.dart b/lib/widgets/textfield_icon_button.dart index 9375c6f31..9be08ef56 100644 --- a/lib/widgets/textfield_icon_button.dart +++ b/lib/widgets/textfield_icon_button.dart @@ -8,6 +8,7 @@ class TextFieldIconButton extends StatefulWidget { this.onTap, required this.child, this.color = Colors.transparent, + this.label = "Button", }) : super(key: key); final double width; @@ -15,6 +16,7 @@ class TextFieldIconButton extends StatefulWidget { final VoidCallback? onTap; final Widget child; final Color color; + final String label; @override State createState() => _TextFieldIconButtonState(); @@ -36,21 +38,25 @@ class _TextFieldIconButtonState extends State { width: widget.width, child: ClipRRect( borderRadius: BorderRadius.circular(100), - child: RawMaterialButton( - constraints: BoxConstraints( - minWidth: widget.width, - minHeight: widget.height, - ), - onPressed: onTap, - child: Container( - width: widget.width, - height: widget.height, - color: widget.color, - child: Center( - child: widget.child, + child: Semantics( + label: widget.label, + excludeSemantics: true, + child: RawMaterialButton( + constraints: BoxConstraints( + minWidth: widget.width, + minHeight: widget.height, + ), + onPressed: onTap, + child: Container( + width: widget.width, + height: widget.height, + color: widget.color, + child: Center( + child: widget.child, + ), ), ), - ), + ) ), ); } From 4c145e14128d62a5b9a4d34306c883d1d77668db Mon Sep 17 00:00:00 2001 From: detherminal <76167420+detherminal@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:31:54 +0300 Subject: [PATCH 02/20] feat: wrap with semantic widget for talkback --- .../sub_widgets/wallet_refresh_button.dart | 98 ++++++++++--------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart index a4b6a34de..06985cd7c 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart @@ -99,56 +99,60 @@ class _RefreshButtonState extends ConsumerState { return SizedBox( height: isDesktop ? 22 : 36, width: isDesktop ? 22 : 36, - child: MaterialButton( - color: isDesktop - ? Theme.of(context).extension()!.buttonBackSecondary - : null, - splashColor: Theme.of(context).extension()!.highlight, - onPressed: () { - if (widget.tokenContractAddress == null) { - final managerProvider = ref - .read(walletsChangeNotifierProvider) - .getManagerProvider(widget.walletId); - final isRefreshing = ref.read(managerProvider).isRefreshing; - if (!isRefreshing) { - _spinController.repeat?.call(); - ref - .read(managerProvider) - .refresh() - .then((_) => _spinController.stop?.call()); + child: Semantics( + label: "Refresh Button. Refreshes The Values In Summary.", + excludeSemantics: true, + child: MaterialButton( + color: isDesktop + ? Theme.of(context).extension()!.buttonBackSecondary + : null, + splashColor: Theme.of(context).extension()!.highlight, + onPressed: () { + if (widget.tokenContractAddress == null) { + final managerProvider = ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(widget.walletId); + final isRefreshing = ref.read(managerProvider).isRefreshing; + if (!isRefreshing) { + _spinController.repeat?.call(); + ref + .read(managerProvider) + .refresh() + .then((_) => _spinController.stop?.call()); + } + } else { + if (!ref.read(tokenServiceProvider)!.isRefreshing) { + ref.read(tokenServiceProvider)!.refresh(); + } } - } else { - if (!ref.read(tokenServiceProvider)!.isRefreshing) { - ref.read(tokenServiceProvider)!.refresh(); - } - } - }, - elevation: 0, - highlightElevation: 0, - hoverElevation: 0, - padding: EdgeInsets.zero, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, + }, + elevation: 0, + highlightElevation: 0, + hoverElevation: 0, + padding: EdgeInsets.zero, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: RotatingArrows( + spinByDefault: widget.initialSyncStatus == WalletSyncStatus.syncing, + width: isDesktop ? 12 : 24, + height: isDesktop ? 12 : 24, + controller: _spinController, + color: widget.overrideIconColor != null + ? widget.overrideIconColor! + : isDesktop + ? Theme.of(context) + .extension()! + .textFieldDefaultSearchIconRight + : Theme.of(context) + .extension()! + .textFavoriteCard, ), ), - child: RotatingArrows( - spinByDefault: widget.initialSyncStatus == WalletSyncStatus.syncing, - width: isDesktop ? 12 : 24, - height: isDesktop ? 12 : 24, - controller: _spinController, - color: widget.overrideIconColor != null - ? widget.overrideIconColor! - : isDesktop - ? Theme.of(context) - .extension()! - .textFieldDefaultSearchIconRight - : Theme.of(context) - .extension()! - .textFavoriteCard, - ), - ), + ) ); } } From 474f411924b4f1ccdc46a853064bf37b9fd67f01 Mon Sep 17 00:00:00 2001 From: Diego Salazar Date: Thu, 27 Apr 2023 18:10:43 -0600 Subject: [PATCH 03/20] Bump version (1.7.4, build 165) --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d84d0d15d..96d1ae623 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Stack Wallet # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.7.4+164 +version: 1.7.4+165 environment: sdk: ">=2.17.0 <3.0.0" From cb8de25e08af9b638c3d872b6d0f99b3a6d9b5e9 Mon Sep 17 00:00:00 2001 From: detherminal <76167420+detherminal@users.noreply.github.com> Date: Fri, 28 Apr 2023 21:21:30 +0300 Subject: [PATCH 04/20] feat: rename label to semanticsLabel and correct misspelling --- .../new_wallet_recovery_phrase_view.dart | 2 +- .../new_wallet_recovery_phrase_warning_view.dart | 2 +- .../restore_wallet_view/restore_wallet_view.dart | 4 ++-- lib/pages/home_view/home_view.dart | 4 ++-- lib/pages/pinpad_views/lock_screen_view.dart | 2 +- lib/pages/receive_view/receive_view.dart | 2 +- lib/pages/send_view/send_view.dart | 8 ++++---- lib/pages/wallet_view/wallet_view.dart | 6 +++--- lib/widgets/custom_buttons/app_bar_icon_button.dart | 12 ++++++------ lib/widgets/custom_pin_put/pin_keyboard.dart | 6 +++--- lib/widgets/textfield_icon_button.dart | 6 +++--- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart index f6a3c74a2..5050324ba 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart @@ -140,7 +140,7 @@ class _NewWalletRecoveryPhraseViewState child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Copy Button. Copies The Recovery Phrase To Clipboard.", + semanticsLabel: "Copy Button. Copies The Recovery Phrase To Clipboard.", color: Theme.of(context) .extension()! .background, diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart index 85e1c1063..50c8bc1eb 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart @@ -87,7 +87,7 @@ class _NewWalletRecoveryPhraseWarningViewState right: 10, ), child: AppBarIconButton( - label: "Question Button. Openes A Dialog For Recovery Phrase Explanation.", + semanticsLabel: "Question Button. Opens A Dialog For Recovery Phrase Explanation.", icon: SvgPicture.asset( Assets.svg.circleQuestion, width: 20, diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index ca3a4f394..7c7ae4523 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -636,7 +636,7 @@ class _RestoreWalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "View QR Code Button. Opens Camera To Scan QR Code For Restoring Wallet.", + semanticsLabel: "View QR Code Button. Opens Camera To Scan QR Code For Restoring Wallet.", key: const Key("restoreWalletViewQrCodeButton"), size: 36, shadows: const [], @@ -663,7 +663,7 @@ class _RestoreWalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Paste Button. Pastes From Clipboard For Restoring Wallet.", + semanticsLabel: "Paste Button. Pastes From Clipboard For Restoring Wallet.", key: const Key("restoreWalletPasteButton"), size: 36, shadows: const [], diff --git a/lib/pages/home_view/home_view.dart b/lib/pages/home_view/home_view.dart index e9c839452..9e8f7fde1 100644 --- a/lib/pages/home_view/home_view.dart +++ b/lib/pages/home_view/home_view.dart @@ -191,7 +191,7 @@ class _HomeViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Notifications Button. Takes To Notifications Page.", + semanticsLabel: "Notifications Button. Takes To Notifications Page.", key: const Key("walletsViewAlertsButton"), size: 36, shadows: const [], @@ -255,7 +255,7 @@ class _HomeViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Settings Button. Takes To Settings Page.", + semanticsLabel: "Settings Button. Takes To Settings Page.", key: const Key("walletsViewSettingsButton"), size: 36, shadows: const [], diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index f99f3ddfc..f0978bd21 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -248,7 +248,7 @@ class _LockscreenViewState extends ConsumerState { ), CustomPinPut( customKey: CustomKey( - customKeyLabel: Platform.isIOS + semanticsLabel: Platform.isIOS ? "Face ID Button. Checks Face ID." : "Fingerprint Button. Checks Fingerprint." , diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart index b74dc5a33..9800c9c38 100644 --- a/lib/pages/receive_view/receive_view.dart +++ b/lib/pages/receive_view/receive_view.dart @@ -143,7 +143,7 @@ class _ReceiveViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Adress List Pop-up Button. Opens A Pop-up For Adress List Button.", + semanticsLabel: "Address List Pop-up Button. Opens A Pop-up For Adress List Button.", key: const Key("walletNetworkSettingsAddNewNodeViewButton"), size: 36, shadows: const [], diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 479c931ae..6f09a647f 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -981,7 +981,7 @@ class _SendViewState extends ConsumerState { children: [ _addressToggleFlag ? TextFieldIconButton( - label: "Clear Button. Clears The Address Field Input.", + semanticsLabel: "Clear Button. Clears The Address Field Input.", key: const Key( "sendViewClearAddressFieldButtonKey"), onTap: () { @@ -998,7 +998,7 @@ class _SendViewState extends ConsumerState { child: const XIcon(), ) : TextFieldIconButton( - label: "Paste Button. Pastes From Clipboard To Address Field Input.", + semanticsLabel: "Paste Button. Pastes From Clipboard To Address Field Input.", key: const Key( "sendViewPasteAddressFieldButtonKey"), onTap: () async { @@ -1048,7 +1048,7 @@ class _SendViewState extends ConsumerState { ), if (sendToController.text.isEmpty) TextFieldIconButton( - label: "Address Book Button. Opens Address Book For Address Field.", + semanticsLabel: "Address Book Button. Opens Address Book For Address Field.", key: const Key( "sendViewAddressBookButtonKey"), onTap: () { @@ -1061,7 +1061,7 @@ class _SendViewState extends ConsumerState { ), if (sendToController.text.isEmpty) TextFieldIconButton( - label: "Scan QR Button. Opens Camera For Scanning QR Code.", + semanticsLabel: "Scan QR Button. Opens Camera For Scanning QR Code.", key: const Key( "sendViewScanQrButtonKey"), onTap: () async { diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 649464722..de7775629 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -473,7 +473,7 @@ class _WalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Network Button. Takes To Network Status Page.", + semanticsLabel: "Network Button. Takes To Network Status Page.", key: const Key("walletViewRadioButton"), size: 36, shadows: const [], @@ -503,7 +503,7 @@ class _WalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Notifications Button. Takes To Notifications Page.", + semanticsLabel: "Notifications Button. Takes To Notifications Page.", key: const Key("walletViewAlertsButton"), size: 36, shadows: const [], @@ -571,7 +571,7 @@ class _WalletViewState extends ConsumerState { child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( - label: "Settings Button. Takes To Wallet Settings Page.", + semanticsLabel: "Settings Button. Takes To Wallet Settings Page.", key: const Key("walletViewSettingsButton"), size: 36, shadows: const [], diff --git a/lib/widgets/custom_buttons/app_bar_icon_button.dart b/lib/widgets/custom_buttons/app_bar_icon_button.dart index 7c3980bb8..71d8910d0 100644 --- a/lib/widgets/custom_buttons/app_bar_icon_button.dart +++ b/lib/widgets/custom_buttons/app_bar_icon_button.dart @@ -13,7 +13,7 @@ class AppBarIconButton extends StatelessWidget { // this.circularBorderRadius = 10.0, this.size = 36.0, this.shadows = const [], - this.label = "Button", + this.semanticsLabel = "Button", }) : super(key: key); final Widget icon; @@ -22,7 +22,7 @@ class AppBarIconButton extends StatelessWidget { // final double circularBorderRadius; final double size; final List shadows; - final String label; + final String semanticsLabel; @override Widget build(BuildContext context) { @@ -36,7 +36,7 @@ class AppBarIconButton extends StatelessWidget { ), child: Semantics( excludeSemantics: true, - label: label, + label: semanticsLabel, child: MaterialButton( splashColor: Theme.of(context).extension()!.highlight, padding: EdgeInsets.zero, @@ -59,14 +59,14 @@ class AppBarBackButton extends StatelessWidget { this.isCompact = false, this.size, this.iconSize, - this.label = "Back Button. Takes Back To Previous Page.", + this.semanticsLabel = "Back Button. Takes Back To Previous Page.", }) : super(key: key); final VoidCallback? onPressed; final bool isCompact; final double? size; final double? iconSize; - final String label; + final String semanticsLabel; @override Widget build(BuildContext context) { @@ -79,7 +79,7 @@ class AppBarBackButton extends StatelessWidget { ) : const EdgeInsets.all(10), child: AppBarIconButton( - label: label, + semanticsLabel: semanticsLabel, size: size ?? (isDesktop ? isCompact diff --git a/lib/widgets/custom_pin_put/pin_keyboard.dart b/lib/widgets/custom_pin_put/pin_keyboard.dart index 954bd257c..fc1c58ea6 100644 --- a/lib/widgets/custom_pin_put/pin_keyboard.dart +++ b/lib/widgets/custom_pin_put/pin_keyboard.dart @@ -202,12 +202,12 @@ class CustomKey extends StatelessWidget { Key? key, required this.onPressed, this.iconAssetName, - this.customKeyLabel = "Button", + this.semanticsLabel = "Button", }) : super(key: key); final VoidCallback onPressed; final String? iconAssetName; - final String customKeyLabel; + final String semanticsLabel; @override Widget build(BuildContext context) { @@ -220,7 +220,7 @@ class CustomKey extends StatelessWidget { shadows: const [], ), child: Semantics( - label: customKeyLabel, + label: semanticsLabel, excludeSemantics: true, child: MaterialButton( // splashColor: Theme.of(context).extension()!.highlight, diff --git a/lib/widgets/textfield_icon_button.dart b/lib/widgets/textfield_icon_button.dart index 9be08ef56..df231a781 100644 --- a/lib/widgets/textfield_icon_button.dart +++ b/lib/widgets/textfield_icon_button.dart @@ -8,7 +8,7 @@ class TextFieldIconButton extends StatefulWidget { this.onTap, required this.child, this.color = Colors.transparent, - this.label = "Button", + this.semanticsLabel = "Button", }) : super(key: key); final double width; @@ -16,7 +16,7 @@ class TextFieldIconButton extends StatefulWidget { final VoidCallback? onTap; final Widget child; final Color color; - final String label; + final String semanticsLabel; @override State createState() => _TextFieldIconButtonState(); @@ -39,7 +39,7 @@ class _TextFieldIconButtonState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(100), child: Semantics( - label: widget.label, + label: widget.semanticsLabel, excludeSemantics: true, child: RawMaterialButton( constraints: BoxConstraints( From 808927a0d14a98b9dca569a425194e72b1ae1e85 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 1 May 2023 08:51:55 -0600 Subject: [PATCH 05/20] 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 06/20] 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 07/20] 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 08/20] 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 09/20] 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 10/20] 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 11/20] 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 12/20] 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 13/20] 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 14/20] 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 15/20] 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 16/20] 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 17/20] 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 18/20] 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 19/20] 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 20/20] 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)); }); }