Added 'AddBalanceViewModel'

Added form validations
This commit is contained in:
OmarHatem28 2022-07-07 14:37:09 +02:00
parent 93e7c129f1
commit 2798132313
6 changed files with 165 additions and 46 deletions

View file

@ -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/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/cake_phone/phone_number_service/number_settings_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/balance_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:cake_wallet/view_model/cake_phone/phone_plan_view_model.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/core/backup_service.dart'; import 'package:cake_wallet/core/backup_service.dart';
@ -662,7 +663,13 @@ Future setup(
)); ));
getIt.registerFactory(() { getIt.registerFactory(() {
return AddBalancePage(/* Add balance view model */); final wallet = getIt.get<AppStore>().wallet;
return AddBalanceViewModel(getIt.get<BuyAmountViewModel>(), wallet: wallet);
});
getIt.registerFactory(() {
return AddBalancePage(addBalanceViewModel: getIt.get<AddBalanceViewModel>());
}); });
_isSetupFinished = true; _isSetupFinished = true;

View file

@ -4,18 +4,26 @@ import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/src/screens/base_page.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/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.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 { class AddBalancePage extends BasePage {
AddBalancePage() AddBalancePage({@required this.addBalanceViewModel})
: _amountFocus = FocusNode(), : _amountFocus = FocusNode(),
_amountController = TextEditingController() { _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})\$'; static const _amountPattern = '^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$';
@ -25,6 +33,7 @@ class AddBalancePage extends BasePage {
"500 additional SMS", "500 additional SMS",
]; ];
final AddBalanceViewModel addBalanceViewModel;
final FocusNode _amountFocus; final FocusNode _amountFocus;
final TextEditingController _amountController; final TextEditingController _amountController;
@ -151,13 +160,19 @@ class AddBalancePage extends BasePage {
], ],
), ),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: LoadingPrimaryButton( bottomSection: Observer(
onPressed: () {}, builder: (_) {
text: S.of(context).buy, return LoadingPrimaryButton(
color: Theme.of(context).accentTextTheme.body2.color, onPressed: () {
textColor: Colors.white,
isLoading: false, },
isDisabled: _amountController.text.isEmpty, text: S.of(context).buy,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
isLoading: false,
isDisabled: addBalanceViewModel.buyAmountViewModel.amount.isEmpty,
);
}
), ),
), ),
), ),

View file

@ -39,6 +39,9 @@ class CakePhoneAuthBody extends StatefulWidget {
class CakePhoneAuthBodyState extends State<CakePhoneAuthBody> { class CakePhoneAuthBodyState extends State<CakePhoneAuthBody> {
final _emailController = TextEditingController(); final _emailController = TextEditingController();
final _formKey = GlobalKey<FormState>();
AutovalidateMode _autoValidate = AutovalidateMode.disabled;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -46,11 +49,23 @@ class CakePhoneAuthBodyState extends State<CakePhoneAuthBody> {
padding: EdgeInsets.only(top: 16), padding: EdgeInsets.only(top: 16),
child: ScrollableWithBottomSection( child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.fromLTRB(24, 100, 24, 20), contentPadding: EdgeInsets.fromLTRB(24, 100, 24, 20),
content: BaseTextFormField( content: Form(
controller: _emailController, key: _formKey,
keyboardType: TextInputType.emailAddress, autovalidateMode: _autoValidate,
maxLines: 1, child: BaseTextFormField(
hintText: S.of(context).email_address, 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), bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24),
bottomSection: Column( bottomSection: Column(
@ -114,11 +129,23 @@ class CakePhoneAuthBodyState extends State<CakePhoneAuthBody> {
void _registerCakePhone() { void _registerCakePhone() {
// TODO: Add Registration logic // 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() { void _loginCakePhone() {
// TODO: Add Login logic // TODO: Add Login logic
Navigator.pushNamed(context, Routes.cakePhoneVerification); if (_formKey.currentState.validate()) {
Navigator.pushNamed(context, Routes.cakePhoneVerification);
} else {
setState(() {
_autoValidate = AutovalidateMode.always;
});
}
} }
} }

View file

@ -37,15 +37,30 @@ class CakePhoneVerificationBody extends StatefulWidget {
class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> { class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
final _codeController = TextEditingController(); final _codeController = TextEditingController();
final _formKey = GlobalKey<FormState>();
AutovalidateMode _autoValidate = AutovalidateMode.disabled;
int resendCount = 0; int resendCount = 0;
int timeLeft = 0; int timeLeft = 0;
bool disabled = true;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_startTimer(); _startTimer();
_codeController.addListener(() {
if (_codeController.text.isEmpty) {
disabled = true;
setState(() {});
} else if (disabled) {
disabled = false;
setState(() {});
}
});
} }
@override @override
@ -70,29 +85,40 @@ class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 25), padding: const EdgeInsets.symmetric(vertical: 25),
child: BaseTextFormField( child: Form(
controller: _codeController, key: _formKey,
maxLines: 1, autovalidateMode: _autoValidate,
hintText: S.of(context).verification_code, child: BaseTextFormField(
suffixIcon: timeLeft > 0 controller: _codeController,
? null maxLines: 1,
: InkWell( hintText: S.of(context).verification_code,
onTap: _startTimer, suffixIcon: timeLeft > 0
child: Container( ? null
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12), : InkWell(
margin: EdgeInsets.only(bottom: 12), onTap: _startTimer,
decoration: BoxDecoration( child: Container(
color: Theme.of(context).accentTextTheme.caption.color, padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
borderRadius: BorderRadius.circular(6), margin: EdgeInsets.only(bottom: 12),
), decoration: BoxDecoration(
child: Text( color: Theme.of(context).accentTextTheme.caption.color,
S.of(context).get_code, borderRadius: BorderRadius.circular(6),
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color,
fontWeight: FontWeight.w900,
), ),
)), 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) if (timeLeft > 0)
@ -123,16 +149,22 @@ class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
children: <Widget>[ children: <Widget>[
PrimaryButton( PrimaryButton(
onPressed: () { onPressed: () {
Navigator.pushNamedAndRemoveUntil( if (_formKey.currentState.validate()) {
context, Navigator.pushNamedAndRemoveUntil(
Routes.cakePhoneProducts, context,
ModalRoute.withName(Routes.cakePhoneWelcome), Routes.cakePhoneProducts,
); ModalRoute.withName(Routes.cakePhoneWelcome),
);
} else {
setState(() {
_autoValidate = AutovalidateMode.always;
});
}
}, },
text: S.of(context).continue_text, text: S.of(context).continue_text,
color: Theme.of(context).accentTextTheme.body2.color, color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white, textColor: Colors.white,
isDisabled: _codeController.text.isEmpty, isDisabled: disabled,
), ),
], ],
), ),

View file

@ -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);
}

View file

@ -594,5 +594,7 @@
"transaction_sent_popup_info": "Your transaction was sent. \n\nIf the screen doesnt proceed after 1 minute, check a block explorer and your email.", "transaction_sent_popup_info": "Your transaction was sent. \n\nIf the screen doesnt proceed after 1 minute, check a block explorer and your email.",
"cake_phone_products_example": "Example independent uses", "cake_phone_products_example": "Example independent uses",
"add_balance": "Add Balance", "add_balance": "Add Balance",
"forwards": "forwards" "forwards": "forwards",
"invalid_email": "Invalid Email",
"invalid_verification_code": "Invalid verification code"
} }