diff --git a/lib/data/repositories/persistence_repository.dart b/lib/data/repositories/persistence_repository.dart index f615e506b..5b6df1ff3 100644 --- a/lib/data/repositories/persistence_repository.dart +++ b/lib/data/repositories/persistence_repository.dart @@ -53,7 +53,7 @@ class PersistenceRepository { Future delete() async { - return await fileStorage.delete(); + return await fileStorage.exisits().then((exists) => exists ? fileStorage.delete() : null); } Future exists() async { diff --git a/lib/main.dart b/lib/main.dart index 98bace2a9..6435a7197 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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_middleware.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/edit/client_edit_vm.dart'; import 'package:invoiceninja/ui/client/view/client_view_vm.dart'; @@ -88,10 +89,13 @@ class _InvoiceNinjaAppState extends State { title: 'Invoice Ninja', routes: { - LoginVM.route: (context) { + InitScreen.route: (context) { widget.store.dispatch(LoadStateRequest(context)); //widget.store.dispatch(LoadUserLogin()); - return LoginVM(); + return InitScreen(); + }, + LoginScreen.route: (context) { + return LoginScreen(); }, DashboardScreen.route: (context) { widget.store.dispatch(LoadDashboardAction()); diff --git a/lib/redux/app/app_middleware.dart b/lib/redux/app/app_middleware.dart index c053ab8c8..e395945ee 100644 --- a/lib/redux/app/app_middleware.dart +++ b/lib/redux/app/app_middleware.dart @@ -140,7 +140,7 @@ Middleware _createLoadState( ..companyState4.replace(company4State) ..companyState5.replace(company5State)); store.dispatch(LoadStateSuccess(appState)); - if (uiState.currentRoute != LoginVM.route && + if (uiState.currentRoute != LoginScreen.route && authState.url.isNotEmpty) { NavigatorState navigator = Navigator.of(action.context); bool isFirst = true; @@ -153,18 +153,18 @@ Middleware _createLoadState( isFirst = false; }); } - }).catchError((error) => _handleError(store, error)); - }).catchError((error) => _handleError(store, error)); - }).catchError((error) => _handleError(store, error)); - }).catchError((error) => _handleError(store, error)); - }).catchError((error) => _handleError(store, error)); - }).catchError((error) => _handleError(store, error)); - }).catchError((error) => _handleError(store, error)); + }).catchError((error) => _handleError(store, error, action.context)); + }).catchError((error) => _handleError(store, error, action.context)); + }).catchError((error) => _handleError(store, error, action.context)); + }).catchError((error) => _handleError(store, error, action.context)); + }).catchError((error) => _handleError(store, error, action.context)); + }).catchError((error) => _handleError(store, error, action.context)); + }).catchError((error) => _handleError(store, error, action.context)); } else { 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); }; @@ -201,10 +201,10 @@ List _getRoutes(AppState state) { return routes; } -_handleError(store, error) { +_handleError(store, error, context) { print(error); store.dispatch(UserLogout()); - store.dispatch(LoadUserLogin()); + store.dispatch(LoadUserLogin(context)); } Middleware _createUserLoggedIn( diff --git a/lib/redux/app/app_reducer.dart b/lib/redux/app/app_reducer.dart index b68c9c34f..7e57c674c 100644 --- a/lib/redux/app/app_reducer.dart +++ b/lib/redux/app/app_reducer.dart @@ -8,7 +8,7 @@ import 'package:invoiceninja/redux/company/company_reducer.dart'; // We create the State reducer by combining many smaller reducers into one! AppState appReducer(AppState state, action) { if (action is UserLogout) { - return AppState(); + return AppState().rebuild((b) => b.authState.replace(state.authState)); } else if (action is LoadStateSuccess) { return action.state.rebuild((b) => b.isLoading = false); } diff --git a/lib/redux/auth/auth_actions.dart b/lib/redux/auth/auth_actions.dart index 39679bbed..515a51292 100644 --- a/lib/redux/auth/auth_actions.dart +++ b/lib/redux/auth/auth_actions.dart @@ -12,7 +12,10 @@ class LoadStateSuccess { LoadStateSuccess(this.state); } -class LoadUserLogin {} +class LoadUserLogin { + final BuildContext context; + LoadUserLogin(this.context); +} class UserLoginLoaded { final String email; @@ -41,5 +44,5 @@ class UserLoginFailure implements StopLoading { UserLoginFailure(this.error); } -class UserLogout {} +class UserLogout implements PersistData {} diff --git a/lib/redux/auth/auth_middleware.dart b/lib/redux/auth/auth_middleware.dart index 50d4838e6..2465f7c77 100644 --- a/lib/redux/auth/auth_middleware.dart +++ b/lib/redux/auth/auth_middleware.dart @@ -1,3 +1,5 @@ +import 'package:flutter/material.dart'; +import 'package:invoiceninja/ui/auth/login_vm.dart'; import 'package:redux/redux.dart'; import 'package:invoiceninja/redux/auth/auth_actions.dart'; import 'package:invoiceninja/redux/app/app_state.dart'; @@ -39,6 +41,7 @@ _loadAuthLocal(Store store, action) async { String secret = prefs.getString('secret'); store.dispatch(UserLoginLoaded(email, password, url, secret)); + Navigator.of(action.context).pushReplacementNamed(LoginScreen.route); } diff --git a/lib/redux/ui/ui_state.dart b/lib/redux/ui/ui_state.dart index 0fb10d07e..4baf6f705 100644 --- a/lib/redux/ui/ui_state.dart +++ b/lib/redux/ui/ui_state.dart @@ -18,7 +18,7 @@ abstract class UIState implements Built { factory UIState() { return _$UIState._( selectedCompanyIndex: 0, - currentRoute: LoginVM.route, + currentRoute: LoginScreen.route, productUIState: ProductUIState(), clientUIState: ClientUIState(), invoiceUIState: InvoiceUIState(), diff --git a/lib/ui/app/app_drawer_vm.dart b/lib/ui/app/app_drawer_vm.dart index a4ebf2dfe..9f19adaf3 100644 --- a/lib/ui/app/app_drawer_vm.dart +++ b/lib/ui/app/app_drawer_vm.dart @@ -56,7 +56,7 @@ class AppDrawerVM { while(Navigator.of(context).canPop()) { Navigator.of(context).pop(); } - Navigator.of(context).pushReplacementNamed(LoginVM.route); + Navigator.of(context).pushReplacementNamed(LoginScreen.route); store.dispatch(UserLogout()); } ); diff --git a/lib/ui/auth/init_screen.dart b/lib/ui/auth/init_screen.dart new file mode 100644 index 000000000..4e36ed646 --- /dev/null +++ b/lib/ui/auth/init_screen.dart @@ -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: [ + Expanded( + child: Container(), + ), + SizedBox( + height: 4.0, + child: LinearProgressIndicator(), + ) + ], + ), + ); + } +} diff --git a/lib/ui/auth/login.dart b/lib/ui/auth/login.dart index 980578927..4ec908e31 100644 --- a/lib/ui/auth/login.dart +++ b/lib/ui/auth/login.dart @@ -6,13 +6,14 @@ import 'package:invoiceninja/ui/app/form_card.dart'; import 'package:invoiceninja/utils/keys.dart'; -class Login extends StatelessWidget { + +class LoginView extends StatefulWidget { final bool isLoading; final bool isDirty; final AuthState authState; final Function(BuildContext, String, String, String, String) onLoginPressed; - Login({ + LoginView({ Key key, @required this.isDirty, @required this.isLoading, @@ -20,23 +21,47 @@ class Login extends StatelessWidget { @required this.onLoginPressed, }) : super(key: key); + @override + _LoginState createState() => new _LoginState(); +} + +class _LoginState extends State { static final GlobalKey _formKey = GlobalKey(); - // add controllers final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _urlController = TextEditingController(); final _secretController = TextEditingController(); - // keys static final ValueKey _emailKey = new Key(LoginKeys.emailKeyString); static final ValueKey _passwordKey = new Key(LoginKeys.passwordKeyString); static final ValueKey _urlKey = new Key(LoginKeys.urlKeyString); 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 Widget build(BuildContext context) { - if (!authState.isInitialized) { + if (!widget.authState.isInitialized) { return Container(); } @@ -98,13 +123,13 @@ class Login extends StatelessWidget { */ obscureText: true, ), - authState.error == null + widget.authState.error == null ? Container() : Container( padding: EdgeInsets.only(top: 26.0, bottom: 4.0), child: Center( child: Text( - authState.error, + widget.authState.error, style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, @@ -116,14 +141,14 @@ class Login extends StatelessWidget { ), ), ProgressButton( - label: 'LOGIN', - isLoading: this.isLoading, - isDirty: this.isDirty, + label: AppLocalization.of(context).login.toUpperCase(), + isLoading: widget.isLoading, + isDirty: widget.isDirty, onPressed: () { if (!_formKey.currentState.validate()) { return; } - this.onLoginPressed( + widget.onLoginPressed( context, _emailController.text, _passwordController.text, diff --git a/lib/ui/auth/login_vm.dart b/lib/ui/auth/login_vm.dart index 871094480..1d3d2e7c8 100644 --- a/lib/ui/auth/login_vm.dart +++ b/lib/ui/auth/login_vm.dart @@ -11,18 +11,18 @@ import 'package:invoiceninja/redux/auth/auth_actions.dart'; import 'package:invoiceninja/ui/auth/login.dart'; import 'package:invoiceninja/redux/auth/auth_state.dart'; -class LoginVM extends StatelessWidget { - LoginVM({Key key}) : super(key: key); +class LoginScreen extends StatelessWidget { + LoginScreen({Key key}) : super(key: key); - static final String route = '/'; + static final String route = '/login'; @override Widget build(BuildContext context) { return Scaffold( - body: StoreConnector( - converter: _ViewModel.fromStore, + body: StoreConnector( + converter: LoginVM.fromStore, builder: (context, vm) { - return Login( + return LoginView( isLoading: vm.isLoading, isDirty: vm.isDirty, authState: vm.authState, @@ -34,21 +34,21 @@ class LoginVM extends StatelessWidget { } } -class _ViewModel { +class LoginVM { bool isLoading; bool isDirty; AuthState authState; final Function(BuildContext, String, String, String, String) onLoginPressed; - _ViewModel({ + LoginVM({ @required this.isLoading, @required this.isDirty, @required this.authState, @required this.onLoginPressed, }); - static _ViewModel fromStore(Store store) { - return _ViewModel( + static LoginVM fromStore(Store store) { + return LoginVM( isDirty: !store.state.authState.isAuthenticated, isLoading: store.state.isLoading, authState: store.state.authState, diff --git a/lib/utils/localization.dart b/lib/utils/localization.dart index 8cb2fc5b6..96604cbd5 100644 --- a/lib/utils/localization.dart +++ b/lib/utils/localization.dart @@ -28,6 +28,7 @@ class AppLocalization { 'secret': 'Secret', 'name': 'Name', 'log_out': 'Log Out', + 'login': 'Login', 'filter': 'Filter', 'sort': 'Sort', 'search': 'Search', @@ -169,6 +170,7 @@ class AppLocalization { String get secret => _localizedValues[locale.languageCode]['secret']; String get name => _localizedValues[locale.languageCode]['name']; String get logOut => _localizedValues[locale.languageCode]['log_out']; + String get login => _localizedValues[locale.languageCode]['login']; String get filter => _localizedValues[locale.languageCode]['filter']; String get sort => _localizedValues[locale.languageCode]['sort']; String get search => _localizedValues[locale.languageCode]['search'];