diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart index 77349f399..87aee413e 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart @@ -8,7 +8,6 @@ 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/providers/global/node_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; @@ -20,15 +19,18 @@ 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/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.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/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart'; import 'package:uuid/uuid.dart'; -import 'package:stackwallet/utilities/util.dart'; - enum AddEditNodeViewType { add, edit } class AddEditNodeView extends ConsumerStatefulWidget { @@ -59,6 +61,7 @@ class _AddEditNodeViewState extends ConsumerState { late final AddEditNodeViewType viewType; late final Coin coin; late final String? nodeId; + late final bool isDesktop; late bool saveEnabled; late bool testConnectionEnabled; @@ -162,8 +165,198 @@ class _AddEditNodeViewState extends ConsumerState { return testPassed; } + Future attemptSave() async { + final canConnect = await _testConnection(showFlushBar: false); + + bool? shouldSave; + + if (!canConnect) { + await showDialog( + context: context, + useSafeArea: true, + barrierDismissible: true, + builder: (_) => isDesktop + ? DesktopDialog( + maxWidth: 440, + maxHeight: 300, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only( + top: 32, + ), + child: Row( + children: [ + const SizedBox( + width: 32, + ), + Text( + "Server currently unreachable", + style: STextStyles.desktopH3(context), + ), + ], + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + top: 16, + bottom: 32, + ), + child: Column( + children: [ + const Spacer(), + Text( + "Would you like to save this node anyways?", + style: STextStyles.desktopTextMedium(context), + ), + const Spacer( + flex: 2, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + desktopMed: true, + onPressed: () => Navigator.of( + context, + rootNavigator: true, + ).pop(false), + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Save", + desktopMed: true, + onPressed: () => Navigator.of( + context, + rootNavigator: true, + ).pop(true), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ) + : StackDialog( + title: "Server currently unreachable", + message: "Would you like to save this node anyways?", + leftButton: TextButton( + onPressed: () async { + Navigator.of(context).pop(false); + }, + child: Text( + "Cancel", + style: STextStyles.button(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorDark), + ), + ), + rightButton: TextButton( + onPressed: () async { + Navigator.of(context).pop(true); + }, + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonColor(context), + child: Text( + "Save", + style: STextStyles.button(context), + ), + ), + ), + ).then((value) { + if (value is bool && value) { + shouldSave = true; + } else { + shouldSave = false; + } + }); + } + + if (!canConnect && !shouldSave!) { + // return without saving + return; + } + + final formData = ref.read(nodeFormDataProvider); + + // strip unused path + String address = formData.host!; + if (coin == Coin.monero || coin == Coin.wownero || coin == Coin.epicCash) { + if (address.startsWith("http")) { + final uri = Uri.parse(address); + address = "${uri.scheme}://${uri.host}"; + } + } + + switch (viewType) { + case AddEditNodeViewType.add: + NodeModel node = NodeModel( + host: address, + port: formData.port!, + name: formData.name!, + id: const Uuid().v1(), + useSSL: formData.useSSL!, + loginName: formData.login, + enabled: true, + coinName: coin.name, + isFailover: formData.isFailover!, + isDown: false, + ); + + await ref.read(nodeServiceChangeNotifierProvider).add( + node, + formData.password, + true, + ); + if (mounted) { + Navigator.of(context) + .popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete)); + } + break; + case AddEditNodeViewType.edit: + NodeModel node = NodeModel( + host: address, + port: formData.port!, + name: formData.name!, + id: nodeId!, + useSSL: formData.useSSL!, + loginName: formData.login, + enabled: true, + coinName: coin.name, + isFailover: formData.isFailover!, + isDown: false, + ); + + await ref.read(nodeServiceChangeNotifierProvider).add( + node, + formData.password, + true, + ); + if (mounted) { + Navigator.of(context) + .popUntil(ModalRoute.withName(widget.routeOnSuccessOrDelete)); + } + break; + } + } + @override void initState() { + isDesktop = Util.isDesktop; ref.refresh(nodeFormDataProvider); viewType = widget.viewType; @@ -196,279 +389,203 @@ class _AddEditNodeViewState extends ConsumerState { .select((value) => value.getNodeById(id: nodeId!))) : null; - return Scaffold( - backgroundColor: Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed(const Duration(milliseconds: 75)); - } - if (mounted) { - Navigator.of(context).pop(); - } - }, - ), - title: Text( - viewType == AddEditNodeViewType.edit ? "Edit node" : "Add node", - style: STextStyles.navBarTitle(context), - ), - actions: [ - if (viewType == AddEditNodeViewType.edit) - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("deleteNodeAppBarButtonKey"), - size: 36, - shadows: const [], - color: Theme.of(context).extension()!.background, - icon: SvgPicture.asset( - Assets.svg.trash, - color: Theme.of(context) - .extension()! - .accentColorDark, - width: 20, - height: 20, - ), - onPressed: () async { - Navigator.popUntil(context, - ModalRoute.withName(widget.routeOnSuccessOrDelete)); + return ConditionalParent( + condition: !isDesktop, + builder: (child) => Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed(const Duration(milliseconds: 75)); + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + title: Text( + viewType == AddEditNodeViewType.edit ? "Edit node" : "Add node", + style: STextStyles.navBarTitle(context), + ), + actions: [ + if (viewType == AddEditNodeViewType.edit) + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("deleteNodeAppBarButtonKey"), + size: 36, + shadows: const [], + color: + Theme.of(context).extension()!.background, + icon: SvgPicture.asset( + Assets.svg.trash, + color: Theme.of(context) + .extension()! + .accentColorDark, + width: 20, + height: 20, + ), + onPressed: () async { + Navigator.popUntil(context, + ModalRoute.withName(widget.routeOnSuccessOrDelete)); - await ref.read(nodeServiceChangeNotifierProvider).delete( - nodeId!, - true, - ); - }, + await ref.read(nodeServiceChangeNotifierProvider).delete( + nodeId!, + true, + ); + }, + ), ), ), - ), - ], - ), - body: Padding( - padding: const EdgeInsets.only( - top: 12, - left: 12, - right: 12, - bottom: 12, + ], ), - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(4), - child: ConstrainedBox( - constraints: - BoxConstraints(minHeight: constraints.maxHeight - 8), - child: IntrinsicHeight( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - NodeForm( - node: node, - secureStore: widget.secureStore, - readOnly: false, - coin: widget.coin, - onChanged: (canSave, canTest) { - if (canSave != saveEnabled && - canTest != testConnectionEnabled) { - setState(() { - saveEnabled = canSave; - testConnectionEnabled = canTest; - }); - } else if (canSave != saveEnabled) { - setState(() { - saveEnabled = canSave; - }); - } else if (canTest != testConnectionEnabled) { - setState(() { - testConnectionEnabled = canTest; - }); - } - }, - ), - const Spacer(), - TextButton( - onPressed: testConnectionEnabled - ? () async { - await _testConnection(); - } - : null, - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonColor(context), - child: Text( - "Test connection", - style: STextStyles.button(context).copyWith( - color: testConnectionEnabled - ? Theme.of(context) - .extension()! - .textDark - : Theme.of(context) - .extension()! - .textWhite, - ), - ), - ), - const SizedBox(height: 16), - TextButton( - style: saveEnabled - ? Theme.of(context) - .extension()! - .getPrimaryEnabledButtonColor(context) - : Theme.of(context) - .extension()! - .getPrimaryDisabledButtonColor(context), - onPressed: saveEnabled - ? () async { - final canConnect = await _testConnection( - showFlushBar: false); - - bool? shouldSave; - - if (!canConnect) { - await showDialog( - context: context, - useSafeArea: true, - barrierDismissible: true, - builder: (_) => StackDialog( - title: "Server currently unreachable", - message: - "Would you like to save this node anyways?", - leftButton: TextButton( - onPressed: () async { - Navigator.of(context).pop(false); - }, - child: Text( - "Cancel", - style: STextStyles.button(context) - .copyWith( - color: Theme.of(context) - .extension< - StackColors>()! - .accentColorDark), - ), - ), - rightButton: TextButton( - onPressed: () async { - Navigator.of(context).pop(true); - }, - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonColor( - context), - child: Text( - "Save", - style: STextStyles.button(context), - ), - ), - ), - ).then((value) { - if (value is bool && value) { - shouldSave = true; - } else { - shouldSave = false; - } - }); - } - - if (!canConnect && !shouldSave!) { - // return without saving - return; - } - - final formData = - ref.read(nodeFormDataProvider); - - // strip unused path - String address = formData.host!; - if (coin == Coin.monero || - coin == Coin.wownero || - coin == Coin.epicCash) { - if (address.startsWith("http")) { - final uri = Uri.parse(address); - address = "${uri.scheme}://${uri.host}"; - } - } - - switch (viewType) { - case AddEditNodeViewType.add: - NodeModel node = NodeModel( - host: address, - port: formData.port!, - name: formData.name!, - id: const Uuid().v1(), - useSSL: formData.useSSL!, - loginName: formData.login, - enabled: true, - coinName: coin.name, - isFailover: formData.isFailover!, - isDown: false, - ); - - await ref - .read( - nodeServiceChangeNotifierProvider) - .add( - node, - formData.password, - true, - ); - if (mounted) { - Navigator.of(context).popUntil( - ModalRoute.withName( - widget.routeOnSuccessOrDelete)); - } - break; - case AddEditNodeViewType.edit: - NodeModel node = NodeModel( - host: address, - port: formData.port!, - name: formData.name!, - id: nodeId!, - useSSL: formData.useSSL!, - loginName: formData.login, - enabled: true, - coinName: coin.name, - isFailover: formData.isFailover!, - isDown: false, - ); - - await ref - .read( - nodeServiceChangeNotifierProvider) - .add( - node, - formData.password, - true, - ); - if (mounted) { - Navigator.of(context).popUntil( - ModalRoute.withName( - widget.routeOnSuccessOrDelete)); - } - break; - } - } - : null, - child: Text( - "Save", - style: STextStyles.button(context), - ), - ), - ], + body: Padding( + padding: const EdgeInsets.only( + top: 12, + left: 12, + right: 12, + bottom: 12, + ), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(4), + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight - 8), + child: IntrinsicHeight( + child: child, ), ), ), + ); + }, + ), + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopDialog( + maxWidth: 580, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + const SizedBox( + width: 8, + ), + const AppBarBackButton( + iconSize: 24, + size: 40, + ), + Text( + "Add new node", + style: STextStyles.desktopH3(context), + ) + ], ), - ); - }, + Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + top: 16, + bottom: 32, + ), + child: child, + ), + ], + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + NodeForm( + node: node, + secureStore: widget.secureStore, + readOnly: false, + coin: widget.coin, + onChanged: (canSave, canTest) { + if (canSave != saveEnabled && + canTest != testConnectionEnabled) { + setState(() { + saveEnabled = canSave; + testConnectionEnabled = canTest; + }); + } else if (canSave != saveEnabled) { + setState(() { + saveEnabled = canSave; + }); + } else if (canTest != testConnectionEnabled) { + setState(() { + testConnectionEnabled = canTest; + }); + } + }, + ), + if (!isDesktop) const Spacer(), + if (isDesktop) + const SizedBox( + height: 78, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Test connection", + enabled: testConnectionEnabled, + desktopMed: true, + onPressed: testConnectionEnabled + ? () async { + await _testConnection(); + } + : null, + ), + ), + if (isDesktop) + const SizedBox( + width: 16, + ), + if (isDesktop) + Expanded( + child: PrimaryButton( + label: "Save", + enabled: saveEnabled, + desktopMed: true, + onPressed: saveEnabled ? attemptSave : null, + ), + ), + ], + ), + if (!isDesktop) + const SizedBox( + height: 16, + ), + if (!isDesktop) + TextButton( + style: saveEnabled + ? Theme.of(context) + .extension()! + .getPrimaryEnabledButtonColor(context) + : Theme.of(context) + .extension()! + .getPrimaryDisabledButtonColor(context), + onPressed: saveEnabled ? attemptSave : null, + child: Text( + "Save", + style: STextStyles.button(context), + ), + ), + ], ), ), ); diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart index e3743d54e..a93b64be3 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart @@ -9,6 +9,7 @@ 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/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'; import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; import 'package:tuple/tuple.dart'; @@ -17,11 +18,13 @@ class CoinNodesView extends ConsumerStatefulWidget { const CoinNodesView({ Key? key, required this.coin, + this.rootNavigator = false, }) : super(key: key); static const String routeName = "/coinNodes"; final Coin coin; + final bool rootNavigator; @override ConsumerState createState() => _CoinNodesViewState(); @@ -63,12 +66,17 @@ class _CoinNodesViewState extends ConsumerState { textAlign: TextAlign.center, ), Expanded( - child: const DesktopDialogCloseButton(), + child: DesktopDialogCloseButton( + onPressedOverride: Navigator.of( + context, + rootNavigator: widget.rootNavigator, + ).pop, + ), ), ], ), Padding( - padding: EdgeInsets.only( + padding: const EdgeInsets.only( left: 32, right: 32, ), @@ -83,14 +91,19 @@ class _CoinNodesViewState extends ConsumerState { ), textAlign: TextAlign.left, ), - RichText( - text: TextSpan( - text: 'Add new nodes', - style: - STextStyles.desktopTextExtraSmall(context).copyWith( - color: Colors.blueAccent, - ), - ), + BlueTextButton( + text: "Add new node", + onTap: () { + Navigator.of(context).pushNamed( + AddEditNodeView.routeName, + arguments: Tuple4( + AddEditNodeViewType.add, + widget.coin, + null, + CoinNodesView.routeName, + ), + ); + }, ), ], ), diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart index c5f797e37..c5e666ce2 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart @@ -17,7 +17,13 @@ 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/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/delete_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:tuple/tuple.dart'; class NodeDetailsView extends ConsumerStatefulWidget { @@ -48,6 +54,8 @@ class _NodeDetailsViewState extends ConsumerState { late final String nodeId; late final String popRouteName; + bool _desktopReadOnly = true; + @override initState() { secureStore = widget.secureStore; @@ -126,130 +134,239 @@ class _NodeDetailsViewState extends ConsumerState { } if (testPassed) { - showFloatingFlushBar( - type: FlushBarType.success, - message: "Server ping success", - context: context, + unawaited( + showFloatingFlushBar( + type: FlushBarType.success, + message: "Server ping success", + context: context, + ), ); } else { - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Server unreachable", - context: context, + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Server unreachable", + context: context, + ), ); } } @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed(const Duration(milliseconds: 75)); - } - if (mounted) { - Navigator.of(context).pop(); - } - }, - ), - title: Text( - "Node details", - style: STextStyles.navBarTitle(context), - ), - actions: [ - if (!nodeId.startsWith("default")) - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("nodeDetailsEditNodeAppBarButtonKey"), - size: 36, - shadows: const [], - color: Theme.of(context).extension()!.background, - icon: SvgPicture.asset( - Assets.svg.pencil, - color: Theme.of(context) - .extension()! - .accentColorDark, - width: 20, - height: 20, + final isDesktop = Util.isDesktop; + + final node = ref.watch(nodeServiceChangeNotifierProvider + .select((value) => value.getNodeById(id: nodeId))); + + return ConditionalParent( + condition: !isDesktop, + builder: (child) => Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed(const Duration(milliseconds: 75)); + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + title: Text( + "Node details", + style: STextStyles.navBarTitle(context), + ), + actions: [ + if (!nodeId.startsWith("default")) + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("nodeDetailsEditNodeAppBarButtonKey"), + size: 36, + shadows: const [], + color: + Theme.of(context).extension()!.background, + icon: SvgPicture.asset( + Assets.svg.pencil, + color: Theme.of(context) + .extension()! + .accentColorDark, + width: 20, + height: 20, + ), + onPressed: () { + Navigator.of(context).pushNamed( + AddEditNodeView.routeName, + arguments: Tuple4( + AddEditNodeViewType.edit, + coin, + nodeId, + popRouteName, + ), + ); + }, ), - onPressed: () { - Navigator.of(context).pushNamed( - AddEditNodeView.routeName, - arguments: Tuple4( - AddEditNodeViewType.edit, - coin, - nodeId, - popRouteName, - ), - ); - }, ), ), - ), - ], - ), - body: Padding( - padding: const EdgeInsets.only( - top: 12, - left: 12, - right: 12, + ], ), - child: LayoutBuilder( - builder: (context, constraints) { - final node = ref.watch(nodeServiceChangeNotifierProvider - .select((value) => value.getNodeById(id: nodeId))); - - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(4), - child: ConstrainedBox( - constraints: - BoxConstraints(minHeight: constraints.maxHeight - 8), - child: IntrinsicHeight( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - NodeForm( - node: node, - secureStore: secureStore, - readOnly: true, - coin: coin, - ), - const Spacer(), - TextButton( - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonColor(context), - onPressed: () async { - await _testConnection(ref, context); - }, - child: Text( - "Test connection", - style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark), - ), - ), - const SizedBox(height: 16), - ], + body: Padding( + padding: const EdgeInsets.only( + top: 12, + left: 12, + right: 12, + ), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(4), + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight - 8), + child: IntrinsicHeight( + child: child, ), ), ), + ); + }, + ), + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopDialog( + maxWidth: 580, + maxHeight: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + const SizedBox( + width: 8, + ), + const AppBarBackButton( + iconSize: 24, + size: 40, + ), + Text( + "Node details", + style: STextStyles.desktopH3(context), + ) + ], ), - ); - }, + Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + top: 16, + bottom: 32, + ), + child: child, + ), + ], + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + NodeForm( + node: node, + secureStore: secureStore, + readOnly: isDesktop ? _desktopReadOnly : true, + coin: coin, + ), + if (!isDesktop) const Spacer(), + if (isDesktop) + const SizedBox( + height: 22, + ), + if (isDesktop) + SizedBox( + height: 56, + child: _desktopReadOnly + ? null + : Row( + children: [ + Expanded( + child: DeleteButton( + label: "Delete node", + desktopMed: true, + onPressed: () async { + Navigator.of(context).pop(); + + await ref + .read(nodeServiceChangeNotifierProvider) + .delete( + node!.id, + true, + ); + }, + ), + ), + const SizedBox( + width: 16, + ), + const Spacer(), + ], + ), + ), + if (isDesktop && !_desktopReadOnly) + const SizedBox( + height: 45, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Test connection", + desktopMed: true, + onPressed: () async { + await _testConnection(ref, context); + }, + ), + ), + if (isDesktop) + const SizedBox( + width: 16, + ), + if (isDesktop) + Expanded( + child: !nodeId.startsWith("default") + ? PrimaryButton( + label: _desktopReadOnly ? "Edit" : "Save", + desktopMed: true, + onPressed: () async { + final shouldSave = _desktopReadOnly == false; + setState(() { + _desktopReadOnly = !_desktopReadOnly; + }); + + if (shouldSave) { + // todo save node + } + }, + ) + : Container(), + ), + ], + ), + if (!isDesktop) + const SizedBox( + height: 16, + ), + ], ), ), ); diff --git a/lib/pages_desktop_specific/home/settings_menu/nodes_settings.dart b/lib/pages_desktop_specific/home/settings_menu/nodes_settings.dart index abba6cccc..1d0317037 100644 --- a/lib/pages_desktop_specific/home/settings_menu/nodes_settings.dart +++ b/lib/pages_desktop_specific/home/settings_menu/nodes_settings.dart @@ -4,6 +4,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart'; import 'package:stackwallet/providers/global/node_service_provider.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -167,8 +168,22 @@ class _NodesSettings extends ConsumerState { onPressed: () { showDialog( context: context, - builder: (context) => CoinNodesView( - coin: coin, + builder: (context) => Navigator( + initialRoute: CoinNodesView.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + FadePageRoute( + CoinNodesView( + coin: coin, + rootNavigator: true, + ), + const RouteSettings( + name: CoinNodesView.routeName, + ), + ), + ]; + }, ), ); }, diff --git a/lib/utilities/theme/stack_colors.dart b/lib/utilities/theme/stack_colors.dart index 8a241db06..8249dccf4 100644 --- a/lib/utilities/theme/stack_colors.dart +++ b/lib/utilities/theme/stack_colors.dart @@ -1468,6 +1468,20 @@ class StackColors extends ThemeExtension { } } + ButtonStyle? getDeleteEnabledButtonColor(BuildContext context) => + Theme.of(context).textButtonTheme.style?.copyWith( + backgroundColor: MaterialStateProperty.all( + textFieldErrorBG, + ), + ); + + ButtonStyle? getDeleteDisabledButtonColor(BuildContext context) => + Theme.of(context).textButtonTheme.style?.copyWith( + backgroundColor: MaterialStateProperty.all( + buttonBackSecondaryDisabled, + ), + ); + ButtonStyle? getPrimaryEnabledButtonColor(BuildContext context) => Theme.of(context).textButtonTheme.style?.copyWith( backgroundColor: MaterialStateProperty.all( diff --git a/lib/widgets/desktop/delete_button.dart b/lib/widgets/desktop/delete_button.dart new file mode 100644 index 000000000..e64c85f34 --- /dev/null +++ b/lib/widgets/desktop/delete_button.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.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/utilities/util.dart'; +import 'package:stackwallet/widgets/desktop/custom_text_button.dart'; + +class DeleteButton extends StatelessWidget { + const DeleteButton({ + Key? key, + this.width, + this.height, + this.label, + this.onPressed, + this.enabled = true, + this.desktopMed = false, + }) : super(key: key); + + final double? width; + final double? height; + final String? label; + final VoidCallback? onPressed; + final bool enabled; + final bool desktopMed; + + TextStyle getStyle(bool isDesktop, BuildContext context) { + if (isDesktop) { + if (desktopMed) { + return STextStyles.desktopTextExtraSmall(context).copyWith( + color: enabled + ? Theme.of(context).extension()!.accentColorRed + : Theme.of(context) + .extension()! + .buttonTextSecondaryDisabled, + ); + } else { + return enabled + ? STextStyles.desktopButtonSecondaryEnabled(context).copyWith( + color: + Theme.of(context).extension()!.accentColorRed) + : STextStyles.desktopButtonSecondaryDisabled(context); + } + } else { + return STextStyles.button(context).copyWith( + color: enabled + ? Theme.of(context).extension()!.accentColorRed + : Theme.of(context) + .extension()! + .buttonTextSecondaryDisabled, + ); + } + } + + @override + Widget build(BuildContext context) { + final isDesktop = Util.isDesktop; + + return CustomTextButtonBase( + height: desktopMed ? 56 : height, + width: width, + textButton: TextButton( + onPressed: enabled ? onPressed : null, + style: enabled + ? Theme.of(context) + .extension()! + .getDeleteEnabledButtonColor(context) + : Theme.of(context) + .extension()! + .getDeleteDisabledButtonColor(context), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.svg.trash, + width: 20, + height: 20, + color: enabled + ? Theme.of(context).extension()!.accentColorRed + : Theme.of(context) + .extension()! + .buttonTextSecondaryDisabled, + ), + if (label != null) + const SizedBox( + width: 10, + ), + if (label != null) + Text( + label!, + style: getStyle(isDesktop, context), + ), + ], + ), + ), + ); + } +}