Create account

This commit is contained in:
Hillel Coren 2019-08-26 12:33:58 +03:00
parent 6e8d419e7f
commit 4bc6f65d11
4 changed files with 225 additions and 168 deletions

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
// This version must be updated in tandem with the pubspec version.
const String kAppVersion = '0.1.47';
const String kSiteUrl = 'https://invoiceninja.com';
const String kAppUrl = 'https://app.invoiceninja.com';
const String kAppUrl = 'https://staging.invoiceninja.com';
const String kPrivacyPolicyURL = 'https://www.invoiceninja.com/privacy-policy';
const String kTermsOfServiceURL = 'https://www.invoiceninja.com/terms';

View File

@ -77,13 +77,18 @@ class UserLogout implements PersistData, PersistUI {}
class UserSignUpRequest implements StartLoading {
UserSignUpRequest({
this.completer,
this.firstName,
this.lastName,
this.email,
this.password,
this.platform,
});
final Completer completer;
final String firstName;
final String lastName;
final String email;
final String password;
final String platform;

View File

@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_state.dart';
import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart';
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
import 'package:invoiceninja_flutter/ui/app/link_text.dart';
import 'package:invoiceninja_flutter/ui/app/progress_button.dart';
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
@ -27,6 +28,8 @@ class LoginView extends StatefulWidget {
class _LoginState extends State<LoginView> {
static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _urlController = TextEditingController();
@ -60,6 +63,8 @@ class _LoginState extends State<LoginView> {
@override
void dispose() {
_firstNameController.dispose();
_lastNameController.dispose();
_emailController.dispose();
_passwordController.dispose();
_urlController.dispose();
@ -68,7 +73,7 @@ class _LoginState extends State<LoginView> {
super.dispose();
}
void _submitLoginForm() {
void _submitSignUpForm() {
final bool isValid = _formKey.currentState.validate();
final localization = AppLocalization.of(context);
@ -103,13 +108,34 @@ class _LoginState extends State<LoginView> {
return;
}
widget.viewModel.onLoginPressed(context,
widget.viewModel.onSignUpPressed(
context,
email: _emailController.text,
password: _passwordController.text,
firstName: _firstNameController.text,
lastName: _lastNameController.text,
);
}
void _submitLoginForm() {
final bool isValid = _formKey.currentState.validate();
setState(() {
_autoValidate = !isValid;
});
if (!isValid) {
return;
}
widget.viewModel.onLoginPressed(
context,
email: _emailController.text,
password: _passwordController.text,
url: _isSelfHosted ? _urlController.text : '',
secret: _isSelfHosted ? _secretController.text : '',
oneTimePassword: _oneTimePasswordController.text,
createAccount: _createAccount);
);
}
@override
@ -161,42 +187,50 @@ class _LoginState extends State<LoginView> {
child: FormCard(
isResponsive: calculateLayout(context) != AppLayout.mobile,
children: <Widget>[
isOneTimePassword
? TextFormField(
if (isOneTimePassword)
TextFormField(
controller: _oneTimePasswordController,
key: ValueKey(localization.oneTimePassword),
autocorrect: false,
decoration: InputDecoration(
labelText: localization.oneTimePassword),
)
: Column(
else
Column(
children: <Widget>[
if (_createAccount)
DecoratedFormField(
label: localization.firstName,
controller: _firstNameController,
),
if (_createAccount)
DecoratedFormField(
label: localization.lastName,
controller: _lastNameController,
),
TextFormField(
controller: _emailController,
key: ValueKey(localization.email),
autocorrect: false,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
labelText: localization.email),
decoration:
InputDecoration(labelText: localization.email),
keyboardType: TextInputType.emailAddress,
autovalidate: _autoValidate,
validator: (val) =>
val.isEmpty || val.trim().isEmpty
validator: (val) => val.isEmpty || val.trim().isEmpty
? localization.pleaseEnterYourEmail
: null,
onFieldSubmitted: (String value) =>
FocusScope.of(context)
.requestFocus(_focusNode1),
FocusScope.of(context).requestFocus(_focusNode1),
),
TextFormField(
controller: _passwordController,
key: ValueKey(localization.password),
autocorrect: false,
autovalidate: _autoValidate,
decoration: InputDecoration(
labelText: localization.password),
validator: (val) =>
val.isEmpty || val.trim().isEmpty
decoration:
InputDecoration(labelText: localization.password),
validator: (val) => val.isEmpty || val.trim().isEmpty
? localization.pleaseEnterYourPassword
: null,
obscureText: true,
@ -210,8 +244,8 @@ class _LoginState extends State<LoginView> {
key: ValueKey(localization.url),
autocorrect: false,
autovalidate: _autoValidate,
decoration: InputDecoration(
labelText: localization.url),
decoration:
InputDecoration(labelText: localization.url),
validator: (val) =>
val.isEmpty || val.trim().isEmpty
? localization.pleaseEnterYourUrl
@ -223,8 +257,8 @@ class _LoginState extends State<LoginView> {
controller: _secretController,
key: ValueKey(localization.secret),
autocorrect: false,
decoration: InputDecoration(
labelText: localization.secret),
decoration:
InputDecoration(labelText: localization.secret),
obscureText: true,
),
if (_createAccount)
@ -237,48 +271,42 @@ class _LoginState extends State<LoginView> {
setState(() => _termsChecked = value),
controlAffinity:
ListTileControlAffinity.leading,
activeColor:
Theme.of(context).accentColor,
activeColor: Theme.of(context).accentColor,
value: _termsChecked,
title: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
style: aboutTextStyle,
text: localization.iAgreeToThe +
' ',
text: localization.iAgreeToThe + ' ',
),
LinkTextSpan(
style: linkStyle,
url: kTermsOfServiceURL,
text: localization
.termsOfServiceLink,
text: localization.termsOfServiceLink,
),
],
),
),
),
CheckboxListTile(
onChanged: (value) => setState(
() => _privacyChecked = value),
onChanged: (value) =>
setState(() => _privacyChecked = value),
controlAffinity:
ListTileControlAffinity.leading,
activeColor:
Theme.of(context).accentColor,
activeColor: Theme.of(context).accentColor,
value: _privacyChecked,
title: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
style: aboutTextStyle,
text: localization.iAgreeToThe +
' ',
text: localization.iAgreeToThe + ' ',
),
LinkTextSpan(
style: linkStyle,
url: kTermsOfServiceURL,
text: localization
.privacyPolicyLink,
text: localization.privacyPolicyLink,
),
],
),
@ -309,7 +337,7 @@ class _LoginState extends State<LoginView> {
? ProgressButton(
isLoading: viewModel.isLoading,
label: localization.signUp.toUpperCase(),
onPressed: () => _submitLoginForm(),
onPressed: () => _submitSignUpForm(),
)
: Row(
children: <Widget>[

View File

@ -39,6 +39,7 @@ class LoginVM {
@required this.isLoading,
@required this.authState,
@required this.onLoginPressed,
@required this.onSignUpPressed,
@required this.onCancel2FAPressed,
@required this.onGoogleLoginPressed,
});
@ -47,13 +48,22 @@ class LoginVM {
AuthState authState;
final Function() onCancel2FAPressed;
final Function(BuildContext,
{@required String email,
final Function(
BuildContext, {
@required String email,
@required String password,
@required String url,
@required String secret,
@required String oneTimePassword,
@required bool createAccount}) onLoginPressed;
}) onLoginPressed;
final Function(
BuildContext, {
@required String firstName,
@required String lastName,
@required String email,
@required String password,
}) onSignUpPressed;
final Function(BuildContext, String, String) onGoogleLoginPressed;
@ -103,13 +113,36 @@ class LoginVM {
print(error);
}
},
onLoginPressed: (BuildContext context,
{@required String email,
onSignUpPressed: (
BuildContext context, {
@required String firstName,
@required String lastName,
@required String email,
@required String password,
}) async {
if (store.state.isLoading) {
return;
}
final Completer<Null> completer = Completer<Null>();
store.dispatch(UserSignUpRequest(
completer: completer,
firstName: firstName.trim(),
lastName: lastName.trim(),
email: email.trim(),
password: password.trim(),
platform: getPlatform(context),
));
completer.future.then((_) => _handleLogin(context));
},
onLoginPressed: (
BuildContext context, {
@required String email,
@required String password,
@required String url,
@required String secret,
@required String oneTimePassword,
@required bool createAccount}) async {
}) async {
if (store.state.isLoading) {
return;
}
@ -119,14 +152,6 @@ class LoginVM {
}
final Completer<Null> completer = Completer<Null>();
if (createAccount) {
store.dispatch(UserSignUpRequest(
completer: completer,
email: email.trim(),
password: password.trim(),
platform: getPlatform(context),
));
} else {
store.dispatch(UserLoginRequest(
completer: completer,
email: email.trim(),
@ -136,7 +161,6 @@ class LoginVM {
platform: getPlatform(context),
oneTimePassword: oneTimePassword.trim(),
));
}
completer.future.then((_) => _handleLogin(context));
});
}