From 49c495f688eef071edd16383a0a0e6a70ed1d2f7 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Tue, 8 Sep 2020 20:21:09 +0300 Subject: [PATCH 01/13] CAKE-38 | changed button position in alerts (contact list page, wallet menu, base exchange widget, nodes list page, base send widget, change language page) --- .../screens/contact/contact_list_page.dart | 16 +++++++------- lib/src/screens/dashboard/wallet_menu.dart | 8 +++---- .../widgets/base_exchange_widget.dart | 8 +++---- lib/src/screens/nodes/nodes_list_page.dart | 16 +++++++------- .../send/widgets/base_send_widget.dart | 8 +++---- lib/src/screens/settings/change_language.dart | 8 +++---- lib/src/widgets/base_alert_dialog.dart | 6 ++--- lib/themes.dart | 22 ------------------- 8 files changed, 35 insertions(+), 57 deletions(-) diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index 40b38cca4..c8757e9b7 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -252,10 +252,10 @@ class ContactListPage extends BasePage { return AlertWithTwoActions( alertTitle: S.of(context).address_remove_contact, alertContent: S.of(context).address_remove_content, - leftButtonText: S.of(context).remove, - rightButtonText: S.of(context).cancel, - actionLeftButton: () => Navigator.of(context).pop(true), - actionRightButton: () => Navigator.of(context).pop(false)); + rightButtonText: S.of(context).remove, + leftButtonText: S.of(context).cancel, + actionRightButton: () => Navigator.of(context).pop(true), + actionLeftButton: () => Navigator.of(context).pop(false)); }); } @@ -267,10 +267,10 @@ class ContactListPage extends BasePage { return AlertWithTwoActions( alertTitle: name, alertContent: address, - leftButtonText: S.of(context).copy, - rightButtonText: S.of(context).cancel, - actionLeftButton: () => Navigator.of(context).pop(true), - actionRightButton: () => Navigator.of(context).pop(false)); + rightButtonText: S.of(context).copy, + leftButtonText: S.of(context).cancel, + actionRightButton: () => Navigator.of(context).pop(true), + actionLeftButton: () => Navigator.of(context).pop(false)); }); } } diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index a3c4666f0..d7a37ce32 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -78,13 +78,13 @@ class WalletMenu { return AlertWithTwoActions( alertTitle: S.of(context).reconnection, alertContent: S.of(context).reconnect_alert_text, - leftButtonText: S.of(context).ok, - rightButtonText: S.of(context).cancel, - actionLeftButton: () { + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () { walletStore.reconnect(); Navigator.of(context).pop(); }, - actionRightButton: () => Navigator.of(context).pop() + actionLeftButton: () => Navigator.of(context).pop() ); }); } diff --git a/lib/src/screens/exchange/widgets/base_exchange_widget.dart b/lib/src/screens/exchange/widgets/base_exchange_widget.dart index f3aa14e0a..46a6d4cb8 100644 --- a/lib/src/screens/exchange/widgets/base_exchange_widget.dart +++ b/lib/src/screens/exchange/widgets/base_exchange_widget.dart @@ -284,14 +284,14 @@ class BaseExchangeWidgetState extends State { return AlertWithTwoActions( alertTitle: S.of(context).template, alertContent: S.of(context).confirm_delete_template, - leftButtonText: S.of(context).delete, - rightButtonText: S.of(context).cancel, - actionLeftButton: () { + rightButtonText: S.of(context).delete, + leftButtonText: S.of(context).cancel, + actionRightButton: () { Navigator.of(dialogContext).pop(); exchangeViewModel.exchangeTemplateStore.remove(template: template); exchangeViewModel.exchangeTemplateStore.update(); }, - actionRightButton: () => Navigator.of(dialogContext).pop() + actionLeftButton: () => Navigator.of(dialogContext).pop() ); } ); diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart index 177348a3e..574b3adcb 100644 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ b/lib/src/screens/nodes/nodes_list_page.dart @@ -36,13 +36,13 @@ class NodeListPage extends BasePage { alertTitle: S.of(context).node_reset_settings_title, alertContent: S.of(context).nodes_list_reset_to_default_message, - leftButtonText: S.of(context).reset, - rightButtonText: S.of(context).cancel, - actionLeftButton: () async { + rightButtonText: S.of(context).reset, + leftButtonText: S.of(context).cancel, + actionRightButton: () async { Navigator.of(context).pop(); await nodeListViewModel.reset(); }, - actionRightButton: () => Navigator.of(context).pop()); + actionLeftButton: () => Navigator.of(context).pop()); }); }, child: Text( @@ -90,11 +90,11 @@ class NodeListPage extends BasePage { return AlertWithTwoActions( alertTitle: S.of(context).remove_node, alertContent: S.of(context).remove_node_message, - leftButtonText: S.of(context).remove, - rightButtonText: S.of(context).cancel, - actionLeftButton: () => - Navigator.pop(context, true), + rightButtonText: S.of(context).remove, + leftButtonText: S.of(context).cancel, actionRightButton: () => + Navigator.pop(context, true), + actionLeftButton: () => Navigator.pop(context, false)); }); }, diff --git a/lib/src/screens/send/widgets/base_send_widget.dart b/lib/src/screens/send/widgets/base_send_widget.dart index c8d03f389..0aa8c0b3a 100644 --- a/lib/src/screens/send/widgets/base_send_widget.dart +++ b/lib/src/screens/send/widgets/base_send_widget.dart @@ -387,14 +387,14 @@ class BaseSendWidget extends StatelessWidget { return AlertWithTwoActions( alertTitle: S.of(context).template, alertContent: S.of(context).confirm_delete_template, - leftButtonText: S.of(context).delete, - rightButtonText: S.of(context).cancel, - actionLeftButton: () { + rightButtonText: S.of(context).delete, + leftButtonText: S.of(context).cancel, + actionRightButton: () { Navigator.of(dialogContext).pop(); sendViewModel.sendTemplateStore.remove(template: template); sendViewModel.sendTemplateStore.update(); }, - actionRightButton: () => Navigator.of(dialogContext).pop() + actionLeftButton: () => Navigator.of(dialogContext).pop() ); } ); diff --git a/lib/src/screens/settings/change_language.dart b/lib/src/screens/settings/change_language.dart index 5ef079216..9e207390b 100644 --- a/lib/src/screens/settings/change_language.dart +++ b/lib/src/screens/settings/change_language.dart @@ -43,15 +43,15 @@ class ChangeLanguage extends BasePage { return AlertWithTwoActions( alertTitle: S.of(context).change_language, alertContent: S.of(context).change_language_to(item), - leftButtonText: S.of(context).change, - rightButtonText: S.of(context).cancel, - actionLeftButton: () { + rightButtonText: S.of(context).change, + leftButtonText: S.of(context).cancel, + actionRightButton: () { settingsStore.saveLanguageCode( languageCode: code); currentLanguage.setCurrentLanguage(code); Navigator.of(context).pop(); }, - actionRightButton: () => Navigator.of(context).pop() + actionLeftButton: () => Navigator.of(context).pop() ); }); } diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index d22d01d5a..7cfafe284 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -47,7 +47,7 @@ class BaseAlertDialog extends StatelessWidget { child: Container( height: 52, padding: EdgeInsets.only(left: 6, right: 6), - color: Palette.blueCraiola, + color: Palette.alizarinRed, child: ButtonTheme( minWidth: double.infinity, child: FlatButton( @@ -72,7 +72,7 @@ class BaseAlertDialog extends StatelessWidget { child: Container( height: 52, padding: EdgeInsets.only(left: 6, right: 6), - color: Palette.alizarinRed, + color: Palette.blueCraiola, child: ButtonTheme( minWidth: double.infinity, child: FlatButton( @@ -92,7 +92,7 @@ class BaseAlertDialog extends StatelessWidget { )), ), ) - ) + ), ], ); } diff --git a/lib/themes.dart b/lib/themes.dart index f8d984baf..e9d074f47 100644 --- a/lib/themes.dart +++ b/lib/themes.dart @@ -168,16 +168,6 @@ class Themes { decorationColor: Palette.darkGray // switch (PIN code) ) ), - - - - cardColor: Palette.blueAlice, - cardTheme: CardTheme( - color: Colors.white, // synced card start - ), - - - ); @@ -346,17 +336,5 @@ class Themes { decorationColor: PaletteDark.lightPurpleBlue // switch (PIN code) ) ), - - - - cardColor: PaletteDark.darkNightBlue, - cardTheme: CardTheme( - color: PaletteDark.moderateBlue, // synced card start - ), - - - - ); - } \ No newline at end of file From 844847f703272db4c273c7cf43193e5a3efe0c37 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 9 Sep 2020 22:12:49 +0300 Subject: [PATCH 02/13] CAKE-39 | changed monero logo (contact list, wallet list, new wallet type pages); changed buttons color on alerts, wallet list page, new wallet type page, receive page, monero account list page --- assets/images/monero_logo.png | Bin 0 -> 70362 bytes lib/palette.dart | 2 ++ .../screens/contact/contact_list_page.dart | 2 +- .../new_wallet/new_wallet_type_page.dart | 2 +- .../screens/wallet_list/wallet_list_page.dart | 2 +- lib/src/widgets/alert_with_one_action.dart | 3 +-- lib/src/widgets/base_alert_dialog.dart | 4 ++-- lib/themes.dart | 21 +++++++++++++----- 8 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 assets/images/monero_logo.png diff --git a/assets/images/monero_logo.png b/assets/images/monero_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d1ebfc0d8cc84920af66abe7e2c417e2bd9388bf GIT binary patch literal 70362 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 literal 0 HcmV?d00001 diff --git a/lib/palette.dart b/lib/palette.dart index 3988a53ef..f9d0a1e3c 100644 --- a/lib/palette.dart +++ b/lib/palette.dart @@ -41,6 +41,8 @@ class Palette { static const Color shadowWhite = Color.fromRGBO(242, 245, 255, 1.0); static const Color niagara = Color.fromRGBO(152, 172, 201, 1.0); static const Color alizarinRed = Color.fromRGBO(233, 45, 45, 1.0); + static const Color moderateSlateBlue = Color.fromRGBO(129, 93, 251, 1.0); + static const Color brightOrange = Color.fromRGBO(255, 102, 0, 1.0); // FIXME: Rename. static const Color eee = Color.fromRGBO(236, 239, 245, 1.0); diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index c8757e9b7..874cb71e0 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -197,7 +197,7 @@ class ContactListPage extends BasePage { Image image; switch (currency) { case CryptoCurrency.xmr: - image = Image.asset('assets/images/monero.png', height: 24, width: 24); + 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); diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart index 673bd9478..0eacbc7b7 100644 --- a/lib/src/screens/new_wallet/new_wallet_type_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart @@ -38,7 +38,7 @@ class WalletTypeFormState extends State { static const aspectRatioImage = 1.22; final moneroIcon = - Image.asset('assets/images/monero.png', height: 24, width: 24); + Image.asset('assets/images/monero_logo.png', height: 24, width: 24); final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); final walletTypeImage = Image.asset('assets/images/wallet_type.png'); diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index bac8e0990..5539ec3e4 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -33,7 +33,7 @@ class WalletListBody extends StatefulWidget { class WalletListBodyState extends State { final moneroIcon = - Image.asset('assets/images/monero.png', height: 24, width: 24); + Image.asset('assets/images/monero_logo.png', height: 24, width: 24); final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); final scrollController = ScrollController(); diff --git a/lib/src/widgets/alert_with_one_action.dart b/lib/src/widgets/alert_with_one_action.dart index e7f1a0d15..5a39adbc5 100644 --- a/lib/src/widgets/alert_with_one_action.dart +++ b/lib/src/widgets/alert_with_one_action.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/base_alert_dialog.dart'; -import 'package:cake_wallet/palette.dart'; class AlertWithOneAction extends BaseAlertDialog { AlertWithOneAction({ @@ -32,7 +31,7 @@ class AlertWithOneAction extends BaseAlertDialog { width: 300, height: 52, padding: EdgeInsets.only(left: 12, right: 12), - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, child: ButtonTheme( minWidth: double.infinity, child: FlatButton( diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index 7cfafe284..b2ac36940 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -47,7 +47,7 @@ class BaseAlertDialog extends StatelessWidget { child: Container( height: 52, padding: EdgeInsets.only(left: 6, right: 6), - color: Palette.alizarinRed, + color: Theme.of(context).accentTextTheme.body2.decorationColor, child: ButtonTheme( minWidth: double.infinity, child: FlatButton( @@ -72,7 +72,7 @@ class BaseAlertDialog extends StatelessWidget { child: Container( height: 52, padding: EdgeInsets.only(left: 6, right: 6), - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, child: ButtonTheme( minWidth: double.infinity, child: FlatButton( diff --git a/lib/themes.dart b/lib/themes.dart index e9d074f47..52ceced06 100644 --- a/lib/themes.dart +++ b/lib/themes.dart @@ -48,7 +48,8 @@ class Themes { ), display3: TextStyle( color: Colors.white, // text color of current tile (receive page), - decorationColor: Palette.blueCraiola // background of current tile (receive page) + //decorationColor: Palette.blueCraiola // background of current tile (receive page) + decorationColor: Palette.moderateSlateBlue // background of current tile (receive page) ), display4: TextStyle( color: Palette.violetBlue, // text color of tiles (account list) @@ -56,7 +57,8 @@ class Themes { ), subtitle: TextStyle( color: Colors.white, // text color of current tile (account list) - decorationColor: Palette.blueCraiola // background of current tile (account list) + //decorationColor: Palette.blueCraiola // background of current tile (account list) + decorationColor: Palette.moderateSlateBlue // background of current tile (account list) ), body1: TextStyle( color: Palette.moderatePurpleBlue, // scrollbar thumb @@ -132,7 +134,8 @@ class Themes { subtitle: TextStyle( color: Palette.darkBlueCraiola, // QR code (exchange trade page) backgroundColor: Palette.wildPeriwinkle, // divider (exchange trade page) - decorationColor: Palette.blueCraiola // crete new wallet button background (wallet list page) + //decorationColor: Palette.blueCraiola // crete new wallet button background (wallet list page) + decorationColor: Palette.moderateSlateBlue // crete new wallet button background (wallet list page) ), headline: TextStyle( color: Palette.moderateLavender, // first gradient color of wallet action buttons (wallet list page) @@ -166,7 +169,11 @@ class Themes { body1: TextStyle( color: Palette.darkGray, // indicators (PIN code) decorationColor: Palette.darkGray // switch (PIN code) - ) + ), + body2: TextStyle( + color: Palette.moderateSlateBlue, // primary buttons, alert right buttons + decorationColor: Palette.brightOrange // alert left button + ), ), ); @@ -334,7 +341,11 @@ class Themes { body1: TextStyle( color: PaletteDark.indicatorVioletBlue, // indicators (PIN code) decorationColor: PaletteDark.lightPurpleBlue // switch (PIN code) - ) + ), + body2: TextStyle( + color: Palette.blueCraiola, // primary buttons, alert right buttons + decorationColor: Palette.alizarinRed // alert left button + ), ), ); } \ No newline at end of file From 163700bcd853cf7470c9accf31faa74bfa814981 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Thu, 10 Sep 2020 13:33:34 +0300 Subject: [PATCH 03/13] CAKE-39 | changed buttons color on base send widget, base exchange widget, exchange confirm page, exchange trade page, node create or edit page, contact page, seed widget, restore wallet from seed details page, restore wallet from keys page, wallet seed page, template tile --- lib/src/screens/contact/contact_page.dart | 4 ++-- lib/src/screens/exchange/widgets/base_exchange_widget.dart | 3 +-- lib/src/screens/exchange_trade/exchange_confirm_page.dart | 3 +-- lib/src/screens/exchange_trade/exchange_trade_page.dart | 2 +- lib/src/screens/nodes/node_create_or_edit_page.dart | 5 ++--- lib/src/screens/restore/restore_wallet_from_keys_page.dart | 2 +- .../screens/restore/restore_wallet_from_seed_details.dart | 3 +-- lib/src/screens/restore/restore_wallet_from_seed_page.dart | 1 - lib/src/screens/seed/wallet_seed_page.dart | 2 +- lib/src/screens/send/widgets/base_send_widget.dart | 3 +-- lib/src/widgets/seed_widget.dart | 6 +++--- lib/src/widgets/template_tile.dart | 2 +- 12 files changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/src/screens/contact/contact_page.dart b/lib/src/screens/contact/contact_page.dart index 67f83cb66..3670af7d9 100644 --- a/lib/src/screens/contact/contact_page.dart +++ b/lib/src/screens/contact/contact_page.dart @@ -115,7 +115,7 @@ class ContactPage extends BasePage { _addressController.text = ''; }, text: S.of(context).reset, - color: Colors.red, + color: Colors.orange, textColor: Colors.white), ), SizedBox(width: 20), @@ -130,7 +130,7 @@ class ContactPage extends BasePage { await contactViewModel.save(); }, text: S.of(context).save, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, isDisabled: !contactViewModel.isReady))) ], diff --git a/lib/src/screens/exchange/widgets/base_exchange_widget.dart b/lib/src/screens/exchange/widgets/base_exchange_widget.dart index 46a6d4cb8..a09181e2f 100644 --- a/lib/src/screens/exchange/widgets/base_exchange_widget.dart +++ b/lib/src/screens/exchange/widgets/base_exchange_widget.dart @@ -1,5 +1,4 @@ import 'dart:ui'; -import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/domain/exchange/exchange_template.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/template_tile.dart'; @@ -356,7 +355,7 @@ class BaseExchangeWidgetState extends State { exchangeViewModel.createTrade(); } }, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, isLoading: exchangeViewModel.tradeState is TradeIsCreating, )), diff --git a/lib/src/screens/exchange_trade/exchange_confirm_page.dart b/lib/src/screens/exchange_trade/exchange_confirm_page.dart index 7cdb61c47..a5569453c 100644 --- a/lib/src/screens/exchange_trade/exchange_confirm_page.dart +++ b/lib/src/screens/exchange_trade/exchange_confirm_page.dart @@ -7,7 +7,6 @@ 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/domain/exchange/trade.dart'; -import 'package:cake_wallet/palette.dart'; class ExchangeConfirmPage extends BasePage { ExchangeConfirmPage({@required this.tradesStore}) : trade = tradesStore.trade; @@ -115,7 +114,7 @@ class ExchangeConfirmPage extends BasePage { onPressed: () => Navigator.of(context) .pushReplacementNamed(Routes.exchangeTrade), text: S.of(context).saved_the_trade_id, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white) ], ), diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart index 61b1b024d..cc295a9ca 100644 --- a/lib/src/screens/exchange_trade/exchange_trade_page.dart +++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart @@ -220,7 +220,7 @@ class ExchangeTradeState extends State { bottomSection: PrimaryButton( onPressed: () {}, text: S.of(context).confirm, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white ) /*Observer( 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 d4b4a5e01..ee13984c5 100644 --- a/lib/src/screens/nodes/node_create_or_edit_page.dart +++ b/lib/src/screens/nodes/node_create_or_edit_page.dart @@ -1,4 +1,3 @@ -import 'package:cake_wallet/palette.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -137,7 +136,7 @@ class NodeCreateOrEditPage extends BasePage { child: PrimaryButton( onPressed: () => nodeCreateOrEditViewModel.reset(), text: S.of(context).reset, - color: Colors.red, + color: Colors.orange, textColor: Colors.white), )), Flexible( @@ -153,7 +152,7 @@ class NodeCreateOrEditPage extends BasePage { Navigator.of(context).pop(); }, text: S.of(context).save, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, isDisabled: !nodeCreateOrEditViewModel.isReady, ), diff --git a/lib/src/screens/restore/restore_wallet_from_keys_page.dart b/lib/src/screens/restore/restore_wallet_from_keys_page.dart index 6f950f6e4..e205423fb 100644 --- a/lib/src/screens/restore/restore_wallet_from_keys_page.dart +++ b/lib/src/screens/restore/restore_wallet_from_keys_page.dart @@ -189,7 +189,7 @@ class _RestoreFromKeysFromState extends State { } }, text: S.of(context).restore_recover, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, //isDisabled: walletRestorationStore.disabledState, ); diff --git a/lib/src/screens/restore/restore_wallet_from_seed_details.dart b/lib/src/screens/restore/restore_wallet_from_seed_details.dart index 3225be9e7..f55d14583 100644 --- a/lib/src/screens/restore/restore_wallet_from_seed_details.dart +++ b/lib/src/screens/restore/restore_wallet_from_seed_details.dart @@ -1,4 +1,3 @@ -import 'package:cake_wallet/palette.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -121,7 +120,7 @@ class _RestoreFromSeedDetailsFormState isLoading: widget.walletRestorationFromSeedVM.state is WalletCreating, text: S.of(context).restore_recover, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, isDisabled: _nameController.text.isNotEmpty, ); diff --git a/lib/src/screens/restore/restore_wallet_from_seed_page.dart b/lib/src/screens/restore/restore_wallet_from_seed_page.dart index bc30a48d8..2920818f6 100644 --- a/lib/src/screens/restore/restore_wallet_from_seed_page.dart +++ b/lib/src/screens/restore/restore_wallet_from_seed_page.dart @@ -6,7 +6,6 @@ import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/seed_widget.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:cake_wallet/core/seed_validator.dart'; -import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/core/mnemonic_length.dart'; class RestoreWalletFromSeedPage extends BasePage { diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index cde60f5b0..5c5e06b70 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -141,7 +141,7 @@ class WalletSeedPage extends BasePage { ); }, text: S.of(context).copy, - color: Colors.blue, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white)), )) ], diff --git a/lib/src/screens/send/widgets/base_send_widget.dart b/lib/src/screens/send/widgets/base_send_widget.dart index 0aa8c0b3a..b87cd920c 100644 --- a/lib/src/screens/send/widgets/base_send_widget.dart +++ b/lib/src/screens/send/widgets/base_send_widget.dart @@ -6,7 +6,6 @@ import 'package:cake_wallet/view_model/send_view_model.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/widgets/address_text_field.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/top_panel.dart'; @@ -436,7 +435,7 @@ class BaseSendWidget extends StatelessWidget { } }, text: S.of(context).send, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, isLoading: sendViewModel.state is TransactionIsCreating || sendViewModel.state is TransactionCommitting, diff --git a/lib/src/widgets/seed_widget.dart b/lib/src/widgets/seed_widget.dart index 5da136c74..7ffe3f609 100644 --- a/lib/src/widgets/seed_widget.dart +++ b/lib/src/widgets/seed_widget.dart @@ -393,7 +393,7 @@ class SeedWidgetState extends State { child: PrimaryButton( onPressed: clear, text: S.of(context).clear, - color: Colors.red, + color: Colors.orange, textColor: Colors.white, isDisabled: items.isEmpty, ), @@ -408,7 +408,7 @@ class SeedWidgetState extends State { onPressed: () => widget.onFinish != null ? widget.onFinish() : null, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white) : PrimaryButton( text: selectedItem != null @@ -419,7 +419,7 @@ class SeedWidgetState extends State { : null, onDisabledPressed: () => showErrorIfExist(), isDisabled: !isCurrentMnemonicValid, - color: Palette.blueCraiola, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white), ), ) diff --git a/lib/src/widgets/template_tile.dart b/lib/src/widgets/template_tile.dart index fbff8b6c1..8ec355d68 100644 --- a/lib/src/widgets/template_tile.dart +++ b/lib/src/widgets/template_tile.dart @@ -128,7 +128,7 @@ class TemplateTileState extends State { child: Container( height: 40, padding: EdgeInsets.only(left: 24, right: 10), - color: Colors.red, + color: Colors.orange, child: content, ), ), From 24e33551456d6fedb89a4bd147e72bae3f2712d5 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Thu, 10 Sep 2020 14:07:49 +0300 Subject: [PATCH 04/13] CAKE-39 | fixed colors of account list tile --- lib/themes.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/themes.dart b/lib/themes.dart index 52ceced06..f78bad80b 100644 --- a/lib/themes.dart +++ b/lib/themes.dart @@ -56,9 +56,8 @@ class Themes { decorationColor: Colors.white // background of tiles (account list) ), subtitle: TextStyle( - color: Colors.white, // text color of current tile (account list) - //decorationColor: Palette.blueCraiola // background of current tile (account list) - decorationColor: Palette.moderateSlateBlue // background of current tile (account list) + color: Palette.moderateSlateBlue, // text color of current tile (account list) + decorationColor: Colors.white // background of current tile (account list) ), body1: TextStyle( color: Palette.moderatePurpleBlue, // scrollbar thumb From 4feee0066b257ae8d7b7c9ce0bfd66a8412b8e71 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Thu, 10 Sep 2020 16:47:29 +0300 Subject: [PATCH 05/13] CAKE-39 | fixed colors of account list button --- lib/src/screens/monero_accounts/monero_account_list_page.dart | 2 +- lib/themes.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/screens/monero_accounts/monero_account_list_page.dart b/lib/src/screens/monero_accounts/monero_account_list_page.dart index 0f4d493fe..ab662629b 100644 --- a/lib/src/screens/monero_accounts/monero_account_list_page.dart +++ b/lib/src/screens/monero_accounts/monero_account_list_page.dart @@ -123,7 +123,7 @@ class MoneroAccountListPage extends StatelessWidget { .pushNamed(Routes.accountCreation), child: Container( height: 62, - color: Theme.of(context).textTheme.subtitle.decorationColor, + color: Theme.of(context).cardColor, padding: EdgeInsets.only(left: 24, right: 24), child: Center( child: Row( diff --git a/lib/themes.dart b/lib/themes.dart index f78bad80b..0decf9777 100644 --- a/lib/themes.dart +++ b/lib/themes.dart @@ -174,6 +174,7 @@ class Themes { decorationColor: Palette.brightOrange // alert left button ), ), + cardColor: Palette.moderateSlateBlue // bottom button (action list) ); @@ -346,5 +347,6 @@ class Themes { decorationColor: Palette.alizarinRed // alert left button ), ), + cardColor: PaletteDark.darkNightBlue // bottom button (action list) ); } \ No newline at end of file From f4b7ccf6193f7797e07e159afeff18c5e1a74b8b Mon Sep 17 00:00:00 2001 From: Oleksandr Sobol Date: Fri, 11 Sep 2020 18:52:55 +0300 Subject: [PATCH 06/13] CAKE-40 | added text to restore wallet from keys page, restore wallet from seed details page, wallet seed page; applied wallet seed page call when new wallet was created --- lib/di.dart | 6 +- lib/generated/i18n.dart | 46 +++++++ lib/router.dart | 2 +- lib/src/screens/dashboard/wallet_menu.dart | 2 +- .../screens/new_wallet/new_wallet_page.dart | 3 +- .../restore_wallet_from_keys_page.dart | 14 +- .../restore_wallet_from_seed_details.dart | 15 ++- lib/src/screens/seed/wallet_seed_page.dart | 127 +++++++++++------- lib/src/screens/wallet_list/wallet_menu.dart | 2 +- res/values/strings_de.arb | 2 + res/values/strings_en.arb | 2 + res/values/strings_es.arb | 2 + res/values/strings_hi.arb | 2 + res/values/strings_ja.arb | 2 + res/values/strings_ko.arb | 2 + res/values/strings_nl.arb | 2 + res/values/strings_pl.arb | 2 + res/values/strings_pt.arb | 2 + res/values/strings_ru.arb | 2 + res/values/strings_uk.arb | 2 + res/values/strings_zh.arb | 2 + 21 files changed, 180 insertions(+), 61 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index 37061f8aa..c85c10482 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -283,10 +283,10 @@ Future setup( getIt .registerFactory(() => WalletSeedViewModel(getIt.get().wallet)); - getIt.registerFactoryParam( - (VoidCallback callback, _) => WalletSeedPage( + getIt.registerFactoryParam( + (bool isWalletCreated, _) => WalletSeedPage( getIt.get(), - onCloseCallback: callback)); + isNewWalletCreated: isWalletCreated)); getIt .registerFactory(() => WalletKeysViewModel(getIt.get().wallet)); diff --git a/lib/generated/i18n.dart b/lib/generated/i18n.dart index a14182527..0467ad495 100644 --- a/lib/generated/i18n.dart +++ b/lib/generated/i18n.dart @@ -151,6 +151,7 @@ class S implements WidgetsLocalizations { String get restore_description_from_keys => "Restore your wallet from generated keystrokes saved from your private keys"; String get restore_description_from_seed => "Restore your wallet from either the 25 word or 13 word combination code"; String get restore_description_from_seed_keys => "Get back your wallet from seed/keys that you've saved to secure place"; + String get restore_from_date_or_blockheight => "Please enter the date you created this wallet. Or if you know the blockheight, please enter it instead"; String get restore_from_seed_placeholder => "Please enter or paste your seed here"; String get restore_new_seed => "New seed"; String get restore_next => "Next"; @@ -180,6 +181,7 @@ class S implements WidgetsLocalizations { String get seed_language_portuguese => "Portuguese"; String get seed_language_russian => "Russian"; String get seed_language_spanish => "Spanish"; + String get seed_reminder => "Please write these down in case you lose or wipe your phone"; String get seed_share => "Share seed"; String get seed_title => "Seed"; String get send => "Send"; @@ -789,6 +791,8 @@ class $de extends S { @override String get choose_account => "Konto auswählen"; @override + String get restore_from_date_or_blockheight => "Bitte geben Sie das Datum ein, an dem Sie diese Brieftasche erstellt haben. Oder wenn Sie die Blockhöhe kennen, geben Sie sie stattdessen ein"; + @override String get settings_only_trades => "Nur Trades"; @override String get trade_state_to_be_created => "Geschaffen werden"; @@ -869,6 +873,8 @@ class $de extends S { @override String get settings_support => "Unterstützung"; @override + String get seed_reminder => "Bitte notieren Sie diese, falls Sie Ihr Telefon verlieren oder abwischen"; + @override String get restore_wallet => "Wallet wiederherstellen"; @override String get ok => "OK"; @@ -1427,6 +1433,8 @@ class $hi extends S { @override String get choose_account => "खाता चुनें"; @override + String get restore_from_date_or_blockheight => "कृपया इस वॉलेट को बनाने की तिथि दर्ज करें। या यदि आप ब्लॉकचाइट जानते हैं, तो कृपया इसके बजाय इसे दर्ज करें"; + @override String get settings_only_trades => "केवल ट्रेड करता है"; @override String get trade_state_to_be_created => "बनाए जाने के लिए"; @@ -1507,6 +1515,8 @@ class $hi extends S { @override String get settings_support => "समर्थन"; @override + String get seed_reminder => "यदि आप अपना फोन खो देते हैं या मिटा देते हैं तो कृपया इन्हें लिख लें"; + @override String get restore_wallet => "वॉलेट को पुनर्स्थापित करें"; @override String get ok => "ठीक है"; @@ -2065,6 +2075,8 @@ class $ru extends S { @override String get choose_account => "Выберите аккаунт"; @override + String get restore_from_date_or_blockheight => "Пожалуйста, введите дату создания кошелька. Или, если вы знаете высоту блока, введите ее значение"; + @override String get settings_only_trades => "Сделки"; @override String get trade_state_to_be_created => "Будет создана"; @@ -2145,6 +2157,8 @@ class $ru extends S { @override String get settings_support => "Поддержка"; @override + String get seed_reminder => "Пожалуйста, запишите мнемоническую фразу на случай потери или очистки телефона"; + @override String get restore_wallet => "Восстановить кошелёк"; @override String get ok => "OK"; @@ -2703,6 +2717,8 @@ class $ko extends S { @override String get choose_account => "계정을 선택하십시오"; @override + String get restore_from_date_or_blockheight => "이 지갑을 만든 날짜를 입력하세요. 또는 블록 높이를 알고있는 경우 대신 입력하십시오."; + @override String get settings_only_trades => "거래 만"; @override String get trade_state_to_be_created => "만들려면"; @@ -2783,6 +2799,8 @@ class $ko extends S { @override String get settings_support => "지원하다"; @override + String get seed_reminder => "휴대 전화를 분실하거나 닦을 경우를 대비해 적어 두세요."; + @override String get restore_wallet => "월렛 복원"; @override String get ok => "승인"; @@ -3341,6 +3359,8 @@ class $pt extends S { @override String get choose_account => "Escolha uma conta"; @override + String get restore_from_date_or_blockheight => "Por favor, insira a data em que você criou esta carteira. Ou se você souber a altura do bloco, insira-o"; + @override String get settings_only_trades => "Somente trocas"; @override String get trade_state_to_be_created => "A ser criada"; @@ -3421,6 +3441,8 @@ class $pt extends S { @override String get settings_support => "Suporte"; @override + String get seed_reminder => "Anote-os para o caso de perder ou limpar seu telefone"; + @override String get restore_wallet => "Restaurar carteira"; @override String get ok => "Ok"; @@ -3979,6 +4001,8 @@ class $uk extends S { @override String get choose_account => "Оберіть акаунт"; @override + String get restore_from_date_or_blockheight => "Будь ласка, введіть дату створення гаманця. Або, якщо ви знаєте висоту блоку, введіть її значення"; + @override String get settings_only_trades => "Операції"; @override String get trade_state_to_be_created => "Буде створена"; @@ -4059,6 +4083,8 @@ class $uk extends S { @override String get settings_support => "Підтримка"; @override + String get seed_reminder => "Будь ласка, запишіть мнемонічну фразу на випадок втрати або очищення телефону"; + @override String get restore_wallet => "Відновити гаманець"; @override String get ok => "OK"; @@ -4617,6 +4643,8 @@ class $ja extends S { @override String get choose_account => "アカウントを選択"; @override + String get restore_from_date_or_blockheight => "このウォレットを作成した日付を入力してください。 または、ブロックの高さがわかっている場合は、代わりに入力してください"; + @override String get settings_only_trades => "取引のみ"; @override String get trade_state_to_be_created => "作成される"; @@ -4697,6 +4725,8 @@ class $ja extends S { @override String get settings_support => "サポート"; @override + String get seed_reminder => "スマートフォンを紛失したりワイプした場合に備えて、これらを書き留めてください"; + @override String get restore_wallet => "ウォレットを復元"; @override String get ok => "OK"; @@ -5259,6 +5289,8 @@ class $pl extends S { @override String get choose_account => "Wybierz konto"; @override + String get restore_from_date_or_blockheight => "Wprowadź datę utworzenia tego portfela. Lub jeśli znasz wysokość bloku, wprowadź go zamiast tego"; + @override String get settings_only_trades => "Tylko transakcje"; @override String get trade_state_to_be_created => "Zostać stworzonym"; @@ -5339,6 +5371,8 @@ class $pl extends S { @override String get settings_support => "Wsparcie"; @override + String get seed_reminder => "Zapisz je na wypadek zgubienia lub wyczyszczenia telefonu"; + @override String get restore_wallet => "Przywróć portfel"; @override String get ok => "Dobrze"; @@ -5897,6 +5931,8 @@ class $es extends S { @override String get choose_account => "Elegir cuenta"; @override + String get restore_from_date_or_blockheight => "Ingrese la fecha en que creó esta billetera. O si conoce la altura del bloque, ingréselo en su lugar"; + @override String get settings_only_trades => "Solo comercia"; @override String get trade_state_to_be_created => "Ser creado"; @@ -5977,6 +6013,8 @@ class $es extends S { @override String get settings_support => "Apoyo"; @override + String get seed_reminder => "Anótelos en caso de que pierda o borre su teléfono"; + @override String get restore_wallet => "Restaurar billetera"; @override String get ok => "OK"; @@ -6535,6 +6573,8 @@ class $nl extends S { @override String get choose_account => "Kies account"; @override + String get restore_from_date_or_blockheight => "Voer de datum in waarop u deze portemonnee heeft gemaakt. Of als u de blokhoogte kent, voer deze dan in"; + @override String get settings_only_trades => "Alleen handel"; @override String get trade_state_to_be_created => "Om gecreëerd te worden"; @@ -6615,6 +6655,8 @@ class $nl extends S { @override String get settings_support => "Ondersteuning"; @override + String get seed_reminder => "Schrijf deze op voor het geval u uw telefoon kwijtraakt of veegt"; + @override String get restore_wallet => "Portemonnee herstellen"; @override String get ok => "OK"; @@ -7173,6 +7215,8 @@ class $zh extends S { @override String get choose_account => "選擇帳號"; @override + String get restore_from_date_or_blockheight => "請輸入創建此錢包的日期。 或者,如果您知道塊高,請改為輸入"; + @override String get settings_only_trades => "只交易"; @override String get trade_state_to_be_created => "待创建"; @@ -7253,6 +7297,8 @@ class $zh extends S { @override String get settings_support => "支持"; @override + String get seed_reminder => "請寫下這些,以防丟失或擦拭手機"; + @override String get restore_wallet => "恢复钱包"; @override String get ok => "好"; diff --git a/lib/router.dart b/lib/router.dart index 14f658e0c..4e1230cfa 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -218,7 +218,7 @@ class Router { case Routes.seed: return MaterialPageRoute( builder: (_) => getIt.get( - param1: settings.arguments as VoidCallback)); + param1: settings.arguments as bool)); case Routes.restoreWalletFromSeed: final args = settings.arguments as List; diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index d7a37ce32..b97a46b9f 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -46,7 +46,7 @@ class WalletMenu { Navigator.of(context).pushNamed(Routes.auth, arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) => isAuthenticatedSuccessfully - ? Navigator.of(auth.context).popAndPushNamed(Routes.seed) + ? Navigator.of(auth.context).popAndPushNamed(Routes.seed, arguments: false) : null); break; diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index 60d780d2d..027bbbb7b 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -53,7 +54,7 @@ class _WalletNameFormState extends State { _stateReaction ??= reaction((_) => _walletNewVM.state, (WalletCreationState state) { if (state is WalletCreatedSuccessfully) { - Navigator.of(context).popUntil((route) => route.isFirst); + Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true); } if (state is WalletCreationFailure) { diff --git a/lib/src/screens/restore/restore_wallet_from_keys_page.dart b/lib/src/screens/restore/restore_wallet_from_keys_page.dart index 6f950f6e4..62e25f0a6 100644 --- a/lib/src/screens/restore/restore_wallet_from_keys_page.dart +++ b/lib/src/screens/restore/restore_wallet_from_keys_page.dart @@ -171,7 +171,19 @@ class _RestoreFromKeysFromState extends State { onHeightChange: (height) { widget.walletRestorationFromKeysVM.height = height; print(height); - })], + }), + Padding( + padding: EdgeInsets.only(left: 40, right: 40, top: 24), + child: Text( + S.of(context).restore_from_date_or_blockheight, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context).hintColor + ), + ), + )], ]), ), bottomSectionPadding: EdgeInsets.only(bottom: 24), diff --git a/lib/src/screens/restore/restore_wallet_from_seed_details.dart b/lib/src/screens/restore/restore_wallet_from_seed_details.dart index 3225be9e7..3815c09d7 100644 --- a/lib/src/screens/restore/restore_wallet_from_seed_details.dart +++ b/lib/src/screens/restore/restore_wallet_from_seed_details.dart @@ -101,13 +101,26 @@ class _RestoreFromSeedDetailsFormState )) ], ), - if (widget.walletRestorationFromSeedVM.hasRestorationHeight) + if (widget.walletRestorationFromSeedVM.hasRestorationHeight) ... [ BlockchainHeightWidget( key: _blockchainHeightKey, onHeightChange: (height) { widget.walletRestorationFromSeedVM.height = height; print(height); }), + Padding( + padding: EdgeInsets.only(left: 40, right: 40, top: 24), + child: Text( + S.of(context).restore_from_date_or_blockheight, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context).hintColor + ), + ), + ) + ] ]), ), bottomSectionPadding: EdgeInsets.only(bottom: 24), diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index cde60f5b0..5b3933cff 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -1,17 +1,18 @@ +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:provider/provider.dart'; import 'package:esys_flutter_share/esys_flutter_share.dart'; import 'package:flutter_mobx/flutter_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/themes.dart'; -import 'package:cake_wallet/theme_changer.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; class WalletSeedPage extends BasePage { - WalletSeedPage(this.walletSeedViewModel, {this.onCloseCallback}); + WalletSeedPage(this.walletSeedViewModel, {@required this.isNewWalletCreated}); static final imageLight = Image.asset('assets/images/crypto_lock_light.png'); static final imageDark = Image.asset('assets/images/crypto_lock.png'); @@ -19,36 +20,38 @@ class WalletSeedPage extends BasePage { @override String get title => S.current.seed_title; - final VoidCallback onCloseCallback; + final bool isNewWalletCreated; final WalletSeedViewModel walletSeedViewModel; @override void onClose(BuildContext context) => - onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop(); + isNewWalletCreated + ? Navigator.of(context).popUntil((route) => route.isFirst) + : Navigator.of(context).pop(); @override Widget leading(BuildContext context) => - onCloseCallback != null ? Offstage() : super.leading(context); + isNewWalletCreated ? Offstage() : super.leading(context); @override Widget trailing(BuildContext context) { - return onCloseCallback != null + return isNewWalletCreated ? GestureDetector( onTap: () => onClose(context), child: Container( width: 100, - height: 42, + height: 32, alignment: Alignment.center, margin: EdgeInsets.only(left: 10), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(16)), - color: Theme.of(context).accentTextTheme.title.color), + color: Theme.of(context).accentTextTheme.caption.color), child: Text( S.of(context).seed_language_next, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Colors.blue), + color: Palette.blueCraiola), ), ), ) @@ -57,9 +60,7 @@ class WalletSeedPage extends BasePage { @override Widget body(BuildContext context) { - final _themeChanger = Provider.of(context); - final image = - _themeChanger.getTheme() == Themes.darkTheme ? imageDark : imageLight; + final image = getIt.get().isDarkTheme ? imageDark : imageLight; return Container( padding: EdgeInsets.all(24), @@ -85,22 +86,23 @@ class WalletSeedPage extends BasePage { walletSeedViewModel.name, style: TextStyle( fontSize: 20, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, color: Theme.of(context) .primaryTextTheme .title .color), ), Padding( - padding: EdgeInsets.only(top: 20), + padding: EdgeInsets.only(top: 20, left: 16, right: 16), child: Text( walletSeedViewModel.seed, textAlign: TextAlign.center, style: TextStyle( fontSize: 14, + fontWeight: FontWeight.normal, color: Theme.of(context) .primaryTextTheme - .caption + .overline .color), ), ) @@ -108,42 +110,63 @@ class WalletSeedPage extends BasePage { ); }), ), - Row( - mainAxisSize: MainAxisSize.max, + Column( children: [ - Flexible( - child: Container( - padding: EdgeInsets.only(right: 8.0), - child: PrimaryButton( - onPressed: () => Share.text( - S.of(context).seed_share, - walletSeedViewModel.seed, - 'text/plain'), - text: S.of(context).save, - color: Colors.green, - textColor: Colors.white), - )), - Flexible( - child: Container( - padding: EdgeInsets.only(left: 8.0), - child: Builder( - builder: (context) => PrimaryButton( - onPressed: () { - Clipboard.setData(ClipboardData( - text: walletSeedViewModel.seed)); - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text( - S.of(context).copied_to_clipboard), - backgroundColor: Colors.green, - duration: Duration(milliseconds: 1500), - ), - ); - }, - text: S.of(context).copy, - color: Colors.blue, - textColor: Colors.white)), - )) + isNewWalletCreated + ? Padding( + padding: EdgeInsets.only(bottom: 52, left: 43, right: 43), + child: Text( + S.of(context).seed_reminder, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context) + .primaryTextTheme + .overline + .color + ), + ), + ) + : Offstage(), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + child: Container( + padding: EdgeInsets.only(right: 8.0), + child: PrimaryButton( + onPressed: () => Share.text( + S.of(context).seed_share, + walletSeedViewModel.seed, + 'text/plain'), + text: S.of(context).save, + color: Colors.green, + textColor: Colors.white), + )), + Flexible( + child: Container( + padding: EdgeInsets.only(left: 8.0), + child: Builder( + builder: (context) => PrimaryButton( + onPressed: () { + Clipboard.setData(ClipboardData( + text: walletSeedViewModel.seed)); + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text( + S.of(context).copied_to_clipboard), + backgroundColor: Colors.green, + duration: Duration(milliseconds: 1500), + ), + ); + }, + text: S.of(context).copy, + color: Palette.blueCraiola, + textColor: Colors.white)), + )) + ], + ) ], ) ], diff --git a/lib/src/screens/wallet_list/wallet_menu.dart b/lib/src/screens/wallet_list/wallet_menu.dart index 360f55759..9ccdedef1 100644 --- a/lib/src/screens/wallet_list/wallet_menu.dart +++ b/lib/src/screens/wallet_list/wallet_menu.dart @@ -116,7 +116,7 @@ class WalletMenu { return; } auth.close(); - await Navigator.of(context).pushNamed(Routes.seed); + await Navigator.of(context).pushNamed(Routes.seed, arguments: false); }); break; case 2: diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 219e17320..fd00ea39e 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Stellen Sie Ihre Brieftasche aus dem 12-Wort-Kombinationscode wieder her", "restore_bitcoin_description_from_keys" : "Stellen Sie Ihre Brieftasche aus der generierten WIF-Zeichenfolge aus Ihren privaten Schlüsseln wieder her", "restore_bitcoin_title_from_keys" : "Aus WIF wiederherstellen", + "restore_from_date_or_blockheight" : "Bitte geben Sie das Datum ein, an dem Sie diese Brieftasche erstellt haben. Oder wenn Sie die Blockhöhe kennen, geben Sie sie stattdessen ein", + "seed_reminder" : "Bitte notieren Sie diese, falls Sie Ihr Telefon verlieren oder abwischen", "seed_title" : "Seed", "seed_share" : "Teilen Sie Seed", "copy" : "Kopieren", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 82afbe400..437ed2490 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Restore your wallet from 12 word combination code", "restore_bitcoin_description_from_keys" : "Restore your wallet from generated WIF string from your private keys", "restore_bitcoin_title_from_keys" : "Restore from WIF", + "restore_from_date_or_blockheight" : "Please enter the date you created this wallet. Or if you know the blockheight, please enter it instead", + "seed_reminder" : "Please write these down in case you lose or wipe your phone", "seed_title" : "Seed", "seed_share" : "Share seed", "copy" : "Copy", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index f01e9002b..535fe09e6 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Restaure su billetera a partir del código de combinación de 12 palabras", "restore_bitcoin_description_from_keys" : "Restaure su billetera a partir de una cadena WIF generada a partir de sus claves privadas", "restore_bitcoin_title_from_keys" : "Restaurar desde WIF", + "restore_from_date_or_blockheight" : "Ingrese la fecha en que creó esta billetera. O si conoce la altura del bloque, ingréselo en su lugar", + "seed_reminder" : "Anótelos en caso de que pierda o borre su teléfono", "seed_title" : "Semilla", "seed_share" : "Compartir semillas", "copy" : "Dupdo", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 7b0d95adf..9e1f08724 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "12 शब्द संयोजन कोड से अपने वॉलेट को पुनर्स्थापित करें", "restore_bitcoin_description_from_keys" : "अपने निजी कुंजी से उत्पन्न WIF स्ट्रिंग से अपने वॉलेट को पुनर्स्थापित करें", "restore_bitcoin_title_from_keys" : "WIF से पुनर्स्थापित करें", + "restore_from_date_or_blockheight" : "कृपया इस वॉलेट को बनाने की तिथि दर्ज करें। या यदि आप ब्लॉकचाइट जानते हैं, तो कृपया इसके बजाय इसे दर्ज करें", + "seed_reminder" : "यदि आप अपना फोन खो देते हैं या मिटा देते हैं तो कृपया इन्हें लिख लें", "seed_title" : "बीज", "seed_share" : "बीज साझा करें", "copy" : "प्रतिलिपि", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 000475bd8..50a796ff4 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "12ワードの組み合わせコードからウォレットを復元する", "restore_bitcoin_description_from_keys" : "秘密鍵から生成されたWIF文字列からウォレットを復元します", "restore_bitcoin_title_from_keys" : "WIFから復元", + "restore_from_date_or_blockheight" : "このウォレットを作成した日付を入力してください。 または、ブロックの高さがわかっている場合は、代わりに入力してください", + "seed_reminder" : "スマートフォンを紛失したりワイプした場合に備えて、これらを書き留めてください", "seed_title" : "シード", "seed_share" : "シードを共有する", "copy" : "コピー", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index c33dded12..720069215 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "12 단어 조합 코드에서 지갑 복원", "restore_bitcoin_description_from_keys" : "개인 키에서 생성 된 WIF 문자열에서 지갑 복원", "restore_bitcoin_title_from_keys" : "WIF에서 복원", + "restore_from_date_or_blockheight" : "이 지갑을 만든 날짜를 입력하세요. 또는 블록 높이를 알고있는 경우 대신 입력하십시오.", + "seed_reminder" : "휴대 전화를 분실하거나 닦을 경우를 대비해 적어 두세요.", "seed_title" : "씨", "seed_share" : "시드 공유", "copy" : "부", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 2fc68aef3..cf04367b6 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Herstel uw portemonnee met een combinatiecode van 12 woorden", "restore_bitcoin_description_from_keys" : "Herstel uw portemonnee van de gegenereerde WIF-string van uw privésleutels", "restore_bitcoin_title_from_keys" : "Herstel van WIF", + "restore_from_date_or_blockheight" : "Voer de datum in waarop u deze portemonnee heeft gemaakt. Of als u de blokhoogte kent, voer deze dan in", + "seed_reminder" : "Schrijf deze op voor het geval u uw telefoon kwijtraakt of veegt", "seed_title" : "Zaad", "seed_share" : "Deel zaad", "copy" : "Kopiëren", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 5970fadb8..d7d6ae418 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Przywróć swój portfel z kodu złożonego z 12 słów", "restore_bitcoin_description_from_keys" : "Przywróć swój portfel z wygenerowanego ciągu WIF z kluczy prywatnych", "restore_bitcoin_title_from_keys" : "Przywróć z WIF", + "restore_from_date_or_blockheight" : "Wprowadź datę utworzenia tego portfela. Lub jeśli znasz wysokość bloku, wprowadź go zamiast tego", + "seed_reminder" : "Zapisz je na wypadek zgubienia lub wyczyszczenia telefonu", "seed_title" : "Ziarno", "seed_share" : "Udostępnij ziarno", "copy" : "Kopiuj", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index f085280bf..9566cccc1 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Restaure sua carteira a partir de um código de combinação de 12 palavras", "restore_bitcoin_description_from_keys" : "Restaure sua carteira a partir da string WIF gerada de suas chaves privadas", "restore_bitcoin_title_from_keys" : "Restaurar de WIF", + "restore_from_date_or_blockheight" : "Por favor, insira a data em que você criou esta carteira. Ou se você souber a altura do bloco, insira-o", + "seed_reminder" : "Anote-os para o caso de perder ou limpar seu telefone", "seed_title" : "Semente", "seed_share" : "Compartilhar semente", "copy" : "Copiar", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 68c1453b3..c6955eda9 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Вы можете восстановить кошелёк используя 12-ти значную мнемоническую фразу", "restore_bitcoin_description_from_keys" : "Вы можете восстановить кошелёк с помощью WIF", "restore_bitcoin_title_from_keys" : "Восстановить с помощью WIF", + "restore_from_date_or_blockheight" : "Пожалуйста, введите дату создания кошелька. Или, если вы знаете высоту блока, введите ее значение", + "seed_reminder" : "Пожалуйста, запишите мнемоническую фразу на случай потери или очистки телефона", "seed_title" : "Мнемоническая фраза", "seed_share" : "Поделиться мнемонической фразой", "copy" : "Скопировать", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 2cf2119b2..5bbbc606f 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "Ви можете відновити гаманець використовуючи 12-ти слівну мнемонічну фразу", "restore_bitcoin_description_from_keys" : "Ви можете відновити гаманець за допомогою WIF", "restore_bitcoin_title_from_keys" : "Відновити за допомогою WIF", + "restore_from_date_or_blockheight" : "Будь ласка, введіть дату створення гаманця. Або, якщо ви знаєте висоту блоку, введіть її значення", + "seed_reminder" : "Будь ласка, запишіть мнемонічну фразу на випадок втрати або очищення телефону", "seed_title" : "Мнемонічна фраза", "seed_share" : "Поділитися мнемонічною фразою", "copy" : "Скопіювати", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 8fcce5de1..195eff3b4 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -168,8 +168,10 @@ "restore_bitcoin_description_from_seed" : "從12個單詞的組合碼恢復您的錢包", "restore_bitcoin_description_from_keys" : "從私鑰中生成的WIF字符串還原您的錢包", "restore_bitcoin_title_from_keys" : "從WIF還原", + "restore_from_date_or_blockheight" : "請輸入創建此錢包的日期。 或者,如果您知道塊高,請改為輸入", + "seed_reminder" : "請寫下這些,以防丟失或擦拭手機", "seed_title" : "种子", "seed_share" : "分享种子", "copy" : "复制", From 586d75cfd1e7f8eb5719e3d95d3c0f62cab5a1ce Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Mon, 14 Sep 2020 20:46:50 +0300 Subject: [PATCH 07/13] redesign | fixed font of the middle text (navigation bar, base page), monero account list page and account tile --- lib/src/screens/base_page.dart | 1 + lib/src/screens/monero_accounts/monero_account_list_page.dart | 2 ++ lib/src/screens/monero_accounts/widgets/account_tile.dart | 1 + 3 files changed, 4 insertions(+) diff --git a/lib/src/screens/base_page.dart b/lib/src/screens/base_page.dart index 33945e5d2..ca4bfe825 100644 --- a/lib/src/screens/base_page.dart +++ b/lib/src/screens/base_page.dart @@ -73,6 +73,7 @@ abstract class BasePage extends StatelessWidget { style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, + fontFamily: 'Poppins', color: titleColor ?? Theme.of(context).primaryTextTheme.title.color), ); diff --git a/lib/src/screens/monero_accounts/monero_account_list_page.dart b/lib/src/screens/monero_accounts/monero_account_list_page.dart index ab662629b..a7b31d2d9 100644 --- a/lib/src/screens/monero_accounts/monero_account_list_page.dart +++ b/lib/src/screens/monero_accounts/monero_account_list_page.dart @@ -53,6 +53,7 @@ class MoneroAccountListPage extends StatelessWidget { style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, + fontFamily: 'Poppins', decoration: TextDecoration.none, color: Colors.white ), @@ -140,6 +141,7 @@ class MoneroAccountListPage extends StatelessWidget { style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, + fontFamily: 'Poppins', color: Colors.white, decoration: TextDecoration.none, ), diff --git a/lib/src/screens/monero_accounts/widgets/account_tile.dart b/lib/src/screens/monero_accounts/widgets/account_tile.dart index 5782ce8e4..c39758bd5 100644 --- a/lib/src/screens/monero_accounts/widgets/account_tile.dart +++ b/lib/src/screens/monero_accounts/widgets/account_tile.dart @@ -32,6 +32,7 @@ class AccountTile extends StatelessWidget { style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, + fontFamily: 'Poppins', color: textColor, decoration: TextDecoration.none, ), From 18ccd5de35d7a5b559122246df4b9b52442909ec Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Mon, 14 Sep 2020 22:07:44 +0300 Subject: [PATCH 08/13] redesign | merged with dev branch --- lib/di.dart | 4 +- lib/reactions/bootstrap.dart | 4 +- lib/src/screens/dashboard/dashboard_page.dart | 18 +- lib/src/screens/dashboard/wallet_menu.dart | 8 +- .../send/widgets/base_send_widget.dart | 530 +++++++++--------- pubspec.lock | 31 +- 6 files changed, 301 insertions(+), 294 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index 88aba04c8..dbd8ca63a 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -257,8 +257,8 @@ Future setup( getIt.registerFactory( () => SendPage(sendViewModel: getIt.get())); - // getIt.registerFactory( - // () => SendTemplatePage(sendViewModel: getIt.get())); + getIt.registerFactory( + () => SendTemplatePage(sendViewModel: getIt.get())); getIt.registerFactory(() => WalletListViewModel( walletInfoSource, getIt.get(), getIt.get())); diff --git a/lib/reactions/bootstrap.dart b/lib/reactions/bootstrap.dart index b371ed74e..c727a0868 100644 --- a/lib/reactions/bootstrap.dart +++ b/lib/reactions/bootstrap.dart @@ -24,8 +24,8 @@ import 'package:cake_wallet/store/dashboard/fiat_convertation_store.dart'; // FIXME: move me Future loadCurrentWallet() async { final appStore = getIt.get(); - final name = 'test'; - getIt.get().getString('current_wallet_name'); + //final name = 'test'; + final name = getIt.get().getString('current_wallet_name'); final typeRaw = getIt.get().getInt('current_wallet_type') ?? 0; final type = deserializeFromInt(typeRaw); diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index d3ae08cbe..4c0ee613c 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -104,9 +104,12 @@ class DashboardPage extends BasePage { )), Container( width: double.infinity, - padding: EdgeInsets.only(left: 44, right: 0, bottom: 24), + padding: EdgeInsets.only( + left: 45, + right: 45, + bottom: 24 + ), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: ActionButton( @@ -120,7 +123,16 @@ class DashboardPage extends BasePage { child: ActionButton( image: exchangeImage, title: S.of(context).exchange, - route: Routes.exchange), + route: Routes.exchange + ), + ), + Flexible( + child: ActionButton( + image: receiveImage, + title: S.of(context).receive, + route: Routes.receive, + alignment: Alignment.centerRight, + ), ) ], ), diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index 07fe0810d..0b13ec9fa 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -79,13 +79,13 @@ class WalletMenu { return AlertWithTwoActions( alertTitle: S.of(context).reconnection, alertContent: S.of(context).reconnect_alert_text, - leftButtonText: S.of(context).ok, - rightButtonText: S.of(context).cancel, - actionLeftButton: () async { + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () async { await reconnect?.call(); Navigator.of(context).pop(); }, - actionRightButton: () => Navigator.of(context).pop()); + actionLeftButton: () => Navigator.of(context).pop()); }); } } diff --git a/lib/src/screens/send/widgets/base_send_widget.dart b/lib/src/screens/send/widgets/base_send_widget.dart index 260923124..871482a4c 100644 --- a/lib/src/screens/send/widgets/base_send_widget.dart +++ b/lib/src/screens/send/widgets/base_send_widget.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/view_model/send/send_view_model.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/widgets/address_text_field.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/top_panel.dart'; @@ -25,9 +26,9 @@ import 'package:cake_wallet/routes.dart'; class BaseSendWidget extends StatelessWidget { BaseSendWidget( {@required this.sendViewModel, - @required this.leading, - @required this.middle, - this.isTemplate = false}); + @required this.leading, + @required this.middle, + this.isTemplate = false}); final SendViewModel sendViewModel; final bool isTemplate; @@ -72,32 +73,33 @@ class BaseSendWidget extends StatelessWidget { children: [ isTemplate ? BaseTextFormField( - controller: _nameController, - hintText: S.of(context).send_name, - borderColor: Theme.of(context) - .primaryTextTheme - .headline - .color, - textStyle: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.white), - placeholderTextStyle: TextStyle( - color: Theme.of(context) - .primaryTextTheme - .headline - .decorationColor, - fontWeight: FontWeight.w500, - fontSize: 14), - validator: sendViewModel.templateValidator, - ) + controller: _nameController, + hintText: S.of(context).send_name, + borderColor: Theme.of(context) + .primaryTextTheme + .headline + .color, + textStyle: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.white), + placeholderTextStyle: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline + .decorationColor, + fontWeight: FontWeight.w500, + fontSize: 14), + validator: sendViewModel.templateValidator, + ) : Offstage(), Padding( padding: EdgeInsets.only(top: isTemplate ? 20 : 0), child: AddressTextField( controller: _addressController, - placeholder: S.of(context).send_address( - sendViewModel.cryptoCurrencyTitle), + // placeholder: S + // .of(context) + // .send_address(sendViewModel.cryptoCurrencyTitle), focusNode: _focusNode, onURIScanned: (uri) { var address = ''; @@ -119,9 +121,9 @@ class BaseSendWidget extends StatelessWidget { AddressTextFieldOption.addressBook ], buttonColor: - Theme.of(context).primaryTextTheme.display1.color, + Theme.of(context).primaryTextTheme.display1.color, borderColor: - Theme.of(context).primaryTextTheme.headline.color, + Theme.of(context).primaryTextTheme.headline.color, textStyle: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, @@ -150,43 +152,43 @@ class BaseSendWidget extends StatelessWidget { prefixIcon: Padding( padding: EdgeInsets.only(top: 9), child: - Text(sendViewModel.currency.title + ':', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white, - )), + Text(sendViewModel.currency.title + ':', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, + )), ), suffixIcon: isTemplate ? Offstage() : Container( - height: 32, - width: 32, - margin: EdgeInsets.only( - left: 14, top: 4, bottom: 10), - decoration: BoxDecoration( - color: Theme.of(context) - .primaryTextTheme - .display1 - .color, - borderRadius: BorderRadius.all( - Radius.circular(6))), - child: InkWell( - onTap: () => - sendViewModel.setSendAll(), - child: Center( - child: Text(S.of(context).all, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Theme.of(context) - .primaryTextTheme - .display1 - .decorationColor)), - ), - ), - ), + height: 32, + width: 32, + margin: EdgeInsets.only( + left: 14, top: 4, bottom: 10), + decoration: BoxDecoration( + color: Theme.of(context) + .primaryTextTheme + .display1 + .color, + borderRadius: BorderRadius.all( + Radius.circular(6))), + child: InkWell( + onTap: () => + sendViewModel.setSendAll(), + child: Center( + child: Text(S.of(context).all, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .primaryTextTheme + .display1 + .decorationColor)), + ), + ), + ), hintText: '0.0000', borderColor: Theme.of(context) .primaryTextTheme @@ -208,37 +210,37 @@ class BaseSendWidget extends StatelessWidget { isTemplate ? Offstage() : Observer( - builder: (_) => Padding( - padding: EdgeInsets.only(top: 10), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - S.of(context).available_balance + ':', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme - .headline - .decorationColor), - )), - Text( - sendViewModel.balance, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme - .headline - .decorationColor), - ) - ], - ), - )), + builder: (_) => Padding( + padding: EdgeInsets.only(top: 10), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + S.of(context).available_balance + ':', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .primaryTextTheme + .headline + .decorationColor), + )), + Text( + sendViewModel.balance, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .primaryTextTheme + .headline + .decorationColor), + ) + ], + ), + )), Padding( padding: const EdgeInsets.only(top: 20), child: BaseTextFormField( @@ -278,52 +280,52 @@ class BaseSendWidget extends StatelessWidget { isTemplate ? Offstage() : Observer( - builder: (_) => GestureDetector( - onTap: () => - _setTransactionPriority(context), - child: Container( - padding: EdgeInsets.only(top: 24), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text(S.of(context).send_estimated_fee, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - //color: Theme.of(context).primaryTextTheme.display2.color, - color: Colors.white)), - Container( - child: Row( - children: [ - Text( - sendViewModel.estimatedFee - .toString() + - ' ' + - sendViewModel - .currency.title, - style: TextStyle( - fontSize: 12, - fontWeight: - FontWeight.w600, - //color: Theme.of(context).primaryTextTheme.display2.color, - color: Colors.white)), - Padding( - padding: - EdgeInsets.only(left: 5), - child: Icon( - Icons.arrow_forward_ios, - size: 12, - color: Colors.white, - ), - ) - ], - ), - ) - ], - ), + builder: (_) => GestureDetector( + onTap: () => + _setTransactionPriority(context), + child: Container( + padding: EdgeInsets.only(top: 24), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text(S.of(context).send_estimated_fee, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + //color: Theme.of(context).primaryTextTheme.display2.color, + color: Colors.white)), + Container( + child: Row( + children: [ + Text( + sendViewModel.estimatedFee + .toString() + + ' ' + + sendViewModel + .currency.title, + style: TextStyle( + fontSize: 12, + fontWeight: + FontWeight.w600, + //color: Theme.of(context).primaryTextTheme.display2.color, + color: Colors.white)), + Padding( + padding: + EdgeInsets.only(left: 5), + child: Icon( + Icons.arrow_forward_ios, + size: 12, + color: Colors.white, + ), + ) + ], ), - )) + ) + ], + ), + ), + )) ], ), ) @@ -333,117 +335,117 @@ class BaseSendWidget extends StatelessWidget { isTemplate ? Offstage() : Padding( - padding: EdgeInsets.only(top: 30, left: 24, bottom: 24), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - S.of(context).send_templates, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme - .display4 - .color), - ) - ], - ), - ), + padding: EdgeInsets.only(top: 30, left: 24, bottom: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + S.of(context).send_templates, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .primaryTextTheme + .display4 + .color), + ) + ], + ), + ), isTemplate ? Offstage() : Container( - height: 40, - width: double.infinity, - padding: EdgeInsets.only(left: 24), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - GestureDetector( - onTap: () => Navigator.of(context) - .pushNamed(Routes.sendTemplate), + height: 40, + width: double.infinity, + padding: EdgeInsets.only(left: 24), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + GestureDetector( + onTap: () => Navigator.of(context) + .pushNamed(Routes.sendTemplate), + child: Container( + padding: EdgeInsets.only(left: 1, right: 10), + child: DottedBorder( + borderType: BorderType.RRect, + dashPattern: [6, 4], + color: Theme.of(context) + .primaryTextTheme + .display2 + .decorationColor, + strokeWidth: 2, + radius: Radius.circular(20), child: Container( - padding: EdgeInsets.only(left: 1, right: 10), - child: DottedBorder( - borderType: BorderType.RRect, - dashPattern: [6, 4], - color: Theme.of(context) - .primaryTextTheme - .display2 - .decorationColor, - strokeWidth: 2, - radius: Radius.circular(20), - child: Container( - height: 34, - width: 75, - padding: EdgeInsets.only(left: 10, right: 10), - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(20)), - color: Colors.transparent, - ), - child: Text( - S.of(context).send_new, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme - .display3 - .color), - ), - )), - ), - ), - // Observer( - // builder: (_) { - // final templates = sendViewModel.templates; - // final itemCount = templates.length; + height: 34, + width: 75, + padding: EdgeInsets.only(left: 10, right: 10), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(20)), + color: Colors.transparent, + ), + child: Text( + S.of(context).send_new, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .primaryTextTheme + .display3 + .color), + ), + )), + ), + ), + // Observer( + // builder: (_) { + // final templates = sendViewModel.templates; + // final itemCount = templates.length; - // return ListView.builder( - // scrollDirection: Axis.horizontal, - // shrinkWrap: true, - // physics: NeverScrollableScrollPhysics(), - // itemCount: itemCount, - // itemBuilder: (context, index) { - // final template = templates[index]; + // return ListView.builder( + // scrollDirection: Axis.horizontal, + // shrinkWrap: true, + // physics: NeverScrollableScrollPhysics(), + // itemCount: itemCount, + // itemBuilder: (context, index) { + // final template = templates[index]; - return TemplateTile( - key: UniqueKey(), - to: template.name, - amount: template.amount, - from: template.cryptoCurrency, - onTap: () { - _addressController.text = template.address; - _cryptoAmountController.text = template.amount; - getOpenaliasRecord(context); - }, - onRemove: () { - showDialog( - context: context, - builder: (dialogContext) { - return AlertWithTwoActions( - alertTitle: S.of(context).template, - alertContent: S.of(context).confirm_delete_template, - rightButtonText: S.of(context).delete, - leftButtonText: S.of(context).cancel, - actionRightButton: () { - Navigator.of(dialogContext).pop(); - sendViewModel.sendTemplateStore.remove(template: template); - sendViewModel.sendTemplateStore.update(); - }, - actionLeftButton: () => Navigator.of(dialogContext).pop() - ); - } - ); - }, - ); - } - ); - } - ) + // return TemplateTile( + // key: UniqueKey(), + // to: template.name, + // amount: template.amount, + // from: template.cryptoCurrency, + // onTap: () { + // _addressController.text = template.address; + // _cryptoAmountController.text = template.amount; + // getOpenaliasRecord(context); + // }, + // onRemove: () { + // showDialog( + // context: context, + // builder: (dialogContext) { + // return AlertWithTwoActions( + // alertTitle: S.of(context).template, + // alertContent: S.of(context).confirm_delete_template, + // leftButtonText: S.of(context).delete, + // rightButtonText: S.of(context).cancel, + // actionLeftButton: () { + // Navigator.of(dialogContext).pop(); + // sendViewModel.sendTemplateStore.remove(template: template); + // sendViewModel.sendTemplateStore.update(); + // }, + // actionRightButton: () => Navigator.of(dialogContext).pop() + // ); + // } + // ); + // }, + // ); + // } + // ); + // } + // ) ], ), ), @@ -453,20 +455,20 @@ class BaseSendWidget extends StatelessWidget { bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), bottomSection: isTemplate ? PrimaryButton( - onPressed: () { - if (_formKey.currentState.validate()) { - // sendViewModel.sendTemplateStore.addTemplate( - // name: _nameController.text, - // address: _addressController.text, - // cryptoCurrency: sendViewModel.currency.title, - // amount: _cryptoAmountController.text); - // sendViewModel.sendTemplateStore.update(); - Navigator.of(context).pop(); - } - }, - text: S.of(context).save, - color: Colors.green, - textColor: Colors.white) + onPressed: () { + if (_formKey.currentState.validate()) { + // sendViewModel.sendTemplateStore.addTemplate( + // name: _nameController.text, + // address: _addressController.text, + // cryptoCurrency: sendViewModel.currency.title, + // amount: _cryptoAmountController.text); + // sendViewModel.sendTemplateStore.update(); + Navigator.of(context).pop(); + } + }, + text: S.of(context).save, + color: Colors.green, + textColor: Colors.white) : Observer(builder: (_) { return LoadingPrimaryButton( onPressed: () { @@ -552,7 +554,7 @@ class BaseSendWidget extends StatelessWidget { alertTitle: S.of(context).confirm_sending, amount: S.of(context).send_amount, amountValue: - sendViewModel.pendingTransaction.amountFormatted, + sendViewModel.pendingTransaction.amountFormatted, fee: S.of(context).send_fee, feeValue: sendViewModel.pendingTransaction.feeFormatted, leftButtonText: S.of(context).ok, @@ -694,14 +696,14 @@ class BaseSendWidget extends StatelessWidget { await showDialog( builder: (_) => Picker( - items: items, - selectedAtIndex: selectedItem, - title: S.of(context).please_select, - mainAxisAlignment: MainAxisAlignment.center, - onItemSelected: (TransactionPriority priority) => null, - // sendViewModel.setTransactionPriority(priority), - isAlwaysShowScrollThumb: true, - ), + items: items, + selectedAtIndex: selectedItem, + title: S.of(context).please_select, + mainAxisAlignment: MainAxisAlignment.center, + onItemSelected: (TransactionPriority priority) => null, + // sendViewModel.setTransactionPriority(priority), + isAlwaysShowScrollThumb: true, + ), context: context); } -} +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index ab4e1d14e..158fdec7c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,7 +42,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.2" + version: "2.4.1" auto_size_text: dependency: "direct main" description: @@ -210,7 +210,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.13" + version: "1.14.12" convert: dependency: transitive description: @@ -224,7 +224,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "2.1.4" csslib: dependency: transitive description: @@ -302,13 +302,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" ffi: dependency: transitive description: @@ -475,7 +468,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.14" + version: "2.1.12" intl: dependency: "direct main" description: @@ -524,7 +517,7 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.8" + version: "0.12.6" meta: dependency: transitive description: @@ -594,7 +587,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.6.4" path_drawing: dependency: transitive description: @@ -650,7 +643,7 @@ packages: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "3.0.4" + version: "2.4.0" platform: dependency: transitive description: @@ -830,7 +823,7 @@ packages: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.5" + version: "1.9.3" stream_channel: dependency: transitive description: @@ -865,7 +858,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.17" + version: "0.2.15" time: dependency: transitive description: @@ -886,7 +879,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.1.6" url_launcher: dependency: "direct main" description: @@ -963,7 +956,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "4.2.0" + version: "3.6.1" yaml: dependency: "direct main" description: @@ -972,5 +965,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.9.0-14.0.dev <3.0.0" + dart: ">=2.7.0 <3.0.0" flutter: ">=1.12.13+hotfix.5 <2.0.0" From 95917b2cdbd335ffe0c5c6af34625a67a89f5cb7 Mon Sep 17 00:00:00 2001 From: M Date: Tue, 15 Sep 2020 23:35:49 +0300 Subject: [PATCH 09/13] Merge redesign part 7. --- analysis_options.yaml | 2 - cw_monero/ios/Classes/monero_api.cpp | 4 +- cw_monero/lib/wallet.dart | 8 +- ios/Podfile.lock | 22 ++++ ios/Runner.xcodeproj/project.pbxproj | 4 + lib/bitcoin/bitcoin_transaction_history.dart | 2 - lib/bitcoin/bitcoin_wallet.dart | 40 ++----- lib/bitcoin/file.dart | 6 +- lib/core/auth_service.dart | 1 - lib/core/wallet_base.dart | 40 +++++-- lib/core/wallet_creation_service.dart | 13 +-- lib/core/wallet_credentials.dart | 8 +- lib/di.dart | 20 ++++ lib/main.dart | 16 ++- lib/monero/monero_wallet.dart | 100 +++++++++++------- lib/monero/monero_wallet_service.dart | 50 +++++---- lib/reactions/bootstrap.dart | 58 +++++----- lib/src/domain/common/fs_migration.dart | 40 +++---- lib/src/domain/common/wallet_info.dart | 31 +++++- lib/src/domain/monero/monero_wallet.dart | 23 ++-- .../domain/monero/monero_wallets_manager.dart | 5 +- lib/src/reactions/set_reactions.dart | 20 ++-- .../exchange/exchange_trade_view_model.dart | 17 ++- .../node_list/node_list_view_model.dart | 3 +- lib/view_model/send_view_model.dart | 34 +++--- ...let_address_edit_or_create_view_model.dart | 8 +- .../wallet_address_list_view_model.dart | 4 +- lib/view_model/wallet_creation_vm.dart | 13 ++- .../wallet_list/wallet_list_view_model.dart | 19 +--- pubspec.lock | 28 +++++ pubspec.yaml | 1 + 31 files changed, 373 insertions(+), 267 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index a217eaf08..52f1e3224 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,5 +1,3 @@ -include: package:pedantic/analysis_options.yaml - analyzer: strong-mode: implicit-casts: false diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp index 8d471b22c..6393eaea9 100644 --- a/cw_monero/ios/Classes/monero_api.cpp +++ b/cw_monero/ios/Classes/monero_api.cpp @@ -180,7 +180,7 @@ extern "C" void change_current_wallet(Monero::Wallet *wallet) { m_wallet = wallet; - // m_listener = nullptr; + m_listener = nullptr; if (wallet != nullptr) @@ -553,7 +553,7 @@ extern "C" if (m_listener != nullptr) { - free(m_listener); + // free(m_listener); } m_listener = new MoneroWalletListener(); diff --git a/cw_monero/lib/wallet.dart b/cw_monero/lib/wallet.dart index e1206ff83..c42cd3523 100644 --- a/cw_monero/lib/wallet.dart +++ b/cw_monero/lib/wallet.dart @@ -254,12 +254,12 @@ class SyncListner { onNewBlock(syncHeight, left, ptc); } - if (newTransactionExist && onNewTransaction != null) { - onNewTransaction(); + if (newTransactionExist) { + onNewTransaction?.call(); } - if (needToRefresh && onNeedToRefresh != null) { - onNeedToRefresh(); + if (needToRefresh) { + onNeedToRefresh?.call(); } }); } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 451d74c1f..63e247dc4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -3,6 +3,13 @@ PODS: - Flutter - MTBBarcodeScanner - SwiftProtobuf + - connectivity (0.0.1): + - Flutter + - Reachability + - connectivity_for_web (0.1.0): + - Flutter + - connectivity_macos (0.0.1): + - Flutter - cw_monero (0.0.2): - cw_monero/Boost (= 0.0.2) - cw_monero/lmdb (= 0.0.2) @@ -40,6 +47,7 @@ PODS: - Flutter - path_provider_macos (0.0.1): - Flutter + - Reachability (3.2) - share (0.0.1): - Flutter - shared_preferences (0.0.1): @@ -62,6 +70,9 @@ PODS: DEPENDENCIES: - barcode_scan (from `.symlinks/plugins/barcode_scan/ios`) + - connectivity (from `.symlinks/plugins/connectivity/ios`) + - connectivity_for_web (from `.symlinks/plugins/connectivity_for_web/ios`) + - connectivity_macos (from `.symlinks/plugins/connectivity_macos/ios`) - cw_monero (from `.symlinks/plugins/cw_monero/ios`) - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - esys_flutter_share (from `.symlinks/plugins/esys_flutter_share/ios`) @@ -86,11 +97,18 @@ DEPENDENCIES: SPEC REPOS: trunk: - MTBBarcodeScanner + - Reachability - SwiftProtobuf EXTERNAL SOURCES: barcode_scan: :path: ".symlinks/plugins/barcode_scan/ios" + connectivity: + :path: ".symlinks/plugins/connectivity/ios" + connectivity_for_web: + :path: ".symlinks/plugins/connectivity_for_web/ios" + connectivity_macos: + :path: ".symlinks/plugins/connectivity_macos/ios" cw_monero: :path: ".symlinks/plugins/cw_monero/ios" devicelocale: @@ -134,6 +152,9 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479 + connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467 + connectivity_for_web: 2b8584556930d4bd490d82b836bcf45067ce345b + connectivity_macos: e2e9731b6b22dda39eb1b128f6969d574460e191 cw_monero: 2e1f79929880cc2293b5bc1b25e28152e4d84649 devicelocale: feebbe5e7a30adb8c4f83185de1b50ff19b44f00 esys_flutter_share: 403498dab005b36ce1f8d7aff377e81f0621b0b4 @@ -146,6 +167,7 @@ SPEC CHECKSUMS: path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4 path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 + Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 share: 0b2c3e82132f5888bccca3351c504d0003b3b410 shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d shared_preferences_linux: afefbfe8d921e207f01ede8b60373d9e3b566b78 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 822dd4355..bd4100941 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -275,8 +275,10 @@ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/../Flutter/Flutter.framework", "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", + "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", "${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework", "${BUILT_PRODUCTS_DIR}/barcode_scan/barcode_scan.framework", + "${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework", "${BUILT_PRODUCTS_DIR}/cw_monero/cw_monero.framework", "${BUILT_PRODUCTS_DIR}/devicelocale/devicelocale.framework", "${BUILT_PRODUCTS_DIR}/esys_flutter_share/esys_flutter_share.framework", @@ -293,8 +295,10 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_monero.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/devicelocale.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/esys_flutter_share.framework", diff --git a/lib/bitcoin/bitcoin_transaction_history.dart b/lib/bitcoin/bitcoin_transaction_history.dart index 4a18d18c1..81ed01fe7 100644 --- a/lib/bitcoin/bitcoin_transaction_history.dart +++ b/lib/bitcoin/bitcoin_transaction_history.dart @@ -9,8 +9,6 @@ import 'package:cake_wallet/bitcoin/electrum.dart'; part 'bitcoin_transaction_history.g.dart'; -// TODO: Think about another transaction store for bitcoin transaction history.. - const _transactionsHistoryFileName = 'transactions.json'; class BitcoinTransactionHistory = BitcoinTransactionHistoryBase diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index 4fc46f8fa..fe6cbf184 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -1,39 +1,29 @@ import 'dart:async'; -import 'dart:typed_data'; import 'dart:convert'; +import 'package:mobx/mobx.dart'; +import 'package:bip39/bip39.dart' as bip39; +import 'package:flutter/foundation.dart'; +import 'package:rxdart/rxdart.dart'; +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart'; -import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_no_inputs_exception.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_wrong_balance_exception.dart'; import 'package:cake_wallet/bitcoin/bitcoin_unspent.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet_keys.dart'; +import 'package:cake_wallet/bitcoin/electrum.dart'; import 'package:cake_wallet/bitcoin/pending_bitcoin_transaction.dart'; import 'package:cake_wallet/bitcoin/script_hash.dart'; import 'package:cake_wallet/bitcoin/utils.dart'; import 'package:cake_wallet/src/domain/bitcoin/bitcoin_amount_format.dart'; -import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; import 'package:cake_wallet/src/domain/common/sync_status.dart'; -import 'package:cake_wallet/src/domain/common/transaction_direction.dart'; import 'package:cake_wallet/src/domain/common/transaction_priority.dart'; -import 'package:cw_monero/transaction_history.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:mobx/mobx.dart'; -import 'package:bip39/bip39.dart' as bip39; -import 'package:flutter/foundation.dart'; -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData; -import 'package:cake_wallet/src/domain/common/wallet_type.dart'; +import 'package:cake_wallet/src/domain/common/wallet_info.dart'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_history.dart'; import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; import 'package:cake_wallet/bitcoin/file.dart'; -import 'package:cake_wallet/bitcoin/electrum.dart'; import 'package:cake_wallet/bitcoin/bitcoin_balance.dart'; import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/core/wallet_base.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:hex/hex.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:shared_preferences/shared_preferences.dart'; part 'bitcoin_wallet.g.dart'; @@ -44,8 +34,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { {@required this.eclient, @required this.path, @required String password, - @required this.name, - List initialAddresses, + @required WalletInfo walletInfo, + @required List initialAddresses, int accountIndex = 0, this.transactionHistory, this.mnemonic, @@ -60,9 +50,7 @@ abstract class BitcoinWalletBase extends WalletBase with Store { syncStatus = NotConnectedSyncStatus(), _password = password, _accountIndex = accountIndex, - _addressesKeys = {} { - type = WalletType.bitcoin; - currency = CryptoCurrency.btc; + super(walletInfo) { _scripthashesUpdateSubject = {}; } @@ -114,7 +102,6 @@ abstract class BitcoinWalletBase extends WalletBase with Store { return BitcoinWallet._internal( eclient: eclient, path: walletPath, - name: name, mnemonic: mnemonic, password: password, accountIndex: accountIndex, @@ -130,9 +117,6 @@ abstract class BitcoinWalletBase extends WalletBase with Store { final ElectrumClient eclient; final String mnemonic; - @override - String name; - @override @observable String address; @@ -147,8 +131,6 @@ abstract class BitcoinWalletBase extends WalletBase with Store { ObservableList addresses; - Map _addressesKeys; - List get scriptHashes => addresses.map((addr) => scriptHash(addr.address)).toList(); @@ -161,8 +143,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { BitcoinWalletKeys get keys => BitcoinWalletKeys( wif: hd.wif, privateKey: hd.privKey, publicKey: hd.pubKey); + final String _password; int _accountIndex; - String _password; Map> _scripthashesUpdateSubject; Future init() async { diff --git a/lib/bitcoin/file.dart b/lib/bitcoin/file.dart index ddb6c23cb..53f8f06b0 100644 --- a/lib/bitcoin/file.dart +++ b/lib/bitcoin/file.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:convert'; import 'package:cake_wallet/bitcoin/key.dart'; import 'package:encrypt/encrypt.dart' as encrypt; import 'package:flutter/foundation.dart'; @@ -28,14 +27,13 @@ Future writeData( f.writeAsStringSync(encrypted); } -Future read( - {@required String path, @required String password}) async { +Future read({@required String path, @required String password}) async { final file = File(path); if (!file.existsSync()) { file.createSync(); } - + final encrypted = file.readAsStringSync(); return decode(password: password, data: encrypted); diff --git a/lib/core/auth_service.dart b/lib/core/auth_service.dart index f66f44a83..557800fd6 100644 --- a/lib/core/auth_service.dart +++ b/lib/core/auth_service.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/lib/core/wallet_base.dart b/lib/core/wallet_base.dart index 3d0246251..b5f8e27cc 100644 --- a/lib/core/wallet_base.dart +++ b/lib/core/wallet_base.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; +import 'package:cake_wallet/src/domain/common/wallet_info.dart'; import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/core/transaction_history.dart'; import 'package:cake_wallet/src/domain/common/transaction_priority.dart'; @@ -7,18 +8,43 @@ import 'package:cake_wallet/src/domain/common/sync_status.dart'; import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart'; +// FIXME: Move me. +CryptoCurrency currencyForWalletType(WalletType type) { + switch (type) { + case WalletType.bitcoin: + return CryptoCurrency.btc; + case WalletType.monero: + return CryptoCurrency.xmr; + default: + return null; + } +} + abstract class WalletBase { - WalletType type; + WalletBase(this.walletInfo); - CryptoCurrency currency; + static String idFor(String name, WalletType type) => + walletTypeToString(type).toLowerCase() + '_' + name; - String get name; + WalletInfo walletInfo; - String address; + WalletType get type => walletInfo.type; - BalaceType balance; + CryptoCurrency get currency => currencyForWalletType(type); - SyncStatus syncStatus; + String get id => walletInfo.id; + + String get name => walletInfo.name; + + String get address; + + set address(String address); + + BalaceType get balance; + + SyncStatus get syncStatus; + + set syncStatus(SyncStatus status); String get seed; @@ -26,8 +52,6 @@ abstract class WalletBase { TransactionHistoryBase transactionHistory; - String get id => walletTypeToString(type).toLowerCase() + '_' + name; - Future connectToNode({@required Node node}); Future startSync(); diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart index 5c5cb43df..ce14fa1e2 100644 --- a/lib/core/wallet_creation_service.dart +++ b/lib/core/wallet_creation_service.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/di.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -32,17 +33,7 @@ class WalletCreationService { void changeWalletType({@required WalletType type}) { this.type = type; - - switch (type) { - case WalletType.monero: - _service = MoneroWalletService(); - break; - case WalletType.bitcoin: - _service = BitcoinWalletService(); - break; - default: - break; - } + _service = getIt.get(param1: type); } Future create(WalletCredentials credentials) async { diff --git a/lib/core/wallet_credentials.dart b/lib/core/wallet_credentials.dart index ee7b1ef58..7dc93f848 100644 --- a/lib/core/wallet_credentials.dart +++ b/lib/core/wallet_credentials.dart @@ -1,6 +1,10 @@ +import 'package:cake_wallet/src/domain/common/wallet_info.dart'; + abstract class WalletCredentials { - WalletCredentials({this.name, this.password}); + WalletCredentials({this.name, this.password, this.height}); final String name; + final int height; String password; -} \ No newline at end of file + WalletInfo walletInfo; +} diff --git a/lib/di.dart b/lib/di.dart index 5453010ca..523f80aed 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,7 +1,11 @@ +import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; import 'package:cake_wallet/core/contact_service.dart'; +import 'package:cake_wallet/core/wallet_service.dart'; +import 'package:cake_wallet/monero/monero_wallet_service.dart'; import 'package:cake_wallet/src/domain/common/contact.dart'; import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/exchange/trade.dart'; +// import 'package:cake_wallet/src/domain/services/wallet_service.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart'; @@ -367,6 +371,22 @@ Future setup( getIt.registerFactory( () => ExchangeTemplatePage(getIt.get())); + + getIt.registerFactory(() => MoneroWalletService(walletInfoSource)); + + getIt.registerFactory(() => BitcoinWalletService()); + + getIt.registerFactoryParam( + (WalletType param1, __) { + switch (param1) { + case WalletType.monero: + return getIt.get(); + case WalletType.bitcoin: + return getIt.get(); + default: + return null; + } + }); } void setupThemeChangerStore(ThemeChanger themeChanger) { diff --git a/lib/main.dart b/lib/main.dart index 8f4080a30..19e4fe1b8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -251,6 +251,17 @@ class MaterialAppWithTheme extends StatelessWidget { _settingsStore.isDarkTheme ? Brightness.light : Brightness.dark; final statusBarIconBrightness = _settingsStore.isDarkTheme ? Brightness.light : Brightness.dark; + final authenticationStore = getIt.get(); + String initialRoute; + + switch (authenticationStore.state) { + case AuthenticationState.denied: + initialRoute = Routes.welcome; + break; + default: + initialRoute = Routes.login; + break; + } SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( statusBarColor: statusBarColor, @@ -258,7 +269,7 @@ class MaterialAppWithTheme extends StatelessWidget { statusBarIconBrightness: statusBarIconBrightness)); return Root( - authenticationStore: getIt.get(), + authenticationStore: authenticationStore, child: MaterialApp( navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, @@ -286,8 +297,7 @@ class MaterialAppWithTheme extends StatelessWidget { nodes: nodes, trades: trades, transactionDescriptions: transactionDescriptions), - initialRoute: Routes.login, // FIXME: get initial route! - // home: Container(color: Colors.blue), + initialRoute: initialRoute, )); } } diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index d7e8a4d26..bf01d0ec7 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/src/domain/common/wallet_info.dart'; import 'package:cake_wallet/src/domain/monero/monero_transaction_creation_credentials.dart'; import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; @@ -9,36 +10,29 @@ import 'package:cake_wallet/monero/monero_transaction_history.dart'; import 'package:cake_wallet/monero/monero_subaddress_list.dart'; import 'package:cake_wallet/monero/monero_account_list.dart'; import 'package:cake_wallet/core/wallet_base.dart'; -import 'package:cake_wallet/core/transaction_history.dart'; -import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; -import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:cake_wallet/src/domain/common/sync_status.dart'; import 'package:cake_wallet/src/domain/monero/account.dart'; -import 'package:cake_wallet/src/domain/monero/account_list.dart'; import 'package:cake_wallet/src/domain/monero/subaddress.dart'; import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/core/pending_transaction.dart'; import 'package:cake_wallet/src/domain/common/transaction_priority.dart'; -import 'package:cake_wallet/src/domain/common/calculate_fiat_amount.dart' - as cfa; part 'monero_wallet.g.dart'; +const moneroBlockSize = 1000; + class MoneroWallet = MoneroWalletBase with _$MoneroWallet; abstract class MoneroWalletBase extends WalletBase with Store { - MoneroWalletBase({String filename, this.isRecovery = false}) + MoneroWalletBase({String filename, WalletInfo walletInfo}) : transactionHistory = MoneroTransactionHistory(), accountList = MoneroAccountList(), - subaddressList = MoneroSubaddressList() { + subaddressList = MoneroSubaddressList(), + super(walletInfo) { _filename = filename; balance = MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: 0), unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0)); - currency = CryptoCurrency.xmr; - type = WalletType.monero; - _rct = reaction( - (_) => syncStatus, (SyncStatus status) => print(status.toString())); _onAccountChangeReaction = reaction((_) => account, (Account account) { subaddressList.update(accountIndex: account.id); subaddress = subaddressList.subaddresses.first; @@ -46,8 +40,6 @@ abstract class MoneroWalletBase extends WalletBase with Store { }); } - ReactionDisposer _rct; - @override final MoneroTransactionHistory transactionHistory; @@ -57,15 +49,17 @@ abstract class MoneroWalletBase extends WalletBase with Store { @observable Subaddress subaddress; + @override @observable SyncStatus syncStatus; @override - String get name => _filename.split('/').last; + @observable + String address; @override @observable - String address; + MoneroBalance balance; @override String get seed => monero_wallet.getSeed(); @@ -81,8 +75,6 @@ abstract class MoneroWalletBase extends WalletBase with Store { final MoneroAccountList accountList; - bool isRecovery; - String _filename; SyncListner _listener; ReactionDisposer _onAccountChangeReaction; @@ -107,8 +99,6 @@ abstract class MoneroWalletBase extends WalletBase with Store { @override Future connectToNode({@required Node node}) async { - final node = Node(uri: 'xmr-node-uk.cakewallet.com:18081'); - try { syncStatus = ConnectingSyncStatus(); await monero_wallet.setupNode( @@ -127,6 +117,10 @@ abstract class MoneroWalletBase extends WalletBase with Store { @override Future startSync() async { + try { + _setInitialHeight(); + } catch (_) {} + try { syncStatus = StartingSyncStatus(); monero_wallet.startRefresh(); @@ -180,25 +174,18 @@ abstract class MoneroWalletBase extends WalletBase with Store { @override Future save() async { -// if (_isSaving) { -// return; -// } - - try { -// _isSaving = true; - await monero_wallet.store(); -// _isSaving = false; - } catch (e) { - print(e); -// _isSaving = false; - rethrow; - } + await monero_wallet.store(); } Future getNodeHeight() async => monero_wallet.getNodeHeight(); Future isConnected() async => monero_wallet.isConnected(); + Future setAsRecovered() async { + walletInfo.isRecovery = false; + await walletInfo.save(); + } + void _setListeners() { _listener?.stop(); _listener = monero_wallet.setListeners( @@ -206,6 +193,41 @@ abstract class MoneroWalletBase extends WalletBase with Store { _listener.start(); } + void _setInitialHeight() { + if (walletInfo.isRecovery) { + return; + } + + final currentHeight = getCurrentHeight(); + print('currentHeight $currentHeight'); + + if (currentHeight <= 1) { + final height = _getHeightByDate(walletInfo.date); + monero_wallet.setRecoveringFromSeed(isRecovery: true); + monero_wallet.setRefreshFromBlockHeight(height: height); + } + } + + int _getHeightDistance(DateTime date) { + final distance = + DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch; + final daysTmp = (distance / 86400).round(); + final days = daysTmp < 1 ? 1 : daysTmp; + + return days * 1000; + } + + int _getHeightByDate(DateTime date) { + final nodeHeight = monero_wallet.getNodeHeightSync(); + final heightDistance = _getHeightDistance(date); + + if (nodeHeight <= 0) { + return 0; + } + + return nodeHeight - heightDistance; + } + void _askForUpdateBalance() { final fullBalance = _getFullBalance(); final unlockedBalance = _getUnlockedBalance(); @@ -236,13 +258,17 @@ abstract class MoneroWalletBase extends WalletBase with Store { syncStatus = SyncedSyncStatus(); - if (isRecovery) { + if (walletInfo.isRecovery) { _askForUpdateTransactionHistory(); } -// if (isRecovery && (nodeHeight - currentHeight < moneroBlockSize)) { -// await setAsRecovered(); -// } + final currentHeight = getCurrentHeight(); + final nodeHeight = monero_wallet.getNodeHeightSync(); + + if (walletInfo.isRecovery && + (nodeHeight - currentHeight < moneroBlockSize)) { + await setAsRecovered(); + } await save(); } diff --git a/lib/monero/monero_wallet_service.dart b/lib/monero/monero_wallet_service.dart index c6e2d20af..637646f2a 100644 --- a/lib/monero/monero_wallet_service.dart +++ b/lib/monero/monero_wallet_service.dart @@ -1,12 +1,14 @@ import 'dart:io'; - +import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:hive/hive.dart'; +import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager; +import 'package:cw_monero/wallet.dart' as monero_wallet; import 'package:cake_wallet/monero/monero_wallet.dart'; import 'package:cake_wallet/core/wallet_credentials.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/src/domain/common/pathForWallet.dart'; +import 'package:cake_wallet/src/domain/common/wallet_info.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart'; -import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager; -import 'package:cw_monero/wallet.dart' as monero_wallet; class MoneroNewWalletCredentials extends WalletCredentials { MoneroNewWalletCredentials({String name, String password, this.language}) @@ -17,11 +19,10 @@ class MoneroNewWalletCredentials extends WalletCredentials { class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials { MoneroRestoreWalletFromSeedCredentials( - {String name, String password, this.mnemonic, this.height}) - : super(name: name, password: password); + {String name, String password, int height, this.mnemonic}) + : super(name: name, password: password, height: height); final String mnemonic; - final int height; } class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials { @@ -32,32 +33,35 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials { this.address, this.viewKey, this.spendKey, - this.height}) - : super(name: name, password: password); + int height}) + : super(name: name, password: password, height: height); final String language; final String address; final String viewKey; final String spendKey; - final int height; } class MoneroWalletService extends WalletService< MoneroNewWalletCredentials, MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> { + MoneroWalletService(this.walletInfoSource); + + final Box walletInfoSource; + @override Future create(MoneroNewWalletCredentials credentials) async { try { final path = await pathForWallet(name: credentials.name, type: WalletType.monero); - await monero_wallet_manager.createWallet( path: path, password: credentials.password, language: credentials.language); - - final wallet = MoneroWallet(filename: monero_wallet.getFilename()); + final wallet = MoneroWallet( + filename: monero_wallet.getFilename(), + walletInfo: credentials.walletInfo); await wallet.init(); return wallet; @@ -84,8 +88,16 @@ class MoneroWalletService extends WalletService< Future openWallet(String name, String password) async { try { final path = await pathForWallet(name: name, type: WalletType.monero); + final file = File(path); + final stat = await file.stat(); + print(stat.changed); + print(stat.modified); + print(stat.accessed); monero_wallet_manager.openWallet(path: path, password: password); - final wallet = MoneroWallet(filename: monero_wallet.getFilename()); + final walletInfo = walletInfoSource.values.firstWhere( + (info) => info.id == WalletBase.idFor(name, WalletType.monero)); + final wallet = MoneroWallet( + filename: monero_wallet.getFilename(), walletInfo: walletInfo); await wallet.init(); return wallet; @@ -107,7 +119,6 @@ class MoneroWalletService extends WalletService< try { final path = await pathForWallet(name: credentials.name, type: WalletType.monero); - await monero_wallet_manager.restoreFromKeys( path: path, password: credentials.password, @@ -116,8 +127,9 @@ class MoneroWalletService extends WalletService< address: credentials.address, viewKey: credentials.viewKey, spendKey: credentials.spendKey); - - final wallet = MoneroWallet(filename: monero_wallet.getFilename()); + final wallet = MoneroWallet( + filename: monero_wallet.getFilename(), + walletInfo: credentials.walletInfo); await wallet.init(); return wallet; @@ -134,14 +146,14 @@ class MoneroWalletService extends WalletService< try { final path = await pathForWallet(name: credentials.name, type: WalletType.monero); - await monero_wallet_manager.restoreFromSeed( path: path, password: credentials.password, seed: credentials.mnemonic, restoreHeight: credentials.height); - - final wallet = MoneroWallet(filename: monero_wallet.getFilename()); + final wallet = MoneroWallet( + filename: monero_wallet.getFilename(), + walletInfo: credentials.walletInfo); await wallet.init(); return wallet; diff --git a/lib/reactions/bootstrap.dart b/lib/reactions/bootstrap.dart index fd41c6ed5..a92ed01b8 100644 --- a/lib/reactions/bootstrap.dart +++ b/lib/reactions/bootstrap.dart @@ -1,28 +1,20 @@ import 'dart:async'; - -import 'package:cake_wallet/core/key_service.dart'; -import 'package:cake_wallet/router.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/domain/common/sync_status.dart'; -import 'package:cake_wallet/src/screens/auth/auth_page.dart'; -import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/widgets.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/di.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; -import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; -import 'package:cake_wallet/monero/monero_wallet_service.dart'; +import 'package:connectivity/connectivity.dart'; +import 'package:cake_wallet/core/key_service.dart'; +import 'package:cake_wallet/router.dart'; +import 'package:cake_wallet/src/domain/common/sync_status.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/authentication_store.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart'; -import 'package:cake_wallet/src/domain/common/secret_store_key.dart'; -import 'package:cake_wallet/src/domain/common/encrypt.dart'; import 'package:cake_wallet/src/domain/services/fiat_convertation_service.dart'; import 'package:cake_wallet/src/domain/common/fiat_currency.dart'; import 'package:cake_wallet/store/dashboard/fiat_convertation_store.dart'; @@ -36,19 +28,7 @@ Future loadCurrentWallet() async { final type = deserializeFromInt(typeRaw); final password = await getIt.get().getWalletPassword(walletName: name); - - WalletService _service; - switch (type) { - case WalletType.monero: - _service = MoneroWalletService(); - break; - case WalletType.bitcoin: - _service = BitcoinWalletService(); - break; - default: - break; - } - + final _service = getIt.get(param1: type); final wallet = await _service.openWallet(name, password); appStore.wallet = wallet; } @@ -79,9 +59,6 @@ Future bootstrap( if (state == AuthenticationState.installed) { await loadCurrentWallet(); - } - - if (state == AuthenticationState.installed) { await navigatorKey.currentState .pushAndRemoveUntil(createLoginRoute(), (_) => false); } @@ -101,16 +78,31 @@ Future bootstrap( reaction((_) => getIt.get().wallet, (WalletBase wallet) async { _onWalletSyncStatusChangeReaction?.reaction?.dispose(); _reconnectionTimer?.cancel(); - _onWalletSyncStatusChangeReaction = reaction( - (_) => wallet.syncStatus is ConnectedSyncStatus, - (Object _) async => await wallet.startSync()); + _onWalletSyncStatusChangeReaction = + reaction((_) => wallet.syncStatus, (SyncStatus status) async { + if (status is ConnectedSyncStatus) { + await wallet.startSync(); + } + }); _reconnectionTimer = Timer.periodic(Duration(seconds: 5), (_) async { + final connectivityResult = await (Connectivity().checkConnectivity()); + + if (connectivityResult == ConnectivityResult.none) { + wallet.syncStatus = FailedSyncStatus(); + return; + } + if (wallet.syncStatus is LostConnectionSyncStatus || wallet.syncStatus is FailedSyncStatus) { try { - await wallet.connectToNode( - node: settingsStore.getCurrentNode(wallet.type)); + final alive = + await settingsStore.getCurrentNode(wallet.type).requestNode(); + + if (alive) { + await wallet.connectToNode( + node: settingsStore.getCurrentNode(wallet.type)); + } } catch (_) {} } }); diff --git a/lib/src/domain/common/fs_migration.dart b/lib/src/domain/common/fs_migration.dart index db17cd134..0d848aeb9 100644 --- a/lib/src/domain/common/fs_migration.dart +++ b/lib/src/domain/common/fs_migration.dart @@ -82,28 +82,28 @@ Future migrate_wallets({Directory appDocDir}) async { Future migrate_ios_wallet_info( {@required Directory appDocDir, @required Box walletsInfo}) async { - final walletsDir = Directory('${appDocDir.path}/wallets'); - final moneroWalletsDir = Directory('${walletsDir.path}/monero'); + // final walletsDir = Directory('${appDocDir.path}/wallets'); + // final moneroWalletsDir = Directory('${walletsDir.path}/monero'); - moneroWalletsDir.listSync().forEach((item) async { - try { - if (item is Directory) { - final name = item.path.split('/').last; - final configFile = File('${item.path}/$name.json'); - final config = - json.decode(configFile.readAsStringSync()) as Map; - final isRecovery = config["isRecovery"] as bool ?? false; - final id = - walletTypeToString(WalletType.monero).toLowerCase() + '_' + name; - final walletInfo = - WalletInfo(id: id, name: name, isRecovery: isRecovery); + // moneroWalletsDir.listSync().forEach((item) async { + // try { + // if (item is Directory) { + // final name = item.path.split('/').last; + // final configFile = File('${item.path}/$name.json'); + // final config = + // json.decode(configFile.readAsStringSync()) as Map; + // final isRecovery = config["isRecovery"] as bool ?? false; + // final id = + // walletTypeToString(WalletType.monero).toLowerCase() + '_' + name; + // final walletInfo = + // WalletInfo(id: id, name: name, isRecovery: isRecovery); - await walletsInfo.add(walletInfo); - } - } catch (e) { - print(e.toString()); - } - }); + // await walletsInfo.add(walletInfo); + // } + // } catch (e) { + // print(e.toString()); + // } + // }); } Future migrate_ios_trades_list( diff --git a/lib/src/domain/common/wallet_info.dart b/lib/src/domain/common/wallet_info.dart index fcc5eac14..c9682f103 100644 --- a/lib/src/domain/common/wallet_info.dart +++ b/lib/src/domain/common/wallet_info.dart @@ -1,12 +1,26 @@ -import 'package:cake_wallet/src/domain/common/wallet_type.dart'; +import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; +import 'package:cake_wallet/src/domain/common/wallet_type.dart'; part 'wallet_info.g.dart'; @HiveType(typeId: 4) class WalletInfo extends HiveObject { - WalletInfo( - {this.id, this.name, this.type, this.isRecovery, this.restoreHeight}); + WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight, + this.timestamp, this.dirPath, this.path); + + factory WalletInfo.external( + {@required String id, + @required String name, + @required WalletType type, + @required bool isRecovery, + @required int restoreHeight, + @required DateTime date, + @required String dirPath, + @required String path}) { + return WalletInfo(id, name, type, isRecovery, restoreHeight, + date.millisecondsSinceEpoch ?? 0, dirPath, path); + } static const boxName = 'WalletInfo'; @@ -24,4 +38,15 @@ class WalletInfo extends HiveObject { @HiveField(4) int restoreHeight; + + @HiveField(5) + int timestamp; + + @HiveField(6) + String dirPath; + + @HiveField(7) + String path; + + DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp); } diff --git a/lib/src/domain/monero/monero_wallet.dart b/lib/src/domain/monero/monero_wallet.dart index 66e46a6fc..1eacfc1c9 100644 --- a/lib/src/domain/monero/monero_wallet.dart +++ b/lib/src/domain/monero/monero_wallet.dart @@ -47,18 +47,19 @@ class MoneroWallet extends Wallet { String name, bool isRecovery = false, int restoreHeight = 0}) async { - const type = WalletType.monero; - final id = walletTypeToString(type).toLowerCase() + '_' + name; - final walletInfo = WalletInfo( - id: id, - name: name, - type: type, - isRecovery: isRecovery, - restoreHeight: restoreHeight); - await walletInfoSource.add(walletInfo); + // const type = WalletType.monero; + // final id = walletTypeToString(type).toLowerCase() + '_' + name; + // final walletInfo = WalletInfo( + // id: id, + // name: name, + // type: type, + // isRecovery: isRecovery, + // restoreHeight: restoreHeight); + // await walletInfoSource.add(walletInfo); - return await configured( - walletInfo: walletInfo, walletInfoSource: walletInfoSource); + // return await configured( + // walletInfo: walletInfo, walletInfoSource: walletInfoSource); + return null; } static Future load( diff --git a/lib/src/domain/monero/monero_wallets_manager.dart b/lib/src/domain/monero/monero_wallets_manager.dart index b75fb3091..71f8406c2 100644 --- a/lib/src/domain/monero/monero_wallets_manager.dart +++ b/lib/src/domain/monero/monero_wallets_manager.dart @@ -12,8 +12,6 @@ import 'package:cake_wallet/src/domain/common/wallet.dart'; import 'package:cake_wallet/src/domain/monero/monero_wallet.dart'; import 'package:cake_wallet/src/domain/common/wallet_description.dart'; - - class MoneroWalletsManager extends WalletsManager { MoneroWalletsManager({@required this.walletInfoSource}); @@ -27,7 +25,8 @@ class MoneroWalletsManager extends WalletsManager { const isRecovery = false; final path = await pathForWallet(name: name, type: WalletType.monero); - await monero_wallet_manager.createWallet(path: path, password: password, language: language); + await monero_wallet_manager.createWallet( + path: path, password: password, language: language); final wallet = await MoneroWallet.createdWallet( walletInfoSource: walletInfoSource, diff --git a/lib/src/reactions/set_reactions.dart b/lib/src/reactions/set_reactions.dart index b95671e95..1584dc89f 100644 --- a/lib/src/reactions/set_reactions.dart +++ b/lib/src/reactions/set_reactions.dart @@ -65,18 +65,18 @@ void onSyncStatusChange( {SyncStore syncStore, WalletStore walletStore, SettingsStore settingsStore}) { - _onSyncStatusChangeDisposer?.call(); + // _onSyncStatusChangeDisposer?.call(); - reaction((_) => syncStore.status, (SyncStatus status) async { - if (status is ConnectedSyncStatus) { - await walletStore.startSync(); - } + // reaction((_) => syncStore.status, (SyncStatus status) async { + // if (status is ConnectedSyncStatus) { + // await walletStore.startSync(); + // } - // Reconnect to the node if the app is not started sync after 30 seconds - if (status is StartingSyncStatus) { - startReconnectionObserver(syncStore: syncStore, walletStore: walletStore); - } - }); + // // Reconnect to the node if the app is not started sync after 30 seconds + // if (status is StartingSyncStatus) { + // startReconnectionObserver(syncStore: syncStore, walletStore: walletStore); + // } + // }); } void startReconnectionObserver({SyncStore syncStore, WalletStore walletStore}) { diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index f773cab37..414a40ebe 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -14,7 +14,8 @@ import 'package:cake_wallet/generated/i18n.dart'; part 'exchange_trade_view_model.g.dart'; -class ExchangeTradeViewModel = ExchangeTradeViewModelBase with _$ExchangeTradeViewModel; +class ExchangeTradeViewModel = ExchangeTradeViewModelBase + with _$ExchangeTradeViewModel; abstract class ExchangeTradeViewModelBase with Store { ExchangeTradeViewModelBase({this.wallet, this.trades, this.tradesStore}) { @@ -38,17 +39,11 @@ abstract class ExchangeTradeViewModelBase with Store { items = ObservableList(); items.addAll([ ExchangeTradeItem( - title: S.current.id, - data: '${trade.id}', - isCopied: true), + title: S.current.id, data: '${trade.id}', isCopied: true), ExchangeTradeItem( - title: S.current.amount, - data: '${trade.amount}', - isCopied: false), + title: S.current.amount, data: '${trade.amount}', isCopied: false), ExchangeTradeItem( - title: S.current.status, - data: '${trade.state}', - isCopied: false), + title: S.current.status, data: '${trade.state}', isCopied: false), ExchangeTradeItem( title: S.current.widgets_address + ':', data: trade.inputAddress, @@ -90,4 +85,4 @@ abstract class ExchangeTradeViewModelBase with Store { print(e.toString()); } } -} \ No newline at end of file +} diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index 4a68d23aa..2807443b1 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -1,5 +1,3 @@ -import 'package:cake_wallet/utils/item_cell.dart'; -import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/wallet_base.dart'; @@ -10,6 +8,7 @@ import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/src/domain/common/default_settings_migration.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:cake_wallet/utils/mobx.dart'; +import 'package:cake_wallet/utils/item_cell.dart'; part 'node_list_view_model.g.dart'; diff --git a/lib/view_model/send_view_model.dart b/lib/view_model/send_view_model.dart index 0942c7841..5c06ad9c4 100644 --- a/lib/view_model/send_view_model.dart +++ b/lib/view_model/send_view_model.dart @@ -1,3 +1,6 @@ +import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; +import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/core/amount_validator.dart'; import 'package:cake_wallet/core/template_validator.dart'; @@ -5,7 +8,6 @@ import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/monero/monero_wallet.dart'; -import 'package:cake_wallet/src/domain/common/balance.dart'; import 'package:cake_wallet/src/domain/common/balance_display_mode.dart'; import 'package:cake_wallet/src/domain/common/calculate_estimated_fee.dart'; import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; @@ -14,15 +16,6 @@ import 'package:cake_wallet/src/domain/common/sync_status.dart'; import 'package:cake_wallet/src/domain/common/transaction_priority.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/templates/send_template_store.dart'; -import 'package:flutter/foundation.dart'; -import 'package:intl/intl.dart'; -import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/monero/monero_wallet_service.dart'; -import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart'; -import 'package:cake_wallet/core/wallet_creation_service.dart'; -import 'package:cake_wallet/core/wallet_credentials.dart'; -import 'package:cake_wallet/src/domain/common/wallet_type.dart'; -import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/domain/common/openalias_record.dart'; import 'package:cake_wallet/store/dashboard/fiat_convertation_store.dart'; @@ -51,12 +44,8 @@ class SendingFailed extends SendViewModelState { class SendViewModel = SendViewModelBase with _$SendViewModel; abstract class SendViewModelBase with Store { - SendViewModelBase( - this._wallet, - this._settingsStore, - this._fiatConvertationStore, - this.sendTemplateStore) { - + SendViewModelBase(this._wallet, this._settingsStore, + this._fiatConvertationStore, this.sendTemplateStore) { state = InitialSendViewModelState(); _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12; @@ -116,9 +105,10 @@ abstract class SendViewModelBase with Store { double get price => _fiatConvertationStore.price; @computed - ObservableList