mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-20 06:08:34 +02:00
* feat: Add support for automatic node switching. This change: - Adds isTrustedNodeForSwitching property to Node class. - Adds UI Checkbox to mark a node as trusted for automatic switching in Node create or edit form. - Implements checkNodeHealth method in wallet classes to verify node connectivity. - Adds NodeSwitchingService for managing automatic node switching based on health checks. - Adds setting option for user to enable/disable automated node switching * fix(automatic-node-switch): Set node health check interval to 5 minutes in NodeSwitchingService * refactor: Update handler method * feat: Automatic Node Switching Improvements This change: - Moves enable automatic node switch to the Manage Nodes Page - Adds check to verify node health before attempting to sync in EVM, Sol and Tron Wallets. - Modifies trusted ndoes list for monero - Adds trigger for Node Health Check when app resumes from background * fix: Increase default request timeout for Tron and update health check interval for node switch * feat: Enhance Node health check functionality. This change adds additional checks for token balance in EVM, Solana and Tron Wallets in checkNodeHealth. * fix: Update health check interval to 30 seconds in NodeSwitchingService * Update bitcoin_electrum_server_list.yml * fix: Electru wallet not connecting to new node after switching * fix: Automated Node Switch not working on Decred Wallet * fix: Add more nodes for AutoSwitching This change: - Enables more nodes across wallet types for auto switching - Modifies CTA for checkbox - Renames flags to fit * fix: Add migration function for setting up default nodes for autoswitching * fix: Review and testing fixes * chore: Adjust balance and history fetch timer
280 lines
11 KiB
Dart
280 lines
11 KiB
Dart
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/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';
|
|
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 {
|
|
NodeForm({
|
|
required this.nodeViewModel,
|
|
required this.formKey,
|
|
this.editingNode,
|
|
this.type,
|
|
}) : _addressController = TextEditingController(text: editingNode?.uri.host.toString()),
|
|
_pathController = TextEditingController(text: editingNode?.path.toString()),
|
|
_portController = TextEditingController(text: editingNode?.uri.port.toString()),
|
|
_loginController = TextEditingController(text: editingNode?.login),
|
|
_passwordController = TextEditingController(text: editingNode?.password),
|
|
_socksAddressController = TextEditingController(text: editingNode?.socksProxyAddress) {
|
|
if (editingNode != null) {
|
|
nodeViewModel
|
|
..setAddress((editingNode!.uri.host.toString()))
|
|
..setPath((editingNode!.path.toString()))
|
|
..setPort((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;
|
|
}
|
|
});
|
|
|
|
reaction((_) => nodeViewModel.password, (String password) {
|
|
if (password != _passwordController.text) {
|
|
_passwordController.text = password;
|
|
}
|
|
});
|
|
}
|
|
reaction((_) => nodeViewModel.address, (String address) {
|
|
if (address != _addressController.text) {
|
|
_addressController.text = address;
|
|
}
|
|
});
|
|
|
|
reaction((_) => nodeViewModel.port, (String port) {
|
|
if (port != _portController.text) {
|
|
_portController.text = port;
|
|
}
|
|
});
|
|
|
|
reaction((_) => nodeViewModel.path, (String path) {
|
|
if (path != _pathController.text) {
|
|
_pathController.text = path;
|
|
}
|
|
});
|
|
|
|
_addressController.addListener(() => nodeViewModel.address = _addressController.text);
|
|
_pathController.addListener(() => nodeViewModel.path = _pathController.text);
|
|
_portController.addListener(() => nodeViewModel.port = _portController.text);
|
|
_loginController.addListener(() => nodeViewModel.login = _loginController.text);
|
|
_passwordController.addListener(() => nodeViewModel.password = _passwordController.text);
|
|
_socksAddressController
|
|
.addListener(() => nodeViewModel.socksProxyAddress = _socksAddressController.text);
|
|
}
|
|
|
|
final NodeCreateOrEditViewModel nodeViewModel;
|
|
final GlobalKey<FormState> formKey;
|
|
final Node? editingNode;
|
|
final WalletType? type;
|
|
|
|
final TextEditingController _addressController;
|
|
final TextEditingController _pathController;
|
|
final TextEditingController _portController;
|
|
final TextEditingController _loginController;
|
|
final TextEditingController _passwordController;
|
|
final TextEditingController _socksAddressController;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Form(
|
|
key: formKey,
|
|
child: Column(
|
|
children: <Widget>[
|
|
Row(
|
|
children: <Widget>[
|
|
Expanded(
|
|
child: BaseTextFormField(
|
|
controller: _addressController,
|
|
hintText: S.of(context).node_address,
|
|
validator: type == WalletType.decred
|
|
? NodeAddressValidatorDecredBlankException()
|
|
: NodeAddressValidator(),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
SizedBox(height: 10.0),
|
|
if (nodeViewModel.hasPathSupport) ...[
|
|
Row(
|
|
children: <Widget>[
|
|
Expanded(
|
|
child: BaseTextFormField(
|
|
controller: _pathController,
|
|
hintText: "/path",
|
|
validator: NodePathValidator(),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
SizedBox(height: 10.0),
|
|
],
|
|
Row(
|
|
children: <Widget>[
|
|
Expanded(
|
|
child: BaseTextFormField(
|
|
controller: _portController,
|
|
hintText: S.of(context).node_port,
|
|
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
|
|
validator: NodePortValidator(),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
SizedBox(height: 10.0),
|
|
Padding(
|
|
padding: EdgeInsets.only(top: 20),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.max,
|
|
children: [
|
|
Observer(
|
|
builder: (_) => StandardCheckbox(
|
|
value: nodeViewModel.useSSL,
|
|
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
|
iconColor: Theme.of(context).colorScheme.primary,
|
|
onChanged: (value) => nodeViewModel.useSSL = value,
|
|
caption: S.of(context).use_ssl,
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 10.0),
|
|
Padding(
|
|
padding: EdgeInsets.only(top: 20),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.max,
|
|
children: [
|
|
Observer(
|
|
builder: (_) => StandardCheckbox(
|
|
value: nodeViewModel.isEnabledForAutoSwitching,
|
|
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
|
iconColor: Theme.of(context).colorScheme.primary,
|
|
onChanged: (value) => nodeViewModel.isEnabledForAutoSwitching = value,
|
|
caption: S.current.enable_for_auto_switching,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 10.0),
|
|
if (nodeViewModel.hasAuthCredentials) ...[
|
|
Row(
|
|
children: <Widget>[
|
|
Expanded(
|
|
child: BaseTextFormField(
|
|
controller: _loginController,
|
|
hintText: S.of(context).login,
|
|
),
|
|
)
|
|
],
|
|
),
|
|
SizedBox(height: 10.0),
|
|
Row(
|
|
children: <Widget>[
|
|
Expanded(
|
|
child: BaseTextFormField(
|
|
controller: _passwordController,
|
|
hintText: S.of(context).password,
|
|
),
|
|
)
|
|
],
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.only(top: 20),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.max,
|
|
children: [
|
|
Observer(
|
|
builder: (_) => StandardCheckbox(
|
|
value: nodeViewModel.trusted,
|
|
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
|
iconColor: Theme.of(context).colorScheme.primary,
|
|
onChanged: (value) => nodeViewModel.trusted = value,
|
|
caption: S.of(context).trusted,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Observer(
|
|
builder: (_) => Column(
|
|
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',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (nodeViewModel.useSocksProxy) ...[
|
|
SizedBox(height: 10.0),
|
|
Row(
|
|
children: <Widget>[
|
|
Expanded(
|
|
child: BaseTextFormField(
|
|
controller: _socksAddressController,
|
|
hintText: '[<ip>:]<port>',
|
|
validator: SocksProxyNodeAddressValidator(),
|
|
))
|
|
],
|
|
),
|
|
]
|
|
],
|
|
)),
|
|
]
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|