diff --git a/lib/core/failure.dart b/lib/core/failure.dart new file mode 100644 index 000000000..312f911bd --- /dev/null +++ b/lib/core/failure.dart @@ -0,0 +1,28 @@ +import 'package:cake_wallet/generated/i18n.dart'; + +abstract class Failure { + const Failure(this.errorMessage); + + final String errorMessage; +} + +class ServerFailure extends Failure { + ServerFailure(int statusCode, {String error}) : super(_formatErrorMessage(statusCode, error)); + + static String _formatErrorMessage(int statusCode, String error) { + switch (statusCode) { + case 401: + return S.current.unauthorized; + case 404: + return S.current.page_not_found; + case 500: + return S.current.server_failure; + default: + return error ?? S.current.something_went_wrong; + } + } +} + +class UnknownFailure extends Failure { + UnknownFailure({String errorMessage}) : super(errorMessage ?? S.current.something_went_wrong); +} diff --git a/lib/di.dart b/lib/di.dart index bf15ceb8c..6308e7ef9 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -674,7 +674,7 @@ Future setup( return AddBalancePage(addBalanceViewModel: getIt.get()); }); - getIt.registerFactory(() { + getIt.registerLazySingleton(() { return CakePhoneAuthViewModel(getIt.get(), getIt.get()); }); diff --git a/lib/providers/cake_phone_provider.dart b/lib/providers/cake_phone_provider.dart index 0502dc374..2480929f8 100644 --- a/lib/providers/cake_phone_provider.dart +++ b/lib/providers/cake_phone_provider.dart @@ -1,12 +1,14 @@ import 'dart:convert'; +import 'package:cake_wallet/core/failure.dart'; +import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class CakePhoneProvider { final _baseUrl = 'cake-phone.cakewallet.com'; - Future authenticate(String email) async { + Future> authenticate(String email) async { try { final headers = {'Content-Type': 'application/json'}; final body = {"email": email}; @@ -16,17 +18,20 @@ class CakePhoneProvider { if (response.statusCode != 200) { debugPrint(response.body); - return false; + return Left(ServerFailure(response.statusCode, error: response.body)); } - return true; + final responseJSON = json.decode(response.body) as Map; + final bool userExists = responseJSON['user_exists'] as bool; + + return Right(userExists ?? false); } catch (err) { debugPrint(err.toString()); - return false; + return Left(UnknownFailure(errorMessage: err.toString())); } } - Future verifyEmail({@required String email, @required String code}) async { + Future> verifyEmail({@required String email, @required String code}) async { try { final headers = {'Content-Type': 'application/json'}; final body = { @@ -39,16 +44,16 @@ class CakePhoneProvider { if (response.statusCode != 200) { debugPrint(response.body); - return null; + return Left(ServerFailure(response.statusCode, error: response.body)); } final responseJSON = json.decode(response.body) as Map; final String token = responseJSON['token'] as String; - return token; + return Right(token); } catch (err) { debugPrint(err.toString()); - return null; + return Left(UnknownFailure(errorMessage: err.toString())); } } } diff --git a/lib/router.dart b/lib/router.dart index 38770f148..9949e5d3d 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -361,7 +361,7 @@ Route createRoute(RouteSettings settings) { case Routes.cakePhoneVerification: return MaterialPageRoute( settings: RouteSettings(name: Routes.cakePhoneVerification), - builder: (_) => CakePhoneVerificationPage(), + builder: (_) => CakePhoneVerificationPage(getIt.get()), ); case Routes.cakePhoneProducts: 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 ceafeb2d6..8e0dbd8f4 100644 --- a/lib/src/screens/cake_phone/cake_phone_auth_page.dart +++ b/lib/src/screens/cake_phone/cake_phone_auth_page.dart @@ -62,7 +62,7 @@ class CakePhoneAuthBodyState extends State { if (state is ExecutedSuccessfullyState) { _authBar?.dismiss(); WidgetsBinding.instance.addPostFrameCallback((_) { - Navigator.pushNamed(context, Routes.cakePhoneVerification, arguments: _emailController.text); + Navigator.pushNamed(context, Routes.cakePhoneVerification); }); } 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 7a4d7b69a..d3ccda705 100644 --- a/lib/src/screens/cake_phone/cake_phone_verification_page.dart +++ b/lib/src/screens/cake_phone/cake_phone_verification_page.dart @@ -1,6 +1,11 @@ import 'dart:async'; +import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:cake_wallet/view_model/cake_phone/cake_phone_auth_view_model.dart'; +import 'package:flushbar/flushbar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/routes.dart'; @@ -8,12 +13,15 @@ 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/widgets/scollable_with_bottom_section.dart'; +import 'package:mobx/mobx.dart'; class CakePhoneVerificationPage extends BasePage { - CakePhoneVerificationPage(); + CakePhoneVerificationPage(this.authViewModel); + + final CakePhoneAuthViewModel authViewModel; @override - Widget body(BuildContext context) => CakePhoneVerificationBody(); + Widget body(BuildContext context) => CakePhoneVerificationBody(authViewModel); @override Widget middle(BuildContext context) { @@ -29,7 +37,9 @@ class CakePhoneVerificationPage extends BasePage { } class CakePhoneVerificationBody extends StatefulWidget { - CakePhoneVerificationBody(); + CakePhoneVerificationBody(this.authViewModel); + + final CakePhoneAuthViewModel authViewModel; @override CakePhoneVerificationBodyState createState() => CakePhoneVerificationBodyState(); @@ -46,6 +56,9 @@ class CakePhoneVerificationBodyState extends State { bool disabled = true; + ReactionDisposer _reaction; + Flushbar _authBar; + @override void initState() { super.initState(); @@ -61,6 +74,35 @@ class CakePhoneVerificationBodyState extends State { setState(() {}); } }); + + _reaction ??= reaction((_) => widget.authViewModel.state, (ExecutionState state) { + if (state is ExecutedSuccessfullyState) { + _authBar?.dismiss(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final userExists = state.payload as bool; + Navigator.pushNamedAndRemoveUntil( + context, + userExists ? Routes.cakePhoneActiveServices : Routes.cakePhoneProducts, + ModalRoute.withName(Routes.cakePhoneWelcome), + ); + /// reset the authentication view model + getIt.resetLazySingleton(instance: widget.authViewModel); + }); + } + + if (state is IsExecutingState) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _authBar = createBar(S.of(context).authentication, duration: null)..show(context); + }); + } + + if (state is FailureState) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _authBar?.dismiss(); + showBar(context, S.of(context).failed_authentication(state.error)); + }); + } + }); } @override @@ -150,11 +192,7 @@ class CakePhoneVerificationBodyState extends State { PrimaryButton( onPressed: () { if (_formKey.currentState.validate()) { - Navigator.pushNamedAndRemoveUntil( - context, - Routes.cakePhoneProducts, - ModalRoute.withName(Routes.cakePhoneWelcome), - ); + widget.authViewModel.verify(_codeController.text); } else { setState(() { _autoValidate = AutovalidateMode.always; diff --git a/lib/view_model/cake_phone/cake_phone_auth_view_model.dart b/lib/view_model/cake_phone/cake_phone_auth_view_model.dart index ee5507988..f54e7edc1 100644 --- a/lib/view_model/cake_phone/cake_phone_auth_view_model.dart +++ b/lib/view_model/cake_phone/cake_phone_auth_view_model.dart @@ -20,28 +20,35 @@ abstract class CakePhoneAuthViewModelBase with Store { final CakePhoneProvider _cakePhoneProvider; final FlutterSecureStorage _secureStorage; + bool _userExists = false; + String _email = ""; + @action Future auth(String email) async { state = IsExecutingState(); - final isSuccessfullyAuthenticated = await _cakePhoneProvider.authenticate(email); + final result = await _cakePhoneProvider.authenticate(email); - if (isSuccessfullyAuthenticated) { - state = ExecutedSuccessfullyState(); - } else { - state = FailureState(""); - } + result.fold( + (failure) => state = FailureState(failure.errorMessage), + (userExists) { + _userExists = userExists; + _email = email; + state = ExecutedSuccessfullyState(); + }, + ); } @action - Future verify(String email, String code) async { - final String token = await _cakePhoneProvider.verifyEmail(email: email, code: code); + Future verify(String code) async { + final result = await _cakePhoneProvider.verifyEmail(email: _email, code: code); - if (token != null) { - state = ExecutedSuccessfullyState(); - await _secureStorage.write(key: PreferencesKey.cakePhoneTokenKey, value: token); - } else { - state = FailureState(""); - } + result.fold( + (failure) => state = FailureState(failure.errorMessage), + (token) { + _secureStorage.write(key: PreferencesKey.cakePhoneTokenKey, value: token); + state = ExecutedSuccessfullyState(payload: _userExists); + }, + ); } } diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 639cd818b..fb3b8b428 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -56,6 +56,7 @@ dependencies: permission_handler: ^5.0.1+1 device_display_brightness: ^0.0.6 country_pickers: ^2.0.0 + dartz: ^0.10.1 dev_dependencies: flutter_test: diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index aeac85094..1a0efad01 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -596,5 +596,9 @@ "add_balance": "Add Balance", "forwards": "forwards", "invalid_email": "Invalid Email", - "invalid_verification_code": "Invalid verification code" + "invalid_verification_code": "Invalid verification code", + "unauthorized": "Un-Authorized access, Please login first then try again", + "page_not_found": "This page was temporary removed, Please check it later", + "server_failure": "Network Error, Please try again later", + "something_went_wrong": "Something went wrong, Please try again later" }