diff --git a/assets/svg/network-wired-2.svg b/assets/svg/network-wired-2.svg new file mode 100644 index 000000000..bbbfa056f --- /dev/null +++ b/assets/svg/network-wired-2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart index 141eb4c99..950d8d79e 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart @@ -1,6 +1,11 @@ import 'package:flutter/material.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/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; class ConfirmFullRescanDialog extends StatelessWidget { @@ -11,40 +16,110 @@ class ConfirmFullRescanDialog extends StatelessWidget { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - return true; - }, - child: StackDialog( - title: "Rescan blockchain", - message: - "Warning! It may take a while. If you exit before completion, you will have to redo the process.", - leftButton: TextButton( - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonColor(context), - child: Text( - "Cancel", - style: STextStyles.itemSubtitle12(context), - ), - onPressed: () { - Navigator.of(context).pop(); - }, + if (Util.isDesktop) { + return DesktopDialog( + maxWidth: 576, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Text( + "Rescan blockchain", + style: STextStyles.desktopH3(context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 8, + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Warning! It may take a while. If you exit before completion, you will have to redo the process.", + style: STextStyles.desktopTextSmall(context), + ), + const SizedBox( + height: 43, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + desktopMed: true, + onPressed: Navigator.of(context).pop, + label: "Cancel", + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + desktopMed: true, + onPressed: () { + Navigator.of(context).pop(); + onConfirm.call(); + }, + label: "Rescan", + ), + ), + ], + ) + ], + ), + ) + ], ), - rightButton: TextButton( - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonColor(context), - child: Text( - "Rescan", - style: STextStyles.button(context), + ); + } else { + return WillPopScope( + onWillPop: () async { + return true; + }, + child: StackDialog( + title: "Rescan blockchain", + message: + "Warning! It may take a while. If you exit before completion, you will have to redo the process.", + leftButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonColor(context), + child: Text( + "Cancel", + style: STextStyles.itemSubtitle12(context), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonColor(context), + child: Text( + "Rescan", + style: STextStyles.button(context), + ), + onPressed: () { + Navigator.of(context).pop(); + onConfirm.call(); + }, ), - onPressed: () { - Navigator.of(context).pop(); - onConfirm.call(); - }, ), - ), - ); + ); + } } } diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart index 5b1c57214..accf244eb 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart @@ -10,6 +10,7 @@ import 'package:stackwallet/pages/settings_views/sub_widgets/nodes_list.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/confirm_full_rescan.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/sub_widgets/rescanning_dialog.dart'; import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart'; import 'package:stackwallet/services/coins/monero/monero_wallet.dart'; import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart'; @@ -23,9 +24,12 @@ import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.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/animated_text.dart'; +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/expandable.dart'; import 'package:stackwallet/widgets/progress_bar.dart'; import 'package:stackwallet/widgets/rounded_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -59,7 +63,7 @@ class _WalletNetworkSettingsViewState extends ConsumerState { final double _padding = 16; final double _boxPadding = 12; - final double _iconSize = 28; + final double _iconSize = Util.isDesktop ? 40 : 28; late final EventBus eventBus; @@ -73,19 +77,22 @@ class _WalletNetworkSettingsViewState late double _percent; late int _blocksRemaining; + bool _advancedIsExpanded = true; Future _attemptRescan() async { - if (!Platform.isLinux) Wakelock.enable(); + if (!Platform.isLinux) await Wakelock.enable(); int maxUnusedAddressGap = 20; const int maxNumberOfIndexesToCheck = 1000; - showDialog( - context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) => const RescanningDialog(), + unawaited( + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) => const RescanningDialog(), + ), ); try { @@ -131,7 +138,7 @@ class _WalletNetworkSettingsViewState ); } } catch (e) { - if (!Platform.isLinux) Wakelock.disable(); + if (!Platform.isLinux) await Wakelock.disable(); if (mounted) { // pop rescanning dialog @@ -162,7 +169,7 @@ class _WalletNetworkSettingsViewState } } - if (!Platform.isLinux) Wakelock.disable(); + if (!Platform.isLinux) await Wakelock.disable(); } String _percentString(double value) { @@ -262,9 +269,11 @@ class _WalletNetworkSettingsViewState @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; + final bool isDesktop = Util.isDesktop; - final progressLength = - screenWidth - (_padding * 2) - (_boxPadding * 3) - _iconSize; + final progressLength = isDesktop + ? 430.0 + : screenWidth - (_padding * 2) - (_boxPadding * 3) - _iconSize; final coin = ref .read(walletsChangeNotifierProvider) @@ -300,443 +309,597 @@ class _WalletNetworkSettingsViewState } } - return Scaffold( - backgroundColor: Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () { - Navigator.of(context).pop(); - }, - ), - title: Text( - "Network", - style: STextStyles.navBarTitle(context), - ), - actions: [ - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, + return ConditionalParent( + condition: !isDesktop, + builder: (child) { + return Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("walletNetworkSettingsAddNewNodeViewButton"), - size: 36, - shadows: const [], - color: Theme.of(context).extension()!.background, - icon: SvgPicture.asset( - Assets.svg.verticalEllipsis, - color: Theme.of(context) - .extension()! - .accentColorDark, - width: 20, - height: 20, + title: Text( + "Network", + style: STextStyles.navBarTitle(context), + ), + actions: [ + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, ), - onPressed: () { - showDialog( - barrierColor: Colors.transparent, - barrierDismissible: true, - context: context, - builder: (_) { - return Stack( - children: [ - Positioned( - top: 9, - right: 10, - child: Container( - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .popupBG, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius), - // boxShadow: [CFColors.standardBoxShadow], - boxShadow: const [], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - onTap: () { - Navigator.of(context).pop(); - showDialog( - context: context, - useSafeArea: false, - barrierDismissible: true, - builder: (context) { - return ConfirmFullRescanDialog( - onConfirm: _attemptRescan, + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("walletNetworkSettingsAddNewNodeViewButton"), + size: 36, + shadows: const [], + color: + Theme.of(context).extension()!.background, + icon: SvgPicture.asset( + Assets.svg.verticalEllipsis, + color: Theme.of(context) + .extension()! + .accentColorDark, + width: 20, + height: 20, + ), + onPressed: () { + showDialog( + barrierColor: Colors.transparent, + barrierDismissible: true, + context: context, + builder: (_) { + return Stack( + children: [ + Positioned( + top: 9, + right: 10, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .popupBG, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius), + // boxShadow: [CFColors.standardBoxShadow], + boxShadow: const [], + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + Navigator.of(context).pop(); + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return ConfirmFullRescanDialog( + onConfirm: _attemptRescan, + ); + }, ); }, - ); - }, - child: RoundedWhiteContainer( - child: Material( - color: Colors.transparent, - child: Text( - "Rescan blockchain", - style: STextStyles.baseXS(context), - ), - ), - ), - ), - ], - ), - ), - ), - ], - ); - }, - ); - }, - ), - ), - ), - ], - ), - body: Padding( - padding: EdgeInsets.only( - top: 12, - left: _padding, - right: _padding, - ), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Blockchain status", - textAlign: TextAlign.left, - style: STextStyles.smallMed12(context), - ), - GestureDetector( - onTap: () { - ref - .read(walletsChangeNotifierProvider) - .getManager(widget.walletId) - .refresh(); - }, - child: Text( - "Resync", - style: STextStyles.link2(context), - ), - ), - ], - ), - const SizedBox( - height: 9, - ), - if (_currentSyncStatus == WalletSyncStatus.synced) - RoundedWhiteContainer( - child: Row( - children: [ - Container( - width: _iconSize, - height: _iconSize, - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .accentColorGreen - .withOpacity(0.2), - borderRadius: BorderRadius.circular(_iconSize), - ), - child: Center( - child: SvgPicture.asset( - Assets.svg.radio, - height: 14, - width: 14, - color: Theme.of(context) - .extension()! - .accentColorGreen, - ), - ), - ), - SizedBox( - width: _boxPadding, - ), - Column( - children: [ - SizedBox( - width: progressLength, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Synchronized", - style: STextStyles.w600_10(context), - ), - Text( - "100%", - style: STextStyles.syncPercent(context) - .copyWith( - color: Theme.of(context) - .extension()! - .accentColorGreen, - ), - ), - ], - ), - ), - const SizedBox( - height: 4, - ), - ProgressBar( - width: progressLength, - height: 5, - fillColor: Theme.of(context) - .extension()! - .accentColorGreen, - backgroundColor: Theme.of(context) - .extension()! - .textFieldDefaultBG, - percent: 1, - ), - ], - ), - ], - ), - ), - if (_currentSyncStatus == WalletSyncStatus.syncing) - RoundedWhiteContainer( - child: Row( - children: [ - Container( - width: _iconSize, - height: _iconSize, - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .accentColorYellow - .withOpacity(0.2), - borderRadius: BorderRadius.circular(_iconSize), - ), - child: Center( - child: SvgPicture.asset( - Assets.svg.radioSyncing, - height: 14, - width: 14, - color: Theme.of(context) - .extension()! - .accentColorYellow, - ), - ), - ), - SizedBox( - width: _boxPadding, - ), - Column( - children: [ - SizedBox( - width: progressLength, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - AnimatedText( - style: STextStyles.w600_10(context), - stringsToLoopThrough: const [ - "Synchronizing", - "Synchronizing.", - "Synchronizing..", - "Synchronizing...", - ], - ), - Row( - children: [ - Text( - _percentString(_percent), - style: - STextStyles.syncPercent(context) - .copyWith( - color: Theme.of(context) - .extension()! - .accentColorYellow, - ), - ), - if (coin == Coin.monero || - coin == Coin.wownero || - coin == Coin.epicCash) - Text( - " (Blocks to go: ${_blocksRemaining == -1 ? "?" : _blocksRemaining})", - style: - STextStyles.syncPercent(context) - .copyWith( - color: Theme.of(context) - .extension()! - .accentColorYellow, + child: RoundedWhiteContainer( + child: Material( + color: Colors.transparent, + child: Text( + "Rescan blockchain", + style: + STextStyles.baseXS(context), ), ), - ], - ) - ], + ), + ), + ], + ), ), ), - const SizedBox( - height: 4, - ), - ProgressBar( - width: progressLength, - height: 5, - fillColor: Theme.of(context) - .extension()! - .accentColorYellow, - backgroundColor: Theme.of(context) - .extension()! - .textFieldDefaultBG, - percent: _percent, - ), ], - ), - ], - ), - ), - if (_currentSyncStatus == WalletSyncStatus.unableToSync) - RoundedWhiteContainer( - child: Row( - children: [ - Container( - width: _iconSize, - height: _iconSize, - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .accentColorRed - .withOpacity(0.2), - borderRadius: BorderRadius.circular(_iconSize), - ), - child: Center( - child: SvgPicture.asset( - Assets.svg.radioProblem, - height: 14, - width: 14, - color: Theme.of(context) - .extension()! - .accentColorRed, - ), - ), - ), - SizedBox( - width: _boxPadding, - ), - Column( - children: [ - SizedBox( - width: progressLength, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Unable to synchronize", - style: - STextStyles.w600_10(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorRed, - ), - ), - Text( - "0%", - style: STextStyles.syncPercent(context) - .copyWith( - color: Theme.of(context) - .extension()! - .accentColorRed, - ), - ), - ], - ), - ), - const SizedBox( - height: 4, - ), - ProgressBar( - width: progressLength, - height: 5, - fillColor: Theme.of(context) - .extension()! - .accentColorRed, - backgroundColor: Theme.of(context) - .extension()! - .textFieldDefaultBG, - percent: 0, - ), - ], - ), - ], - ), - ), - if (_currentSyncStatus == WalletSyncStatus.unableToSync) - Padding( - padding: const EdgeInsets.only( - top: 12, - ), - child: RoundedContainer( - color: Theme.of(context) - .extension()! - .warningBackground, - child: Text( - "Please check your internet connection and make sure your current node is not having issues.", - style: STextStyles.baseXS(context).copyWith( - color: Theme.of(context) - .extension()! - .warningForeground, - ), - ), - ), - ), - const SizedBox( - height: 20, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "${ref.watch(walletsChangeNotifierProvider.select((value) => value.getManager(widget.walletId).coin)).prettyName} nodes", - textAlign: TextAlign.left, - style: STextStyles.smallMed12(context), - ), - BlueTextButton( - text: "Add new node", - onTap: () { - Navigator.of(context).pushNamed( - AddEditNodeView.routeName, - arguments: Tuple4( - AddEditNodeViewType.add, - ref - .read(walletsChangeNotifierProvider) - .getManager(widget.walletId) - .coin, - null, - WalletNetworkSettingsView.routeName, - ), ); }, - ), - ], + ); + }, ), - const SizedBox( - height: 8, - ), - NodesList( - coin: ref.watch(walletsChangeNotifierProvider.select( - (value) => value.getManager(widget.walletId).coin)), - popBackToRoute: WalletNetworkSettingsView.routeName, - ), - ], + ), ), ], ), - ), + body: Padding( + padding: EdgeInsets.only( + top: 12, + left: _padding, + right: _padding, + ), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + child, + ], + ), + ), + ), + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Blockchain status", + textAlign: TextAlign.left, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + GestureDetector( + onTap: () { + ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .refresh(); + }, + child: Text( + "Resync", + style: STextStyles.link2(context), + ), + ), + ], + ), + SizedBox( + height: isDesktop ? 12 : 9, + ), + if (_currentSyncStatus == WalletSyncStatus.synced) + RoundedWhiteContainer( + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + children: [ + Container( + width: _iconSize, + height: _iconSize, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .accentColorGreen + .withOpacity(0.2), + borderRadius: BorderRadius.circular(_iconSize), + ), + child: Center( + child: SvgPicture.asset( + Assets.svg.radio, + height: isDesktop ? 19 : 14, + width: isDesktop ? 19 : 14, + color: Theme.of(context) + .extension()! + .accentColorGreen, + ), + ), + ), + SizedBox( + width: _boxPadding, + ), + Column( + children: [ + SizedBox( + width: progressLength, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Synchronized", + style: STextStyles.w600_10(context), + ), + Text( + "100%", + style: STextStyles.syncPercent(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorGreen, + ), + ), + ], + ), + ), + const SizedBox( + height: 4, + ), + ProgressBar( + width: progressLength, + height: 5, + fillColor: Theme.of(context) + .extension()! + .accentColorGreen, + backgroundColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + percent: 1, + ), + ], + ), + ], + ), + ), + if (_currentSyncStatus == WalletSyncStatus.syncing) + RoundedWhiteContainer( + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + children: [ + Container( + width: _iconSize, + height: _iconSize, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .accentColorYellow + .withOpacity(0.2), + borderRadius: BorderRadius.circular(_iconSize), + ), + child: Center( + child: SvgPicture.asset( + Assets.svg.radioSyncing, + height: 14, + width: 14, + color: Theme.of(context) + .extension()! + .accentColorYellow, + ), + ), + ), + SizedBox( + width: _boxPadding, + ), + Column( + children: [ + SizedBox( + width: progressLength, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AnimatedText( + style: STextStyles.w600_10(context), + stringsToLoopThrough: const [ + "Synchronizing", + "Synchronizing.", + "Synchronizing..", + "Synchronizing...", + ], + ), + Row( + children: [ + Text( + _percentString(_percent), + style: + STextStyles.syncPercent(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorYellow, + ), + ), + if (coin == Coin.monero || + coin == Coin.wownero || + coin == Coin.epicCash) + Text( + " (Blocks to go: ${_blocksRemaining == -1 ? "?" : _blocksRemaining})", + style: STextStyles.syncPercent(context) + .copyWith( + color: Theme.of(context) + .extension()! + .accentColorYellow, + ), + ), + ], + ) + ], + ), + ), + const SizedBox( + height: 4, + ), + ProgressBar( + width: progressLength, + height: 5, + fillColor: Theme.of(context) + .extension()! + .accentColorYellow, + backgroundColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + percent: _percent, + ), + ], + ), + ], + ), + ), + if (_currentSyncStatus == WalletSyncStatus.unableToSync) + RoundedWhiteContainer( + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + children: [ + Container( + width: _iconSize, + height: _iconSize, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .accentColorRed + .withOpacity(0.2), + borderRadius: BorderRadius.circular(_iconSize), + ), + child: Center( + child: SvgPicture.asset( + Assets.svg.radioProblem, + height: 14, + width: 14, + color: Theme.of(context) + .extension()! + .accentColorRed, + ), + ), + ), + SizedBox( + width: _boxPadding, + ), + Column( + children: [ + SizedBox( + width: progressLength, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Unable to synchronize", + style: STextStyles.w600_10(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorRed, + ), + ), + Text( + "0%", + style: STextStyles.syncPercent(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorRed, + ), + ), + ], + ), + ), + const SizedBox( + height: 4, + ), + ProgressBar( + width: progressLength, + height: 5, + fillColor: Theme.of(context) + .extension()! + .accentColorRed, + backgroundColor: Theme.of(context) + .extension()! + .textFieldDefaultBG, + percent: 0, + ), + ], + ), + ], + ), + ), + if (_currentSyncStatus == WalletSyncStatus.unableToSync) + Padding( + padding: const EdgeInsets.only( + top: 12, + ), + child: RoundedContainer( + color: Theme.of(context) + .extension()! + .warningBackground, + child: Text( + "Please check your internet connection and make sure your current node is not having issues.", + style: STextStyles.baseXS(context).copyWith( + color: Theme.of(context) + .extension()! + .warningForeground, + ), + ), + ), + ), + SizedBox( + height: isDesktop ? 32 : 20, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "${ref.watch(walletsChangeNotifierProvider.select((value) => value.getManager(widget.walletId).coin)).prettyName} nodes", + textAlign: TextAlign.left, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + BlueTextButton( + text: "Add new node", + onTap: () { + Navigator.of(context).pushNamed( + AddEditNodeView.routeName, + arguments: Tuple4( + AddEditNodeViewType.add, + ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .coin, + null, + WalletNetworkSettingsView.routeName, + ), + ); + }, + ), + ], + ), + SizedBox( + height: isDesktop ? 12 : 8, + ), + NodesList( + coin: ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId).coin)), + popBackToRoute: WalletNetworkSettingsView.routeName, + ), + if (isDesktop) + const SizedBox( + height: 32, + ), + if (isDesktop) + Padding( + padding: const EdgeInsets.only( + bottom: 12, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "Advanced", + textAlign: TextAlign.left, + style: STextStyles.desktopTextExtraExtraSmall(context), + ), + ], + ), + ), + if (isDesktop) + RoundedWhiteContainer( + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Expandable( + onExpandChanged: (state) { + setState(() { + _advancedIsExpanded = state == ExpandableState.expanded; + }); + }, + header: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: _iconSize, + height: _iconSize, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: BorderRadius.circular(_iconSize), + ), + child: Center( + child: SvgPicture.asset( + Assets.svg.networkWired, + width: 24, + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + ), + const SizedBox( + width: 10, + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Advanced", + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + Text( + "Rescan blockchain", + style: STextStyles.desktopTextExtraExtraSmall( + context), + ), + ], + ) + ], + ), + SvgPicture.asset( + _advancedIsExpanded + ? Assets.svg.chevronDown + : Assets.svg.chevronUp, + width: 12, + height: 6, + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + ], + ), + body: Row( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 50, + top: 16, + bottom: 6, + ), + child: BlueTextButton( + text: "Rescan", + onTap: () async { + await Navigator.of(context).push( + FadePageRoute( + ConfirmFullRescanDialog( + onConfirm: _attemptRescan, + ), + const RouteSettings(), + ), + ); + // await showDialog( + // context: context, + // builder: (context) { + // return ConfirmFullRescanDialog( + // onConfirm: _attemptRescan, + // ); + // }, + // ); + }, + ), + ), + ], + ), + ), + ), + ], ), ); } diff --git a/lib/pages/stack_privacy_calls.dart b/lib/pages/stack_privacy_calls.dart index 7e981b91a..fd6f60def 100644 --- a/lib/pages/stack_privacy_calls.dart +++ b/lib/pages/stack_privacy_calls.dart @@ -285,6 +285,7 @@ class _PrivacyToggleState extends State { children: [ Expanded( child: RawMaterialButton( + elevation: 0, fillColor: Theme.of(context).extension()!.popupBG, shape: RoundedRectangleBorder( side: !externalCallsEnabled diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart index d1e415b26..6f23f2e01 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -25,6 +25,7 @@ import 'package:stackwallet/utilities/logger.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/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; @@ -260,11 +261,19 @@ class _TransactionDetailsViewState bottom: 32, ) : const EdgeInsets.all(0), - child: RoundedWhiteContainer( - borderColor: isDesktop - ? Theme.of(context).extension()!.background - : null, - padding: const EdgeInsets.all(0), + child: ConditionalParent( + condition: isDesktop, + builder: (child) { + return RoundedWhiteContainer( + borderColor: isDesktop + ? Theme.of(context) + .extension()! + .background + : null, + padding: const EdgeInsets.all(0), + child: child, + ); + }, child: SingleChildScrollView( primary: isDesktop ? false : null, child: Padding( diff --git a/lib/pages_desktop_specific/home/advanced_settings/debug_info_dialog.dart b/lib/pages_desktop_specific/home/advanced_settings/debug_info_dialog.dart deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/pages_desktop_specific/home/desktop_settings_view.dart b/lib/pages_desktop_specific/home/desktop_settings_view.dart index 34cb07f27..63e1ee613 100644 --- a/lib/pages_desktop_specific/home/desktop_settings_view.dart +++ b/lib/pages_desktop_specific/home/desktop_settings_view.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/pages_desktop_specific/home/advanced_settings/advanced_settings.dart'; +import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/appearance_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/backup_and_restore_settings.dart'; -import 'package:stackwallet/pages_desktop_specific/home/settings_menu/currency_settings.dart'; -import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings.dart'; +import 'package:stackwallet/pages_desktop_specific/home/settings_menu/currency_settings/currency_settings.dart'; +import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/nodes_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/security_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/settings_menu.dart'; diff --git a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/network_info_button.dart b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/network_info_button.dart index 830b5e667..c001f9bf3 100644 --- a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/network_info_button.dart +++ b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/network_info_button.dart @@ -6,12 +6,16 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/utilities/assets.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/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; import 'package:tuple/tuple.dart'; class NetworkInfoButton extends ConsumerStatefulWidget { @@ -150,14 +154,116 @@ class _NetworkInfoButtonState extends ConsumerState { Widget build(BuildContext context) { return GestureDetector( onTap: () { - Navigator.of(context).pushNamed( - WalletNetworkSettingsView.routeName, - arguments: Tuple3( - walletId, - _currentSyncStatus, - _currentNodeStatus, - ), - ); + if (Util.isDesktop) { + // showDialog( + // context: context, + // builder: (context) => DesktopDialog( + // maxHeight: MediaQuery.of(context).size.height - 64, + // maxWidth: 580, + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Padding( + // padding: const EdgeInsets.only( + // left: 32, + // ), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // "Network", + // style: STextStyles.desktopH3(context), + // ), + // const DesktopDialogCloseButton(), + // ], + // ), + // ), + // Padding( + // padding: const EdgeInsets.only( + // top: 16, + // left: 32, + // right: 32, + // bottom: 32, + // ), + // child: WalletNetworkSettingsView( + // walletId: walletId, + // initialSyncStatus: _currentSyncStatus, + // initialNodeStatus: _currentNodeStatus, + // ), + // ), + // ], + // ), + // ), + // ); + + showDialog( + context: context, + builder: (context) => Navigator( + initialRoute: WalletNetworkSettingsView.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + FadePageRoute( + DesktopDialog( + maxHeight: MediaQuery.of(context).size.height - 64, + maxWidth: 580, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Network", + style: STextStyles.desktopH3(context), + ), + DesktopDialogCloseButton( + onPressedOverride: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 16, + left: 32, + right: 32, + bottom: 32, + ), + child: WalletNetworkSettingsView( + walletId: walletId, + initialSyncStatus: _currentSyncStatus, + initialNodeStatus: _currentNodeStatus, + ), + ), + ], + ), + ), + const RouteSettings( + name: WalletNetworkSettingsView.routeName, + ), + ), + ]; + }, + ), + ); + } else { + Navigator.of(context).pushNamed( + WalletNetworkSettingsView.routeName, + arguments: Tuple3( + walletId, + _currentSyncStatus, + _currentNodeStatus, + ), + ); + } }, child: Container( color: Colors.transparent, diff --git a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart index 37f047eed..e65820737 100644 --- a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart +++ b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart'; import 'package:stackwallet/providers/providers.dart'; -import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -22,6 +21,8 @@ class UnlockWalletKeysDesktop extends ConsumerStatefulWidget { final String walletId; + static const String routeName = "/desktopUnlockWalletKeys"; + @override ConsumerState createState() => _UnlockWalletKeysDesktopState(); @@ -59,8 +60,13 @@ class _UnlockWalletKeysDesktopState children: [ Row( mainAxisAlignment: MainAxisAlignment.end, - children: const [ - DesktopDialogCloseButton(), + children: [ + DesktopDialogCloseButton( + onPressedOverride: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), ], ), const SizedBox( @@ -175,7 +181,10 @@ class _UnlockWalletKeysDesktopState Expanded( child: SecondaryButton( label: "Cancel", - onPressed: Navigator.of(context).pop, + onPressed: Navigator.of( + context, + rootNavigator: true, + ).pop, ), ), const SizedBox( @@ -188,29 +197,35 @@ class _UnlockWalletKeysDesktopState onPressed: continueEnabled ? () async { // todo: check password - Navigator.of(context).pop(); + // Navigator.of(context).pop(); final words = await ref .read(walletsChangeNotifierProvider) .getManager(widget.walletId) .mnemonic; - await showDialog( - context: context, - barrierDismissible: false, - builder: (context) => Navigator( - initialRoute: WalletKeysDesktopPopup.routeName, - onGenerateRoute: RouteGenerator.generateRoute, - onGenerateInitialRoutes: (_, __) { - return [ - RouteGenerator.generateRoute( - RouteSettings( - name: WalletKeysDesktopPopup.routeName, - arguments: words, - ), - ) - ]; - }, - ), + + await Navigator.of(context).pushReplacementNamed( + WalletKeysDesktopPopup.routeName, + arguments: words, ); + // + // await showDialog( + // context: context, + // barrierDismissible: false, + // builder: (context) => Navigator( + // initialRoute: WalletKeysDesktopPopup.routeName, + // onGenerateRoute: RouteGenerator.generateRoute, + // onGenerateInitialRoutes: (_, __) { + // return [ + // RouteGenerator.generateRoute( + // RouteSettings( + // name: WalletKeysDesktopPopup.routeName, + // arguments: words, + // ), + // ) + // ]; + // }, + // ), + // ); } : null, ), diff --git a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart index d4921276d..649433d52 100644 --- a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart +++ b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart'; +import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -20,10 +21,29 @@ class WalletKeysButton extends StatelessWidget { showDialog( context: context, barrierDismissible: false, - builder: (context) => UnlockWalletKeysDesktop( - walletId: walletId, + builder: (context) => Navigator( + initialRoute: UnlockWalletKeysDesktop.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + RouteGenerator.generateRoute( + RouteSettings( + name: UnlockWalletKeysDesktop.routeName, + arguments: walletId, + ), + ) + ]; + }, ), ); + + // showDialog( + // context: context, + // barrierDismissible: false, + // builder: (context) => UnlockWalletKeysDesktop( + // walletId: walletId, + // ), + // ); }, child: Container( color: Colors.transparent, diff --git a/lib/pages_desktop_specific/home/advanced_settings/advanced_settings.dart b/lib/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart similarity index 93% rename from lib/pages_desktop_specific/home/advanced_settings/advanced_settings.dart rename to lib/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart index 7361d9773..1632576fd 100644 --- a/lib/pages_desktop_specific/home/advanced_settings/advanced_settings.dart +++ b/lib/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/pages_desktop_specific/home/advanced_settings/stack_privacy_dialog.dart'; +import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings/stack_privacy_dialog.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -165,7 +166,7 @@ class _AdvancedSettings extends ConsumerState { .textDark), textAlign: TextAlign.left, ), - const ShowLogsButton(), + ShowLogsButton(), ], ), ), @@ -190,7 +191,7 @@ class StackPrivacyButton extends ConsumerWidget { useSafeArea: false, barrierDismissible: true, builder: (context) { - return const StackPrivacyDialog(); + return StackPrivacyDialog(); }, ); } @@ -223,20 +224,19 @@ class ShowLogsButton extends ConsumerWidget { const ShowLogsButton({ Key? key, }) : super(key: key); - - Future viewDebugLogs() async { - // await showDialog( - // context: context, - // useSafeArea: false, - // barrierDismissible: true, - // builder: (context) { - // return const DebugInfoDialog(); - // }, - // ); - } - @override Widget build(BuildContext context, WidgetRef ref) { + // Future viewDebugLogs() async { + // await showDialog( + // context: context, + // useSafeArea: false, + // barrierDismissible: true, + // builder: (context) { + // return const DebugInfoDialog(); + // }, + // ); + // } + return SizedBox( width: 101, height: 37, @@ -246,7 +246,7 @@ class ShowLogsButton extends ConsumerWidget { .getPrimaryEnabledButtonColor(context), onPressed: () { // - viewDebugLogs(); + // viewDebugLogs(); }, child: Text( "Show logs", diff --git a/lib/pages_desktop_specific/home/settings_menu/advanced_settings/debug_info_dialog.dart b/lib/pages_desktop_specific/home/settings_menu/advanced_settings/debug_info_dialog.dart new file mode 100644 index 000000000..342d9180a --- /dev/null +++ b/lib/pages_desktop_specific/home/settings_menu/advanced_settings/debug_info_dialog.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/models/isar/models/log.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; + +// import '../../../utilities/assets.dart'; +// import '../../../utilities/util.dart'; +// import '../../../widgets/icon_widgets/x_icon.dart'; +// import '../../../widgets/stack_text_field.dart'; +// import '../../../widgets/textfield_icon_button.dart'; + +class DebugInfoDialog extends StatefulWidget { + const DebugInfoDialog({Key? key}) : super(key: key); + + @override + State createState() => _DebugInfoDialog(); +} + +class _DebugInfoDialog extends State { + final _searchController = TextEditingController(); + final _searchFocusNode = FocusNode(); + + final scrollController = ScrollController(); + + String _searchTerm = ""; + + List filtered(List unfiltered, String filter) { + if (filter == "") { + return unfiltered; + } + return unfiltered + .where( + (e) => (e.toString().toLowerCase().contains(filter.toLowerCase()))) + .toList(); + } + + BorderRadius? _borderRadius(int index, int listLength) { + if (index == 0 && listLength == 1) { + return BorderRadius.circular( + Constants.size.circularBorderRadius, + ); + } else if (index == 0) { + return BorderRadius.vertical( + bottom: Radius.circular( + Constants.size.circularBorderRadius, + ), + ); + } else if (index == listLength - 1) { + return BorderRadius.vertical( + top: Radius.circular( + Constants.size.circularBorderRadius, + ), + ); + } + return null; + } + + @override + void initState() { + // ref.read(debugServiceProvider).updateRecentLogs(); + super.initState(); + } + + @override + void dispose() { + _searchController.dispose(); + scrollController.dispose(); + _searchFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return DesktopDialog( + maxHeight: 800, + maxWidth: 600, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.all(32), + child: Text( + "Debug info", + style: STextStyles.desktopH3(context), + textAlign: TextAlign.center, + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Row( + children: [ + // ClipRRect( + // borderRadius: BorderRadius.circular( + // Constants.size.circularBorderRadius, + // ), + // child: TextField( + // key: const Key("desktopSettingDebugInfo"), + // autocorrect: Util.isDesktop ? false : true, + // enableSuggestions: Util.isDesktop ? false : true, + // controller: _searchController, + // focusNode: _searchFocusNode, + // // onChanged: (newString) { + // // setState(() => _searchTerm = newString); + // // }, + // style: STextStyles.field(context), + // decoration: standardInputDecoration( + // "Search", + // _searchFocusNode, + // context, + // ).copyWith( + // prefixIcon: Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 10, + // vertical: 16, + // ), + // child: SvgPicture.asset( + // Assets.svg.search, + // width: 16, + // height: 16, + // ), + // ), + // suffixIcon: _searchController.text.isNotEmpty + // ? Padding( + // padding: const EdgeInsets.only(right: 0), + // child: UnconstrainedBox( + // child: Row( + // children: [ + // TextFieldIconButton( + // child: const XIcon(), + // onTap: () async { + // setState(() { + // _searchController.text = ""; + // _searchTerm = ""; + // }); + // }, + // ), + // ], + // ), + // ), + // ) + // : null, + // ), + // ), + // ), + ], + ), + // Column( + // children: [ + // + // ], + // ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(32), + child: Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Clear logs", + onPressed: () {}, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Save logs to file", + onPressed: () {}, + ), + ) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages_desktop_specific/home/advanced_settings/stack_privacy_dialog.dart b/lib/pages_desktop_specific/home/settings_menu/advanced_settings/stack_privacy_dialog.dart similarity index 96% rename from lib/pages_desktop_specific/home/advanced_settings/stack_privacy_dialog.dart rename to lib/pages_desktop_specific/home/settings_menu/advanced_settings/stack_privacy_dialog.dart index 8e385fd37..32c20c010 100644 --- a/lib/pages_desktop_specific/home/advanced_settings/stack_privacy_dialog.dart +++ b/lib/pages_desktop_specific/home/settings_menu/advanced_settings/stack_privacy_dialog.dart @@ -3,21 +3,20 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/hive/db.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/providers/global/price_provider.dart'; +import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/constants.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/desktop_dialog.dart'; import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; - -import '../../../hive/db.dart'; -import '../../../providers/global/prefs_provider.dart'; -import '../../../providers/global/price_provider.dart'; -import '../../../services/exchange/exchange_data_loading_service.dart'; -import '../../../utilities/assets.dart'; -import '../../../utilities/constants.dart'; -import '../../../utilities/theme/stack_colors.dart'; -import '../../../utilities/util.dart'; -import '../../../widgets/rounded_white_container.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; class StackPrivacyDialog extends ConsumerStatefulWidget { const StackPrivacyDialog({Key? key}) : super(key: key); diff --git a/lib/pages_desktop_specific/home/settings_menu/backup_and_restore/create_auto_backup.dart b/lib/pages_desktop_specific/home/settings_menu/backup_and_restore/create_auto_backup.dart index 16bda2ff7..f3e502bcb 100644 --- a/lib/pages_desktop_specific/home/settings_menu/backup_and_restore/create_auto_backup.dart +++ b/lib/pages_desktop_specific/home/settings_menu/backup_and_restore/create_auto_backup.dart @@ -40,20 +40,6 @@ class _CreateAutoBackup extends State { "Every 30 minutes", ]; - // List> get dropdownItems { - // List> menuItems = [ - // const DropdownMenuItem( - // value: "Every 10 minutes", - // child: Text("Every 10 minutes"), - // ), - // const DropdownMenuItem( - // value: "Every 20 minutes", - // child: Text("Every 20 minutes"), - // ), - // ]; - // return menuItems; - // } - @override void initState() { fileLocationController = TextEditingController(); diff --git a/lib/pages_desktop_specific/home/settings_menu/currency_settings/currency_dialog.dart b/lib/pages_desktop_specific/home/settings_menu/currency_settings/currency_dialog.dart new file mode 100644 index 000000000..bbe98c1af --- /dev/null +++ b/lib/pages_desktop_specific/home/settings_menu/currency_settings/currency_dialog.dart @@ -0,0 +1,371 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/providers/global/base_currencies_provider.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/constants.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/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/rounded_container.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class CurrencyDialog extends ConsumerStatefulWidget { + const CurrencyDialog({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _CurrencyDialog(); +} + +class _CurrencyDialog extends ConsumerState { + late String current; + late List currenciesWithoutSelected; + + late final TextEditingController searchCurrencyController; + + late final FocusNode searchCurrencyFocusNode; + + void onTap(int index) { + if (currenciesWithoutSelected[index] == current || current.isEmpty) { + // ignore if already selected currency + return; + } + current = currenciesWithoutSelected[index]; + currenciesWithoutSelected.remove(current); + currenciesWithoutSelected.insert(0, current); + ref.read(prefsChangeNotifierProvider).currency = current; + } + + BorderRadius? _borderRadius(int index) { + if (index == 0 && currenciesWithoutSelected.length == 1) { + return BorderRadius.circular( + Constants.size.circularBorderRadius, + ); + } else if (index == 0) { + return BorderRadius.vertical( + top: Radius.circular( + Constants.size.circularBorderRadius, + ), + ); + } else if (index == currenciesWithoutSelected.length - 1) { + return BorderRadius.vertical( + bottom: Radius.circular( + Constants.size.circularBorderRadius, + ), + ); + } + return null; + } + + String filter = ""; + + List _filtered() { + final currencyMap = ref.read(baseCurrenciesProvider).map; + return currenciesWithoutSelected.where((element) { + return element.toLowerCase().contains(filter.toLowerCase()) || + (currencyMap[element]?.toLowerCase().contains(filter.toLowerCase()) ?? + false); + }).toList(); + } + + @override + void initState() { + searchCurrencyController = TextEditingController(); + + searchCurrencyFocusNode = FocusNode(); + + super.initState(); + } + + @override + void dispose() { + searchCurrencyController.dispose(); + + searchCurrencyFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + current = ref + .watch(prefsChangeNotifierProvider.select((value) => value.currency)); + + currenciesWithoutSelected = ref + .watch(baseCurrenciesProvider.select((value) => value.map)) + .keys + .toList(); + if (current.isNotEmpty) { + currenciesWithoutSelected.remove(current); + currenciesWithoutSelected.insert(0, current); + } + currenciesWithoutSelected = _filtered(); + + return DesktopDialog( + maxHeight: 800, + maxWidth: 600, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.all(32), + child: Text( + "Select currency", + style: STextStyles.desktopH3(context), + textAlign: TextAlign.center, + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Expanded( + flex: 24, + child: NestedScrollView( + floatHeaderSlivers: true, + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor( + context), + sliver: SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 16, horizontal: 32), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: + Util.isDesktop ? false : true, + controller: searchCurrencyController, + focusNode: searchCurrencyFocusNode, + onChanged: (newString) { + setState(() => filter = newString); + }, + style: STextStyles.field(context), + decoration: standardInputDecoration( + "Search", + searchCurrencyFocusNode, + context, + ).copyWith( + prefixIcon: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 16, + ), + child: SvgPicture.asset( + Assets.svg.search, + width: 16, + height: 16, + ), + ), + suffixIcon: searchCurrencyController + .text.isNotEmpty + ? Padding( + padding: + const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + searchCurrencyController + .text = ""; + filter = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ]; + }, + body: Builder( + builder: (context) { + return CustomScrollView( + slivers: [ + SliverOverlapInjector( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor( + context, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .popupBG, + borderRadius: _borderRadius(index), + ), + child: Padding( + padding: const EdgeInsets.all(4), + key: Key( + "desktopSettingsCurrencySelect_${currenciesWithoutSelected[index]}"), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 32), + child: RoundedContainer( + padding: const EdgeInsets.all(0), + color: currenciesWithoutSelected[index] == + current + ? Theme.of(context) + .extension()! + .currencyListItemBG + : Theme.of(context) + .extension()! + .popupBG, + child: RawMaterialButton( + onPressed: () async { + onTap(index); + }, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, + materialTapTargetSize: + MaterialTapTargetSize + .shrinkWrap, + value: true, + groupValue: + currenciesWithoutSelected[ + index] == + current, + onChanged: (_) { + onTap(index); + }, + ), + ), + const SizedBox( + width: 12, + ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + currenciesWithoutSelected[ + index], + key: (currenciesWithoutSelected[ + index] == + current) + ? const Key( + "desktopSettingsSelectedCurrencyText") + : null, + style: + STextStyles.largeMedium14( + context), + ), + const SizedBox( + height: 2, + ), + Text( + ref.watch(baseCurrenciesProvider + .select((value) => + value.map))[ + currenciesWithoutSelected[ + index]] ?? + "", + key: (currenciesWithoutSelected[ + index] == + current) + ? const Key( + "desktopSelectedCurrencyTextDescription") + : null, + style: + STextStyles.itemSubtitle( + context), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ); + }, + childCount: currenciesWithoutSelected.length, + ), + ), + ], + ); + }, + ), + ), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(32), + child: Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Save Changes", + onPressed: () {}, + ), + ) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages_desktop_specific/home/settings_menu/currency_settings/currency_settings.dart b/lib/pages_desktop_specific/home/settings_menu/currency_settings/currency_settings.dart new file mode 100644 index 000000000..b8ce76d25 --- /dev/null +++ b/lib/pages_desktop_specific/home/settings_menu/currency_settings/currency_settings.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +import 'currency_dialog.dart'; + +class CurrencySettings extends ConsumerStatefulWidget { + const CurrencySettings({Key? key}) : super(key: key); + + static const String routeName = "/settingsMenuCurrency"; + + @override + ConsumerState createState() => _CurrencySettings(); +} + +class _CurrencySettings extends ConsumerState { + @override + Widget build(BuildContext context) { + debugPrint("BUILD: $runtimeType"); + return Column( + children: [ + Padding( + padding: const EdgeInsets.only( + right: 30, + ), + child: RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset( + Assets.svg.circleDollarSign, + width: 48, + height: 48, + ), + Center( + child: Padding( + padding: const EdgeInsets.all(10), + child: RichText( + textAlign: TextAlign.start, + text: TextSpan( + children: [ + TextSpan( + text: "Currency", + style: STextStyles.desktopTextSmall(context), + ), + TextSpan( + text: + "\n\nProtect your Stack Wallet with a strong password. Stack Wallet does not store " + "your password, and is therefore NOT able to restore it. Keep your password safe and secure.", + style: + STextStyles.desktopTextExtraExtraSmall(context), + ), + ], + ), + ), + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Padding( + padding: EdgeInsets.all( + 10, + ), + child: NewPasswordButton(), + ), + ], + ), + ], + ), + ), + ), + ], + ); + } +} + +class NewPasswordButton extends ConsumerWidget { + const NewPasswordButton({ + Key? key, + }) : super(key: key); + @override + Widget build(BuildContext context, WidgetRef ref) { + Future chooseCurrency() async { + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return CurrencyDialog(); + }, + ); + } + + return SizedBox( + width: 200, + height: 48, + child: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonColor(context), + onPressed: () { + chooseCurrency(); + }, + child: Text( + "Change currency", + style: STextStyles.button(context), + ), + ), + ); + } +} diff --git a/lib/pages_desktop_specific/home/settings_menu/language_settings/language_dialog.dart b/lib/pages_desktop_specific/home/settings_menu/language_settings/language_dialog.dart new file mode 100644 index 000000000..d07c9729f --- /dev/null +++ b/lib/pages_desktop_specific/home/settings_menu/language_settings/language_dialog.dart @@ -0,0 +1,349 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/languages_enum.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; + +import '../../../../utilities/assets.dart'; +import '../../../../utilities/theme/stack_colors.dart'; +import '../../../../widgets/icon_widgets/x_icon.dart'; +import '../../../../widgets/rounded_container.dart'; +import '../../../../widgets/textfield_icon_button.dart'; + +class LanguageDialog extends ConsumerStatefulWidget { + const LanguageDialog({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _LanguageDialog(); +} + +class _LanguageDialog extends ConsumerState { + late final TextEditingController searchLanguageController; + + late final FocusNode searchLanguageFocusNode; + + final languages = Language.values.map((e) => e.description).toList(); + + late String current; + late List listWithoutSelected; + + void onTap(int index) { + if (index == 0 || current.isEmpty) { + // ignore if already selected language + return; + } + current = listWithoutSelected[index]; + listWithoutSelected.remove(current); + listWithoutSelected.insert(0, current); + ref.read(prefsChangeNotifierProvider).language = current; + } + + BorderRadius? _borderRadius(int index) { + if (index == 0 && listWithoutSelected.length == 1) { + return BorderRadius.circular( + Constants.size.circularBorderRadius, + ); + } else if (index == 0) { + return BorderRadius.vertical( + top: Radius.circular( + Constants.size.circularBorderRadius, + ), + ); + } else if (index == listWithoutSelected.length - 1) { + return BorderRadius.vertical( + bottom: Radius.circular( + Constants.size.circularBorderRadius, + ), + ); + } + return null; + } + + String filter = ""; + + List _filtered() { + return listWithoutSelected + .where( + (element) => element.toLowerCase().contains(filter.toLowerCase())) + .toList(); + } + + @override + void initState() { + searchLanguageController = TextEditingController(); + + searchLanguageFocusNode = FocusNode(); + + super.initState(); + } + + @override + void dispose() { + searchLanguageController.dispose(); + + searchLanguageFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + current = ref + .watch(prefsChangeNotifierProvider.select((value) => value.language)); + + listWithoutSelected = languages; + if (current.isNotEmpty) { + listWithoutSelected.remove(current); + listWithoutSelected.insert(0, current); + } + listWithoutSelected = _filtered(); + + return DesktopDialog( + maxHeight: 700, + maxWidth: 600, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.all(32), + child: Text( + "Select language", + style: STextStyles.desktopH3(context), + textAlign: TextAlign.center, + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Expanded( + flex: 24, + child: NestedScrollView( + floatHeaderSlivers: true, + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor( + context), + sliver: SliverToBoxAdapter( + child: Padding( + padding: + EdgeInsets.symmetric(vertical: 16, horizontal: 32), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: false, + enableSuggestions: false, + controller: searchLanguageController, + focusNode: searchLanguageFocusNode, + style: STextStyles.desktopTextMedium(context) + .copyWith( + height: 2, + ), + textAlign: TextAlign.left, + decoration: standardInputDecoration("Search", + searchLanguageFocusNode, context) + .copyWith( + prefixIcon: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 16, + ), + child: SvgPicture.asset( + Assets.svg.search, + width: 16, + height: 16, + ), + ), + suffixIcon: searchLanguageController + .text.isNotEmpty + ? Padding( + padding: + const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + searchLanguageController + .text = ""; + filter = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ]; + }, + body: Builder( + builder: (context) { + return CustomScrollView( + slivers: [ + SliverOverlapInjector( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor( + context, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .popupBG, + borderRadius: _borderRadius(index), + ), + child: Padding( + padding: const EdgeInsets.all(4), + key: Key( + "desktopSelectLanguage_${listWithoutSelected[index]}"), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 32), + child: RoundedContainer( + padding: const EdgeInsets.all(0), + color: index == 0 + ? Theme.of(context) + .extension()! + .currencyListItemBG + : Theme.of(context) + .extension()! + .popupBG, + child: RawMaterialButton( + onPressed: () async { + onTap(index); + }, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Radio( + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, + value: true, + groupValue: index == 0, + onChanged: (_) { + onTap(index); + }, + ), + ), + const SizedBox( + width: 12, + ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + listWithoutSelected[index], + key: (index == 0) + ? const Key( + "desktopSettingsSelectedLanguageText") + : null, + style: + STextStyles.largeMedium14( + context), + ), + const SizedBox( + height: 2, + ), + Text( + listWithoutSelected[index], + key: (index == 0) + ? const Key( + "desktopSettingsSelectedLanguageTextDescription") + : null, + style: + STextStyles.itemSubtitle( + context), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ); + }, + childCount: listWithoutSelected.length, + ), + ), + ], + ); + }, + ), + ), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(32), + child: Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Save Changes", + onPressed: () {}, + ), + ) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages_desktop_specific/home/settings_menu/language_settings.dart b/lib/pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart similarity index 88% rename from lib/pages_desktop_specific/home/settings_menu/language_settings.dart rename to lib/pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart index 7655188e1..97b807b3b 100644 --- a/lib/pages_desktop_specific/home/settings_menu/language_settings.dart +++ b/lib/pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings/language_dialog.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -87,6 +88,17 @@ class ChangeLanguageButton extends ConsumerWidget { }) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { + Future chooseLanguage() async { + await showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return LanguageDialog(); + }, + ); + } + return SizedBox( width: 200, height: 48, @@ -94,7 +106,9 @@ class ChangeLanguageButton extends ConsumerWidget { style: Theme.of(context) .extension()! .getPrimaryEnabledButtonColor(context), - onPressed: () {}, + onPressed: () { + chooseLanguage(); + }, child: Text( "Change language", style: STextStyles.button(context), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 613e0d509..a6f23ffdc 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -85,17 +85,16 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_sear import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; import 'package:stackwallet/pages/wallets_view/wallets_view.dart'; import 'package:stackwallet/pages_desktop_specific/create_password/create_password_view.dart'; -import 'package:stackwallet/pages_desktop_specific/home/advanced_settings/advanced_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/desktop_settings_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart'; +import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/appearance_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/backup_and_restore_settings.dart'; -import 'package:stackwallet/pages_desktop_specific/home/settings_menu/currency_settings.dart'; -import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings.dart'; +import 'package:stackwallet/pages_desktop_specific/home/settings_menu/currency_settings/currency_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/nodes_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/security_settings.dart'; import 'package:stackwallet/pages_desktop_specific/home/settings_menu/settings_menu.dart'; @@ -107,6 +106,9 @@ import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:tuple/tuple.dart'; +import 'pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart'; +import 'pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart'; + class RouteGenerator { static const bool useMaterialPageRoute = true; @@ -1084,29 +1086,67 @@ class RouteGenerator { case WalletKeysDesktopPopup.routeName: if (args is List) { - return getRoute( - shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => WalletKeysDesktopPopup( + return FadePageRoute( + WalletKeysDesktopPopup( words: args, ), - settings: RouteSettings( + RouteSettings( name: settings.name, ), ); + // return getRoute( + // shouldUseMaterialRoute: useMaterialPageRoute, + // builder: (_) => WalletKeysDesktopPopup( + // words: args, + // ), + // settings: RouteSettings( + // name: settings.name, + // ), + // ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case UnlockWalletKeysDesktop.routeName: + if (args is String) { + return FadePageRoute( + UnlockWalletKeysDesktop( + walletId: args, + ), + RouteSettings( + name: settings.name, + ), + ); + // return getRoute( + // shouldUseMaterialRoute: useMaterialPageRoute, + // builder: (_) => WalletKeysDesktopPopup( + // words: args, + // ), + // settings: RouteSettings( + // name: settings.name, + // ), + // ); } return _routeError("${settings.name} invalid args: ${args.toString()}"); case QRCodeDesktopPopupContent.routeName: if (args is String) { - return getRoute( - shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => QRCodeDesktopPopupContent( + return FadePageRoute( + QRCodeDesktopPopupContent( value: args, ), - settings: RouteSettings( + RouteSettings( name: settings.name, ), ); + // return getRoute( + // shouldUseMaterialRoute: useMaterialPageRoute, + // builder: (_) => QRCodeDesktopPopupContent( + // value: args, + // ), + // settings: RouteSettings( + // name: settings.name, + // ), + // ); } return _routeError("${settings.name} invalid args: ${args.toString()}"); @@ -1180,3 +1220,37 @@ class RouteGenerator { builder: (_) => errorView); } } + +class FadePageRoute extends PageRoute { + FadePageRoute(this.child, RouteSettings settings) : _settings = settings; + + final Widget child; + final RouteSettings _settings; + + @override + Color? get barrierColor => null; + + @override + String? get barrierLabel => null; + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return FadeTransition( + opacity: animation, + child: child, + ); + } + + @override + bool get maintainState => true; + + @override + Duration get transitionDuration => const Duration(milliseconds: 100); + + @override + RouteSettings get settings => _settings; +} diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index 78535c19b..386ea1cd8 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -101,6 +101,7 @@ class _SVG { String get downloadFolder => "assets/svg/folder-down.svg"; String get lock => "assets/svg/lock-keyhole.svg"; String get network => "assets/svg/network-wired.svg"; + String get networkWired => "assets/svg/network-wired-2.svg"; String get addressBook => "assets/svg/address-book.svg"; String get addressBook2 => "assets/svg/address-book2.svg"; String get arrowRotate3 => "assets/svg/rotate-exclamation.svg"; diff --git a/lib/widgets/conditional_parent.dart b/lib/widgets/conditional_parent.dart index 6db50c6e8..757c8f992 100644 --- a/lib/widgets/conditional_parent.dart +++ b/lib/widgets/conditional_parent.dart @@ -4,13 +4,13 @@ class ConditionalParent extends StatelessWidget { const ConditionalParent({ Key? key, required this.condition, - required this.child, required this.builder, + required this.child, }) : super(key: key); final bool condition; - final Widget child; final Widget Function(Widget) builder; + final Widget child; @override Widget build(BuildContext context) { diff --git a/lib/widgets/custom_buttons/blue_text_button.dart b/lib/widgets/custom_buttons/blue_text_button.dart index 18757ab93..aa7f75b1f 100644 --- a/lib/widgets/custom_buttons/blue_text_button.dart +++ b/lib/widgets/custom_buttons/blue_text_button.dart @@ -5,11 +5,16 @@ import 'package:stackwallet/providers/ui/color_theme_provider.dart'; import 'package:stackwallet/utilities/text_styles.dart'; class BlueTextButton extends ConsumerStatefulWidget { - const BlueTextButton({Key? key, required this.text, this.onTap}) - : super(key: key); + const BlueTextButton({ + Key? key, + required this.text, + this.onTap, + this.enabled = true, + }) : super(key: key); final String text; final VoidCallback? onTap; + final bool enabled; @override ConsumerState createState() => _BlueTextButtonState(); @@ -17,38 +22,42 @@ class BlueTextButton extends ConsumerStatefulWidget { class _BlueTextButtonState extends ConsumerState with SingleTickerProviderStateMixin { - late AnimationController controller; - late Animation animation; + AnimationController? controller; + Animation? animation; late Color color; @override void initState() { - color = ref.read(colorThemeProvider.state).state.buttonTextBorderless; - controller = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 100), - ); - animation = ColorTween( - begin: ref.read(colorThemeProvider.state).state.buttonTextBorderless, - end: ref - .read(colorThemeProvider.state) - .state - .buttonTextBorderless - .withOpacity(0.4), - ).animate(controller); + if (widget.enabled) { + color = ref.read(colorThemeProvider.state).state.buttonTextBorderless; + controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 100), + ); + animation = ColorTween( + begin: ref.read(colorThemeProvider.state).state.buttonTextBorderless, + end: ref + .read(colorThemeProvider.state) + .state + .buttonTextBorderless + .withOpacity(0.4), + ).animate(controller!); - animation.addListener(() { - setState(() { - color = animation.value as Color; + animation!.addListener(() { + setState(() { + color = animation!.value as Color; + }); }); - }); + } else { + color = ref.read(colorThemeProvider.state).state.textSubtitle1; + } super.initState(); } @override void dispose() { - controller.dispose(); + controller?.dispose(); super.dispose(); } @@ -59,11 +68,13 @@ class _BlueTextButtonState extends ConsumerState text: TextSpan( text: widget.text, style: STextStyles.link2(context).copyWith(color: color), - recognizer: TapGestureRecognizer() - ..onTap = () { - widget.onTap?.call(); - controller.forward().then((value) => controller.reverse()); - }, + recognizer: widget.enabled + ? (TapGestureRecognizer() + ..onTap = () { + widget.onTap?.call(); + controller?.forward().then((value) => controller?.reverse()); + }) + : null, ), ); } diff --git a/lib/widgets/node_card.dart b/lib/widgets/node_card.dart index 1f0287013..bf9d2746e 100644 --- a/lib/widgets/node_card.dart +++ b/lib/widgets/node_card.dart @@ -1,15 +1,31 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/electrumx_rpc/electrumx.dart'; +import 'package:stackwallet/models/node_model.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; +import 'package:stackwallet/utilities/enums/sync_type_enum.dart'; +import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/test_epic_box_connection.dart'; +import 'package:stackwallet/utilities/test_monero_node_connection.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/custom_buttons/blue_text_button.dart'; +import 'package:stackwallet/widgets/expandable.dart'; import 'package:stackwallet/widgets/node_options_sheet.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; +import 'package:tuple/tuple.dart'; class NodeCard extends ConsumerStatefulWidget { const NodeCard({ @@ -30,6 +46,125 @@ class NodeCard extends ConsumerStatefulWidget { class _NodeCardState extends ConsumerState { String _status = "Disconnected"; late final String nodeId; + bool _advancedIsExpanded = true; + + Future _notifyWalletsOfUpdatedNode(WidgetRef ref) async { + final managers = ref + .read(walletsChangeNotifierProvider) + .managers + .where((e) => e.coin == widget.coin); + final prefs = ref.read(prefsChangeNotifierProvider); + + switch (prefs.syncType) { + case SyncingType.currentWalletOnly: + for (final manager in managers) { + if (manager.isActiveWallet) { + manager.updateNode(true); + } else { + manager.updateNode(false); + } + } + break; + case SyncingType.selectedWalletsAtStartup: + final List walletIdsToSync = prefs.walletIdsSyncOnStartup; + for (final manager in managers) { + if (walletIdsToSync.contains(manager.walletId)) { + manager.updateNode(true); + } else { + manager.updateNode(false); + } + } + break; + case SyncingType.allWalletsOnStartup: + for (final manager in managers) { + manager.updateNode(true); + } + break; + } + } + + Future _testConnection( + NodeModel node, + BuildContext context, + WidgetRef ref, + ) async { + bool testPassed = false; + + switch (widget.coin) { + case Coin.epicCash: + try { + final String uriString = "${node.host}:${node.port}/v1/version"; + + testPassed = await testEpicBoxNodeConnection(Uri.parse(uriString)); + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Warning); + } + break; + + case Coin.monero: + case Coin.wownero: + try { + final uri = Uri.parse(node.host); + if (uri.scheme.startsWith("http")) { + final String path = uri.path.isEmpty ? "/json_rpc" : uri.path; + + String uriString = "${uri.scheme}://${uri.host}:${node.port}$path"; + + testPassed = await testMoneroNodeConnection(Uri.parse(uriString)); + } + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Warning); + } + + break; + + case Coin.bitcoin: + case Coin.litecoin: + case Coin.dogecoin: + case Coin.firo: + case Coin.bitcoinTestNet: + case Coin.firoTestNet: + case Coin.dogecoinTestNet: + case Coin.bitcoincash: + case Coin.litecoinTestNet: + case Coin.namecoin: + case Coin.bitcoincashTestnet: + final client = ElectrumX( + host: node.host, + port: node.port, + useSSL: node.useSSL, + failovers: [], + prefs: ref.read(prefsChangeNotifierProvider), + ); + + try { + testPassed = await client.ping(); + } catch (_) { + testPassed = false; + } + + break; + } + + if (testPassed) { + // showFloatingFlushBar( + // type: FlushBarType.success, + // message: "Server ping success", + // context: context, + // ); + } else { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + iconAsset: Assets.svg.circleAlert, + message: "Could not connect to node", + context: context, + ), + ); + } + + return testPassed; + } @override void initState() { @@ -50,91 +185,176 @@ class _NodeCardState extends ConsumerState { _status = "Disconnected"; } + final isDesktop = Util.isDesktop; + return RoundedWhiteContainer( padding: const EdgeInsets.all(0), - child: RawMaterialButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - onPressed: () { - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - builder: (_) => NodeOptionsSheet( - nodeId: nodeId, - coin: widget.coin, - popBackToRoute: widget.popBackToRoute, + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + child: ConditionalParent( + condition: !isDesktop, + builder: (child) { + return RawMaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), ), + onPressed: () { + showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + builder: (_) => NodeOptionsSheet( + nodeId: nodeId, + coin: widget.coin, + popBackToRoute: widget.popBackToRoute, + ), + ); + }, + child: child, ); }, - child: Padding( - padding: const EdgeInsets.all(12), - child: Row( - children: [ - Container( - width: 24, - height: 24, - decoration: BoxDecoration( - color: _node.name == DefaultNodes.defaultName - ? Theme.of(context) - .extension()! - .buttonBackSecondary - : Theme.of(context) - .extension()! - .infoItemIcons - .withOpacity(0.2), - borderRadius: BorderRadius.circular(100), + child: ConditionalParent( + condition: isDesktop, + builder: (child) { + return Expandable( + onExpandChanged: (state) { + setState(() { + _advancedIsExpanded = state == ExpandableState.expanded; + }); + }, + header: child, + body: Padding( + padding: const EdgeInsets.only( + bottom: 24, ), - child: Center( - child: SvgPicture.asset( - Assets.svg.node, - height: 11, - width: 14, + child: Row( + children: [ + const SizedBox( + width: 66, + ), + BlueTextButton( + text: "Connect", + enabled: _status == "Disconnected", + onTap: () async { + final canConnect = + await _testConnection(_node, context, ref); + if (!canConnect) { + return; + } + + await ref + .read(nodeServiceChangeNotifierProvider) + .setPrimaryNodeFor( + coin: widget.coin, + node: _node, + shouldNotifyListeners: true, + ); + + await _notifyWalletsOfUpdatedNode(ref); + }, + ), + const SizedBox( + width: 48, + ), + BlueTextButton( + text: "Details", + onTap: () { + Navigator.of(context).pushNamed( + NodeDetailsView.routeName, + arguments: Tuple3( + widget.coin, + widget.nodeId, + widget.popBackToRoute, + ), + ); + }, + ), + ], + ), + ), + ); + }, + child: Padding( + padding: EdgeInsets.all(isDesktop ? 16 : 12), + child: Row( + children: [ + Container( + width: isDesktop ? 40 : 24, + height: isDesktop ? 40 : 24, + decoration: BoxDecoration( color: _node.name == DefaultNodes.defaultName ? Theme.of(context) .extension()! - .accentColorDark + .buttonBackSecondary : Theme.of(context) .extension()! - .infoItemIcons, + .infoItemIcons + .withOpacity(0.2), + borderRadius: BorderRadius.circular(100), + ), + child: Center( + child: SvgPicture.asset( + Assets.svg.node, + height: isDesktop ? 18 : 11, + width: isDesktop ? 20 : 14, + color: _node.name == DefaultNodes.defaultName + ? Theme.of(context) + .extension()! + .accentColorDark + : Theme.of(context) + .extension()! + .infoItemIcons, + ), ), ), - ), - const SizedBox( - width: 12, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _node.name, - style: STextStyles.titleBold12(context), + const SizedBox( + width: 12, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _node.name, + style: STextStyles.titleBold12(context), + ), + const SizedBox( + height: 2, + ), + Text( + _status, + style: STextStyles.label(context), + ), + ], + ), + const Spacer(), + if (!isDesktop) + SvgPicture.asset( + Assets.svg.network, + color: _status == "Connected" + ? Theme.of(context) + .extension()! + .accentColorGreen + : Theme.of(context) + .extension()! + .buttonBackSecondary, + width: 20, + height: 20, ), - const SizedBox( - height: 2, - ), - Text( - _status, - style: STextStyles.label(context), - ), - ], - ), - const Spacer(), - SvgPicture.asset( - Assets.svg.network, - color: _status == "Connected" - ? Theme.of(context) + if (isDesktop) + SvgPicture.asset( + _advancedIsExpanded + ? Assets.svg.chevronDown + : Assets.svg.chevronUp, + width: 12, + height: 6, + color: Theme.of(context) .extension()! - .accentColorGreen - : Theme.of(context) - .extension()! - .buttonBackSecondary, - width: 20, - height: 20, - ), - ], + .textSubtitle1, + ), + ], + ), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 29d25cb84..b84135f4a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -237,6 +237,7 @@ flutter: - assets/svg/rotate-exclamation.svg - assets/svg/folder-down.svg - assets/svg/network-wired.svg + - assets/svg/network-wired-2.svg - assets/svg/address-book.svg - assets/svg/address-book2.svg - assets/svg/arrow-right.svg diff --git a/test/widget_tests/node_card_test.dart b/test/widget_tests/node_card_test.dart index 2728fc304..22e0661bf 100644 --- a/test/widget_tests/node_card_test.dart +++ b/test/widget_tests/node_card_test.dart @@ -1,15 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:stackwallet/models/node_model.dart'; import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/theme/light_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; -import 'package:stackwallet/services/node_service.dart'; +import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/widgets/node_card.dart'; import 'package:stackwallet/widgets/node_options_sheet.dart'; @@ -190,13 +191,22 @@ void main() { await tester.tap(find.byType(NodeCard)); await tester.pumpAndSettle(); - expect(find.text("Connect"), findsOneWidget); - expect(find.text("Details"), findsOneWidget); - expect(find.byType(NodeOptionsSheet), findsOneWidget); - expect(find.byType(Text), findsNWidgets(7)); + if (Util.isDesktop) { + expect(find.text("Connect"), findsNothing); + expect(find.text("Details"), findsNothing); + + verify(nodeService.getPrimaryNodeFor(coin: Coin.bitcoin)).called(1); + verify(nodeService.getNodeById(id: "node id")).called(1); + } else { + expect(find.text("Connect"), findsOneWidget); + expect(find.text("Details"), findsOneWidget); + expect(find.byType(NodeOptionsSheet), findsOneWidget); + expect(find.byType(Text), findsNWidgets(7)); + + verify(nodeService.getPrimaryNodeFor(coin: Coin.bitcoin)).called(2); + verify(nodeService.getNodeById(id: "node id")).called(2); + } - verify(nodeService.getPrimaryNodeFor(coin: Coin.bitcoin)).called(2); - verify(nodeService.getNodeById(id: "node id")).called(2); verify(nodeService.addListener(any)).called(1); verifyNoMoreInteractions(nodeService);