From 27981323137583bff80705f75e6691888c15ca2e Mon Sep 17 00:00:00 2001 From: OmarHatem28 Date: Thu, 7 Jul 2022 14:37:09 +0200 Subject: [PATCH] Added 'AddBalanceViewModel' Added form validations --- lib/di.dart | 9 +- .../screens/cake_phone/add_balance_page.dart | 33 +++++-- .../cake_phone/cake_phone_auth_page.dart | 41 +++++++-- .../cake_phone_verification_page.dart | 88 +++++++++++++------ .../cake_phone/add_balance_view_model.dart | 36 ++++++++ res/values/strings_en.arb | 4 +- 6 files changed, 165 insertions(+), 46 deletions(-) create mode 100644 lib/view_model/cake_phone/add_balance_view_model.dart diff --git a/lib/di.dart b/lib/di.dart index 29a5872c6..7a2175aec 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/src/screens/cake_phone/phone_number_service/auto_renew_settings_page.dart'; import 'package:cake_wallet/src/screens/cake_phone/phone_number_service/number_settings_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; +import 'package:cake_wallet/view_model/cake_phone/add_balance_view_model.dart'; import 'package:cake_wallet/view_model/cake_phone/phone_plan_view_model.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cake_wallet/core/backup_service.dart'; @@ -662,7 +663,13 @@ Future setup( )); getIt.registerFactory(() { - return AddBalancePage(/* Add balance view model */); + final wallet = getIt.get().wallet; + + return AddBalanceViewModel(getIt.get(), wallet: wallet); + }); + + getIt.registerFactory(() { + return AddBalancePage(addBalanceViewModel: getIt.get()); }); _isSetupFinished = true; diff --git a/lib/src/screens/cake_phone/add_balance_page.dart b/lib/src/screens/cake_phone/add_balance_page.dart index 53ad51703..080d5aa34 100644 --- a/lib/src/screens/cake_phone/add_balance_page.dart +++ b/lib/src/screens/cake_phone/add_balance_page.dart @@ -4,18 +4,26 @@ 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:keyboard_actions/keyboard_actions.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:cake_wallet/view_model/cake_phone/add_balance_view_model.dart'; class AddBalancePage extends BasePage { - AddBalancePage() + AddBalancePage({@required this.addBalanceViewModel}) : _amountFocus = FocusNode(), _amountController = TextEditingController() { - _amountController.addListener(() {}); + _amountController.addListener(() { + final amount = _amountController.text; + + if (amount != addBalanceViewModel.buyAmountViewModel.amount) { + addBalanceViewModel.buyAmountViewModel.amount = amount; + } + }); } static const _amountPattern = '^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$'; @@ -25,6 +33,7 @@ class AddBalancePage extends BasePage { "500 additional SMS", ]; + final AddBalanceViewModel addBalanceViewModel; final FocusNode _amountFocus; final TextEditingController _amountController; @@ -151,13 +160,19 @@ class AddBalancePage extends BasePage { ], ), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: LoadingPrimaryButton( - onPressed: () {}, - text: S.of(context).buy, - color: Theme.of(context).accentTextTheme.body2.color, - textColor: Colors.white, - isLoading: false, - isDisabled: _amountController.text.isEmpty, + bottomSection: Observer( + builder: (_) { + return LoadingPrimaryButton( + onPressed: () { + + }, + text: S.of(context).buy, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + isLoading: false, + isDisabled: addBalanceViewModel.buyAmountViewModel.amount.isEmpty, + ); + } ), ), ), diff --git a/lib/src/screens/cake_phone/cake_phone_auth_page.dart b/lib/src/screens/cake_phone/cake_phone_auth_page.dart index e9b8f826b..102d04c62 100644 --- a/lib/src/screens/cake_phone/cake_phone_auth_page.dart +++ b/lib/src/screens/cake_phone/cake_phone_auth_page.dart @@ -39,6 +39,9 @@ class CakePhoneAuthBody extends StatefulWidget { class CakePhoneAuthBodyState extends State { final _emailController = TextEditingController(); + final _formKey = GlobalKey(); + + AutovalidateMode _autoValidate = AutovalidateMode.disabled; @override Widget build(BuildContext context) { @@ -46,11 +49,23 @@ class CakePhoneAuthBodyState extends State { padding: EdgeInsets.only(top: 16), child: ScrollableWithBottomSection( contentPadding: EdgeInsets.fromLTRB(24, 100, 24, 20), - content: BaseTextFormField( - controller: _emailController, - keyboardType: TextInputType.emailAddress, - maxLines: 1, - hintText: S.of(context).email_address, + content: Form( + key: _formKey, + autovalidateMode: _autoValidate, + child: BaseTextFormField( + controller: _emailController, + keyboardType: TextInputType.emailAddress, + maxLines: 1, + hintText: S.of(context).email_address, + validator: (String text) { + text = text.trim(); + if (text.isNotEmpty && RegExp(r"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$").hasMatch(text)) { + return null; + } + + return S.of(context).invalid_email; + }, + ), ), bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24), bottomSection: Column( @@ -114,11 +129,23 @@ class CakePhoneAuthBodyState extends State { void _registerCakePhone() { // TODO: Add Registration logic - Navigator.pushNamed(context, Routes.cakePhoneVerification); + if (_formKey.currentState.validate()) { + Navigator.pushNamed(context, Routes.cakePhoneVerification); + } else { + setState(() { + _autoValidate = AutovalidateMode.always; + }); + } } void _loginCakePhone() { // TODO: Add Login logic - Navigator.pushNamed(context, Routes.cakePhoneVerification); + if (_formKey.currentState.validate()) { + Navigator.pushNamed(context, Routes.cakePhoneVerification); + } else { + setState(() { + _autoValidate = AutovalidateMode.always; + }); + } } } diff --git a/lib/src/screens/cake_phone/cake_phone_verification_page.dart b/lib/src/screens/cake_phone/cake_phone_verification_page.dart index eb956aa47..7a4d7b69a 100644 --- a/lib/src/screens/cake_phone/cake_phone_verification_page.dart +++ b/lib/src/screens/cake_phone/cake_phone_verification_page.dart @@ -37,15 +37,30 @@ class CakePhoneVerificationBody extends StatefulWidget { class CakePhoneVerificationBodyState extends State { final _codeController = TextEditingController(); + final _formKey = GlobalKey(); + + AutovalidateMode _autoValidate = AutovalidateMode.disabled; int resendCount = 0; int timeLeft = 0; + bool disabled = true; + @override void initState() { super.initState(); _startTimer(); + + _codeController.addListener(() { + if (_codeController.text.isEmpty) { + disabled = true; + setState(() {}); + } else if (disabled) { + disabled = false; + setState(() {}); + } + }); } @override @@ -70,29 +85,40 @@ class CakePhoneVerificationBodyState extends State { ), Padding( padding: const EdgeInsets.symmetric(vertical: 25), - child: BaseTextFormField( - controller: _codeController, - maxLines: 1, - hintText: S.of(context).verification_code, - suffixIcon: timeLeft > 0 - ? null - : InkWell( - onTap: _startTimer, - child: Container( - padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12), - margin: EdgeInsets.only(bottom: 12), - decoration: BoxDecoration( - color: Theme.of(context).accentTextTheme.caption.color, - borderRadius: BorderRadius.circular(6), - ), - child: Text( - S.of(context).get_code, - style: TextStyle( - color: Theme.of(context).primaryTextTheme.title.color, - fontWeight: FontWeight.w900, + child: Form( + key: _formKey, + autovalidateMode: _autoValidate, + child: BaseTextFormField( + controller: _codeController, + maxLines: 1, + hintText: S.of(context).verification_code, + suffixIcon: timeLeft > 0 + ? null + : InkWell( + onTap: _startTimer, + child: Container( + padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12), + margin: EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Theme.of(context).accentTextTheme.caption.color, + borderRadius: BorderRadius.circular(6), ), - )), - ), + child: Text( + S.of(context).get_code, + style: TextStyle( + color: Theme.of(context).primaryTextTheme.title.color, + fontWeight: FontWeight.w900, + ), + )), + ), + validator: (String text) { + // TODO: check and apply verification constraints with backend + if (text.length < 4) { + return S.of(context).invalid_verification_code; + } + return null; + }, + ), ), ), if (timeLeft > 0) @@ -123,16 +149,22 @@ class CakePhoneVerificationBodyState extends State { children: [ PrimaryButton( onPressed: () { - Navigator.pushNamedAndRemoveUntil( - context, - Routes.cakePhoneProducts, - ModalRoute.withName(Routes.cakePhoneWelcome), - ); + if (_formKey.currentState.validate()) { + Navigator.pushNamedAndRemoveUntil( + context, + Routes.cakePhoneProducts, + ModalRoute.withName(Routes.cakePhoneWelcome), + ); + } else { + setState(() { + _autoValidate = AutovalidateMode.always; + }); + } }, text: S.of(context).continue_text, color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, - isDisabled: _codeController.text.isEmpty, + isDisabled: disabled, ), ], ), diff --git a/lib/view_model/cake_phone/add_balance_view_model.dart b/lib/view_model/cake_phone/add_balance_view_model.dart new file mode 100644 index 000000000..cc1266b65 --- /dev/null +++ b/lib/view_model/cake_phone/add_balance_view_model.dart @@ -0,0 +1,36 @@ +import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/foundation.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/wallet_base.dart'; + +part 'add_balance_view_model.g.dart'; + +class AddBalanceViewModel = AddBalanceViewModelBase with _$AddBalanceViewModel; + +abstract class AddBalanceViewModelBase with Store { + AddBalanceViewModelBase(this.buyAmountViewModel, {@required this.wallet}) { + isRunning = false; + isDisabled = true; + } + + final BuyAmountViewModel buyAmountViewModel; + final WalletBase wallet; + + @observable + bool isRunning; + + @observable + bool isDisabled; + + WalletType get type => wallet.type; + + double get doubleAmount => buyAmountViewModel.doubleAmount; + + @computed + FiatCurrency get fiatCurrency => buyAmountViewModel.fiatCurrency; + + CryptoCurrency get cryptoCurrency => walletTypeToCryptoCurrency(type); +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 73f1421ef..aeac85094 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -594,5 +594,7 @@ "transaction_sent_popup_info": "Your transaction was sent. \n\nIf the screen doesn’t proceed after 1 minute, check a block explorer and your email.", "cake_phone_products_example": "Example independent uses", "add_balance": "Add Balance", - "forwards": "forwards" + "forwards": "forwards", + "invalid_email": "Invalid Email", + "invalid_verification_code": "Invalid verification code" }