diff --git a/lib/helpers/account_helper.dart b/lib/helpers/account_helper.dart index 4595bbd..f61f8f5 100644 --- a/lib/helpers/account_helper.dart +++ b/lib/helpers/account_helper.dart @@ -3,6 +3,7 @@ import 'package:comunic/helpers/preferences_helper.dart'; import 'package:comunic/models/api_request.dart'; import 'package:comunic/models/authentication_details.dart'; import 'package:comunic/models/login_tokens.dart'; +import 'package:comunic/models/new_account.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Account helper @@ -16,6 +17,13 @@ enum AuthResult { INVALID_CREDENTIALS } +enum CreateAccountResult { + SUCCESS, + ERROR_TOO_MANY_REQUESTS, + ERROR_EXISTING_EMAIL, + ERROR +} + class AccountHelper { static const _USER_ID_PREFERENCE_NAME = "user_id"; @@ -76,6 +84,34 @@ class AccountHelper { _currentUserID = 0; } + /// Create a new user account + Future createAccount(NewAccount info) async { + final response = await APIRequest( + uri: "account/create", + needLogin: false, + args: { + "firstName": info.firstName, + "lastName": info.lastName, + "emailAddress": info.email, + "password": info.password, + }, + ).exec(); + + switch (response.code) { + case 200: + return CreateAccountResult.SUCCESS; + + case 409: + return CreateAccountResult.ERROR_EXISTING_EMAIL; + + case 429: + return CreateAccountResult.ERROR_TOO_MANY_REQUESTS; + + default: + return CreateAccountResult.ERROR; + } + } + /// Get current user ID from the server Future _downloadCurrentUserID() async { final response = await APIRequest( diff --git a/lib/main_dev.dart b/lib/main_dev.dart index 944fc48..817d07f 100644 --- a/lib/main_dev.dart +++ b/lib/main_dev.dart @@ -12,7 +12,7 @@ void main() { apiServerSecure: false, serviceName: "ComunicFlutter", serviceToken: "G9sZCBmb3IgVWJ1bnR1CkNvbW1lbnRbbmVdPeCkieCkrOCkq", - termOfServicesURL: "http://devweb.local/comunic/current/about.php?cgu", + termsOfServicesURL: "http://devweb.local/comunic/current/about.php?cgu", )); subMain(); diff --git a/lib/models/config.dart b/lib/models/config.dart index fdac4af..bd177bb 100644 --- a/lib/models/config.dart +++ b/lib/models/config.dart @@ -11,7 +11,7 @@ class Config { final bool apiServerSecure; final String serviceName; final String serviceToken; - final String termOfServicesURL; + final String termsOfServicesURL; const Config({ @required this.apiServerName, @@ -19,13 +19,13 @@ class Config { @required this.apiServerSecure, @required this.serviceName, @required this.serviceToken, - @required this.termOfServicesURL, + @required this.termsOfServicesURL, }) : assert(apiServerName != null), assert(apiServerUri != null), assert(apiServerSecure != null), assert(serviceName != null), assert(serviceToken != null), - assert(termOfServicesURL != null); + assert(termsOfServicesURL != null); /// Get and set static configuration static Config _config; diff --git a/lib/models/new_account.dart b/lib/models/new_account.dart new file mode 100644 index 0000000..8195633 --- /dev/null +++ b/lib/models/new_account.dart @@ -0,0 +1,22 @@ +import 'package:flutter/widgets.dart'; + +/// New account information container +/// +/// @author Pierre HUBERT + +class NewAccount { + final String firstName; + final String lastName; + final String email; + final String password; + + NewAccount({ + @required this.firstName, + @required this.lastName, + @required this.email, + @required this.password, + }) : assert(firstName != null), + assert(lastName != null), + assert(email != null), + assert(password != null); +} diff --git a/lib/ui/routes/create_account_route.dart b/lib/ui/routes/create_account_route.dart index d9450c3..6fb215b 100644 --- a/lib/ui/routes/create_account_route.dart +++ b/lib/ui/routes/create_account_route.dart @@ -1,6 +1,10 @@ +import 'package:comunic/helpers/account_helper.dart'; import 'package:comunic/models/config.dart'; +import 'package:comunic/models/new_account.dart'; import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -34,6 +38,9 @@ class __CreateAccountRouteBodyState extends State<_CreateAccountRouteBody> { final _verifyPasswordController = TextEditingController(); bool _acceptedTOS = false; + bool _isCreating = false; + CreateAccountResult _createAccountResult; + bool _showErrors = false; bool get _isFirstNameValid => _firstNameController.text.length > 3; @@ -55,12 +62,30 @@ class __CreateAccountRouteBodyState extends State<_CreateAccountRouteBody> { _isPasswordConfirmationValid && _acceptedTOS; + String get errorMessage => _createAccountResult == + CreateAccountResult.ERROR_EXISTING_EMAIL + ? tr("An account is already associated to this email address!") + : _createAccountResult == CreateAccountResult.ERROR_TOO_MANY_REQUESTS + ? tr( + "Too many accounts have been created from this IP address for now. Please try again later.") + : tr( + "An error occured while creating your account. Please try again."); + @override Widget build(BuildContext context) { + if (_isCreating) + return Center( + child: CircularProgressIndicator(), + ); + return Padding( padding: const EdgeInsets.all(8.0), child: ListView( children: [ + _createAccountResult != null + ? buildErrorCard(errorMessage) + : Container(), + // First name _InputEntry( controller: _firstNameController, @@ -119,9 +144,9 @@ class __CreateAccountRouteBodyState extends State<_CreateAccountRouteBody> { : null, ), - // TOSs + // TOS CheckboxListTile( - title: Text("I have read and accepted the Term Of Service."), + title: Text(tr("I have read and accepted the Terms Of Service.")), value: _acceptedTOS, onChanged: (b) { _acceptedTOS = b; @@ -129,7 +154,7 @@ class __CreateAccountRouteBodyState extends State<_CreateAccountRouteBody> { }, subtitle: _showErrors && !_acceptedTOS ? Text( - tr("You must accept the Term Of Service to continue."), + tr("You must accept the Terms Of Service to continue."), style: TextStyle(color: Colors.redAccent), ) : null, @@ -160,16 +185,73 @@ class __CreateAccountRouteBodyState extends State<_CreateAccountRouteBody> { }); } - void _submitForm() { + void _submitForm() async { if (!_isFormValid) { _showErrors = true; _updateUI(); return; } + + setState(() { + _isCreating = true; + }); + + final result = await AccountHelper().createAccount(NewAccount( + firstName: _firstNameController.text, + lastName: _lastNameController.text, + email: _emailController.text, + password: _passwordController.text, + )); + + setState(() { + _isCreating = false; + }); + + if (result != CreateAccountResult.SUCCESS) { + _createAccountResult = result; + _showCreateAccountError(); + _updateUI(); + return; + } + + _accountCreated(); } void _openTOS() { - launch(config().termOfServicesURL); + launch(config().termsOfServicesURL); + } + + void _showCreateAccountError() async { + await showCupertinoDialog( + context: context, + builder: (c) => CupertinoAlertDialog( + title: Text(tr("Error while creating your account")), + content: Text(errorMessage), + actions: [ + CupertinoButton( + child: Text(tr("Ok").toUpperCase()), + onPressed: () => Navigator.of(context).pop(), + ) + ], + ), + ); + } + + void _accountCreated() async { + await showCupertinoDialog( + context: context, + builder: (c) => CupertinoAlertDialog( + title: Text(tr("Account created")), + content: Text(tr( + "Your account has been successfully created. You can now login to start to use it.")), + actions: [ + CupertinoButton( + child: Text(tr("Login")), onPressed: () => Navigator.of(c).pop()) + ], + ), + ); + + Navigator.of(context).pop(); } }