diff --git a/lib/src/screens/nodes/node_create_or_edit_page.dart b/lib/src/screens/nodes/node_create_or_edit_page.dart index db8a92a81..f5bd48704 100644 --- a/lib/src/screens/nodes/node_create_or_edit_page.dart +++ b/lib/src/screens/nodes/node_create_or_edit_page.dart @@ -1,91 +1,45 @@ import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/widgets/primary_button.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; -import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; class NodeCreateOrEditPage extends BasePage { - NodeCreateOrEditPage( - {required this.nodeCreateOrEditViewModel, this.editingNode, this.isSelected, this.type}) - : _formKey = GlobalKey(), - _addressController = TextEditingController(), - _pathController = TextEditingController(), - _portController = TextEditingController(), - _loginController = TextEditingController(), - _passwordController = TextEditingController() { - reaction((_) => nodeCreateOrEditViewModel.address, (String address) { - if (address != _addressController.text) { - _addressController.text = address; - } - }); - - reaction((_) => nodeCreateOrEditViewModel.port, (String port) { - if (port != _portController.text) { - _portController.text = port; - } - }); - - if (nodeCreateOrEditViewModel.hasAuthCredentials) { - reaction((_) => nodeCreateOrEditViewModel.login, (String login) { - if (login != _loginController.text) { - _loginController.text = login; - } - }); - - reaction((_) => nodeCreateOrEditViewModel.password, (String password) { - if (password != _passwordController.text) { - _passwordController.text = password; - } - }); - } - - _addressController - .addListener(() => nodeCreateOrEditViewModel.address = _addressController.text); - _pathController.addListener(() => nodeCreateOrEditViewModel.path = _pathController.text); - _portController.addListener(() => nodeCreateOrEditViewModel.port = _portController.text); - _loginController.addListener(() => nodeCreateOrEditViewModel.login = _loginController.text); - _passwordController - .addListener(() => nodeCreateOrEditViewModel.password = _passwordController.text); - } + NodeCreateOrEditPage({ + required this.nodeCreateOrEditViewModel, + this.editingNode, + this.isSelected, + this.type, + }) : _formKey = GlobalKey(); final GlobalKey _formKey; - final TextEditingController _addressController; - final TextEditingController _pathController; - final TextEditingController _portController; - final TextEditingController _loginController; - final TextEditingController _passwordController; + final NodeCreateOrEditViewModel nodeCreateOrEditViewModel; + final Node? editingNode; + final bool? isSelected; + final WalletType? type; @override String get title => editingNode != null ? S.current.edit_node : S.current.node_new; @override Widget trailing(BuildContext context) => IconButton( - onPressed: () async { - await nodeCreateOrEditViewModel.scanQRCodeForNewNode(context); - }, + onPressed: () => nodeCreateOrEditViewModel.scanQRCodeForNewNode(context), splashColor: Colors.transparent, highlightColor: Colors.transparent, hoverColor: Colors.transparent, - icon: Image.asset( - 'assets/images/qr_code_icon.png', - ), + icon: Image.asset('assets/images/qr_code_icon.png'), ); - final NodeCreateOrEditViewModel nodeCreateOrEditViewModel; - final Node? editingNode; - final bool? isSelected; - final WalletType? type; - @override Widget body(BuildContext context) { reaction( @@ -93,116 +47,110 @@ class NodeCreateOrEditPage extends BasePage { (ExecutionState state) { if (state is ExecutedSuccessfullyState) { WidgetsBinding.instance.addPostFrameCallback( - (_) { - showPopUp( - context: context, - builder: (BuildContext context) => AlertWithOneAction( - alertTitle: S.of(context).new_node_testing, - alertContent: state.payload as bool - ? S.of(context).node_connection_successful - : S.of(context).node_connection_failed, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop(), - ), - ); - }, + (_) => showPopUp( + context: context, + builder: (context) => AlertWithOneAction( + alertTitle: S.of(context).new_node_testing, + alertContent: state.payload as bool + ? S.of(context).node_connection_successful + : S.of(context).node_connection_failed, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop(), + ), + ), ); } if (state is FailureState) { WidgetsBinding.instance.addPostFrameCallback( - (_) { - showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).error, - alertContent: state.error, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop(), - ); - }, - ); - }, + (_) => showPopUp( + context: context, + builder: (context) => AlertWithOneAction( + alertTitle: S.of(context).error, + alertContent: state.error, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop(), + ), + ), ); } }, ); return Container( - padding: EdgeInsets.only(left: 24, right: 24), - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 24.0, top: 8), - content: NodeForm( - formKey: _formKey, - nodeViewModel: nodeCreateOrEditViewModel, - editingNode: editingNode, - type: type, + padding: const EdgeInsets.only(left: 24, right: 24), + child: ScrollableWithBottomSection( + contentPadding: const EdgeInsets.only(bottom: 24.0, top: 8), + content: NodeForm( + formKey: _formKey, + nodeViewModel: nodeCreateOrEditViewModel, + editingNode: editingNode, + type: type, + ), + bottomSectionPadding: const EdgeInsets.only(bottom: 24), + bottomSection: Observer( + builder: (_) => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: Container( + padding: const EdgeInsets.only(right: 8.0), + child: LoadingPrimaryButton( + onPressed: () async { + final confirmed = await showPopUp( + context: context, + builder: (context) => AlertWithTwoActions( + alertTitle: S.of(context).remove_node, + alertContent: S.of(context).remove_node_message, + rightButtonText: S.of(context).remove, + leftButtonText: S.of(context).cancel, + actionRightButton: () => Navigator.pop(context, true), + actionLeftButton: () => Navigator.pop(context, false), + ), + ) ?? + false; + + if (confirmed) { + await editingNode!.delete(); + Navigator.of(context).pop(); + } + }, + text: S.of(context).delete, + isDisabled: editingNode == null || + !nodeCreateOrEditViewModel.isReady || + (isSelected ?? false), + color: Theme.of(context).colorScheme.errorContainer, + textColor: Theme.of(context).colorScheme.onErrorContainer, + ), + ), + ), + Flexible( + child: Container( + padding: const EdgeInsets.only(left: 8.0), + child: PrimaryButton( + onPressed: () async { + if (_formKey.currentState != null && !_formKey.currentState!.validate()) { + return; + } + + await nodeCreateOrEditViewModel.save( + editingNode: editingNode, saveAsCurrent: isSelected ?? false); + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + text: S.of(context).save, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, + isDisabled: (!nodeCreateOrEditViewModel.isReady) || + (nodeCreateOrEditViewModel.connectionState is IsExecutingState), + ), + ), + ), + ], ), - bottomSectionPadding: EdgeInsets.only(bottom: 24), - bottomSection: Observer( - builder: (_) => Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: Container( - padding: EdgeInsets.only(right: 8.0), - child: LoadingPrimaryButton( - onPressed: () async { - final confirmed = await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).remove_node, - alertContent: S.of(context).remove_node_message, - rightButtonText: S.of(context).remove, - leftButtonText: S.of(context).cancel, - actionRightButton: () => Navigator.pop(context, true), - actionLeftButton: () => Navigator.pop(context, false), - ); - }, - ) ?? - false; - - if (confirmed) { - await editingNode!.delete(); - Navigator.of(context).pop(); - } - }, - text: S.of(context).delete, - isDisabled: editingNode == null || - !nodeCreateOrEditViewModel.isReady || - (isSelected ?? false), - color: Theme.of(context).colorScheme.errorContainer, - textColor: Theme.of(context).colorScheme.onErrorContainer, - ), - ), - ), - Flexible( - child: Container( - padding: EdgeInsets.only(left: 8.0), - child: PrimaryButton( - onPressed: () async { - if (_formKey.currentState != null && - !_formKey.currentState!.validate()) { - return; - } - - await nodeCreateOrEditViewModel.save( - editingNode: editingNode, saveAsCurrent: isSelected ?? false); - if (context.mounted) { - Navigator.of(context).pop(); - } - }, - text: S.of(context).save, - color: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary, - isDisabled: (!nodeCreateOrEditViewModel.isReady) || - (nodeCreateOrEditViewModel.connectionState is IsExecutingState), - ), - )), - ], - )), - )); + ), + ), + ); } } diff --git a/lib/src/screens/nodes/widgets/node_form.dart b/lib/src/screens/nodes/widgets/node_form.dart index 05a365c21..3d3dd175e 100644 --- a/lib/src/screens/nodes/widgets/node_form.dart +++ b/lib/src/screens/nodes/widgets/node_form.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/core/node_address_validator.dart'; import 'package:cake_wallet/core/node_port_validator.dart'; import 'package:cake_wallet/core/socks_proxy_node_address_validator.dart'; +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; @@ -8,15 +9,30 @@ import 'package:cw_core/node.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:cake_wallet/generated/i18n.dart'; import 'package:mobx/mobx.dart'; -class NodeForm extends StatelessWidget { +class NodeForm extends StatefulWidget { NodeForm({ required this.nodeViewModel, required this.formKey, this.editingNode, this.type, + }); + + final NodeCreateOrEditViewModel nodeViewModel; + final GlobalKey formKey; + final Node? editingNode; + final WalletType? type; + + @override + State createState() => + _NodeFormState(nodeViewModel: nodeViewModel, editingNode: this.editingNode); +} + +class _NodeFormState extends State { + _NodeFormState({ + required this.nodeViewModel, + Node? editingNode, }) : _addressController = TextEditingController(text: editingNode?.uri.host.toString()), _pathController = TextEditingController(text: editingNode?.path.toString()), _portController = TextEditingController( @@ -28,28 +44,25 @@ class NodeForm extends StatelessWidget { _socksAddressController = TextEditingController(text: editingNode?.socksProxyAddress) { if (editingNode != null) { nodeViewModel - ..setAddress((editingNode!.uri.host.toString())) - ..setPath((editingNode!.path.toString())) - ..setPort((editingNode!.uri.hasPort ? editingNode!.uri.port.toString() : '')) - ..setPassword((editingNode!.password ?? '')) - ..setLogin((editingNode!.login ?? '')) - ..setSSL((editingNode!.isSSL)) - ..setTrusted((editingNode!.trusted)) - ..setIsEnabledForAutoSwitching((editingNode!.isEnabledForAutoSwitching)) - ..setSocksProxy((editingNode!.useSocksProxy)) - ..setSocksProxyAddress((editingNode!.socksProxyAddress ?? '')); + ..setAddress(editingNode.uri.host.toString()) + ..setPath(editingNode.path.toString()) + ..setPort(editingNode.uri.hasPort ? editingNode.uri.port.toString() : '') + ..setPassword(editingNode.password ?? '') + ..setLogin(editingNode.login ?? '') + ..setSSL(editingNode.isSSL) + ..setTrusted(editingNode.trusted) + ..setIsEnabledForAutoSwitching(editingNode.isEnabledForAutoSwitching) + ..setSocksProxy(editingNode.useSocksProxy) + ..setSocksProxyAddress(editingNode.socksProxyAddress ?? ''); } + if (nodeViewModel.hasAuthCredentials) { reaction((_) => nodeViewModel.login, (String login) { - if (login != _loginController.text) { - _loginController.text = login; - } + if (login != _loginController.text) _loginController.text = login; }); reaction((_) => nodeViewModel.password, (String password) { - if (password != _passwordController.text) { - _passwordController.text = password; - } + if (password != _passwordController.text) _passwordController.text = password; }); } reaction((_) => nodeViewModel.address, (String address) { @@ -59,15 +72,11 @@ class NodeForm extends StatelessWidget { }); reaction((_) => nodeViewModel.port, (String port) { - if (port != _portController.text) { - _portController.text = port; - } + if (port != _portController.text) _portController.text = port; }); reaction((_) => nodeViewModel.path, (String path) { - if (path != _pathController.text) { - _pathController.text = path; - } + if (path != _pathController.text) _pathController.text = path; }); _addressController.addListener(() => nodeViewModel.address = _addressController.text); @@ -80,9 +89,6 @@ class NodeForm extends StatelessWidget { } final NodeCreateOrEditViewModel nodeViewModel; - final GlobalKey formKey; - final Node? editingNode; - final WalletType? type; final TextEditingController _addressController; final TextEditingController _pathController; @@ -92,54 +98,46 @@ class NodeForm extends StatelessWidget { final TextEditingController _socksAddressController; @override - Widget build(BuildContext context) { - return Form( - key: formKey, - child: Column( - children: [ - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _addressController, - hintText: S.of(context).node_address, - validator: type == WalletType.decred - ? NodeAddressValidatorDecredBlankException() - : NodeAddressValidator(), - ), - ) - ], - ), - SizedBox(height: 10.0), + Widget build(BuildContext context) => Form( + key: widget.formKey, + child: Column(children: [ + Row(children: [ + Expanded( + child: BaseTextFormField( + controller: _addressController, + hintText: S.of(context).node_address, + validator: widget.type == WalletType.decred + ? NodeAddressValidatorDecredBlankException() + : NodeAddressValidator(), + ), + ) + ]), + const SizedBox(height: 10), if (nodeViewModel.hasPathSupport) ...[ - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _pathController, - hintText: "/path", - validator: NodePathValidator(), - ), - ) - ], - ), - SizedBox(height: 10.0), - ], - Row( - children: [ + Row(children: [ Expanded( child: BaseTextFormField( - controller: _portController, - hintText: S.of(context).node_port, - keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false), - validator: NodePortValidator(), + controller: _pathController, + hintText: "/path", + validator: NodePathValidator(), ), ) - ], - ), - SizedBox(height: 10.0), + ]), + const SizedBox(height: 10), + ], + Row(children: [ + Expanded( + child: BaseTextFormField( + controller: _portController, + hintText: S.of(context).node_port, + keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false), + validator: NodePortValidator(), + ), + ) + ]), + const SizedBox(height: 10), Padding( - padding: EdgeInsets.only(top: 20), + padding: const EdgeInsets.only(top: 20), child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, @@ -156,9 +154,9 @@ class NodeForm extends StatelessWidget { ], ), ), - SizedBox(height: 10.0), + const SizedBox(height: 10), Padding( - padding: EdgeInsets.only(top: 20), + padding: const EdgeInsets.only(top: 20), child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, @@ -175,31 +173,27 @@ class NodeForm extends StatelessWidget { ], ), ), - SizedBox(height: 10.0), + const SizedBox(height: 10), if (nodeViewModel.hasAuthCredentials) ...[ - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _loginController, - hintText: S.of(context).login, - ), - ) - ], - ), - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _passwordController, - hintText: S.of(context).password, - ), - ) - ], - ), + Row(children: [ + Expanded( + child: BaseTextFormField( + controller: _loginController, + hintText: S.of(context).login, + ), + ) + ]), + const SizedBox(height: 10), + Row(children: [ + Expanded( + child: BaseTextFormField( + controller: _passwordController, + hintText: S.of(context).password, + ), + ) + ]), Padding( - padding: EdgeInsets.only(top: 20), + padding: const EdgeInsets.only(top: 20), child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, @@ -217,66 +211,60 @@ class NodeForm extends StatelessWidget { ), ), Observer( - builder: (_) => Column( + builder: (_) => Column(children: [ + if (nodeViewModel.usesEmbeddedProxy) ...[ + Padding( + padding: const EdgeInsets.only(top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, children: [ - if (nodeViewModel.usesEmbeddedProxy) ...[ - Padding( - padding: EdgeInsets.only(top: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - StandardCheckbox( - value: nodeViewModel.usesEmbeddedProxy, - gradientBackground: false, - borderColor: Theme.of(context).dividerColor, - iconColor: Theme.of(context).colorScheme.primary, - onChanged: null, - caption: 'Embedded Tor SOCKS Proxy', - ), - ], - ), - ), - ], - Padding( - padding: EdgeInsets.only(top: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - StandardCheckbox( - value: nodeViewModel.useSocksProxy, - borderColor: Theme.of(context).colorScheme.surfaceContainerHighest, - iconColor: Theme.of(context).colorScheme.primary, - onChanged: (value) { - if (!value) { - _socksAddressController.text = ''; - } - nodeViewModel.useSocksProxy = value; - }, - caption: 'SOCKS Proxy', - ), - ], - ), + StandardCheckbox( + value: nodeViewModel.usesEmbeddedProxy, + gradientBackground: false, + borderColor: Theme.of(context).dividerColor, + iconColor: Theme.of(context).colorScheme.primary, + onChanged: null, + caption: 'Embedded Tor SOCKS Proxy', ), - if (nodeViewModel.useSocksProxy) ...[ - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _socksAddressController, - hintText: '[:]', - validator: SocksProxyNodeAddressValidator(), - )) - ], - ), - ] ], - )), + ), + ), + ], + Padding( + padding: const EdgeInsets.only(top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + StandardCheckbox( + value: nodeViewModel.useSocksProxy, + borderColor: Theme.of(context).colorScheme.surfaceContainerHighest, + iconColor: Theme.of(context).colorScheme.primary, + onChanged: (value) { + if (!value) _socksAddressController.text = ''; + nodeViewModel.useSocksProxy = value; + }, + caption: 'SOCKS Proxy', + ), + ], + ), + ), + if (nodeViewModel.useSocksProxy) ...[ + const SizedBox(height: 10), + Row(children: [ + Expanded( + child: BaseTextFormField( + controller: _socksAddressController, + hintText: '[:]', + validator: SocksProxyNodeAddressValidator(), + ), + ) + ]), + ] + ]), + ), ] - ], - ), - ); - } + ]), + ); }