Support token login

This commit is contained in:
Hillel Coren 2021-07-08 09:17:07 +03:00
parent eaa6ffe85d
commit e3a349c811
3 changed files with 163 additions and 116 deletions

View File

@ -37,6 +37,11 @@ class OAuthLoginRequest implements StartLoading {
final String oneTimePassword; final String oneTimePassword;
} }
class TokenLogin {
TokenLogin(this.token);
final String token;
}
class UserLoadUrl { class UserLoadUrl {
UserLoadUrl({this.url}); UserLoadUrl({this.url});

View File

@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/redux/ui/pref_state.dart'; import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
import 'package:invoiceninja_flutter/ui/app/buttons/app_text_button.dart'; import 'package:invoiceninja_flutter/ui/app/buttons/app_text_button.dart';
import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/alert_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/dialogs/alert_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/forms/app_toggle_buttons.dart'; import 'package:invoiceninja_flutter/ui/app/forms/app_toggle_buttons.dart';
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart'; import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
@ -46,6 +47,7 @@ class _LoginState extends State<LoginView> {
final _urlController = TextEditingController(); final _urlController = TextEditingController();
final _secretController = TextEditingController(); final _secretController = TextEditingController();
final _oneTimePasswordController = TextEditingController(); final _oneTimePasswordController = TextEditingController();
final _tokenController = TextEditingController();
final _buttonController = RoundedLoadingButtonController(); final _buttonController = RoundedLoadingButtonController();
@ -53,6 +55,7 @@ class _LoginState extends State<LoginView> {
String _loginError = ''; String _loginError = '';
bool _tokenLogin = false;
bool _emailLogin = false; bool _emailLogin = false;
bool _isSelfHosted = false; bool _isSelfHosted = false;
bool _createAccount = true; bool _createAccount = true;
@ -110,6 +113,8 @@ class _LoginState extends State<LoginView> {
_passwordController.dispose(); _passwordController.dispose();
_urlController.dispose(); _urlController.dispose();
_secretController.dispose(); _secretController.dispose();
_oneTimePasswordController.dispose();
_tokenController.dispose();
super.dispose(); super.dispose();
} }
@ -294,9 +299,34 @@ class _LoginState extends State<LoginView> {
onTap: () { onTap: () {
launch(kSiteUrl, forceSafariVC: false, forceWebView: false); launch(kSiteUrl, forceSafariVC: false, forceWebView: false);
}, },
onLongPress: () {
if (kReleaseMode) {
return;
}
setState(() => _tokenLogin = !_tokenLogin);
},
), ),
), ),
), ),
if (_tokenLogin)
FormCard(
forceNarrow: calculateLayout(context) != AppLayout.mobile,
children: [
DecoratedFormField(
label: localization.token,
controller: _tokenController,
),
AppButton(
label: localization.submit.toUpperCase(),
onPressed: () {
final Completer<Null> completer = Completer<Null>();
viewModel.onTokenLoginPressed(context, completer,
token: _tokenController.text);
},
)
],
),
AnimatedOpacity( AnimatedOpacity(
duration: Duration(milliseconds: 500), duration: Duration(milliseconds: 500),
opacity: viewModel.authState.isAuthenticated ? 0 : 1, opacity: viewModel.authState.isAuthenticated ? 0 : 1,

View File

@ -48,6 +48,7 @@ class LoginVM {
@required this.onSignUpPressed, @required this.onSignUpPressed,
@required this.onGoogleLoginPressed, @required this.onGoogleLoginPressed,
@required this.onGoogleSignUpPressed, @required this.onGoogleSignUpPressed,
@required this.onTokenLoginPressed,
}); });
AppState state; AppState state;
@ -79,6 +80,12 @@ class LoginVM {
@required String password, @required String password,
}) onSignUpPressed; }) onSignUpPressed;
final Function(
BuildContext,
Completer<Null> completer, {
@required String token,
}) onTokenLoginPressed;
final Function(BuildContext, Completer<Null> completer, final Function(BuildContext, Completer<Null> completer,
{String url, String secret, String oneTimePassword}) onGoogleLoginPressed; {String url, String secret, String oneTimePassword}) onGoogleLoginPressed;
final Function(BuildContext, Completer<Null> completer) onGoogleSignUpPressed; final Function(BuildContext, Completer<Null> completer) onGoogleSignUpPressed;
@ -120,132 +127,137 @@ class LoginVM {
} }
return LoginVM( return LoginVM(
state: store.state, state: store.state,
isLoading: store.state.isLoading, isLoading: store.state.isLoading,
authState: store.state.authState, authState: store.state.authState,
onGoogleLoginPressed: ( onGoogleLoginPressed: (
BuildContext context, BuildContext context,
Completer<Null> completer, { Completer<Null> completer, {
@required String url, @required String url,
@required String secret, @required String secret,
@required String oneTimePassword, @required String oneTimePassword,
}) async { }) async {
try { try {
await GoogleOAuth.signOut(); await GoogleOAuth.signOut();
final signedIn = await GoogleOAuth.signIn((idToken, accessToken) { final signedIn = await GoogleOAuth.signIn((idToken, accessToken) {
if (idToken.isEmpty || accessToken.isEmpty) { if (idToken.isEmpty || accessToken.isEmpty) {
GoogleOAuth.signOut(); GoogleOAuth.signOut();
completer.completeError(
AppLocalization.of(context).anErrorOccurredTryAgain);
} else {
store.dispatch(OAuthLoginRequest(
completer: completer,
idToken: idToken,
accessToken: accessToken,
url: _formatApiUrl(url),
secret: secret.trim(),
platform: getPlatform(context),
oneTimePassword: oneTimePassword,
));
completer.future.then((_) => _handleLogin(context: context));
}
});
if (!signedIn) {
completer.completeError( completer.completeError(
AppLocalization.of(context).anErrorOccurredTryAgain); AppLocalization.of(context).anErrorOccurredTryAgain);
} else {
store.dispatch(OAuthLoginRequest(
completer: completer,
idToken: idToken,
accessToken: accessToken,
url: _formatApiUrl(url),
secret: secret.trim(),
platform: getPlatform(context),
oneTimePassword: oneTimePassword,
));
completer.future.then((_) => _handleLogin(context: context));
} }
} catch (error) { });
completer.completeError(error); if (!signedIn) {
print('## onGoogleLoginPressed: $error'); completer.completeError(
AppLocalization.of(context).anErrorOccurredTryAgain);
} }
}, } catch (error) {
onGoogleSignUpPressed: completer.completeError(error);
(BuildContext context, Completer<Null> completer) async { print('## onGoogleLoginPressed: $error');
try { }
await GoogleOAuth.signOut(); },
final signedIn = await GoogleOAuth.signUp((idToken, accessToken) { onGoogleSignUpPressed:
if (idToken.isEmpty || accessToken.isEmpty) { (BuildContext context, Completer<Null> completer) async {
GoogleOAuth.signOut(); try {
completer.completeError( await GoogleOAuth.signOut();
AppLocalization.of(context).anErrorOccurredTryAgain); final signedIn = await GoogleOAuth.signUp((idToken, accessToken) {
} else { if (idToken.isEmpty || accessToken.isEmpty) {
store.dispatch(OAuthSignUpRequest( GoogleOAuth.signOut();
completer: completer,
idToken: idToken,
accessToken: accessToken,
));
completer.future.then(
(_) => _handleLogin(context: context, isSignUp: true));
}
});
if (!signedIn) {
completer.completeError( completer.completeError(
AppLocalization.of(context).anErrorOccurredTryAgain); AppLocalization.of(context).anErrorOccurredTryAgain);
} else {
store.dispatch(OAuthSignUpRequest(
completer: completer,
idToken: idToken,
accessToken: accessToken,
));
completer.future
.then((_) => _handleLogin(context: context, isSignUp: true));
} }
} catch (error) { });
completer.completeError(error); if (!signedIn) {
print('## onGoogleSignUpPressed: $error'); completer.completeError(
} AppLocalization.of(context).anErrorOccurredTryAgain);
},
onSignUpPressed: (
BuildContext context,
Completer<Null> completer, {
@required String email,
@required String password,
}) async {
if (store.state.isLoading) {
return;
} }
} catch (error) {
completer.completeError(error);
print('## onGoogleSignUpPressed: $error');
}
},
onSignUpPressed: (
BuildContext context,
Completer<Null> completer, {
@required String email,
@required String password,
}) async {
if (store.state.isLoading) {
return;
}
store.dispatch(UserSignUpRequest( store.dispatch(UserSignUpRequest(
completer: completer, completer: completer,
email: email.trim(), email: email.trim(),
password: password.trim(), password: password.trim(),
)); ));
completer.future completer.future
.then((_) => _handleLogin(context: context, isSignUp: true)); .then((_) => _handleLogin(context: context, isSignUp: true));
}, },
onRecoverPressed: ( onRecoverPressed: (
BuildContext context, BuildContext context,
Completer<Null> completer, { Completer<Null> completer, {
@required String email, @required String email,
@required String url, @required String url,
@required String secret, @required String secret,
}) async { }) async {
if (store.state.isLoading) { if (store.state.isLoading) {
return; return;
} }
store.dispatch(RecoverPasswordRequest( store.dispatch(RecoverPasswordRequest(
completer: completer, completer: completer,
email: email.trim(), email: email.trim(),
url: _formatApiUrl(url), url: _formatApiUrl(url),
secret: secret.trim(), secret: secret.trim(),
)); ));
}, },
onLoginPressed: ( onLoginPressed: (
BuildContext context, BuildContext context,
Completer<Null> completer, { Completer<Null> completer, {
@required String email, @required String email,
@required String password, @required String password,
@required String url, @required String url,
@required String secret, @required String secret,
@required String oneTimePassword, @required String oneTimePassword,
}) async { }) async {
if (store.state.isLoading) { if (store.state.isLoading) {
return; return;
} }
store.dispatch(UserLoginRequest( store.dispatch(UserLoginRequest(
completer: completer, completer: completer,
email: email.trim(), email: email.trim(),
password: password.trim(), password: password.trim(),
url: _formatApiUrl(url), url: _formatApiUrl(url),
secret: secret.trim(), secret: secret.trim(),
platform: getPlatform(context), platform: getPlatform(context),
oneTimePassword: oneTimePassword.trim(), oneTimePassword: oneTimePassword.trim(),
)); ));
completer.future.then((_) => _handleLogin(context: context)); completer.future.then((_) => _handleLogin(context: context));
}); },
onTokenLoginPressed: (BuildContext context, Completer<Null> completer,
{@required String token}) {
//
},
);
} }
} }