Handle invalid credentails

This commit is contained in:
unknown 2018-05-24 06:04:06 -07:00
parent 9e5e260eb1
commit 5b255b1745
6 changed files with 138 additions and 80 deletions

View File

@ -2,15 +2,30 @@ import 'package:json_annotation/json_annotation.dart';
part 'entities.g.dart'; part 'entities.g.dart';
@JsonSerializable()
class ErrorResponse extends Object with _$ErrorResponseSerializerMixin {
final String message;
ErrorResponse(
this.message,
);
factory ErrorResponse.fromJson(Map<String, dynamic> json) => _$ErrorResponseFromJson(json);
}
@JsonSerializable() @JsonSerializable()
class BaseListResponse extends Object with _$BaseListResponseSerializerMixin { class BaseListResponse extends Object with _$BaseListResponseSerializerMixin {
//final String message;
@JsonKey(name: "data") @JsonKey(name: "data")
final List<dynamic> data; final List<dynamic> data;
final ErrorResponse error;
BaseListResponse( BaseListResponse(
this.data, this.data,
this.error,
); );
factory BaseListResponse.fromJson(Map<String, dynamic> json) => _$BaseListResponseFromJson(json); factory BaseListResponse.fromJson(Map<String, dynamic> json) => _$BaseListResponseFromJson(json);
@ -22,9 +37,11 @@ class BaseItemResponse extends Object with _$BaseItemResponseSerializerMixin {
@JsonKey(name: "data") @JsonKey(name: "data")
final dynamic data; final dynamic data;
final ErrorResponse error;
BaseItemResponse( BaseItemResponse(
this.data, this.data,
this.error,
); );
factory BaseItemResponse.fromJson(Map<String, dynamic> json) => _$BaseItemResponseFromJson(json); factory BaseItemResponse.fromJson(Map<String, dynamic> json) => _$BaseItemResponseFromJson(json);

View File

@ -6,20 +6,42 @@ part of 'entities.dart';
// Generator: JsonSerializableGenerator // Generator: JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
ErrorResponse _$ErrorResponseFromJson(Map<String, dynamic> json) =>
new ErrorResponse(json['message'] as String);
abstract class _$ErrorResponseSerializerMixin {
String get message;
Map<String, dynamic> toJson() => <String, dynamic>{'message': message};
}
BaseListResponse _$BaseListResponseFromJson(Map<String, dynamic> json) => BaseListResponse _$BaseListResponseFromJson(Map<String, dynamic> json) =>
new BaseListResponse(json['data'] as List); new BaseListResponse(
json['data'] as List,
json['error'] == null
? null
: new ErrorResponse.fromJson(
json['error'] as Map<String, dynamic>));
abstract class _$BaseListResponseSerializerMixin { abstract class _$BaseListResponseSerializerMixin {
List<dynamic> get data; List<dynamic> get data;
Map<String, dynamic> toJson() => <String, dynamic>{'data': data}; ErrorResponse get error;
Map<String, dynamic> toJson() =>
<String, dynamic>{'data': data, 'error': error};
} }
BaseItemResponse _$BaseItemResponseFromJson(Map<String, dynamic> json) => BaseItemResponse _$BaseItemResponseFromJson(Map<String, dynamic> json) =>
new BaseItemResponse(json['data']); new BaseItemResponse(
json['data'],
json['error'] == null
? null
: new ErrorResponse.fromJson(
json['error'] as Map<String, dynamic>));
abstract class _$BaseItemResponseSerializerMixin { abstract class _$BaseItemResponseSerializerMixin {
dynamic get data; dynamic get data;
Map<String, dynamic> toJson() => <String, dynamic>{'data': data}; ErrorResponse get error;
Map<String, dynamic> toJson() =>
<String, dynamic>{'data': data, 'error': error};
} }
CompanyEntity _$CompanyEntityFromJson(Map<String, dynamic> json) => CompanyEntity _$CompanyEntityFromJson(Map<String, dynamic> json) =>

View File

@ -35,26 +35,35 @@ class WebClient {
Future<dynamic> fetchItem(String url, String token) async { Future<dynamic> fetchItem(String url, String token) async {
final http.Response response = await sendGetRequest(url, token); final http.Response response = await sendGetRequest(url, token);
return BaseItemResponse final result = BaseItemResponse.fromJson(json.decode(response.body));
.fromJson(json.decode(response.body))
.data; if (result.error.message != null) {
throw(result.error.message);
} else {
return result.data;
}
} }
Future<List<dynamic>> fetchList(String url, String token) async { Future<List<dynamic>> fetchList(String url, String token) async {
final http.Response response = await sendGetRequest(url, token); final http.Response response = await sendGetRequest(url, token);
return BaseListResponse final result = BaseListResponse.fromJson(json.decode(response.body));
.fromJson(json.decode(response.body))
.data
.toList();
}
if (result.error.message != null) {
throw(result.error.message);
} else {
return result.data.toList();
}
}
Future<List<dynamic>> postList(String url, String token, var data) async { Future<List<dynamic>> postList(String url, String token, var data) async {
final http.Response response = await sendPostRequest(url, token, data); final http.Response response = await sendPostRequest(url, token, data);
return BaseListResponse final result = BaseListResponse.fromJson(json.decode(response.body));
.fromJson(json.decode(response.body))
.data if (result.error.message != null) {
.toList(); throw(result.error.message);
} else {
return result.data.toList();
}
} }
} }

View File

@ -4,7 +4,7 @@ import 'package:invoiceninja/redux/app/app_state.dart';
import 'package:invoiceninja/data/models/models.dart'; import 'package:invoiceninja/data/models/models.dart';
class UserLoginRequest { class UserLoginRequest {
final dynamic context; final BuildContext context;
final String email; final String email;
final String password; final String password;
final String url; final String url;

View File

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:invoiceninja/redux/auth/auth_actions.dart'; import 'package:invoiceninja/redux/auth/auth_actions.dart';
@ -53,8 +54,18 @@ Middleware<AppState> _createLoginRequest(AuthRepository repository) {
Navigator.of(action.context).pushNamed(AppRoutes.dashboard); Navigator.of(action.context).pushNamed(AppRoutes.dashboard);
} }
).catchError((error) => store.dispatch(UserLoginFailure(error))); ).catchError((error) {
store.dispatch(UserLoginFailure(null));
final snackBar = new SnackBar(
duration: Duration(seconds: 3),
content: new Text('Error: ' + error.toString()),
);
Scaffold.of(action.context).showSnackBar(snackBar);
});
next(action); next(action);
}; };
} }

