import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:invoiceninja_flutter/.env.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; import 'package:invoiceninja_flutter/redux/company/company_actions.dart'; import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; import 'package:invoiceninja_flutter/ui/auth/login_vm.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:redux/redux.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:invoiceninja_flutter/data/repositories/auth_repository.dart'; List> createStoreAuthMiddleware([ AuthRepository repository = const AuthRepository(), ]) { final userLogout = _createUserLogout(); final loginRequest = _createLoginRequest(repository); final oauthLoginRequest = _createOAuthLoginRequest(repository); final signUpRequest = _createSignUpRequest(repository); final oauthSignUpRequest = _createOAuthSignUpRequest(repository); final refreshRequest = _createRefreshRequest(repository); final recoverRequest = _createRecoverRequest(repository); final addCompany = _createCompany(repository); final deleteCompany = _deleteCompany(repository); final purgeData = _purgeData(repository); return [ TypedMiddleware(userLogout), TypedMiddleware(loginRequest), TypedMiddleware(oauthLoginRequest), TypedMiddleware(signUpRequest), TypedMiddleware(oauthSignUpRequest), TypedMiddleware(refreshRequest), TypedMiddleware(recoverRequest), TypedMiddleware(addCompany), TypedMiddleware(deleteCompany), TypedMiddleware(purgeData), ]; } void _saveAuthLocal( {String email = '', String url = '', String secret = ''}) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString(kSharedPrefEmail, email ?? ''); prefs.setString(kSharedPrefUrl, formatApiUrl(url)); } void _loadAuthLocal(Store store) async { if (kIsWeb) { return; } final SharedPreferences prefs = await SharedPreferences.getInstance(); final String email = kReleaseMode ? (prefs.getString(kSharedPrefEmail) ?? '') : Config.TEST_EMAIL; final String url = formatApiUrl(prefs.getString(kSharedPrefUrl) ?? ''); store.dispatch(UserLoginLoaded(email, url)); } Middleware _createUserLogout() { return (Store store, dynamic dynamicAction, NextDispatcher next) { final action = dynamicAction as UserLogout; next(action); _loadAuthLocal(store); Navigator.of(action.context).pushNamedAndRemoveUntil( LoginScreen.route, (Route route) => false); store.dispatch(UpdateCurrentRoute(LoginScreen.route)); }; } Middleware _createLoginRequest(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) { final action = dynamicAction as UserLoginRequest; repository .login( email: action.email, password: action.password, url: action.url, secret: action.secret, platform: action.platform, oneTimePassword: action.oneTimePassword) .then((data) { _saveAuthLocal( email: action.email, secret: action.secret, url: action.url, ); store.dispatch( LoadAccountSuccess(completer: action.completer, loginResponse: data)); }).catchError((Object error) { print(error); var message = error.toString(); if (message.toLowerCase().contains('no host specified')) { message = 'Please check the URL is correct'; } else if (message.contains('404')) { message += ', you may need to add /public to the URL'; } print('Login error: $message'); if (action.completer != null) { action.completer.completeError(message); } store.dispatch(UserLoginFailure(message)); }); next(action); }; } Middleware _createSignUpRequest(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) { final action = dynamicAction as UserSignUpRequest; repository .signUp( email: action.email, password: action.password, ) .then((data) { _saveAuthLocal(email: action.email, secret: '', url: ''); store.dispatch( LoadAccountSuccess(completer: action.completer, loginResponse: data)); }).catchError((Object error) { print('Signup error: $error'); if (action.completer != null) { action.completer.completeError(error); } store.dispatch(UserLoginFailure(error)); }); next(action); }; } Middleware _createOAuthLoginRequest(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) { final action = dynamicAction as OAuthLoginRequest; repository .oauthLogin( idToken: action.idToken, accessToken: action.accessToken, serverAuthCode: action.serverAuthCode, url: action.url, secret: action.secret, platform: action.platform) .then((data) { _saveAuthLocal( email: action.email, secret: action.secret, url: action.url, ); store.dispatch( LoadAccountSuccess(completer: action.completer, loginResponse: data)); }).catchError((Object error) { print('Oauth login error: $error'); if (action.completer != null) { action.completer.completeError(error); } store.dispatch(UserLoginFailure(error)); }); next(action); }; } Middleware _createOAuthSignUpRequest(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) { final action = dynamicAction as OAuthSignUpRequest; repository .oauthSignUp( accessToken: action.accessToken, idToken: action.idToken, serverAuthCode: action.serverAuthCode, ) .then((data) { _saveAuthLocal(email: '', secret: '', url: ''); store.dispatch( LoadAccountSuccess(completer: action.completer, loginResponse: data)); }).catchError((Object error) { print('OAuth signup error: $error'); if (action.completer != null) { action.completer.completeError(error); } store.dispatch(UserLoginFailure(error)); }); next(action); }; } Middleware _createRefreshRequest(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) async { final action = dynamicAction as RefreshData; _loadAuthLocal(store); String url; String token; final SharedPreferences prefs = await SharedPreferences.getInstance(); url = formatApiUrl(prefs.getString(kSharedPrefUrl) ?? Config.TEST_URL); token = prefs.getString(kSharedPrefToken); repository.refresh(url: url, token: token).then((data) { store.dispatch(LoadAccountSuccess( completer: action.completer, loginResponse: data, loadCompanies: action.loadCompanies)); }).catchError((Object error) { print('Refresh data error: $error'); if (action.completer != null) { action.completer.completeError(error); } store.dispatch(RefreshDataFailure(error)); }); next(action); }; } Middleware _createRecoverRequest(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) { final action = dynamicAction as RecoverPasswordRequest; repository .recoverPassword( email: action.email, url: action.url, secret: action.secret, ) .then((data) { store.dispatch(RecoverPasswordSuccess()); action.completer.complete(null); }).catchError((Object error) { if (action.completer != null) { store.dispatch(RecoverPasswordFailure(error.toString())); action.completer.completeError(error); } }); next(action); }; } Middleware _createCompany(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) async { final action = dynamicAction as AddCompany; final state = store.state; repository.addCompany(credentials: state.credentials).then((dynamic value) { store.dispatch(RefreshData( completer: Completer() ..future.then((_) { store.dispatch(SelectCompany(state.companies.length)); store.dispatch(ViewDashboard( navigator: Navigator.of(action.context), force: true)); store.dispatch(LoadClients()); }), )); }); next(action); }; } Middleware _deleteCompany(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) async { final action = dynamicAction as DeleteCompanyRequest; final state = store.state; repository .deleteCompany( credentials: state.credentials, password: action.password, companyId: state.company.id) .then((dynamic value) { store.dispatch(DeleteCompanySuccess()); action.completer.complete(null); }).catchError((Object error) { store.dispatch(DeleteCompanyFailure(error)); }); next(action); }; } Middleware _purgeData(AuthRepository repository) { return (Store store, dynamic dynamicAction, NextDispatcher next) async { final action = dynamicAction as DeleteCompanyRequest; final state = store.state; repository .purgeData( token: state.credentials.token, password: action.password, companyId: state.company.id) .then((dynamic value) { action.completer.complete(null); store.dispatch(PurgeDataSuccess()); }).catchError((Object error) { store.dispatch(PurgeDataFailure(error)); }); next(action); }; }