From 5b255b1745f3d6d33bcb72ef482e28993e4a382c Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 24 May 2018 06:04:06 -0700 Subject: [PATCH] Handle invalid credentails --- lib/data/models/entities.dart | 19 ++++- lib/data/models/entities.g.dart | 30 ++++++- lib/data/web_client.dart | 33 +++++--- lib/redux/auth/auth_actions.dart | 2 +- lib/redux/auth/auth_middleware.dart | 13 ++- lib/ui/auth/login.dart | 121 ++++++++++++++-------------- 6 files changed, 138 insertions(+), 80 deletions(-) diff --git a/lib/data/models/entities.dart b/lib/data/models/entities.dart index 602170862..b0e9d7f68 100644 --- a/lib/data/models/entities.dart +++ b/lib/data/models/entities.dart @@ -2,15 +2,30 @@ import 'package:json_annotation/json_annotation.dart'; part 'entities.g.dart'; + +@JsonSerializable() +class ErrorResponse extends Object with _$ErrorResponseSerializerMixin { + + final String message; + + ErrorResponse( + this.message, + ); + + factory ErrorResponse.fromJson(Map json) => _$ErrorResponseFromJson(json); +} + + @JsonSerializable() class BaseListResponse extends Object with _$BaseListResponseSerializerMixin { - //final String message; @JsonKey(name: "data") final List data; + final ErrorResponse error; BaseListResponse( this.data, + this.error, ); factory BaseListResponse.fromJson(Map json) => _$BaseListResponseFromJson(json); @@ -22,9 +37,11 @@ class BaseItemResponse extends Object with _$BaseItemResponseSerializerMixin { @JsonKey(name: "data") final dynamic data; + final ErrorResponse error; BaseItemResponse( this.data, + this.error, ); factory BaseItemResponse.fromJson(Map json) => _$BaseItemResponseFromJson(json); diff --git a/lib/data/models/entities.g.dart b/lib/data/models/entities.g.dart index f67229781..1c15b2ddf 100644 --- a/lib/data/models/entities.g.dart +++ b/lib/data/models/entities.g.dart @@ -6,20 +6,42 @@ part of 'entities.dart'; // Generator: JsonSerializableGenerator // ************************************************************************** +ErrorResponse _$ErrorResponseFromJson(Map json) => + new ErrorResponse(json['message'] as String); + +abstract class _$ErrorResponseSerializerMixin { + String get message; + Map toJson() => {'message': message}; +} + BaseListResponse _$BaseListResponseFromJson(Map json) => - new BaseListResponse(json['data'] as List); + new BaseListResponse( + json['data'] as List, + json['error'] == null + ? null + : new ErrorResponse.fromJson( + json['error'] as Map)); abstract class _$BaseListResponseSerializerMixin { List get data; - Map toJson() => {'data': data}; + ErrorResponse get error; + Map toJson() => + {'data': data, 'error': error}; } BaseItemResponse _$BaseItemResponseFromJson(Map json) => - new BaseItemResponse(json['data']); + new BaseItemResponse( + json['data'], + json['error'] == null + ? null + : new ErrorResponse.fromJson( + json['error'] as Map)); abstract class _$BaseItemResponseSerializerMixin { dynamic get data; - Map toJson() => {'data': data}; + ErrorResponse get error; + Map toJson() => + {'data': data, 'error': error}; } CompanyEntity _$CompanyEntityFromJson(Map json) => diff --git a/lib/data/web_client.dart b/lib/data/web_client.dart index d3d335af7..de77b79f7 100644 --- a/lib/data/web_client.dart +++ b/lib/data/web_client.dart @@ -35,26 +35,35 @@ class WebClient { Future fetchItem(String url, String token) async { final http.Response response = await sendGetRequest(url, token); - return BaseItemResponse - .fromJson(json.decode(response.body)) - .data; + final result = BaseItemResponse.fromJson(json.decode(response.body)); + + if (result.error.message != null) { + throw(result.error.message); + } else { + return result.data; + } } Future> fetchList(String url, String token) async { final http.Response response = await sendGetRequest(url, token); - return BaseListResponse - .fromJson(json.decode(response.body)) - .data - .toList(); - } + final result = BaseListResponse.fromJson(json.decode(response.body)); + if (result.error.message != null) { + throw(result.error.message); + } else { + return result.data.toList(); + } + } Future> postList(String url, String token, var data) async { final http.Response response = await sendPostRequest(url, token, data); - return BaseListResponse - .fromJson(json.decode(response.body)) - .data - .toList(); + final result = BaseListResponse.fromJson(json.decode(response.body)); + + if (result.error.message != null) { + throw(result.error.message); + } else { + return result.data.toList(); + } } } diff --git a/lib/redux/auth/auth_actions.dart b/lib/redux/auth/auth_actions.dart index c8126e805..dafb05a69 100644 --- a/lib/redux/auth/auth_actions.dart +++ b/lib/redux/auth/auth_actions.dart @@ -4,7 +4,7 @@ import 'package:invoiceninja/redux/app/app_state.dart'; import 'package:invoiceninja/data/models/models.dart'; class UserLoginRequest { - final dynamic context; + final BuildContext context; final String email; final String password; final String url; diff --git a/lib/redux/auth/auth_middleware.dart b/lib/redux/auth/auth_middleware.dart index deae73cfe..143d57d4c 100644 --- a/lib/redux/auth/auth_middleware.dart +++ b/lib/redux/auth/auth_middleware.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:redux/redux.dart'; import 'package:invoiceninja/redux/auth/auth_actions.dart'; @@ -53,8 +54,18 @@ Middleware _createLoginRequest(AuthRepository repository) { 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); }; } + diff --git a/lib/ui/auth/login.dart b/lib/ui/auth/login.dart index 14d9be21e..b0ebfb951 100644 --- a/lib/ui/auth/login.dart +++ b/lib/ui/auth/login.dart @@ -47,69 +47,68 @@ class _LoginScreenState extends State { @override Widget build(BuildContext context) { - return StoreConnector( - converter: (Store store) { - return (BuildContext context, String email, String password, String url) { - store.dispatch(UserLoginRequest(context, email, password, url)); - }; - }, builder: (BuildContext context, loginAction) { - return Scaffold( + return Scaffold( body: Form( - key: _formKey, - child: ListView( - shrinkWrap: true, - padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0), - children: [ - Padding( - padding: EdgeInsets.only(top: 20.0, bottom: 20.0), - child: new Image.asset('assets/images/logo.png', width: 100.0, height: 100.0), - ), - TextFormField( - controller: _emailTextController, - decoration: InputDecoration(labelText: 'Email'), - keyboardType: TextInputType.emailAddress, - validator: (val) => - val.isEmpty ? 'Please enter your email.' : null, - onSaved: (val) => _email = val, - ), - TextFormField( - controller: _passwordTextController, - decoration: InputDecoration(labelText: 'Password'), - validator: (val) => - val.isEmpty ? 'Please enter your password.' : null, - onSaved: (val) => _password = val, - obscureText: true, - ), - TextFormField( - controller: _urlTextController, - decoration: InputDecoration(labelText: 'URL'), - validator: (val) => - val.isEmpty ? 'Please enter your URL.' : null, - onSaved: (val) => _url = val, - ), - Padding( - padding: EdgeInsets.only(top: 20.0), - child: Material( - shadowColor: Colors.lightBlueAccent.shade100, - elevation: 5.0, - child: MaterialButton( - minWidth: 200.0, - height: 42.0, - onPressed: () { - _submit(); - loginAction(context, _email, _password, _url); - //Navigator.of(context).pushNamed(HomeScreen.tag); - }, - color: Colors.lightBlueAccent, - child: - Text('LOGIN', style: TextStyle(color: Colors.white)), - ), + key: _formKey, + child: + StoreConnector(converter: (Store store) { + return (BuildContext context, String email, String password, + String url) { + store.dispatch(UserLoginRequest(context, email, password, url)); + }; + }, builder: (BuildContext context, loginAction) { + return ListView( + shrinkWrap: true, + padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0), + children: [ + Padding( + padding: EdgeInsets.only(top: 20.0, bottom: 20.0), + child: new Image.asset('assets/images/logo.png', + width: 100.0, height: 100.0), + ), + TextFormField( + controller: _emailTextController, + decoration: InputDecoration(labelText: 'Email'), + keyboardType: TextInputType.emailAddress, + validator: (val) => + val.isEmpty ? 'Please enter your email.' : null, + onSaved: (val) => _email = val, + ), + TextFormField( + controller: _passwordTextController, + decoration: InputDecoration(labelText: 'Password'), + validator: (val) => + val.isEmpty ? 'Please enter your password.' : null, + onSaved: (val) => _password = val, + obscureText: true, + ), + TextFormField( + controller: _urlTextController, + decoration: InputDecoration(labelText: 'URL'), + validator: (val) => val.isEmpty ? 'Please enter your URL.' : null, + onSaved: (val) => _url = val, + ), + Padding( + padding: EdgeInsets.only(top: 20.0), + child: Material( + shadowColor: Colors.lightBlueAccent.shade100, + elevation: 5.0, + child: MaterialButton( + minWidth: 200.0, + height: 42.0, + onPressed: () { + _submit(); + loginAction(context, _email, _password, _url); + //Navigator.of(context).pushNamed(HomeScreen.tag); + }, + color: Colors.lightBlueAccent, + child: Text('LOGIN', style: TextStyle(color: Colors.white)), ), ), - ], - ), - ), - ); - }); + ), + ], + ); + }), + )); } }