From f504c5e03e78b6698235579f342ee99bea84e99f Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Fri, 25 Sep 2020 16:47:49 +0300 Subject: [PATCH 1/3] CAKE-65 | changed color of the rescan button on rescan page; added text under blockchain height widget --- lib/src/screens/rescan/rescan_page.dart | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index 9bfe1df2f..a1eaed7f5 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -21,7 +21,23 @@ class RescanPage extends BasePage { padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - BlockchainHeightWidget(key: _blockchainHeightWidgetKey), + Column( + children: [ + BlockchainHeightWidget(key: _blockchainHeightWidgetKey), + 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 + ), + ), + ) + ], + ), Observer( builder: (_) => LoadingPrimaryButton( isLoading: @@ -33,7 +49,7 @@ class RescanPage extends BasePage { _blockchainHeightWidgetKey.currentState.height); Navigator.of(context).pop(); }, - color: Colors.blue, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, )) ]), From e08e10bc3537bd6f854b75a8ad2bf9525c83b36a Mon Sep 17 00:00:00 2001 From: M Date: Mon, 28 Sep 2020 22:02:30 +0300 Subject: [PATCH 2/3] Fixes --- ios/Runner.xcodeproj/project.pbxproj | 6 +++--- lib/core/transaction_history.dart | 4 ++++ lib/monero/monero_wallet.dart | 8 +++++-- lib/router.dart | 3 ++- lib/src/screens/root/root.dart | 2 +- lib/src/screens/send/send_page.dart | 18 ++++++++++++++-- lib/store/settings_store.dart | 32 +++++++++++++++++++++++----- 7 files changed, 59 insertions(+), 14 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c1e62ab95..333591d2d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -493,7 +493,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -526,7 +526,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/lib/core/transaction_history.dart b/lib/core/transaction_history.dart index 02cff8351..dd91fb203 100644 --- a/lib/core/transaction_history.dart +++ b/lib/core/transaction_history.dart @@ -19,6 +19,10 @@ abstract class TransactionHistoryBase { try { _isUpdating = true; final _transactions = await fetchTransactions(); + transactions.keys + .toSet() + .difference(_transactions.keys.toSet()) + .forEach((k) => transactions.remove(k)); _transactions.forEach((key, value) => transactions[key] = value); _isUpdating = false; } catch (e) { diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index 4a053b788..b1133dd5b 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -191,7 +191,12 @@ abstract class MoneroWalletBase extends WalletBase with Store { @override Future rescan({int height}) async { + monero_wallet.setRefreshFromBlockHeight(height: height); monero_wallet.rescanBlockchainAsync(); + await startSync(); + _askForUpdateBalance(); + await _askForUpdateTransactionHistory(); + await save(); } void _setListeners() { @@ -247,9 +252,7 @@ abstract class MoneroWalletBase extends WalletBase with Store { } Future _askForUpdateTransactionHistory() async { - print('start'); await transactionHistory.update(); - print('end'); } int _getFullBalance() => @@ -270,6 +273,7 @@ abstract class MoneroWalletBase extends WalletBase with Store { if (walletInfo.isRecovery) { _askForUpdateTransactionHistory(); + _askForUpdateBalance(); } final currentHeight = getCurrentHeight(); diff --git a/lib/router.dart b/lib/router.dart index 7407e9b11..1c579c708 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -236,7 +236,8 @@ class Router { case Routes.login: return CupertinoPageRoute( - builder: (context) => getIt.get(instanceName: 'login')); + builder: (context) => getIt.get(instanceName: 'login'), + fullscreenDialog: true); case Routes.accountCreation: return CupertinoPageRoute( diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 7b3032513..1bb2054ab 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -65,7 +65,7 @@ class RootState extends State with WidgetsBindingObserver { }); } - return widget.child; + return WillPopScope(onWillPop: () async => false, child: widget.child); } void _reset() { diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 1bc42ccca..8080f65a8 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -425,8 +425,10 @@ class SendPage extends BasePage { EdgeInsets.only(left: 24, right: 24, bottom: 24), bottomSection: Observer(builder: (_) { return LoadingPrimaryButton( - onPressed: () { - if (_formKey.currentState.validate()) {} + onPressed: () async { + if (_formKey.currentState.validate()) { + await sendViewModel.createTransaction(); + } }, text: S.of(context).send, color: Theme.of(context).accentTextTheme.body2.color, @@ -445,6 +447,18 @@ class SendPage extends BasePage { return; } + _cryptoAmountController.addListener(() { + final amount = _cryptoAmountController.text; + + if (sendViewModel.sendAll && amount != S.current.all) { + sendViewModel.sendAll = false; + } + + if (amount != sendViewModel.cryptoAmount) { + sendViewModel.setCryptoAmount(amount); + } + }); + reaction((_) => sendViewModel.sendAll, (bool all) { if (all) { _cryptoAmountController.text = S.current.all; diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 399cae107..c62c9b2e8 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -47,6 +47,28 @@ abstract class SettingsStoreBase with Store { this.nodes = ObservableMap.of(nodes); _sharedPreferences = sharedPreferences; + reaction( + (_) => fiatCurrency, + (FiatCurrency fiatCurrency) => sharedPreferences.setString( + PreferencesKey.currentFiatCurrencyKey, fiatCurrency.serialize())); + + reaction( + (_) => transactionPriority, + (TransactionPriority priority) => sharedPreferences.setInt( + PreferencesKey.currentTransactionPriorityKey, + priority.serialize())); + + reaction( + (_) => shouldSaveRecipientAddress, + (bool shouldSaveRecipientAddress) => sharedPreferences.setBool( + PreferencesKey.shouldSaveRecipientAddressKey, + shouldSaveRecipientAddress)); + + reaction( + (_) => isDarkTheme, + (bool isDarkTheme) => sharedPreferences.setBool( + PreferencesKey.currentDarkTheme, isDarkTheme)); + reaction( (_) => allowBiometricalAuthentication, (bool biometricalAuthentication) => sharedPreferences.setBool( @@ -61,9 +83,10 @@ abstract class SettingsStoreBase with Store { reaction((_) => currentNode, (Node node) => _saveCurrentNode(node, WalletType.monero)); - reaction((_) => languageCode, - (String languageCode) => sharedPreferences.setString( - PreferencesKey.currentLanguageCode, languageCode)); + reaction( + (_) => languageCode, + (String languageCode) => sharedPreferences.setString( + PreferencesKey.currentLanguageCode, languageCode)); } static const defaultPinLength = 4; @@ -165,8 +188,7 @@ abstract class SettingsStoreBase with Store { initialDarkTheme: savedDarkTheme, actionlistDisplayMode: actionListDisplayMode, initialPinLength: pinLength, - initialLanguageCode: savedLanguageCode - ); + initialLanguageCode: savedLanguageCode); } Future _saveCurrentNode(Node node, WalletType walletType) async { From 696b808698a9b12f855f4340e38594f9a752bfab Mon Sep 17 00:00:00 2001 From: M Date: Tue, 29 Sep 2020 20:56:11 +0300 Subject: [PATCH 3/3] Fixes --- lib/main.dart | 1 + lib/src/screens/base_page.dart | 8 +- lib/src/screens/exchange/exchange_page.dart | 648 ++++++++------- .../exchange/widgets/exchange_card.dart | 322 ++++---- lib/src/screens/root/root.dart | 10 +- lib/src/screens/send/send_page.dart | 777 +++++++++--------- lib/src/widgets/base_text_form_field.dart | 5 +- lib/src/widgets/keyboard_done_button.dart | 19 + pubspec.lock | 7 + pubspec.yaml | 1 + 10 files changed, 950 insertions(+), 848 deletions(-) create mode 100644 lib/src/widgets/keyboard_done_button.dart diff --git a/lib/main.dart b/lib/main.dart index 4a49b646a..6c73a5bc8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -131,6 +131,7 @@ class App extends StatelessWidget { return Observer(builder: (BuildContext context) { return Root( authenticationStore: authenticationStore, + navigatorKey: navigatorKey, child: MaterialApp( navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, diff --git a/lib/src/screens/base_page.dart b/lib/src/screens/base_page.dart index f8cfa5ae1..440de962b 100644 --- a/lib/src/screens/base_page.dart +++ b/lib/src/screens/base_page.dart @@ -37,7 +37,7 @@ abstract class BasePage extends StatelessWidget { Widget Function(BuildContext, Widget) get rootWrapper => null; - bool get _isDarkTheme => getIt.get().isDarkTheme; + bool get isDarkTheme => getIt.get().isDarkTheme; void onOpenEndDrawer() => _scaffoldKey.currentState.openEndDrawer(); @@ -51,7 +51,7 @@ abstract class BasePage extends StatelessWidget { final _backButton = Image.asset('assets/images/back_arrow.png', color: titleColor ?? Theme.of(context).primaryTextTheme.title.color); final _closeButton = - _isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage; + isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage; return SizedBox( height: 37, @@ -88,7 +88,7 @@ abstract class BasePage extends StatelessWidget { ObstructingPreferredSizeWidget appBar(BuildContext context) { final appBarColor = - _isDarkTheme ? backgroundDarkColor : backgroundLightColor; + isDarkTheme ? backgroundDarkColor : backgroundLightColor; switch (appBarStyle) { case AppBarStyle.regular: @@ -133,7 +133,7 @@ abstract class BasePage extends StatelessWidget { final root = Scaffold( key: _scaffoldKey, backgroundColor: - _isDarkTheme ? backgroundDarkColor : backgroundLightColor, + isDarkTheme ? backgroundDarkColor : backgroundLightColor, resizeToAvoidBottomPadding: resizeToAvoidBottomPadding, extendBodyBehindAppBar: extendBodyBehindAppBar, endDrawer: endDrawer, diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index a602fc995..c734ef5a0 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -1,7 +1,9 @@ import 'dart:ui'; +import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/exchange/exchange_template.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/template_tile.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; @@ -9,11 +11,13 @@ import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart'; + // import 'package:cake_wallet/exchange/exchange_trade_state.dart'; // import 'package:cake_wallet/exchange/limits_state.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart'; @@ -32,6 +36,8 @@ class ExchangePage extends BasePage { final depositKey = GlobalKey(); final receiveKey = GlobalKey(); final _formKey = GlobalKey(); + final _depositAmountFocus = FocusNode(); + final _receiveAmountFocus = FocusNode(); var _isReactionsSet = false; @override @@ -54,11 +60,8 @@ class ExchangePage extends BasePage { PresentProviderPicker(exchangeViewModel: exchangeViewModel); @override - Widget trailing(BuildContext context) => - TrailButton( - caption: S.of(context).reset, - onPressed: () => exchangeViewModel.reset() - ); + Widget trailing(BuildContext context) => TrailButton( + caption: S.of(context).reset, onPressed: () => exchangeViewModel.reset()); @override Widget body(BuildContext context) { @@ -74,305 +77,334 @@ class ExchangePage extends BasePage { ); final depositWalletName = - exchangeViewModel.depositCurrency == CryptoCurrency.xmr - ? exchangeViewModel.wallet.name - : null; + exchangeViewModel.depositCurrency == CryptoCurrency.xmr + ? exchangeViewModel.wallet.name + : null; final receiveWalletName = - exchangeViewModel.receiveCurrency == CryptoCurrency.xmr - ? exchangeViewModel.wallet.name - : null; + exchangeViewModel.receiveCurrency == CryptoCurrency.xmr + ? exchangeViewModel.wallet.name + : null; WidgetsBinding.instance .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel)); - return Container( - color: Theme.of(context).backgroundColor, - child: Form( - key: _formKey, - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 24), - content: Column( - children: [ - Container( - padding: EdgeInsets.only(bottom: 32), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24) - ), - gradient: LinearGradient( - colors: [ - Theme.of(context).primaryTextTheme.body1.color, - Theme.of(context).primaryTextTheme.body1.decorationColor, - ], - stops: [0.35, 1.0], - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - child: Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24) - ), - gradient: LinearGradient( - colors: [ - Theme.of(context) - .primaryTextTheme - .subtitle - .color, - Theme.of(context) - .primaryTextTheme - .subtitle - .decorationColor, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - padding: EdgeInsets.fromLTRB(24, 90, 24, 32), - child: Observer( - builder: (_) => ExchangeCard( - key: depositKey, - title: S.of(context).you_will_send, - initialCurrency: - exchangeViewModel.depositCurrency, - initialWalletName: depositWalletName, - initialAddress: exchangeViewModel - .depositCurrency == - exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address - : exchangeViewModel.depositAddress, - initialIsAmountEditable: true, - initialIsAddressEditable: exchangeViewModel - .isDepositAddressEnabled, - isAmountEstimated: false, - currencies: CryptoCurrency.all, - onCurrencySelected: (currency) => - exchangeViewModel.changeDepositCurrency( - currency: currency), - imageArrow: arrowBottomPurple, - currencyButtonColor: Colors.transparent, - addressButtonsColor: - Theme.of(context).focusColor, - borderColor: Theme.of(context) - .primaryTextTheme - .body2 - .color, - currencyValueValidator: AmountValidator( - type: exchangeViewModel.wallet.type), - addressTextFieldValidator: AddressValidator( - type: - exchangeViewModel.depositCurrency), - ), - ), - ), - Padding( - padding: EdgeInsets.only(top: 29, left: 24, right: 24), - child: Observer( - builder: (_) => ExchangeCard( - key: receiveKey, - title: S.of(context).you_will_get, - initialCurrency: - exchangeViewModel.receiveCurrency, - initialWalletName: receiveWalletName, - initialAddress: - exchangeViewModel.receiveCurrency == - exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.address - : exchangeViewModel.receiveAddress, - initialIsAmountEditable: false, - initialIsAddressEditable: - exchangeViewModel.isReceiveAddressEnabled, - isAmountEstimated: true, - currencies: CryptoCurrency.all, - onCurrencySelected: (currency) => - exchangeViewModel.changeReceiveCurrency( - currency: currency), - imageArrow: arrowBottomCakeGreen, - currencyButtonColor: Colors.transparent, - addressButtonsColor: - Theme.of(context).focusColor, - borderColor: Theme.of(context) + return KeyboardActions( + config: KeyboardActionsConfig( + keyboardActionsPlatform: KeyboardActionsPlatform.IOS, + keyboardBarColor: isDarkTheme + ? Color.fromRGBO(48, 51, 60, 1.0) + : Color.fromRGBO(98, 98, 98, 1.0), + nextFocus: false, + actions: [ + KeyboardActionsItem( + focusNode: _depositAmountFocus, + toolbarButtons: [(_) => KeyboardDoneButton()]), + KeyboardActionsItem( + focusNode: _receiveAmountFocus, + toolbarButtons: [(_) => KeyboardDoneButton()]) + ]), + child: Container( + height: 1, + color: Theme.of(context).backgroundColor, + child: Form( + key: _formKey, + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.only(bottom: 24), + content: Column( + children: [ + Container( + padding: EdgeInsets.only(bottom: 32), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24)), + gradient: LinearGradient( + colors: [ + Theme.of(context).primaryTextTheme.body1.color, + Theme.of(context) .primaryTextTheme - .body2 + .body1 .decorationColor, - currencyValueValidator: AmountValidator( - type: exchangeViewModel.wallet.type), - addressTextFieldValidator: AddressValidator( - type: exchangeViewModel.receiveCurrency), - )), - ) - ], - ), - ), - 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), - ) - ], - ), - ), - 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.exchangeTemplate), - child: Container( - padding: EdgeInsets.only(left: 1, right: 10), - child: DottedBorder( - borderType: BorderType.RRect, - dashPattern: [6, 4], - color: Theme.of(context) + ], + stops: [ + 0.35, + 1.0 + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight), + ), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24)), + gradient: LinearGradient( + colors: [ + Theme.of(context) .primaryTextTheme - .display2 + .subtitle + .color, + Theme.of(context) + .primaryTextTheme + .subtitle .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), - ), - )), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight), + ), + padding: EdgeInsets.fromLTRB(24, 100, 24, 32), + child: Observer( + builder: (_) => ExchangeCard( + amountFocusNode: _depositAmountFocus, + key: depositKey, + title: S.of(context).you_will_send, + initialCurrency: + exchangeViewModel.depositCurrency, + initialWalletName: depositWalletName, + initialAddress: + exchangeViewModel.depositCurrency == + exchangeViewModel.wallet.currency + ? exchangeViewModel.wallet.address + : exchangeViewModel.depositAddress, + initialIsAmountEditable: true, + initialIsAddressEditable: + exchangeViewModel.isDepositAddressEnabled, + isAmountEstimated: false, + currencies: CryptoCurrency.all, + onCurrencySelected: (currency) => + exchangeViewModel.changeDepositCurrency( + currency: currency), + imageArrow: arrowBottomPurple, + currencyButtonColor: Colors.transparent, + addressButtonsColor: + Theme.of(context).focusColor, + borderColor: Theme.of(context) + .primaryTextTheme + .body2 + .color, + currencyValueValidator: AmountValidator( + type: exchangeViewModel.wallet.type), + addressTextFieldValidator: AddressValidator( + type: exchangeViewModel.depositCurrency), ), ), - Observer(builder: (_) { - final templates = exchangeViewModel.templates; - final itemCount = - exchangeViewModel.templates.length; - - return ListView.builder( - scrollDirection: Axis.horizontal, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: itemCount, - itemBuilder: (context, index) { - final template = templates[index]; - - return TemplateTile( - key: UniqueKey(), - amount: template.amount, - from: template.depositCurrency, - to: template.receiveCurrency, - onTap: () { - applyTemplate( - exchangeViewModel, template); - }, - onRemove: () { - showPopUp( - 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(); - exchangeViewModel - .exchangeTemplateStore - .remove( - template: - template); - exchangeViewModel - .exchangeTemplateStore - .update(); - }, - actionRightButton: () => - Navigator.of( - dialogContext) - .pop()); - }); - }, - ); - }); - }), - ], - ))) - ], - ), - bottomSectionPadding: - EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Column(children: [ - Padding( - padding: EdgeInsets.only(bottom: 15), - child: Observer(builder: (_) { - final description = - exchangeViewModel.provider is XMRTOExchangeProvider - ? S.of(context).amount_is_guaranteed - : S.of(context).amount_is_estimate; - return Center( - child: Text( - description, - style: TextStyle( - color: Theme.of(context) - .primaryTextTheme - .display4 - .decorationColor, - fontWeight: FontWeight.w500, - fontSize: 12), + ), + Padding( + padding: + EdgeInsets.only(top: 29, left: 24, right: 24), + child: Observer( + builder: (_) => ExchangeCard( + amountFocusNode: _receiveAmountFocus, + key: receiveKey, + title: S.of(context).you_will_get, + initialCurrency: + exchangeViewModel.receiveCurrency, + initialWalletName: receiveWalletName, + initialAddress: exchangeViewModel + .receiveCurrency == + exchangeViewModel.wallet.currency + ? exchangeViewModel.wallet.address + : exchangeViewModel.receiveAddress, + initialIsAmountEditable: false, + initialIsAddressEditable: + exchangeViewModel + .isReceiveAddressEnabled, + isAmountEstimated: true, + currencies: CryptoCurrency.all, + onCurrencySelected: (currency) => + exchangeViewModel + .changeReceiveCurrency( + currency: currency), + imageArrow: arrowBottomCakeGreen, + currencyButtonColor: Colors.transparent, + addressButtonsColor: + Theme.of(context).focusColor, + borderColor: Theme.of(context) + .primaryTextTheme + .body2 + .decorationColor, + currencyValueValidator: AmountValidator( + type: exchangeViewModel.wallet.type), + addressTextFieldValidator: + AddressValidator( + type: exchangeViewModel + .receiveCurrency), + )), + ) + ], + ), ), - ); - }), - ), - Observer( - builder: (_) => LoadingPrimaryButton( - text: S.of(context).exchange, - onPressed: () { - if (_formKey.currentState.validate()) { - exchangeViewModel.createTrade(); - } - }, - color: Theme.of(context).accentTextTheme.body2.color, - textColor: Colors.white, - isLoading: - false, // FIXME: FIXME exchangeViewModel.tradeState is TradeIsCreating, - )), - ]), - )), - ); + 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), + ) + ], + ), + ), + 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.exchangeTemplate), + 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 = exchangeViewModel.templates; + final itemCount = + exchangeViewModel.templates.length; + + return ListView.builder( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: itemCount, + itemBuilder: (context, index) { + final template = templates[index]; + + return TemplateTile( + key: UniqueKey(), + amount: template.amount, + from: template.depositCurrency, + to: template.receiveCurrency, + onTap: () { + applyTemplate( + exchangeViewModel, template); + }, + onRemove: () { + showPopUp( + 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(); + exchangeViewModel + .exchangeTemplateStore + .remove( + template: + template); + exchangeViewModel + .exchangeTemplateStore + .update(); + }, + actionRightButton: () => + Navigator.of( + dialogContext) + .pop()); + }); + }, + ); + }); + }), + ], + ))) + ], + ), + bottomSectionPadding: + EdgeInsets.only(left: 24, right: 24, bottom: 24), + bottomSection: Column(children: [ + Padding( + padding: EdgeInsets.only(bottom: 15), + child: Observer(builder: (_) { + final description = + exchangeViewModel.provider is XMRTOExchangeProvider + ? S.of(context).amount_is_guaranteed + : S.of(context).amount_is_estimate; + return Center( + child: Text( + description, + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .display4 + .decorationColor, + fontWeight: FontWeight.w500, + fontSize: 12), + ), + ); + }), + ), + Observer( + builder: (_) => LoadingPrimaryButton( + text: S.of(context).exchange, + onPressed: () { + if (_formKey.currentState.validate()) { + exchangeViewModel.createTrade(); + } + }, + color: + Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + isLoading: exchangeViewModel.tradeState + is IsExecutingState, + )), + ]), + )), + )); } void applyTemplate( @@ -435,23 +467,23 @@ class ExchangePage extends BasePage { exchangeViewModel.depositCurrency, exchangeViewModel, depositKey); reaction( - (_) => exchangeViewModel.wallet.name, - (String _) => _onWalletNameChange( + (_) => exchangeViewModel.wallet.name, + (String _) => _onWalletNameChange( exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey)); reaction( - (_) => exchangeViewModel.wallet.name, - (String _) => _onWalletNameChange( + (_) => exchangeViewModel.wallet.name, + (String _) => _onWalletNameChange( exchangeViewModel, exchangeViewModel.depositCurrency, depositKey)); reaction( - (_) => exchangeViewModel.receiveCurrency, - (CryptoCurrency currency) => + (_) => exchangeViewModel.receiveCurrency, + (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, receiveKey)); reaction( - (_) => exchangeViewModel.depositCurrency, - (CryptoCurrency currency) => + (_) => exchangeViewModel.depositCurrency, + (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey)); reaction((_) => exchangeViewModel.depositAmount, (String amount) { @@ -467,9 +499,9 @@ class ExchangePage extends BasePage { }); reaction((_) => exchangeViewModel.isDepositAddressEnabled, - (bool isEnabled) { - depositKey.currentState.isAddressEditable(isEditable: isEnabled); - }); + (bool isEnabled) { + depositKey.currentState.isAddressEditable(isEditable: isEnabled); + }); reaction((_) => exchangeViewModel.receiveAmount, (String amount) { if (receiveKey.currentState.amountController.text != amount) { @@ -484,9 +516,9 @@ class ExchangePage extends BasePage { }); reaction((_) => exchangeViewModel.isReceiveAddressEnabled, - (bool isEnabled) { - receiveKey.currentState.isAddressEditable(isEditable: isEnabled); - }); + (bool isEnabled) { + receiveKey.currentState.isAddressEditable(isEditable: isEnabled); + }); // FIXME: FIXME @@ -535,7 +567,7 @@ class ExchangePage extends BasePage { // }); depositAddressController.addListener( - () => exchangeViewModel.depositAddress = depositAddressController.text); + () => exchangeViewModel.depositAddress = depositAddressController.text); depositAmountController.addListener(() { if (depositAmountController.text != exchangeViewModel.depositAmount) { @@ -545,7 +577,7 @@ class ExchangePage extends BasePage { }); receiveAddressController.addListener( - () => exchangeViewModel.receiveAddress = receiveAddressController.text); + () => exchangeViewModel.receiveAddress = receiveAddressController.text); receiveAmountController.addListener(() { if (receiveAmountController.text != exchangeViewModel.receiveAmount) { diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index cd2fa6527..e88f1b966 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -24,7 +24,8 @@ class ExchangeCard extends StatefulWidget { this.addressButtonsColor = Colors.transparent, this.borderColor = Colors.transparent, this.currencyValueValidator, - this.addressTextFieldValidator}) + this.addressTextFieldValidator, + this.amountFocusNode}) : super(key: key); final List currencies; @@ -42,6 +43,7 @@ class ExchangeCard extends StatefulWidget { final Color borderColor; final FormFieldValidator currencyValueValidator; final FormFieldValidator addressTextFieldValidator; + final FocusNode amountFocusNode; @override ExchangeCardState createState() => ExchangeCardState(); @@ -114,191 +116,191 @@ class ExchangeCardState extends State { @override Widget build(BuildContext context) { final copyImage = Image.asset('assets/images/copy_content.png', - height: 16, width: 16, + height: 16, + width: 16, color: Theme.of(context).primaryTextTheme.display2.color); return Container( width: double.infinity, color: Colors.transparent, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: < + Widget>[ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( _title, style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Theme.of(context).textTheme.headline.color - ), + fontSize: 18, + fontWeight: FontWeight.w600, + color: Theme.of(context).textTheme.headline.color), ) ], ), Padding( - padding: EdgeInsets.only(top: 20), - child: Stack( - children: [ - BaseTextFormField( - controller: amountController, - enabled: _isAmountEditable, - textAlign: TextAlign.left, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: true), - // inputFormatters: [ - // LengthLimitingTextInputFormatter(15), - // BlacklistingTextInputFormatter( - // RegExp('[\\-|\\ |\\,]')) - // ], - hintText: '0.0000', - borderColor: widget.borderColor, - textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white + padding: EdgeInsets.only(top: 20), + child: Stack( + children: [ + BaseTextFormField( + focusNode: widget.amountFocusNode, + controller: amountController, + enabled: _isAmountEditable, + textAlign: TextAlign.left, + keyboardType: TextInputType.numberWithOptions( + signed: false, decimal: true), + // inputFormatters: [ + // LengthLimitingTextInputFormatter(15), + // BlacklistingTextInputFormatter( + // RegExp('[\\-|\\ |\\,]')) + // ], + hintText: '0.0000', + borderColor: widget.borderColor, + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white), + placeholderTextStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .textTheme + .subhead + .decorationColor), + validator: _isAmountEditable + ? widget.currencyValueValidator + : null), + Positioned( + top: 8, + right: 0, + child: Container( + height: 32, + padding: EdgeInsets.only(left: 10), + color: widget.currencyButtonColor, + child: InkWell( + onTap: () => _presentPicker(context), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + Text(_selectedCurrency.toString(), + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + color: Colors.white)), + Padding( + padding: EdgeInsets.only(left: 5), + child: widget.imageArrow, + ) + ]), + ), ), - placeholderTextStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Theme.of(context).textTheme.subhead.decorationColor - ), - validator: _isAmountEditable - ? widget.currencyValueValidator - : null - ), - Positioned( - top: 8, - right: 0, - child: Container( - height: 32, - padding: EdgeInsets.only(left: 10), - color: widget.currencyButtonColor, - child: InkWell( - onTap: () => _presentPicker(context), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - _selectedCurrency.toString(), - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16, - color: Colors.white)), - Padding( - padding: EdgeInsets.only(left: 5), - child: widget.imageArrow, - ) - ]), - ), - ), - ) - ], - ) - ), + ) + ], + )), Padding( padding: EdgeInsets.only(top: 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _min != null + child: Row(mainAxisAlignment: MainAxisAlignment.start, children: < + Widget>[ + _min != null ? Text( - S.of(context).min_value( - _min, _selectedCurrency.toString()), - style: TextStyle( - fontSize: 10, - height: 1.2, - color: Theme.of(context).textTheme.subhead.decorationColor), - ) - : Offstage(), - _min != null ? SizedBox(width: 10) : Offstage(), - _max != null - ? Text( - S.of(context).max_value( - _max, _selectedCurrency.toString()), + S.of(context).min_value(_min, _selectedCurrency.toString()), style: TextStyle( fontSize: 10, height: 1.2, - color: Theme.of(context).textTheme.subhead.decorationColor)) + color: Theme.of(context) + .textTheme + .subhead + .decorationColor), + ) : Offstage(), - ]), + _min != null ? SizedBox(width: 10) : Offstage(), + _max != null + ? Text( + S.of(context).max_value(_max, _selectedCurrency.toString()), + style: TextStyle( + fontSize: 10, + height: 1.2, + color: Theme.of(context) + .textTheme + .subhead + .decorationColor)) + : Offstage(), + ]), ), _isAddressEditable - ? Offstage() - : Padding( - padding: EdgeInsets.only(top: 20), - child: Text( - S.of(context).refund_address, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Theme.of(context).textTheme.subhead.decorationColor - ), - ) - ), + ? Offstage() + : Padding( + padding: EdgeInsets.only(top: 20), + child: Text( + S.of(context).refund_address, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: + Theme.of(context).textTheme.subhead.decorationColor), + )), _isAddressEditable - ? Padding( - padding: EdgeInsets.only(top: 20), - child: AddressTextField( - controller: addressController, - options: [ - AddressTextFieldOption.paste, - AddressTextFieldOption.qrCode, - AddressTextFieldOption.addressBook, - ], - isBorderExist: false, - textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), - hintStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Theme.of(context).textTheme.subhead.decorationColor), - buttonColor: widget.addressButtonsColor, - validator: widget.addressTextFieldValidator, - ), - ) - : Padding( - padding: EdgeInsets.only(top: 10), - child: Builder( - builder: (context) => GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData( - text: addressController.text)); - Scaffold.of(context).showSnackBar(SnackBar( - content: Text( - S.of(context).copied_to_clipboard, - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.green, - duration: Duration(milliseconds: 500), - )); - }, - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Text( - addressController.text, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), - ), - ), - Padding( - padding: EdgeInsets.only(left: 16), - child: copyImage, - ) + ? Padding( + padding: EdgeInsets.only(top: 20), + child: AddressTextField( + controller: addressController, + options: [ + AddressTextFieldOption.paste, + AddressTextFieldOption.qrCode, + AddressTextFieldOption.addressBook, ], + isBorderExist: false, + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white), + hintStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: + Theme.of(context).textTheme.subhead.decorationColor), + buttonColor: widget.addressButtonsColor, + validator: widget.addressTextFieldValidator, ), ) - ), - ), + : Padding( + padding: EdgeInsets.only(top: 10), + child: Builder( + builder: (context) => GestureDetector( + onTap: () { + Clipboard.setData( + ClipboardData(text: addressController.text)); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text( + S.of(context).copied_to_clipboard, + style: TextStyle(color: Colors.white), + ), + backgroundColor: Colors.green, + duration: Duration(milliseconds: 500), + )); + }, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Text( + addressController.text, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white), + ), + ), + Padding( + padding: EdgeInsets.only(left: 16), + child: copyImage, + ) + ], + ), + )), + ), ]), ); } @@ -310,9 +312,9 @@ class ExchangeCardState extends State { items: widget.currencies, title: S.of(context).change_currency, onItemSelected: (CryptoCurrency item) => - widget.onCurrencySelected != null - ? widget.onCurrencySelected(item) - : null), + widget.onCurrencySelected != null + ? widget.onCurrencySelected(item) + : null), context: context); } } diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 1bb2054ab..3cd94f39c 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -6,11 +6,17 @@ import 'package:cake_wallet/store/authentication_store.dart'; import 'package:cake_wallet/entities/qr_scanner.dart'; class Root extends StatefulWidget { - Root({Key key, this.authenticationStore, this.appStore, this.child}) + Root( + {Key key, + this.authenticationStore, + this.appStore, + this.child, + this.navigatorKey}) : super(key: key); final AuthenticationStore authenticationStore; final AppStore appStore; + final GlobalKey navigatorKey; final Widget child; @override @@ -53,7 +59,7 @@ class RootState extends State with WidgetsBindingObserver { if (_isInactive && !_postFrameCallback) { _postFrameCallback = true; WidgetsBinding.instance.addPostFrameCallback((_) { - Navigator.of(context).pushNamed(Routes.unlock, + widget.navigatorKey.currentState.pushNamed(Routes.unlock, arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { if (!isAuthenticatedSuccessfully) { return; diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 8080f65a8..4a1a15443 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,31 +1,26 @@ import 'dart:ui'; - -// import 'package:cake_wallet/src/domain/common/transaction_priority.dart'; +import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; +import 'package:keyboard_actions/keyboard_actions.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/view_model/send/send_view_model.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -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/src/widgets/address_text_field.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'; - -// import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart'; -import 'package:cake_wallet/src/widgets/template_tile.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; -import 'package:cake_wallet/routes.dart'; class SendPage extends BasePage { SendPage({@required this.sendViewModel}); @@ -34,8 +29,9 @@ class SendPage extends BasePage { final _addressController = TextEditingController(); final _cryptoAmountController = TextEditingController(); final _fiatAmountController = TextEditingController(); - final _focusNode = FocusNode(); final _formKey = GlobalKey(); + final _cryptoAmountFocus = FocusNode(); + final _fiatAmountFocus = FocusNode(); bool _effectsInstalled = false; @@ -62,180 +58,75 @@ class SendPage extends BasePage { Widget body(BuildContext context) { _setEffects(context); - return Container( - color: Theme.of(context).backgroundColor, - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 24), - content: Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24)), - gradient: LinearGradient(colors: [ - Theme.of(context).primaryTextTheme.subhead.color, - Theme.of(context).primaryTextTheme.subhead.decorationColor, - ], begin: Alignment.topLeft, end: Alignment.bottomRight), - ), - child: Form( - key: _formKey, - child: Column(children: [ - Padding( - padding: EdgeInsets.fromLTRB(24, 90, 24, 32), - child: Column( - children: [ - AddressTextField( - controller: _addressController, - focusNode: _focusNode, - onURIScanned: (uri) { - var address = ''; - var amount = ''; + return KeyboardActions( + config: KeyboardActionsConfig( + keyboardActionsPlatform: KeyboardActionsPlatform.IOS, + keyboardBarColor: isDarkTheme + ? Color.fromRGBO(48, 51, 60, 1.0) + : Color.fromRGBO(98, 98, 98, 1.0), + nextFocus: false, + actions: [ + KeyboardActionsItem( + focusNode: _cryptoAmountFocus, + toolbarButtons: [(_) => KeyboardDoneButton()], + ), + KeyboardActionsItem( + focusNode: _fiatAmountFocus, + toolbarButtons: [(_) => KeyboardDoneButton()], + ) + ]), + child: Container( + height: 0, + color: Theme.of(context).backgroundColor, + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.only(bottom: 24), + content: Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24)), + gradient: LinearGradient(colors: [ + Theme.of(context).primaryTextTheme.subhead.color, + Theme.of(context) + .primaryTextTheme + .subhead + .decorationColor, + ], begin: Alignment.topLeft, end: Alignment.bottomRight), + ), + child: Form( + key: _formKey, + child: Column(children: [ + Padding( + padding: EdgeInsets.fromLTRB(24, 100, 24, 32), + child: Column( + children: [ + AddressTextField( + controller: _addressController, + onURIScanned: (uri) { + var address = ''; + var amount = ''; - if (uri != null) { - address = uri.path; - amount = uri.queryParameters['tx_amount']; - } else { - address = uri.toString(); - } + if (uri != null) { + address = uri.path; + amount = uri.queryParameters['tx_amount']; + } else { + address = uri.toString(); + } - _addressController.text = address; - _cryptoAmountController.text = amount; - }, - options: [ - AddressTextFieldOption.paste, - AddressTextFieldOption.qrCode, - AddressTextFieldOption.addressBook - ], - buttonColor: Theme.of(context) - .primaryTextTheme - .display1 - .color, - borderColor: Theme.of(context) - .primaryTextTheme - .headline - .color, - textStyle: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.white), - hintStyle: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Theme.of(context) + _addressController.text = address; + _cryptoAmountController.text = amount; + }, + options: [ + AddressTextFieldOption.paste, + AddressTextFieldOption.qrCode, + AddressTextFieldOption.addressBook + ], + buttonColor: Theme.of(context) .primaryTextTheme - .headline - .decorationColor), - validator: sendViewModel.addressValidator, - ), - Padding( - padding: const EdgeInsets.only(top: 20), - child: BaseTextFormField( - controller: _cryptoAmountController, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: true), - prefixIcon: Padding( - padding: EdgeInsets.only(top: 9), - child: - Text(sendViewModel.currency.title + ':', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white, - )), - ), - suffixIcon: 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)), - ), - ), - ), - hintText: '0.0000', - 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.amountValidator)), - 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), - ) - ], - ), - )), - Padding( - padding: const EdgeInsets.only(top: 20), - child: BaseTextFormField( - controller: _fiatAmountController, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: true), - prefixIcon: Padding( - padding: EdgeInsets.only(top: 9), - child: Text(sendViewModel.fiat.title + ':', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white, - )), - ), - hintText: '0.00', + .display1 + .color, borderColor: Theme.of(context) .primaryTextTheme .headline @@ -244,202 +135,342 @@ class SendPage extends BasePage { fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white), - placeholderTextStyle: TextStyle( + hintStyle: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, color: Theme.of(context) .primaryTextTheme .headline - .decorationColor, - fontWeight: FontWeight.w500, - fontSize: 14), - )), - 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, + .decorationColor), + validator: sendViewModel.addressValidator, + ), + Padding( + padding: const EdgeInsets.only(top: 20), + child: BaseTextFormField( + focusNode: _cryptoAmountFocus, + controller: _cryptoAmountController, + keyboardType: + TextInputType.numberWithOptions( + signed: false, decimal: true), + prefixIcon: Padding( + padding: EdgeInsets.only(top: 9), + child: Text( + sendViewModel.currency.title + ':', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, + )), + ), + suffixIcon: 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)), + ), + ), + ), + hintText: '0.0000', + 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.amountValidator)), + 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.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, - ), - ) - ], - ), - ) - ], - ), + 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( + focusNode: _fiatAmountFocus, + controller: _fiatAmountController, + keyboardType: + TextInputType.numberWithOptions( + signed: false, decimal: true), + prefixIcon: Padding( + padding: EdgeInsets.only(top: 9), + child: + Text(sendViewModel.fiat.title + ':', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, + )), ), - )) - ], - ), - ) - ]), - ), - ), - 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), - ) - ], - ), - ), - 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), - child: Container( - padding: EdgeInsets.only(left: 1, right: 10), - child: DottedBorder( - borderType: BorderType.RRect, - dashPattern: [6, 4], + hintText: '0.00', + 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), + )), + 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, + ), + ) + ], + ), + ) + ], + ), + ), + )) + ], + ), + ) + ]), + ), + ), + 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 - .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; - - // 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: () { - // showPopUp( - // 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() - // ); - // } - // ); - // }, - // ); - // } - // ); - // } - // ) - ], + .display4 + .color), + ) + ], + ), ), - ), - ) - ], - ), - bottomSectionPadding: - EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Observer(builder: (_) { - return LoadingPrimaryButton( - onPressed: () async { - if (_formKey.currentState.validate()) { - await sendViewModel.createTransaction(); - } - }, - text: S.of(context).send, - color: Theme.of(context).accentTextTheme.body2.color, - textColor: Colors.white, - isLoading: sendViewModel.state is IsExecutingState || - sendViewModel.state is TransactionCommitting, - isDisabled: - false // FIXME !(syncStore.status is SyncedSyncStatus), - ); - })), - ); + 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), + 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; + + // 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: () { + // showPopUp( + // 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() + // ); + // } + // ); + // }, + // ); + // } + // ); + // } + // ) + ], + ), + ), + ) + ], + ), + bottomSectionPadding: + EdgeInsets.only(left: 24, right: 24, bottom: 24), + bottomSection: Observer(builder: (_) { + return LoadingPrimaryButton( + onPressed: () async { + if (_formKey.currentState.validate()) { + await sendViewModel.createTransaction(); + } + }, + text: S.of(context).send, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + isLoading: sendViewModel.state is IsExecutingState || + sendViewModel.state is TransactionCommitting, + isDisabled: + false // FIXME !(syncStore.status is SyncedSyncStatus), + ); + })), + )); } void _setEffects(BuildContext context) { diff --git a/lib/src/widgets/base_text_form_field.dart b/lib/src/widgets/base_text_form_field.dart index c1c34345b..e7a2d5650 100644 --- a/lib/src/widgets/base_text_form_field.dart +++ b/lib/src/widgets/base_text_form_field.dart @@ -22,7 +22,8 @@ class BaseTextFormField extends StatelessWidget { this.validator, this.textStyle, this.placeholderTextStyle, - this.maxLength}); + this.maxLength, + this.focusNode}); final TextEditingController controller; final TextInputType keyboardType; @@ -44,10 +45,12 @@ class BaseTextFormField extends StatelessWidget { final TextStyle placeholderTextStyle; final TextStyle textStyle; final int maxLength; + final FocusNode focusNode; @override Widget build(BuildContext context) { return TextFormField( + focusNode: focusNode, controller: controller, keyboardType: keyboardType, textInputAction: textInputAction, diff --git a/lib/src/widgets/keyboard_done_button.dart b/lib/src/widgets/keyboard_done_button.dart new file mode 100644 index 000000000..828c626b2 --- /dev/null +++ b/lib/src/widgets/keyboard_done_button.dart @@ -0,0 +1,19 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class KeyboardDoneButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + // FIXME: Add translation + return CupertinoButton( + padding: EdgeInsets.only(right: 24.0, top: 8.0, bottom: 8.0), + onPressed: () { + FocusScope.of(context).requestFocus(FocusNode()); + }, + child: Text( + 'Done', + style: TextStyle(color: Colors.blueAccent,fontWeight: FontWeight.bold) + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index f9bbbbd26..8a8469917 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -534,6 +534,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0" + keyboard_actions: + dependency: "direct main" + description: + name: keyboard_actions + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0+1" local_auth: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d6ac4bf73..541ca0259 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,7 @@ dependencies: bitcoin_flutter: ^2.0.0 get_it: ^4.0.2 connectivity: ^0.4.9+2 + keyboard_actions: ^3.3.0 dev_dependencies: flutter_test: