mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
Cw 602 nano bad rep (#1356)
* add support for paths in node settings * update translations and fixes * fix node path * add rep warning flag * update translations * code cleanup [skip ci] * add additional node options * add migration * update transaction history rpc to be under the limit * review fixes [skip ci] * [skip ci] updates * move n2_node.dart * minor code improvements * more minor code cleanup
This commit is contained in:
@@ -8,6 +8,7 @@ import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/introducing_card.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||
@@ -183,6 +184,22 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
if (!dashboardViewModel.showRepWarning) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: DashBoardRoundedCardWidget(
|
||||
title: S.current.rep_warning,
|
||||
subTitle: S.current.rep_warning_sub,
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.changeRep),
|
||||
onClose: () {
|
||||
dashboardViewModel.settingsStore.shouldShowRepWarning = false;
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return ListView.separated(
|
||||
|
||||
@@ -5,10 +5,12 @@ 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/store/settings_store.dart';
|
||||
import 'package:cake_wallet/themes/extensions/address_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/n2_node.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
@@ -21,9 +23,7 @@ class NanoChangeRepPage extends BasePage {
|
||||
: _wallet = wallet,
|
||||
_settingsStore = settingsStore,
|
||||
_addressController = TextEditingController(),
|
||||
_formKey = GlobalKey<FormState>() {
|
||||
_addressController.text = nano!.getRepresentative(wallet);
|
||||
}
|
||||
_formKey = GlobalKey<FormState>() {}
|
||||
|
||||
final TextEditingController _addressController;
|
||||
final WalletBase _wallet;
|
||||
@@ -34,105 +34,314 @@ class NanoChangeRepPage extends BasePage {
|
||||
@override
|
||||
String get title => S.current.change_rep;
|
||||
|
||||
N2Node getCurrentRepNode(List<N2Node> nodes) {
|
||||
final currentRepAccount = nano!.getRepresentative(_wallet);
|
||||
final currentNode = nodes.firstWhere(
|
||||
(node) => node.account == currentRepAccount,
|
||||
orElse: () => N2Node(
|
||||
account: currentRepAccount,
|
||||
alias: currentRepAccount,
|
||||
score: 0,
|
||||
uptime: "???",
|
||||
weight: 0,
|
||||
),
|
||||
);
|
||||
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||
content: Container(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: AddressTextField(
|
||||
controller: _addressController,
|
||||
onURIScanned: (uri) {
|
||||
final paymentRequest = PaymentRequest.fromUri(uri);
|
||||
_addressController.text = paymentRequest.address;
|
||||
},
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.qrCode,
|
||||
],
|
||||
buttonColor: Theme.of(context).extension<AddressTheme>()!.actionButtonColor,
|
||||
validator: AddressValidator(type: CryptoCurrency.nano),
|
||||
child: FutureBuilder(
|
||||
future: nano!.getN2Reps(_wallet),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == null) {
|
||||
return SizedBox();
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
topSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
topSection: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: AddressTextField(
|
||||
controller: _addressController,
|
||||
onURIScanned: (uri) {
|
||||
final paymentRequest = PaymentRequest.fromUri(uri);
|
||||
_addressController.text = paymentRequest.address;
|
||||
},
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.qrCode,
|
||||
],
|
||||
buttonColor:
|
||||
Theme.of(context).extension<AddressTheme>()!.actionButtonColor,
|
||||
validator: AddressValidator(type: CryptoCurrency.nano),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
S.current.nano_current_rep,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
_buildSingleRepresentative(
|
||||
context,
|
||||
getCurrentRepNode(snapshot.data as List<N2Node>),
|
||||
isList: false,
|
||||
),
|
||||
Divider(height: 20),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
S.current.nano_pick_new_rep,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: Container(
|
||||
child: Column(
|
||||
children: _getRepresentativeWidgets(context, snapshot.data as List<N2Node>),
|
||||
),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
onPressed: () => _onSubmit(context),
|
||||
text: S.of(context).change,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
)),
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onSubmit(BuildContext context) async {
|
||||
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final confirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).change_rep,
|
||||
alertContent: S.of(context).change_rep_message,
|
||||
rightButtonText: S.of(context).change,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () => Navigator.pop(context, true),
|
||||
actionLeftButton: () => Navigator.pop(context, false));
|
||||
}) ??
|
||||
false;
|
||||
|
||||
if (confirmed) {
|
||||
try {
|
||||
_settingsStore.defaultNanoRep = _addressController.text;
|
||||
|
||||
await nano!.changeRep(_wallet, _addressController.text);
|
||||
|
||||
// reset this flag whenever we successfully change reps:
|
||||
_settingsStore.shouldShowRepWarning = true;
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).successful,
|
||||
alertContent: S.of(context).change_rep_successful,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.pop(context));
|
||||
});
|
||||
} catch (e) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: e.toString(),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.pop(context));
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _getRepresentativeWidgets(BuildContext context, List<N2Node>? list) {
|
||||
if (list == null) {
|
||||
return [];
|
||||
}
|
||||
final List<Widget> ret = [];
|
||||
for (final N2Node node in list) {
|
||||
if (node.alias != null && node.alias!.trim().isNotEmpty) {
|
||||
ret.add(_buildSingleRepresentative(context, node));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Widget _buildSingleRepresentative(BuildContext context, N2Node rep, {bool isList = true}) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
if (isList)
|
||||
Divider(
|
||||
height: 2,
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
onPressed: () async {
|
||||
if (!isList) {
|
||||
return;
|
||||
}
|
||||
_addressController.text = rep.account!;
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: const EdgeInsetsDirectional.only(start: 24),
|
||||
width: MediaQuery.of(context).size.width * 0.50,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
_sanitizeAlias(rep.alias),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 7),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: "${S.current.voting_weight}: ${rep.weight.toString()}%",
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: '',
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "${S.current.uptime}: ",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.secondaryTextColor,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: rep.uptime,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.secondaryTextColor,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsetsDirectional.only(end: 24, start: 14),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.verified,
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 50,
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(13),
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: const AlignmentDirectional(-0.03, 0.03),
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: Text(
|
||||
(rep.score).toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState != null &&
|
||||
!_formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final confirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).change_rep,
|
||||
alertContent: S.of(context).change_rep_message,
|
||||
rightButtonText: S.of(context).change,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () => Navigator.pop(context, true),
|
||||
actionLeftButton: () => Navigator.pop(context, false));
|
||||
}) ??
|
||||
false;
|
||||
|
||||
if (confirmed) {
|
||||
try {
|
||||
_settingsStore.defaultNanoRep = _addressController.text;
|
||||
|
||||
await nano!.changeRep(_wallet, _addressController.text);
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).successful,
|
||||
alertContent: S.of(context).change_rep_successful,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.pop(context));
|
||||
});
|
||||
} catch (e) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: e.toString(),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.pop(context));
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
},
|
||||
text: S.of(context).change,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
)),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _sanitizeAlias(String? alias) {
|
||||
if (alias != null) {
|
||||
return alias.replaceAll(RegExp(r'[^a-zA-Z_.!?_;:-]'), '');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class NodeCreateOrEditPage extends BasePage {
|
||||
NodeCreateOrEditPage({required this.nodeCreateOrEditViewModel,this.editingNode, this.isSelected})
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_addressController = TextEditingController(),
|
||||
_pathController = TextEditingController(),
|
||||
_portController = TextEditingController(),
|
||||
_loginController = TextEditingController(),
|
||||
_passwordController = TextEditingController() {
|
||||
@@ -49,6 +50,8 @@ class NodeCreateOrEditPage extends BasePage {
|
||||
|
||||
_addressController.addListener(
|
||||
() => nodeCreateOrEditViewModel.address = _addressController.text);
|
||||
_pathController.addListener(
|
||||
() => nodeCreateOrEditViewModel.path = _pathController.text);
|
||||
_portController.addListener(
|
||||
() => nodeCreateOrEditViewModel.port = _portController.text);
|
||||
_loginController.addListener(
|
||||
@@ -59,6 +62,7 @@ class NodeCreateOrEditPage extends BasePage {
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _pathController;
|
||||
final TextEditingController _portController;
|
||||
final TextEditingController _loginController;
|
||||
final TextEditingController _passwordController;
|
||||
|
||||
@@ -16,13 +16,15 @@ class NodeForm extends StatelessWidget {
|
||||
required this.formKey,
|
||||
this.editingNode,
|
||||
}) : _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){
|
||||
_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 ?? ''))
|
||||
@@ -57,10 +59,12 @@ class NodeForm extends StatelessWidget {
|
||||
});
|
||||
|
||||
_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);
|
||||
_socksAddressController
|
||||
.addListener(() => nodeViewModel.socksProxyAddress = _socksAddressController.text);
|
||||
}
|
||||
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
@@ -68,6 +72,7 @@ class NodeForm extends StatelessWidget {
|
||||
final Node? editingNode;
|
||||
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _pathController;
|
||||
final TextEditingController _portController;
|
||||
final TextEditingController _loginController;
|
||||
final TextEditingController _passwordController;
|
||||
@@ -91,6 +96,18 @@ class NodeForm extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _pathController,
|
||||
hintText: "/path",
|
||||
validator: NodePathValidator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
@@ -103,6 +120,26 @@ class NodeForm extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
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,
|
||||
gradientBackground: true,
|
||||
borderColor: Theme.of(context).dividerColor,
|
||||
iconColor: Colors.white,
|
||||
onChanged: (value) => nodeViewModel.useSSL = value,
|
||||
caption: S.of(context).use_ssl,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
if (nodeViewModel.hasAuthCredentials) ...[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
@@ -123,25 +160,6 @@ class NodeForm extends StatelessWidget {
|
||||
))
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardCheckbox(
|
||||
value: nodeViewModel.useSSL,
|
||||
gradientBackground: true,
|
||||
borderColor: Theme.of(context).dividerColor,
|
||||
iconColor: Colors.white,
|
||||
onChanged: (value) => nodeViewModel.useSSL = value,
|
||||
caption: S.of(context).use_ssl,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
@@ -163,44 +181,44 @@ class NodeForm extends StatelessWidget {
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
StandardCheckbox(
|
||||
value: nodeViewModel.useSocksProxy,
|
||||
gradientBackground: true,
|
||||
borderColor: Theme.of(context).dividerColor,
|
||||
iconColor: Colors.white,
|
||||
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(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
StandardCheckbox(
|
||||
value: nodeViewModel.useSocksProxy,
|
||||
gradientBackground: true,
|
||||
borderColor: Theme.of(context).dividerColor,
|
||||
iconColor: Colors.white,
|
||||
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(),
|
||||
))
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
)),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
@@ -4,15 +4,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
|
||||
|
||||
class DashBoardRoundedCardWidget extends StatelessWidget {
|
||||
|
||||
|
||||
DashBoardRoundedCardWidget({
|
||||
required this.onTap,
|
||||
required this.title,
|
||||
required this.subTitle,
|
||||
this.onClose,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback? onClose;
|
||||
final String title;
|
||||
final String subTitle;
|
||||
|
||||
@@ -26,7 +26,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(20),
|
||||
padding: EdgeInsets.fromLTRB(20, 20, 40, 20),
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
@@ -35,32 +35,40 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
),
|
||||
),
|
||||
child:
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
subTitle,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'Lato'),
|
||||
)
|
||||
],
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
subTitle,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'Lato'),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (onClose != null)
|
||||
Positioned(
|
||||
top: 10,
|
||||
right: 10,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: onClose,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.cardTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,21 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ScrollableWithBottomSection extends StatefulWidget {
|
||||
ScrollableWithBottomSection(
|
||||
{required this.content,
|
||||
required this.bottomSection,
|
||||
this.contentPadding,
|
||||
this.bottomSectionPadding});
|
||||
ScrollableWithBottomSection({
|
||||
required this.content,
|
||||
required this.bottomSection,
|
||||
this.topSection,
|
||||
this.contentPadding,
|
||||
this.bottomSectionPadding,
|
||||
this.topSectionPadding,
|
||||
});
|
||||
|
||||
final Widget content;
|
||||
final Widget bottomSection;
|
||||
final Widget? topSection;
|
||||
final EdgeInsets? contentPadding;
|
||||
final EdgeInsets? bottomSectionPadding;
|
||||
final EdgeInsets? topSectionPadding;
|
||||
|
||||
@override
|
||||
ScrollableWithBottomSectionState createState() => ScrollableWithBottomSectionState();
|
||||
@@ -22,6 +27,12 @@ class ScrollableWithBottomSectionState extends State<ScrollableWithBottomSection
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
if (widget.topSection != null)
|
||||
Padding(
|
||||
padding: widget.topSectionPadding?.copyWith(top: 10) ??
|
||||
EdgeInsets.only(top: 10, bottom: 20, right: 20, left: 20),
|
||||
child: widget.topSection,
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
|
||||
Reference in New Issue
Block a user