Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-225-pin-timeout

This commit is contained in:
Godwin Asuquo
2022-12-09 19:42:07 +01:00
59 changed files with 912 additions and 396 deletions
+24 -86
View File
@@ -9,24 +9,22 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/src/widgets/collapsible_standart_list.dart';
class ContactListPage extends BasePage {
ContactListPage(this.contactListViewModel, {this.isEditable = true});
ContactListPage(this.contactListViewModel);
final ContactListViewModel contactListViewModel;
final bool isEditable;
@override
String get title => S.current.address_book;
@override
Widget? trailing(BuildContext context) {
if (!isEditable) {
if (!contactListViewModel.isEditable) {
return null;
}
@@ -60,11 +58,14 @@ class ContactListPage extends BasePage {
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) {
return CollapsibleSectionList(
builder: (_) {
final contacts = contactListViewModel.contactsToShow;
final walletContacts = contactListViewModel.walletContactsToShow;
return CollapsibleSectionList(
context: context,
sectionCount: 2,
themeColor: Theme.of(context).primaryTextTheme.headline6!.color!,
@@ -82,35 +83,37 @@ class ContactListPage extends BasePage {
child: Text(title, style: TextStyle(fontSize: 36)));
},
itemCounter: (int sectionIndex) => sectionIndex == 0
? contactListViewModel.walletContacts.length
: contactListViewModel.contacts.length,
? walletContacts.length
: contacts.length,
itemBuilder: (_, sectionIndex, index) {
if (sectionIndex == 0) {
final walletInfo = contactListViewModel.walletContacts[index];
final walletInfo = walletContacts[index];
return generateRaw(context, walletInfo);
}
final contact = contactListViewModel.contacts[index];
final contact = contacts[index];
final content = generateRaw(context, contact);
return !isEditable
? content
: Slidable(
return contactListViewModel.isEditable
? Slidable(
key: Key('${contact.key}'),
endActionPane: _actionPane(context, contact),
child: content,
);
)
: content;
},
);
},
));
);})
);
}
Widget generateRaw(BuildContext context, ContactBase contact) {
final image = _getCurrencyImage(contact.type);
final image = contact.type.iconPath;
final currencyIcon = image != null ? Image.asset(image, height: 24, width: 24)
: const SizedBox(height: 24, width: 24);
return GestureDetector(
onTap: () async {
if (!isEditable) {
if (!contactListViewModel.isEditable) {
Navigator.of(context).pop(contact);
return;
}
@@ -131,12 +134,10 @@ class ContactListPage extends BasePage {
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
image ?? Offstage(),
currencyIcon,
Expanded(
child: Padding(
padding: image != null
? EdgeInsets.only(left: 12)
: EdgeInsets.only(left: 0),
padding: EdgeInsets.only(left: 12),
child: Text(
contact.name,
style: TextStyle(
@@ -152,69 +153,6 @@ class ContactListPage extends BasePage {
);
}
Image? _getCurrencyImage(CryptoCurrency currency) {
Image? image;
switch (currency) {
case CryptoCurrency.xmr:
image =
Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
break;
case CryptoCurrency.ada:
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
break;
case CryptoCurrency.bch:
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
break;
case CryptoCurrency.bnb:
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
break;
case CryptoCurrency.btc:
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
break;
case CryptoCurrency.dai:
image = Image.asset('assets/images/dai.png', height: 24, width: 24);
break;
case CryptoCurrency.dash:
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
break;
case CryptoCurrency.eos:
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
break;
case CryptoCurrency.eth:
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
break;
case CryptoCurrency.ltc:
image =
Image.asset('assets/images/litecoin.png', height: 24, width: 24);
break;
case CryptoCurrency.nano:
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
break;
case CryptoCurrency.trx:
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
break;
case CryptoCurrency.usdt:
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
break;
case CryptoCurrency.usdterc20:
image = Image.asset('assets/images/usdterc.png', height: 24, width: 24);
break;
case CryptoCurrency.xlm:
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
break;
case CryptoCurrency.xrp:
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
break;
case CryptoCurrency.xhv:
image = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
break;
default:
image = null;
}
return image;
}
Future<bool> showAlertDialog(BuildContext context) async {
return await showPopUp<bool>(
context: context,
@@ -57,7 +57,9 @@ class TransactionsPage extends StatelessWidget {
formattedDate: DateFormat('HH:mm')
.format(transaction.date),
formattedAmount: item.formattedCryptoAmount,
formattedFiatAmount: item.formattedFiatAmount,
formattedFiatAmount:
dashboardViewModel.balanceViewModel.isFiatDisabled
? '' : item.formattedFiatAmount,
isPending: transaction.isPending));
}
@@ -395,7 +395,8 @@ class ExchangeCardState extends State<ExchangeCard> {
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton
onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency
),
)
@@ -378,12 +378,10 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
});
},
actionLeftButton: () => Navigator.of(context).pop(),
feeFiatAmount: widget.exchangeTradeViewModel.sendViewModel.pendingTransactionFeeFiatAmount
+ ' ' + widget.exchangeTradeViewModel.sendViewModel.fiat.title,
fiatAmountValue: widget.exchangeTradeViewModel.sendViewModel
.pendingTransactionFiatAmount +
' ' +
widget.exchangeTradeViewModel.sendViewModel.fiat.title,
feeFiatAmount: widget.exchangeTradeViewModel
.pendingTransactionFeeFiatAmountFormatted,
fiatAmountValue: widget.exchangeTradeViewModel
.pendingTransactionFiatAmountValueFormatted,
outputs: widget.exchangeTradeViewModel.sendViewModel
.outputs);
});
@@ -0,0 +1,111 @@
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class AdvancedPrivacySettingsPage extends BasePage {
AdvancedPrivacySettingsPage(this.advancedPrivacySettingsViewModel, this.nodeViewModel);
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
final NodeCreateOrEditViewModel nodeViewModel;
@override
String get title => S.current.privacy_settings;
@override
Widget body(BuildContext context) =>
AdvancedPrivacySettingsBody(advancedPrivacySettingsViewModel, nodeViewModel);
}
class AdvancedPrivacySettingsBody extends StatefulWidget {
const AdvancedPrivacySettingsBody(this.privacySettingsViewModel, this.nodeViewModel, {Key? key})
: super(key: key);
final AdvancedPrivacySettingsViewModel privacySettingsViewModel;
final NodeCreateOrEditViewModel nodeViewModel;
@override
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
}
class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBody> {
_AdvancedPrivacySettingsBodyState();
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
...widget.privacySettingsViewModel.settings.map(
(item) => Observer(
builder: (_) => SettingsSwitcherCell(
title: item.title,
value: item.value(),
onValueChange: item.onValueChange,
),
),
),
Observer(
builder: (_) {
if (widget.privacySettingsViewModel.addCustomNode) {
return Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: NodeForm(
formKey: _formKey,
nodeViewModel: widget.nodeViewModel,
),
);
}
return const SizedBox();
},
),
],
),
bottomSectionPadding: EdgeInsets.all(24),
bottomSection: Column(
children: [
LoadingPrimaryButton(
onPressed: () {
if (widget.privacySettingsViewModel.addCustomNode) {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
widget.nodeViewModel.save(saveAsCurrent: true);
}
Navigator.pop(context);
},
text: S.of(context).continue_text,
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
textColor: Colors.white,
),
const SizedBox(height: 25),
Padding(
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.15),
child: Text(
S.of(context).settings_can_be_changed_later,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).accentTextTheme.headline2?.color,
),
),
),
],
),
),
);
}
}
+24 -12
View File
@@ -200,18 +200,30 @@ class _WalletNameFormState extends State<WalletNameForm> {
]
]),
bottomSectionPadding:
EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white,
isLoading: _walletNewVM.state is IsExecutingState,
isDisabled: _walletNewVM.name.isEmpty,
);
},
EdgeInsets.all(24),
bottomSection: Column(
children: [
Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white,
isLoading: _walletNewVM.state is IsExecutingState,
isDisabled: _walletNewVM.name.isEmpty,
);
},
),
const SizedBox(height: 25),
GestureDetector(
onTap: () {
Navigator.of(context)
.pushNamed(Routes.advancedPrivacySettings, arguments: _walletNewVM.type);
},
child: Text(S.of(context).advanced_privacy_settings),
),
],
)),
);
}
@@ -1,16 +1,13 @@
import 'package:cake_wallet/core/execution_state.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/standard_checkbox.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/node_address_validator.dart';
import 'package:cake_wallet/core/node_port_validator.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.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';
@@ -108,91 +105,10 @@ class NodeCreateOrEditPage extends BasePage {
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24.0),
content: Form(
key: _formKey,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _addressController,
hintText: S.of(context).node_address,
validator: NodeAddressValidator(),
)
)
],
),
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),
if (nodeCreateOrEditViewModel.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: nodeCreateOrEditViewModel.useSSL,
onChanged: (value) =>
nodeCreateOrEditViewModel.useSSL = value,
caption: S.of(context).use_ssl,
))
],
)),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Observer(
builder: (_) => StandardCheckbox(
value: nodeCreateOrEditViewModel.trusted,
onChanged: (value) =>
nodeCreateOrEditViewModel.trusted = value,
caption: S.of(context).trusted,
))
],
)),
]
],
)),
content: NodeForm(
formKey: _formKey,
nodeViewModel: nodeCreateOrEditViewModel,
),
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(
builder: (_) => Row(
@@ -0,0 +1,151 @@
import 'package:cake_wallet/core/node_address_validator.dart';
import 'package:cake_wallet/core/node_port_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: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,
}) : _addressController = TextEditingController(),
_portController = TextEditingController(),
_loginController = TextEditingController(),
_passwordController = TextEditingController() {
reaction((_) => nodeViewModel.address, (String address) {
if (address != _addressController.text) {
_addressController.text = address;
}
});
reaction((_) => nodeViewModel.port, (String port) {
if (port != _portController.text) {
_portController.text = port;
}
});
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;
}
});
}
_addressController
.addListener(() => nodeViewModel.address = _addressController.text);
_portController
.addListener(() => nodeViewModel.port = _portController.text);
_loginController
.addListener(() => nodeViewModel.login = _loginController.text);
_passwordController
.addListener(() => nodeViewModel.password = _passwordController.text);
}
final NodeCreateOrEditViewModel nodeViewModel;
final GlobalKey<FormState> formKey;
final TextEditingController _addressController;
final TextEditingController _portController;
final TextEditingController _loginController;
final TextEditingController _passwordController;
@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: NodeAddressValidator(),
),
)
],
),
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),
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.useSSL,
onChanged: (value) => nodeViewModel.useSSL = value,
caption: S.of(context).use_ssl,
),
)
],
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Observer(
builder: (_) => StandardCheckbox(
value: nodeViewModel.trusted,
onChanged: (value) => nodeViewModel.trusted = value,
caption: S.of(context).trusted,
),
),
],
),
),
]
],
),
);
}
}
+2 -8
View File
@@ -381,16 +381,10 @@ class SendPage extends BasePage {
amount: S.of(context).send_amount,
amountValue:
sendViewModel.pendingTransaction!.amountFormatted,
fiatAmountValue:
sendViewModel.pendingTransactionFiatAmount +
' ' +
sendViewModel.fiat.title,
fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(context).send_fee,
feeValue: sendViewModel.pendingTransaction!.feeFormatted,
feeFiatAmount:
sendViewModel.pendingTransactionFeeFiatAmount +
' ' +
sendViewModel.fiat.title,
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted,
outputs: sendViewModel.outputs,
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
+6 -3
View File
@@ -154,6 +154,7 @@ class SendCardState extends State<SendCard>
await output.fetchParsedAddress(context);
},
validator: validator,
selectedCurrency: sendViewModel.currency,
);
}),
if (output.isParsedAddress) Padding(
@@ -331,7 +332,8 @@ class SendCardState extends State<SendCard>
],
),
)),
Padding(
if (!sendViewModel.isFiatDisabled)
Padding(
padding: const EdgeInsets.only(top: 20),
child: BaseTextFormField(
focusNode: fiatAmountFocus,
@@ -437,8 +439,9 @@ class SendCardState extends State<SendCard>
Padding(
padding:
EdgeInsets.only(top: 5),
child: Text(
output
child: sendViewModel.isFiatDisabled
? const SizedBox(height: 14)
: Text(output
.estimatedFeeFiatAmount
+ ' ' +
sendViewModel
@@ -34,7 +34,8 @@ class DisplaySettingsPage extends BasePage {
onValueChange: (_, bool value) {
_displaySettingsViewModel.setShouldDisplayBalance(value);
}),
if (!isHaven)
//if (!isHaven) it does not work correctly
if(!_displaySettingsViewModel.disabledFiatApiMode)
SettingsPickerCell<FiatCurrency>(
title: S.current.settings_currency,
searchHintText: S.current.search_currency,
+6 -2
View File
@@ -18,10 +18,15 @@ class PrivacyPage extends BasePage {
return Container(
padding: EdgeInsets.only(top: 10),
child: Observer(builder: (_) {
return Observer(builder: (_) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SettingsSwitcherCell(
title: S.current.disable_fiat,
value: _privacySettingsViewModel.isFiatDisabled,
onValueChange: (BuildContext context, bool value) {
_privacySettingsViewModel.setFiatMode(value);
}),
SettingsSwitcherCell(
title: S.current.disable_exchange,
value: _privacySettingsViewModel.disableExchange,
@@ -36,7 +41,6 @@ class PrivacyPage extends BasePage {
})
],
);
});
}),
);
}
+5 -2
View File
@@ -4,6 +4,7 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:cake_wallet/entities/contact_base.dart';
import 'package:cw_core/crypto_currency.dart';
enum AddressTextFieldOption { paste, qrCode, addressBook }
@@ -26,7 +27,8 @@ class AddressTextField extends StatelessWidget {
this.hintStyle,
this.validator,
this.onPushPasteButton,
this.onPushAddressBookButton});
this.onPushAddressBookButton,
this.selectedCurrency});
static const prefixIconWidth = 34.0;
static const prefixIconHeight = 34.0;
@@ -47,6 +49,7 @@ class AddressTextField extends StatelessWidget {
final FocusNode? focusNode;
final Function(BuildContext context)? onPushPasteButton;
final Function(BuildContext context)? onPushAddressBookButton;
final CryptoCurrency? selectedCurrency;
@override
Widget build(BuildContext context) {
@@ -207,7 +210,7 @@ class AddressTextField extends StatelessWidget {
Future<void> _presetAddressBookPicker(BuildContext context) async {
final contact = await Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.pickerAddressBook);
.pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency);
if (contact is ContactBase && contact.address != null) {
controller?.text = contact.address;