From 9229aa4ce3990992235d21cf40083e2575cb10fc Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 9 Sep 2022 10:51:34 -0600 Subject: [PATCH] add load into selected wallet immediately on startup setting --- lib/main.dart | 9 +- lib/pages/pinpad_views/lock_screen_view.dart | 38 ++- .../global_settings_view.dart | 13 + .../startup_preferences_view.dart | 269 ++++++++++++++++++ .../startup_wallet_selection_view.dart | 197 +++++++++++++ lib/route_generator.dart | 14 + lib/utilities/prefs.dart | 61 +++- 7 files changed, 583 insertions(+), 18 deletions(-) create mode 100644 lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart create mode 100644 lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart diff --git a/lib/main.dart b/lib/main.dart index 10067ac19..5aeb89508 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -589,9 +589,16 @@ class _MaterialAppWithThemeState extends ConsumerState _loadChangeNowData(); } - return const LockscreenView( + String? startupWalletId; + if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) { + startupWalletId = + ref.read(prefsChangeNotifierProvider).startupWalletId; + } + + return LockscreenView( isInitialAppLogin: true, routeOnSuccess: HomeView.routeName, + routeOnSuccessArguments: startupWalletId, biometricsAuthenticationTitle: "Unlock Stack", biometricsLocalizedReason: "Unlock your stack wallet using biometrics", diff --git a/lib/pages/pinpad_views/lock_screen_view.dart b/lib/pages/pinpad_views/lock_screen_view.dart index b22cebb9b..03dd5bb26 100644 --- a/lib/pages/pinpad_views/lock_screen_view.dart +++ b/lib/pages/pinpad_views/lock_screen_view.dart @@ -1,9 +1,14 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/home_view/home_view.dart'; +import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; // import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; // import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/biometrics.dart'; @@ -15,6 +20,7 @@ import 'package:stackwallet/utilities/text_styles.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/shake/shake.dart'; +import 'package:tuple/tuple.dart'; class LockscreenView extends ConsumerStatefulWidget { const LockscreenView({ @@ -75,10 +81,20 @@ class _LockscreenViewState extends ConsumerState { if (widget.popOnSuccess) { Navigator.of(context).pop(widget.routeOnSuccessArguments); } else { - Navigator.of(context).pushReplacementNamed( + unawaited(Navigator.of(context).pushReplacementNamed( widget.routeOnSuccess, arguments: widget.routeOnSuccessArguments, - ); + )); + if (widget.routeOnSuccess == HomeView.routeName && + widget.routeOnSuccessArguments is String) { + final walletId = widget.routeOnSuccessArguments as String; + unawaited(Navigator.of(context).pushNamed(WalletView.routeName, + arguments: Tuple2( + walletId, + ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(walletId)))); + } } } @@ -105,7 +121,7 @@ class _LockscreenViewState extends ConsumerState { // await walletsService.getWalletId(currentWalletName)); // } - _onUnlock(); + unawaited(_onUnlock()); } // leave this commented to enable pin fall back should biometrics not work properly // else { @@ -250,10 +266,10 @@ class _LockscreenViewState extends ConsumerState { _timeout = const Duration(minutes: 60); } - Future.delayed(_timeout).then((_) { + unawaited(Future.delayed(_timeout).then((_) { _attemptLock = false; _attempts = 0; - }); + })); } if (_attemptLock) { @@ -264,13 +280,13 @@ class _LockscreenViewState extends ConsumerState { prettyTime += "${_timeout.inSeconds} seconds"; } - showFloatingFlushBar( + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Incorrect PIN entered too many times. Please wait $prettyTime", context: context, iconAsset: Assets.svg.alertCircle, - ); + )); await Future.delayed( const Duration(milliseconds: 100)); @@ -286,15 +302,15 @@ class _LockscreenViewState extends ConsumerState { if (storedPin == pin) { await Future.delayed( const Duration(milliseconds: 200)); - _onUnlock(); + unawaited(_onUnlock()); } else { - _shakeController.shake(); - showFloatingFlushBar( + unawaited(_shakeController.shake()); + unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: "Incorrect PIN. Please try again", context: context, iconAsset: Assets.svg.alertCircle, - ); + )); await Future.delayed( const Duration(milliseconds: 100)); diff --git a/lib/pages/settings_views/global_settings_view/global_settings_view.dart b/lib/pages/settings_views/global_settings_view/global_settings_view.dart index 8c739ac00..2f52b9c37 100644 --- a/lib/pages/settings_views/global_settings_view/global_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/global_settings_view.dart @@ -9,6 +9,7 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/language_v import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/support_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart'; @@ -166,6 +167,18 @@ class GlobalSettingsView extends StatelessWidget { const SizedBox( height: 8, ), + SettingsListButton( + iconAssetName: Assets.svg.arrowUpRight, + iconSize: 16, + title: "Startup", + onPressed: () { + Navigator.of(context).pushNamed( + StartupPreferencesView.routeName); + }, + ), + const SizedBox( + height: 8, + ), SettingsListButton( iconAssetName: Assets.svg.sun, iconSize: 18, diff --git a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart new file mode 100644 index 000000000..861886625 --- /dev/null +++ b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart @@ -0,0 +1,269 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/utilities/cfcolors.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class StartupPreferencesView extends ConsumerStatefulWidget { + const StartupPreferencesView({Key? key}) : super(key: key); + + static const String routeName = "/startupPreferences"; + + @override + ConsumerState createState() => + _StartupPreferencesViewState(); +} + +class _StartupPreferencesViewState + extends ConsumerState { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: CFColors.almostWhite, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Startup preferences", + style: STextStyles.navBarTitle, + ), + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.all(4.0), + child: RawMaterialButton( + // splashColor: CFColors.splashLight, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: () { + ref + .read(prefsChangeNotifierProvider) + .gotoWalletOnStartup = false; + }, + child: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: CFColors.link2, + value: false, + groupValue: ref.watch( + prefsChangeNotifierProvider + .select((value) => value + .gotoWalletOnStartup), + ), + onChanged: (value) { + if (value is bool) { + ref + .read( + prefsChangeNotifierProvider) + .gotoWalletOnStartup = value; + } + }, + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Home screen", + style: STextStyles.titleBold12, + textAlign: TextAlign.left, + ), + Text( + "Stack Wallet home screen", + style: STextStyles.itemSubtitle, + textAlign: TextAlign.left, + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(4), + child: RawMaterialButton( + // splashColor: CFColors.splashLight, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: () { + ref + .read(prefsChangeNotifierProvider) + .gotoWalletOnStartup = true; + }, + child: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: CFColors.link2, + value: true, + groupValue: ref.watch( + prefsChangeNotifierProvider + .select((value) => value + .gotoWalletOnStartup), + ), + onChanged: (value) { + if (value is bool) { + ref + .read( + prefsChangeNotifierProvider) + .gotoWalletOnStartup = value; + } + }, + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Specific wallet", + style: STextStyles.titleBold12, + textAlign: TextAlign.left, + ), + Text( + "Select a specific wallet to load into on startup", + style: STextStyles.itemSubtitle, + textAlign: TextAlign.left, + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + if (!ref.watch(prefsChangeNotifierProvider + .select((value) => value.gotoWalletOnStartup))) + const SizedBox( + height: 12, + ), + if (ref.watch(prefsChangeNotifierProvider + .select((value) => value.gotoWalletOnStartup))) + Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.only( + left: 12.0, + right: 12, + bottom: 12, + ), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const SizedBox( + width: 12 + 20, + height: 12, + ), + Flexible( + child: RawMaterialButton( + // splashColor: CFColors.splashLight, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants + .size.circularBorderRadius, + ), + ), + onPressed: () { + Navigator.of(context).pushNamed( + StartupWalletSelectionView + .routeName); + }, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Select wallet...", + style: STextStyles.link2, + textAlign: TextAlign.left, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart new file mode 100644 index 000000000..9569419e5 --- /dev/null +++ b/lib/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart @@ -0,0 +1,197 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/cfcolors.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class StartupWalletSelectionView extends ConsumerStatefulWidget { + const StartupWalletSelectionView({Key? key}) : super(key: key); + + static const String routeName = "/startupWalletSelection"; + @override + ConsumerState createState() => + _StartupWalletSelectionViewState(); +} + +class _StartupWalletSelectionViewState + extends ConsumerState { + final Map _controllers = {}; + + @override + Widget build(BuildContext context) { + final managers = ref + .watch(walletsChangeNotifierProvider.select((value) => value.managers)); + + _controllers.clear(); + for (final manager in managers) { + _controllers[manager.walletId] = DSBController(); + } + + return Scaffold( + backgroundColor: CFColors.almostWhite, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + title: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + "Select startup wallet", + style: STextStyles.navBarTitle, + ), + ), + ), + body: LayoutBuilder(builder: (context, constraints) { + return Padding( + padding: const EdgeInsets.only( + left: 12, + top: 12, + right: 12, + ), + child: SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - 24, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 4, + ), + Text( + "Select a wallet to load into immediately on startup", + style: STextStyles.smallMed12, + ), + const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: Column( + children: [ + ...managers.map( + (manager) => Padding( + padding: const EdgeInsets.all(12), + child: Row( + key: Key( + "startupWalletSelectionGroupKey_${manager.walletId}"), + children: [ + Container( + decoration: BoxDecoration( + color: CFColors.coin + .forCoin(manager.coin) + .withOpacity(0.5), + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: Padding( + padding: const EdgeInsets.all(4), + child: SvgPicture.asset( + Assets.svg + .iconFor(coin: manager.coin), + width: 20, + height: 20, + ), + ), + ), + const SizedBox( + width: 12, + ), + Column( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + manager.walletName, + style: STextStyles.titleBold12, + ), + // const SizedBox( + // height: 2, + // ), + // FutureBuilder( + // future: manager.totalBalance, + // builder: (builderContext, + // AsyncSnapshot snapshot) { + // if (snapshot.connectionState == + // ConnectionState.done && + // snapshot.hasData) { + // return Text( + // "${Format.localizedStringAsFixed( + // value: snapshot.data!, + // locale: ref.watch( + // localeServiceChangeNotifierProvider + // .select((value) => + // value.locale)), + // decimalPlaces: 8, + // )} ${manager.coin.ticker}", + // style: STextStyles.itemSubtitle, + // ); + // } else { + // return AnimatedText( + // stringsToLoopThrough: const [ + // "Loading balance", + // "Loading balance.", + // "Loading balance..", + // "Loading balance..." + // ], + // style: STextStyles.itemSubtitle, + // ); + // } + // }, + // ), + ], + ), + const Spacer(), + SizedBox( + height: 20, + width: 20, + child: Radio( + activeColor: CFColors.link2, + value: manager.walletId, + groupValue: ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.startupWalletId), + ), + onChanged: (value) { + if (value is String) { + ref + .read( + prefsChangeNotifierProvider) + .startupWalletId = value; + } + }, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ); + }), + ); + } +} diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 22d546267..df15f65fe 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -56,6 +56,8 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/support_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; @@ -224,6 +226,18 @@ class RouteGenerator { builder: (_) => const SyncingPreferencesView(), settings: RouteSettings(name: settings.name)); + case StartupPreferencesView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const StartupPreferencesView(), + settings: RouteSettings(name: settings.name)); + + case StartupWalletSelectionView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const StartupWalletSelectionView(), + settings: RouteSettings(name: settings.name)); + case ManageNodesView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index cd297f121..578cf35a4 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -34,6 +34,8 @@ class Prefs extends ChangeNotifier { _backupFrequencyType = await _getBackupFrequencyType(); _lastAutoBackup = await _getLastAutoBackup(); _hideBlockExplorerWarning = await _getHideBlockExplorerWarning(); + _gotoWalletOnStartup = await _getGotoWalletOnStartup(); + _startupWalletId = await _getStartupWalletId(); _initialized = true; } @@ -468,8 +470,6 @@ class Prefs extends ChangeNotifier { boxName: DB.boxNamePrefs, key: "autoBackupFileUri") as DateTime?; } - - // auto backup bool _hideBlockExplorerWarning = false; @@ -480,9 +480,9 @@ class Prefs extends ChangeNotifier { if (_hideBlockExplorerWarning != hideBlockExplorerWarning) { DB.instance .put( - boxName: DB.boxNamePrefs, - key: "hideBlockExplorerWarning", - value: hideBlockExplorerWarning) + boxName: DB.boxNamePrefs, + key: "hideBlockExplorerWarning", + value: hideBlockExplorerWarning) .then((_) { _hideBlockExplorerWarning = hideBlockExplorerWarning; notifyListeners(); @@ -492,7 +492,56 @@ class Prefs extends ChangeNotifier { Future _getHideBlockExplorerWarning() async { return await DB.instance.get( - boxName: DB.boxNamePrefs, key: "hideBlockExplorerWarning") as bool? ?? + boxName: DB.boxNamePrefs, + key: "hideBlockExplorerWarning") as bool? ?? false; } + + // auto backup + + bool _gotoWalletOnStartup = false; + + bool get gotoWalletOnStartup => _gotoWalletOnStartup; + + set gotoWalletOnStartup(bool gotoWalletOnStartup) { + if (_gotoWalletOnStartup != gotoWalletOnStartup) { + DB.instance + .put( + boxName: DB.boxNamePrefs, + key: "gotoWalletOnStartup", + value: gotoWalletOnStartup) + .then((_) { + _gotoWalletOnStartup = gotoWalletOnStartup; + notifyListeners(); + }); + } + } + + Future _getGotoWalletOnStartup() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, key: "gotoWalletOnStartup") as bool? ?? + false; + } + + // startup wallet id + + String? _startupWalletId; + + String? get startupWalletId => _startupWalletId; + + set startupWalletId(String? startupWalletId) { + if (this.startupWalletId != startupWalletId) { + DB.instance.put( + boxName: DB.boxNamePrefs, + key: "startupWalletId", + value: startupWalletId); + _startupWalletId = startupWalletId; + notifyListeners(); + } + } + + Future _getStartupWalletId() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, key: "startupWalletId") as String?; + } }