This commit is contained in:
unknown 2018-06-21 06:46:25 -07:00
parent 66cdde323d
commit 89cf484f67
12 changed files with 102 additions and 41 deletions

View File

@ -53,7 +53,7 @@ class PersistenceRepository {
Future<FileSystemEntity> delete() async { Future<FileSystemEntity> delete() async {
return await fileStorage.delete(); return await fileStorage.exisits().then((exists) => exists ? fileStorage.delete() : null);
} }
Future<bool> exists() async { Future<bool> exists() async {

View File

@ -5,6 +5,7 @@ import 'package:invoiceninja/redux/app/app_middleware.dart';
import 'package:invoiceninja/redux/client/client_actions.dart'; import 'package:invoiceninja/redux/client/client_actions.dart';
import 'package:invoiceninja/redux/client/client_middleware.dart'; import 'package:invoiceninja/redux/client/client_middleware.dart';
import 'package:invoiceninja/redux/invoice/invoice_actions.dart'; import 'package:invoiceninja/redux/invoice/invoice_actions.dart';
import 'package:invoiceninja/ui/auth/init_screen.dart';
import 'package:invoiceninja/ui/client/client_screen.dart'; import 'package:invoiceninja/ui/client/client_screen.dart';
import 'package:invoiceninja/ui/client/edit/client_edit_vm.dart'; import 'package:invoiceninja/ui/client/edit/client_edit_vm.dart';
import 'package:invoiceninja/ui/client/view/client_view_vm.dart'; import 'package:invoiceninja/ui/client/view/client_view_vm.dart';
@ -88,10 +89,13 @@ class _InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
title: 'Invoice Ninja', title: 'Invoice Ninja',
routes: { routes: {
LoginVM.route: (context) { InitScreen.route: (context) {
widget.store.dispatch(LoadStateRequest(context)); widget.store.dispatch(LoadStateRequest(context));
//widget.store.dispatch(LoadUserLogin()); //widget.store.dispatch(LoadUserLogin());
return LoginVM(); return InitScreen();
},
LoginScreen.route: (context) {
return LoginScreen();
}, },
DashboardScreen.route: (context) { DashboardScreen.route: (context) {
widget.store.dispatch(LoadDashboardAction()); widget.store.dispatch(LoadDashboardAction());

View File

@ -140,7 +140,7 @@ Middleware<AppState> _createLoadState(
..companyState4.replace(company4State) ..companyState4.replace(company4State)
..companyState5.replace(company5State)); ..companyState5.replace(company5State));
store.dispatch(LoadStateSuccess(appState)); store.dispatch(LoadStateSuccess(appState));
if (uiState.currentRoute != LoginVM.route && if (uiState.currentRoute != LoginScreen.route &&
authState.url.isNotEmpty) { authState.url.isNotEmpty) {
NavigatorState navigator = Navigator.of(action.context); NavigatorState navigator = Navigator.of(action.context);
bool isFirst = true; bool isFirst = true;
@ -153,18 +153,18 @@ Middleware<AppState> _createLoadState(
isFirst = false; isFirst = false;
}); });
} }
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
} else { } else {
store.dispatch(UserLogout()); store.dispatch(UserLogout());
store.dispatch(LoadUserLogin()); store.dispatch(LoadUserLogin(action.context));
} }
}).catchError((error) => _handleError(store, error)); }).catchError((error) => _handleError(store, error, action.context));
next(action); next(action);
}; };
@ -201,10 +201,10 @@ List<String> _getRoutes(AppState state) {
return routes; return routes;
} }
_handleError(store, error) { _handleError(store, error, context) {
print(error); print(error);
store.dispatch(UserLogout()); store.dispatch(UserLogout());
store.dispatch(LoadUserLogin()); store.dispatch(LoadUserLogin(context));
} }
Middleware<AppState> _createUserLoggedIn( Middleware<AppState> _createUserLoggedIn(

View File

@ -8,7 +8,7 @@ import 'package:invoiceninja/redux/company/company_reducer.dart';
// We create the State reducer by combining many smaller reducers into one! // We create the State reducer by combining many smaller reducers into one!
AppState appReducer(AppState state, action) { AppState appReducer(AppState state, action) {
if (action is UserLogout) { if (action is UserLogout) {
return AppState(); return AppState().rebuild((b) => b.authState.replace(state.authState));
} else if (action is LoadStateSuccess) { } else if (action is LoadStateSuccess) {
return action.state.rebuild((b) => b.isLoading = false); return action.state.rebuild((b) => b.isLoading = false);
} }

View File

@ -12,7 +12,10 @@ class LoadStateSuccess {
LoadStateSuccess(this.state); LoadStateSuccess(this.state);
} }
class LoadUserLogin {} class LoadUserLogin {
final BuildContext context;
LoadUserLogin(this.context);
}
class UserLoginLoaded { class UserLoginLoaded {
final String email; final String email;
@ -41,5 +44,5 @@ class UserLoginFailure implements StopLoading {
UserLoginFailure(this.error); UserLoginFailure(this.error);
} }
class UserLogout {} class UserLogout implements PersistData {}

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:invoiceninja/ui/auth/login_vm.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';
import 'package:invoiceninja/redux/app/app_state.dart'; import 'package:invoiceninja/redux/app/app_state.dart';
@ -39,6 +41,7 @@ _loadAuthLocal(Store<AppState> store, action) async {
String secret = prefs.getString('secret'); String secret = prefs.getString('secret');
store.dispatch(UserLoginLoaded(email, password, url, secret)); store.dispatch(UserLoginLoaded(email, password, url, secret));
Navigator.of(action.context).pushReplacementNamed(LoginScreen.route);
} }

View File

@ -18,7 +18,7 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
factory UIState() { factory UIState() {
return _$UIState._( return _$UIState._(
selectedCompanyIndex: 0, selectedCompanyIndex: 0,
currentRoute: LoginVM.route, currentRoute: LoginScreen.route,
productUIState: ProductUIState(), productUIState: ProductUIState(),
clientUIState: ClientUIState(), clientUIState: ClientUIState(),
invoiceUIState: InvoiceUIState(), invoiceUIState: InvoiceUIState(),

View File

@ -56,7 +56,7 @@ class AppDrawerVM {
while(Navigator.of(context).canPop()) { while(Navigator.of(context).canPop()) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
Navigator.of(context).pushReplacementNamed(LoginVM.route); Navigator.of(context).pushReplacementNamed(LoginScreen.route);
store.dispatch(UserLogout()); store.dispatch(UserLogout());
} }
); );

View File

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
class InitScreen extends StatelessWidget {
static final String route = '/';
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: <Widget>[
Expanded(
child: Container(),
),
SizedBox(
height: 4.0,
child: LinearProgressIndicator(),
)
],
),
);
}
}

View File

@ -6,13 +6,14 @@ import 'package:invoiceninja/ui/app/form_card.dart';
import 'package:invoiceninja/utils/keys.dart'; import 'package:invoiceninja/utils/keys.dart';
class Login extends StatelessWidget {
class LoginView extends StatefulWidget {
final bool isLoading; final bool isLoading;
final bool isDirty; final bool isDirty;
final AuthState authState; final AuthState authState;
final Function(BuildContext, String, String, String, String) onLoginPressed; final Function(BuildContext, String, String, String, String) onLoginPressed;
Login({ LoginView({
Key key, Key key,
@required this.isDirty, @required this.isDirty,
@required this.isLoading, @required this.isLoading,
@ -20,23 +21,47 @@ class Login extends StatelessWidget {
@required this.onLoginPressed, @required this.onLoginPressed,
}) : super(key: key); }) : super(key: key);
@override
_LoginState createState() => new _LoginState();
}
class _LoginState extends State<LoginView> {
static final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
// add controllers
final _emailController = TextEditingController(); final _emailController = TextEditingController();
final _passwordController = TextEditingController(); final _passwordController = TextEditingController();
final _urlController = TextEditingController(); final _urlController = TextEditingController();
final _secretController = TextEditingController(); final _secretController = TextEditingController();
// keys
static final ValueKey _emailKey = new Key(LoginKeys.emailKeyString); static final ValueKey _emailKey = new Key(LoginKeys.emailKeyString);
static final ValueKey _passwordKey = new Key(LoginKeys.passwordKeyString); static final ValueKey _passwordKey = new Key(LoginKeys.passwordKeyString);
static final ValueKey _urlKey = new Key(LoginKeys.urlKeyString); static final ValueKey _urlKey = new Key(LoginKeys.urlKeyString);
static final ValueKey _secretKey = new Key(LoginKeys.secretKeyString); static final ValueKey _secretKey = new Key(LoginKeys.secretKeyString);
@override
void didChangeDependencies() {
_emailController.text = widget.authState.email;
_passwordController.text = widget.authState.password;
_urlController.text = widget.authState.url;
_secretController.text = widget.authState.secret;
super.didChangeDependencies();
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_urlController.dispose();
_secretController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!authState.isInitialized) { if (!widget.authState.isInitialized) {
return Container(); return Container();
} }
@ -98,13 +123,13 @@ class Login extends StatelessWidget {
*/ */
obscureText: true, obscureText: true,
), ),
authState.error == null widget.authState.error == null
? Container() ? Container()
: Container( : Container(
padding: EdgeInsets.only(top: 26.0, bottom: 4.0), padding: EdgeInsets.only(top: 26.0, bottom: 4.0),
child: Center( child: Center(
child: Text( child: Text(
authState.error, widget.authState.error,
style: TextStyle( style: TextStyle(
color: Colors.red, color: Colors.red,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -116,14 +141,14 @@ class Login extends StatelessWidget {
), ),
), ),
ProgressButton( ProgressButton(
label: 'LOGIN', label: AppLocalization.of(context).login.toUpperCase(),
isLoading: this.isLoading, isLoading: widget.isLoading,
isDirty: this.isDirty, isDirty: widget.isDirty,
onPressed: () { onPressed: () {
if (!_formKey.currentState.validate()) { if (!_formKey.currentState.validate()) {
return; return;
} }
this.onLoginPressed( widget.onLoginPressed(
context, context,
_emailController.text, _emailController.text,
_passwordController.text, _passwordController.text,

View File

@ -11,18 +11,18 @@ import 'package:invoiceninja/redux/auth/auth_actions.dart';
import 'package:invoiceninja/ui/auth/login.dart'; import 'package:invoiceninja/ui/auth/login.dart';
import 'package:invoiceninja/redux/auth/auth_state.dart'; import 'package:invoiceninja/redux/auth/auth_state.dart';
class LoginVM extends StatelessWidget { class LoginScreen extends StatelessWidget {
LoginVM({Key key}) : super(key: key); LoginScreen({Key key}) : super(key: key);
static final String route = '/'; static final String route = '/login';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: StoreConnector<AppState, _ViewModel>( body: StoreConnector<AppState, LoginVM>(
converter: _ViewModel.fromStore, converter: LoginVM.fromStore,
builder: (context, vm) { builder: (context, vm) {
return Login( return LoginView(
isLoading: vm.isLoading, isLoading: vm.isLoading,
isDirty: vm.isDirty, isDirty: vm.isDirty,
authState: vm.authState, authState: vm.authState,
@ -34,21 +34,21 @@ class LoginVM extends StatelessWidget {
} }
} }
class _ViewModel { class LoginVM {
bool isLoading; bool isLoading;
bool isDirty; bool isDirty;
AuthState authState; AuthState authState;
final Function(BuildContext, String, String, String, String) onLoginPressed; final Function(BuildContext, String, String, String, String) onLoginPressed;
_ViewModel({ LoginVM({
@required this.isLoading, @required this.isLoading,
@required this.isDirty, @required this.isDirty,
@required this.authState, @required this.authState,
@required this.onLoginPressed, @required this.onLoginPressed,
}); });
static _ViewModel fromStore(Store<AppState> store) { static LoginVM fromStore(Store<AppState> store) {
return _ViewModel( return LoginVM(
isDirty: !store.state.authState.isAuthenticated, isDirty: !store.state.authState.isAuthenticated,
isLoading: store.state.isLoading, isLoading: store.state.isLoading,
authState: store.state.authState, authState: store.state.authState,

View File

@ -28,6 +28,7 @@ class AppLocalization {
'secret': 'Secret', 'secret': 'Secret',
'name': 'Name', 'name': 'Name',
'log_out': 'Log Out', 'log_out': 'Log Out',
'login': 'Login',
'filter': 'Filter', 'filter': 'Filter',
'sort': 'Sort', 'sort': 'Sort',
'search': 'Search', 'search': 'Search',
@ -169,6 +170,7 @@ class AppLocalization {
String get secret => _localizedValues[locale.languageCode]['secret']; String get secret => _localizedValues[locale.languageCode]['secret'];
String get name => _localizedValues[locale.languageCode]['name']; String get name => _localizedValues[locale.languageCode]['name'];
String get logOut => _localizedValues[locale.languageCode]['log_out']; String get logOut => _localizedValues[locale.languageCode]['log_out'];
String get login => _localizedValues[locale.languageCode]['login'];
String get filter => _localizedValues[locale.languageCode]['filter']; String get filter => _localizedValues[locale.languageCode]['filter'];
String get sort => _localizedValues[locale.languageCode]['sort']; String get sort => _localizedValues[locale.languageCode]['sort'];
String get search => _localizedValues[locale.languageCode]['search']; String get search => _localizedValues[locale.languageCode]['search'];