Connect verify page with the authentication flow

Add Failure handlers to API calls
This commit is contained in:
OmarHatem28 2022-07-13 15:54:05 +02:00
parent 9ab2be35d2
commit 9ee5857912
9 changed files with 117 additions and 34 deletions

28
lib/core/failure.dart Normal file
View file

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

View file

@ -674,7 +674,7 @@ Future setup(
return AddBalancePage(addBalanceViewModel: getIt.get<AddBalanceViewModel>());
});
getIt.registerFactory(() {
getIt.registerLazySingleton(() {
return CakePhoneAuthViewModel(getIt.get<CakePhoneProvider>(), getIt.get<FlutterSecureStorage>());
});

View file

@ -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<bool> authenticate(String email) async {
Future<Either<Failure, bool>> authenticate(String email) async {
try {
final headers = {'Content-Type': 'application/json'};
final body = <String, String>{"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<String, dynamic>;
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<String> verifyEmail({@required String email, @required String code}) async {
Future<Either<Failure, String>> verifyEmail({@required String email, @required String code}) async {
try {
final headers = {'Content-Type': 'application/json'};
final body = <String, String>{
@ -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<String, dynamic>;
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()));
}
}
}

View file

@ -361,7 +361,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.cakePhoneVerification:
return MaterialPageRoute<CakePhoneVerificationPage>(
settings: RouteSettings(name: Routes.cakePhoneVerification),
builder: (_) => CakePhoneVerificationPage(),
builder: (_) => CakePhoneVerificationPage(getIt.get<CakePhoneAuthViewModel>()),
);
case Routes.cakePhoneProducts:

View file

@ -62,7 +62,7 @@ class CakePhoneAuthBodyState extends State<CakePhoneAuthBody> {
if (state is ExecutedSuccessfullyState) {
_authBar?.dismiss();
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushNamed(context, Routes.cakePhoneVerification, arguments: _emailController.text);
Navigator.pushNamed(context, Routes.cakePhoneVerification);
});
}

View file

@ -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<CakePhoneVerificationBody> {
bool disabled = true;
ReactionDisposer _reaction;
Flushbar<void> _authBar;
@override
void initState() {
super.initState();
@ -61,6 +74,35 @@ class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
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<CakePhoneAuthViewModel>(instance: widget.authViewModel);
});
}
if (state is IsExecutingState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_authBar = createBar<void>(S.of(context).authentication, duration: null)..show(context);
});
}
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_authBar?.dismiss();
showBar<void>(context, S.of(context).failed_authentication(state.error));
});
}
});
}
@override
@ -150,11 +192,7 @@ class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
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;

View file

@ -20,28 +20,35 @@ abstract class CakePhoneAuthViewModelBase with Store {
final CakePhoneProvider _cakePhoneProvider;
final FlutterSecureStorage _secureStorage;
bool _userExists = false;
String _email = "";
@action
Future<void> 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<void> verify(String email, String code) async {
final String token = await _cakePhoneProvider.verifyEmail(email: email, code: code);
Future<void> 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);
},
);
}
}

View file

@ -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:

View file

@ -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"
}