mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-25 08:39:06 +00:00
Connect verify page with the authentication flow
Add Failure handlers to API calls
This commit is contained in:
parent
9ab2be35d2
commit
9ee5857912
9 changed files with 117 additions and 34 deletions
28
lib/core/failure.dart
Normal file
28
lib/core/failure.dart
Normal 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);
|
||||||
|
}
|
|
@ -674,7 +674,7 @@ Future setup(
|
||||||
return AddBalancePage(addBalanceViewModel: getIt.get<AddBalanceViewModel>());
|
return AddBalancePage(addBalanceViewModel: getIt.get<AddBalanceViewModel>());
|
||||||
});
|
});
|
||||||
|
|
||||||
getIt.registerFactory(() {
|
getIt.registerLazySingleton(() {
|
||||||
return CakePhoneAuthViewModel(getIt.get<CakePhoneProvider>(), getIt.get<FlutterSecureStorage>());
|
return CakePhoneAuthViewModel(getIt.get<CakePhoneProvider>(), getIt.get<FlutterSecureStorage>());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/core/failure.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
class CakePhoneProvider {
|
class CakePhoneProvider {
|
||||||
final _baseUrl = 'cake-phone.cakewallet.com';
|
final _baseUrl = 'cake-phone.cakewallet.com';
|
||||||
|
|
||||||
Future<bool> authenticate(String email) async {
|
Future<Either<Failure, bool>> authenticate(String email) async {
|
||||||
try {
|
try {
|
||||||
final headers = {'Content-Type': 'application/json'};
|
final headers = {'Content-Type': 'application/json'};
|
||||||
final body = <String, String>{"email": email};
|
final body = <String, String>{"email": email};
|
||||||
|
@ -16,17 +18,20 @@ class CakePhoneProvider {
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
debugPrint(response.body);
|
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) {
|
} catch (err) {
|
||||||
debugPrint(err.toString());
|
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 {
|
try {
|
||||||
final headers = {'Content-Type': 'application/json'};
|
final headers = {'Content-Type': 'application/json'};
|
||||||
final body = <String, String>{
|
final body = <String, String>{
|
||||||
|
@ -39,16 +44,16 @@ class CakePhoneProvider {
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
debugPrint(response.body);
|
debugPrint(response.body);
|
||||||
return null;
|
return Left(ServerFailure(response.statusCode, error: response.body));
|
||||||
}
|
}
|
||||||
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final String token = responseJSON['token'] as String;
|
final String token = responseJSON['token'] as String;
|
||||||
|
|
||||||
return token;
|
return Right(token);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint(err.toString());
|
debugPrint(err.toString());
|
||||||
return null;
|
return Left(UnknownFailure(errorMessage: err.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,7 +361,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
case Routes.cakePhoneVerification:
|
case Routes.cakePhoneVerification:
|
||||||
return MaterialPageRoute<CakePhoneVerificationPage>(
|
return MaterialPageRoute<CakePhoneVerificationPage>(
|
||||||
settings: RouteSettings(name: Routes.cakePhoneVerification),
|
settings: RouteSettings(name: Routes.cakePhoneVerification),
|
||||||
builder: (_) => CakePhoneVerificationPage(),
|
builder: (_) => CakePhoneVerificationPage(getIt.get<CakePhoneAuthViewModel>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
case Routes.cakePhoneProducts:
|
case Routes.cakePhoneProducts:
|
||||||
|
|
|
@ -62,7 +62,7 @@ class CakePhoneAuthBodyState extends State<CakePhoneAuthBody> {
|
||||||
if (state is ExecutedSuccessfullyState) {
|
if (state is ExecutedSuccessfullyState) {
|
||||||
_authBar?.dismiss();
|
_authBar?.dismiss();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
Navigator.pushNamed(context, Routes.cakePhoneVerification, arguments: _emailController.text);
|
Navigator.pushNamed(context, Routes.cakePhoneVerification);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import 'dart:async';
|
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/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/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:cake_wallet/routes.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/widgets/primary_button.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/scollable_with_bottom_section.dart';
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
class CakePhoneVerificationPage extends BasePage {
|
class CakePhoneVerificationPage extends BasePage {
|
||||||
CakePhoneVerificationPage();
|
CakePhoneVerificationPage(this.authViewModel);
|
||||||
|
|
||||||
|
final CakePhoneAuthViewModel authViewModel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) => CakePhoneVerificationBody();
|
Widget body(BuildContext context) => CakePhoneVerificationBody(authViewModel);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget middle(BuildContext context) {
|
Widget middle(BuildContext context) {
|
||||||
|
@ -29,7 +37,9 @@ class CakePhoneVerificationPage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CakePhoneVerificationBody extends StatefulWidget {
|
class CakePhoneVerificationBody extends StatefulWidget {
|
||||||
CakePhoneVerificationBody();
|
CakePhoneVerificationBody(this.authViewModel);
|
||||||
|
|
||||||
|
final CakePhoneAuthViewModel authViewModel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CakePhoneVerificationBodyState createState() => CakePhoneVerificationBodyState();
|
CakePhoneVerificationBodyState createState() => CakePhoneVerificationBodyState();
|
||||||
|
@ -46,6 +56,9 @@ class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
|
||||||
|
|
||||||
bool disabled = true;
|
bool disabled = true;
|
||||||
|
|
||||||
|
ReactionDisposer _reaction;
|
||||||
|
Flushbar<void> _authBar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -61,6 +74,35 @@ class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
|
||||||
setState(() {});
|
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
|
@override
|
||||||
|
@ -150,11 +192,7 @@ class CakePhoneVerificationBodyState extends State<CakePhoneVerificationBody> {
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (_formKey.currentState.validate()) {
|
if (_formKey.currentState.validate()) {
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
widget.authViewModel.verify(_codeController.text);
|
||||||
context,
|
|
||||||
Routes.cakePhoneProducts,
|
|
||||||
ModalRoute.withName(Routes.cakePhoneWelcome),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_autoValidate = AutovalidateMode.always;
|
_autoValidate = AutovalidateMode.always;
|
||||||
|
|
|
@ -20,28 +20,35 @@ abstract class CakePhoneAuthViewModelBase with Store {
|
||||||
final CakePhoneProvider _cakePhoneProvider;
|
final CakePhoneProvider _cakePhoneProvider;
|
||||||
final FlutterSecureStorage _secureStorage;
|
final FlutterSecureStorage _secureStorage;
|
||||||
|
|
||||||
|
bool _userExists = false;
|
||||||
|
String _email = "";
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> auth(String email) async {
|
Future<void> auth(String email) async {
|
||||||
state = IsExecutingState();
|
state = IsExecutingState();
|
||||||
|
|
||||||
final isSuccessfullyAuthenticated = await _cakePhoneProvider.authenticate(email);
|
final result = await _cakePhoneProvider.authenticate(email);
|
||||||
|
|
||||||
if (isSuccessfullyAuthenticated) {
|
result.fold(
|
||||||
state = ExecutedSuccessfullyState();
|
(failure) => state = FailureState(failure.errorMessage),
|
||||||
} else {
|
(userExists) {
|
||||||
state = FailureState("");
|
_userExists = userExists;
|
||||||
}
|
_email = email;
|
||||||
|
state = ExecutedSuccessfullyState();
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> verify(String email, String code) async {
|
Future<void> verify(String code) async {
|
||||||
final String token = await _cakePhoneProvider.verifyEmail(email: email, code: code);
|
final result = await _cakePhoneProvider.verifyEmail(email: _email, code: code);
|
||||||
|
|
||||||
if (token != null) {
|
result.fold(
|
||||||
state = ExecutedSuccessfullyState();
|
(failure) => state = FailureState(failure.errorMessage),
|
||||||
await _secureStorage.write(key: PreferencesKey.cakePhoneTokenKey, value: token);
|
(token) {
|
||||||
} else {
|
_secureStorage.write(key: PreferencesKey.cakePhoneTokenKey, value: token);
|
||||||
state = FailureState("");
|
state = ExecutedSuccessfullyState(payload: _userExists);
|
||||||
}
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ dependencies:
|
||||||
permission_handler: ^5.0.1+1
|
permission_handler: ^5.0.1+1
|
||||||
device_display_brightness: ^0.0.6
|
device_display_brightness: ^0.0.6
|
||||||
country_pickers: ^2.0.0
|
country_pickers: ^2.0.0
|
||||||
|
dartz: ^0.10.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -596,5 +596,9 @@
|
||||||
"add_balance": "Add Balance",
|
"add_balance": "Add Balance",
|
||||||
"forwards": "forwards",
|
"forwards": "forwards",
|
||||||
"invalid_email": "Invalid Email",
|
"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"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue