This commit is contained in:
Hillel Coren 2018-08-20 17:19:42 -07:00
parent 878aea966f
commit 6c7f8902e1
8 changed files with 109 additions and 65 deletions

View File

@ -18,12 +18,14 @@ class AuthRepository {
String password, String password,
String url, String url,
String secret, String secret,
String platform}) async { String platform,
String oneTimePassword}) async {
final credentials = { final credentials = {
'token_name': 'invoice-ninja-$platform-app', 'token_name': 'invoice-ninja-$platform-app',
'api_secret': secret, 'api_secret': secret,
'email': email, 'email': email,
'password': password, 'password': password,
'one_time_password': oneTimePassword,
}; };
url = formatApiUrlMachine(url) + '/login'; url = formatApiUrlMachine(url) + '/login';

View File

@ -5,15 +5,19 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart';
class LoadStateRequest { class LoadStateRequest {
final BuildContext context; final BuildContext context;
LoadStateRequest(this.context); LoadStateRequest(this.context);
} }
class LoadStateSuccess { class LoadStateSuccess {
final AppState state; final AppState state;
LoadStateSuccess(this.state); LoadStateSuccess(this.state);
} }
class LoadUserLogin { class LoadUserLogin {
final BuildContext context; final BuildContext context;
LoadUserLogin(this.context); LoadUserLogin(this.context);
} }
@ -31,7 +35,9 @@ class OAuthLoginRequest implements StartLoading {
final String url; final String url;
final String secret; final String secret;
final String platform; final String platform;
OAuthLoginRequest({this.completer, this.token, this.url, this.secret, this.platform});
OAuthLoginRequest(
{this.completer, this.token, this.url, this.secret, this.platform});
} }
class UserLoginRequest implements StartLoading { class UserLoginRequest implements StartLoading {
@ -41,8 +47,16 @@ class UserLoginRequest implements StartLoading {
final String url; final String url;
final String secret; final String secret;
final String platform; final String platform;
final String oneTimePassword;
UserLoginRequest({this.completer, this.email, this.password, this.url, this.secret, this.platform}); UserLoginRequest(
{this.completer,
this.email,
this.password,
this.url,
this.secret,
this.platform,
this.oneTimePassword});
} }
class UserLoginSuccess implements StopLoading {} class UserLoginSuccess implements StopLoading {}
@ -54,4 +68,3 @@ class UserLoginFailure implements StopLoading {
} }
class UserLogout implements PersistData {} class UserLogout implements PersistData {}

View File