View File

@ -47,69 +47,68 @@ class _LoginScreenState extends State<LoginScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StoreConnector<AppState, dynamic>( return Scaffold(
converter: (Store<AppState> store) {
return (BuildContext context, String email, String password, String url) {
store.dispatch(UserLoginRequest(context, email, password, url));
};
}, builder: (BuildContext context, loginAction) {
return Scaffold(
body: Form( body: Form(
key: _formKey, key: _formKey,
child: ListView( child:
shrinkWrap: true, StoreConnector<AppState, dynamic>(converter: (Store<AppState> store) {
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0), return (BuildContext context, String email, String password,
children: [ String url) {
Padding( store.dispatch(UserLoginRequest(context, email, password, url));
padding: EdgeInsets.only(top: 20.0, bottom: 20.0), };
child: new Image.asset('assets/images/logo.png', width: 100.0, height: 100.0), }, builder: (BuildContext context, loginAction) {
), return ListView(
TextFormField( shrinkWrap: true,
controller: _emailTextController, padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0),
decoration: InputDecoration(labelText: 'Email'), children: [
keyboardType: TextInputType.emailAddress, Padding(
validator: (val) => padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
val.isEmpty ? 'Please enter your email.' : null, child: new Image.asset('assets/images/logo.png',
onSaved: (val) => _email = val, width: 100.0, height: 100.0),
), ),
TextFormField( TextFormField(
controller: _passwordTextController, controller: _emailTextController,
decoration: InputDecoration(labelText: 'Password'), decoration: InputDecoration(labelText: 'Email'),
validator: (val) => keyboardType: TextInputType.emailAddress,
val.isEmpty ? 'Please enter your password.' : null, validator: (val) =>
onSaved: (val) => _password = val, val.isEmpty ? 'Please enter your email.' : null,
obscureText: true, onSaved: (val) => _email = val,
), ),
TextFormField( TextFormField(
controller: _urlTextController, controller: _passwordTextController,
decoration: InputDecoration(labelText: 'URL'), decoration: InputDecoration(labelText: 'Password'),
validator: (val) => validator: (val) =>
val.isEmpty ? 'Please enter your URL.' : null, val.isEmpty ? 'Please enter your password.' : null,
onSaved: (val) => _url = val, onSaved: (val) => _password = val,
), obscureText: true,
Padding( ),
padding: EdgeInsets.only(top: 20.0), TextFormField(
child: Material( controller: _urlTextController,
shadowColor: Colors.lightBlueAccent.shade100, decoration: InputDecoration(labelText: 'URL'),
elevation: 5.0, validator: (val) => val.isEmpty ? 'Please enter your URL.' : null,
child: MaterialButton( onSaved: (val) => _url = val,
minWidth: 200.0, ),
height: 42.0, Padding(
onPressed: () { padding: EdgeInsets.only(top: 20.0),
_submit(); child: Material(
loginAction(context, _email, _password, _url); shadowColor: Colors.lightBlueAccent.shade100,
//Navigator.of(context).pushNamed(HomeScreen.tag); elevation: 5.0,
}, child: MaterialButton(
color: Colors.lightBlueAccent, minWidth: 200.0,
child: height: 42.0,
Text('LOGIN', style: TextStyle(color: Colors.white)), onPressed: () {
), _submit();
loginAction(context, _email, _password, _url);
//Navigator.of(context).pushNamed(HomeScreen.tag);
},
color: Colors.lightBlueAccent,
child: Text('LOGIN', style: TextStyle(color: Colors.white)),
), ),
), ),
], ),
), ],
), );
); }),
}); ));
} }
} }