@ -63,7 +63,8 @@ Middleware<AppState> _createLoginRequest(AuthRepository repository) {
password: action.password, password: action.password,
url: action.url, url: action.url,
secret: action.secret, secret: action.secret,
platform: action.platform) platform: action.platform,
oneTimePassword: action.oneTimePassword)
.then((data) { .then((data) {
_saveAuthLocal(action); _saveAuthLocal(action);

View File

@ -26,15 +26,16 @@ class _LoginState extends State<LoginView> {
final _passwordController = TextEditingController(); final _passwordController = TextEditingController();
final _urlController = TextEditingController(); final _urlController = TextEditingController();
final _secretController = TextEditingController(); final _secretController = TextEditingController();
final _oneTimePasswordController = TextEditingController();
static final ValueKey _emailKey = Key(LoginKeys.emailKeyString); static final ValueKey _emailKey = Key(LoginKeys.emailKeyString);
static final ValueKey _passwordKey = Key(LoginKeys.passwordKeyString); static final ValueKey _passwordKey = Key(LoginKeys.passwordKeyString);
static final ValueKey _urlKey = Key(LoginKeys.urlKeyString); static final ValueKey _urlKey = Key(LoginKeys.urlKeyString);
static final ValueKey _secretKey = Key(LoginKeys.secretKeyString); static final ValueKey _secretKey = Key(LoginKeys.secretKeyString);
static final ValueKey _oneTimePasswordKey =
Key(LoginKeys.oneTimePasswordKeyString);
FocusNode focusNode1 = new FocusNode(); FocusNode focusNode1 = new FocusNode();
FocusNode focusNode2 = new FocusNode();
FocusNode focusNode3 = new FocusNode();
@override @override
void didChangeDependencies() { void didChangeDependencies() {
@ -61,6 +62,7 @@ class _LoginState extends State<LoginView> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final viewModel = widget.viewModel; final viewModel = widget.viewModel;
final error = viewModel.authState.error;
if (!viewModel.authState.isInitialized) { if (!viewModel.authState.isInitialized) {
return Container(); return Container();
@ -77,12 +79,24 @@ class _LoginState extends State<LoginView> {
Form( Form(
key: _formKey, key: _formKey,
child: FormCard( child: FormCard(
children: <Widget>[
(error != null && error.contains('2FA')) ||
_oneTimePasswordController.text.isNotEmpty
? TextFormField(
controller: _oneTimePasswordController,
key: _oneTimePasswordKey,
autocorrect: false,
decoration: InputDecoration(
labelText: localization.oneTimePassword),
)
: Column(
children: <Widget>[ children: <Widget>[
TextFormField( TextFormField(
controller: _emailController, controller: _emailController,
key: _emailKey, key: _emailKey,
autocorrect: false, autocorrect: false,
decoration: InputDecoration(labelText: localization.email), decoration:
InputDecoration(labelText: localization.email),
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
validator: (val) => val.isEmpty || val.trim().isEmpty validator: (val) => val.isEmpty || val.trim().isEmpty
? localization.pleaseEnterYourEmail ? localization.pleaseEnterYourEmail
@ -94,37 +108,36 @@ class _LoginState extends State<LoginView> {
controller: _passwordController, controller: _passwordController,
key: _passwordKey, key: _passwordKey,
autocorrect: false, autocorrect: false,
decoration: InputDecoration(labelText: localization.password), decoration:
InputDecoration(labelText: localization.password),
validator: (val) => val.isEmpty || val.trim().isEmpty validator: (val) => val.isEmpty || val.trim().isEmpty
? localization.pleaseEnterYourPassword ? localization.pleaseEnterYourPassword
: null, : null,
obscureText: true, obscureText: true,
focusNode: focusNode1, focusNode: focusNode1,
onFieldSubmitted: (String value) =>
FocusScope.of(context).requestFocus(focusNode2),
), ),
TextFormField( TextFormField(
controller: _urlController, controller: _urlController,
key: _urlKey, key: _urlKey,
autocorrect: false, autocorrect: false,
decoration: InputDecoration(labelText: localization.url), decoration:
InputDecoration(labelText: localization.url),
validator: (val) => val.isEmpty || val.trim().isEmpty validator: (val) => val.isEmpty || val.trim().isEmpty
? localization.pleaseEnterYourUrl ? localization.pleaseEnterYourUrl
: null, : null,
keyboardType: TextInputType.url, keyboardType: TextInputType.url,
focusNode: focusNode2,
onFieldSubmitted: (String value) =>
FocusScope.of(context).requestFocus(focusNode3),
), ),
TextFormField( TextFormField(
controller: _secretController, controller: _secretController,
key: _secretKey, key: _secretKey,
autocorrect: false, autocorrect: false,
decoration: InputDecoration(labelText: localization.secret), decoration:
InputDecoration(labelText: localization.secret),
obscureText: true, obscureText: true,
focusNode: focusNode3,
), ),
viewModel.authState.error == null ],
),
viewModel.authState.error == null || error.contains('2FA')
? Container() ? Container()
: Container( : Container(
padding: EdgeInsets.only(top: 26.0, bottom: 4.0), padding: EdgeInsets.only(top: 26.0, bottom: 4.0),
@ -148,12 +161,12 @@ class _LoginState extends State<LoginView> {
if (!_formKey.currentState.validate()) { if (!_formKey.currentState.validate()) {
return; return;
} }
viewModel.onLoginPressed( viewModel.onLoginPressed(context,
context, email: _emailController.text,
_emailController.text, password: _passwordController.text,
_passwordController.text, url: _urlController.text,
_urlController.text, secret: _secretController.text,
_secretController.text); oneTimePassword: _oneTimePasswordController.text);
}, },
), ),
/* /*

View File

@ -9,7 +9,7 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
import 'package:invoiceninja_flutter/ui/auth/login.dart'; import 'package:invoiceninja_flutter/ui/auth/login_view.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_state.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_state.dart';
import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in/google_sign_in.dart';
@ -36,7 +36,12 @@ class LoginScreen extends StatelessWidget {
class LoginVM { class LoginVM {
bool isLoading; bool isLoading;
AuthState authState; AuthState authState;
final Function(BuildContext, String, String, String, String) onLoginPressed; final Function(BuildContext,
{String email,
String password,
String url,
String secret,
String oneTimePassword}) onLoginPressed;
final Function(BuildContext, String, String) onGoogleLoginPressed; final Function(BuildContext, String, String) onGoogleLoginPressed;
LoginVM({ LoginVM({
@ -83,8 +88,12 @@ class LoginVM {
print(error); print(error);
} }
}, },
onLoginPressed: (BuildContext context, String email, String password, onLoginPressed: (BuildContext context,
String url, String secret) async { {String email,
String password,
String url,
String secret,
String oneTimePassword}) async {
if (store.state.isLoading) { if (store.state.isLoading) {
return; return;
} }
@ -97,6 +106,7 @@ class LoginVM {
url: url.trim(), url: url.trim(),
secret: secret.trim(), secret: secret.trim(),
platform: getPlatform(context), platform: getPlatform(context),
oneTimePassword: oneTimePassword.trim(),
)); ));
completer.future.then((_) => _handleLogin(context)); completer.future.then((_) => _handleLogin(context));
}); });

View File

@ -4,6 +4,7 @@ class LoginKeys {
static const String passwordKeyString = 'loginPassword'; static const String passwordKeyString = 'loginPassword';
static const String urlKeyString = 'loginUrl'; static const String urlKeyString = 'loginUrl';
static const String secretKeyString = 'loginSecret'; static const String secretKeyString = 'loginSecret';
static const String oneTimePasswordKeyString = 'loginOneTimePassword';
} }
// Keys for Product Screen // Keys for Product Screen

View File

@ -262,6 +262,7 @@ class AppLocalization {
'activity_45': ':user deleted task :task', 'activity_45': ':user deleted task :task',
'activity_46': ':user restored task :task', 'activity_46': ':user restored task :task',
'activity_47': ':user updated expense :expense', 'activity_47': ':user updated expense :expense',
'one_time_password': 'One Time Password',
}, },
'sq': { 'sq': {
'billing_address': 'Adresa e faturimit', 'billing_address': 'Adresa e faturimit',
@ -7658,6 +7659,9 @@ class AppLocalization {
String get activity_47 => String get activity_47 =>
_localizedValues[locale.languageCode]['activity_47']; _localizedValues[locale.languageCode]['activity_47'];
String get oneTimePassword =>
_localizedValues[locale.languageCode]['one_time_password'];
String lookup(String key) { String lookup(String key) {
return _localizedValues[locale.languageCode][toSnakeCase(key)] ?? key; return _localizedValues[locale.languageCode][toSnakeCase(key)] ?? key;
} }

View File

@ -1,6 +1,6 @@
import 'package:flutter_driver/flutter_driver.dart'; import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
//import 'package:invoiceninja_flutter/ui/auth/login.dart'; //import 'package:invoiceninja_flutter/ui/auth/login_view.dart';
void main() { void main() {
group('scrolling performance test', () { group('scrolling performance test', () {