1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-10-23 15:03:22 +00:00
comunicmobile/lib/ui/widgets/create_account_widget.dart

294 lines
8.7 KiB
Dart

import 'package:comunic/helpers/account_helper.dart';
import 'package:comunic/helpers/server_config_helper.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';
import 'new_password_input_widget.dart';
/// Create account widget
///
/// @author Pierre Hubert
class CreateAccountWidget extends StatefulWidget {
final void Function() onCreated;
const CreateAccountWidget({Key? key, required this.onCreated})
: super(key: key);
@override
_CreateAccountWidgetState createState() => _CreateAccountWidgetState();
}
class _CreateAccountWidgetState extends State<CreateAccountWidget> {
final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordInputKey = GlobalKey<NewPasswordInputWidgetState>();
final _verifyPasswordController = TextEditingController();
bool? _acceptedTOS = false;
bool _isCreating = false;
CreateAccountResult? _createAccountResult;
bool _showErrors = false;
bool get _isFirstNameValid =>
_firstNameController.text.length >=
srvConfig!.accountInformationPolicy.minFirstNameLength;
bool get _isLastNameValid =>
_lastNameController.text.length >=
srvConfig!.accountInformationPolicy.minLastNameLength;
bool get _isEmailValid => validateEmail(_emailController.text);
bool get _isPasswordValid => _passwordInputKey.currentState!.valid;
bool get _isPasswordConfirmationValid =>
_passwordInputKey.currentState != null &&
_passwordInputKey.currentState!.value == _verifyPasswordController.text;
bool get _isFormValid =>
_isFirstNameValid &&
_isLastNameValid &&
_isEmailValid &&
_isPasswordValid &&
_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 occurred while creating your account. Please try again.");
@override
Widget build(BuildContext context) {
if (_isCreating)
return Center(
child: CircularProgressIndicator(),
);
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 370),
child: ListView(
children: <Widget>[
_createAccountResult != null
? buildErrorCard(errorMessage)
: Container(),
// First name
_InputEntry(
controller: _firstNameController,
label: tr("First name")!,
onEdited: _updateUI,
icon: Icon(Icons.perm_identity),
maxLength:
srvConfig!.accountInformationPolicy.maxFirstNameLength,
error: _showErrors && !_isFirstNameValid
? tr("Invalid first name!")
: null,
),
// Last name
_InputEntry(
controller: _lastNameController,
label: tr("Last name")!,
onEdited: _updateUI,
icon: Icon(Icons.perm_identity),
maxLength: srvConfig!.accountInformationPolicy.maxLastNameLength,
error: _showErrors && !_isLastNameValid
? tr("Invalid last name!")
: null,
),
// Email address
_InputEntry(
controller: _emailController,
label: tr("Email address")!,
onEdited: _updateUI,
icon: Icon(Icons.email),
keyboard: TextInputType.emailAddress,
error: _showErrors && !_isEmailValid
? tr("Invalid email address!")
: null,
),
// Password
NewPasswordInputWidget(
key: _passwordInputKey,
label: tr("Password")!,
onEdited: _updateUI,
icon: Icon(Icons.lock),
user: UserInfoForPassword(
firstName: _firstNameController.text,
lastName: _lastNameController.text,
email: _emailController.text,
),
),
// Verify password
_InputEntry(
controller: _verifyPasswordController,
label: tr("Confirm your password")!,
onEdited: _updateUI,
icon: Icon(Icons.lock_outline),
isPassword: true,
error: _showErrors && !_isPasswordConfirmationValid
? tr("The password and its confirmation do not match!")
: null,
),
SizedBox(height: 10),
// TOS
CheckboxListTile(
title:
Text(tr("I have read and accepted the Terms Of Service.")!),
value: _acceptedTOS,
onChanged: (b) {
_acceptedTOS = b;
_updateUI();
},
subtitle: _showErrors && !_acceptedTOS!
? Text(
tr("You must accept the Terms Of Service to continue.")!,
style: TextStyle(color: Colors.redAccent),
)
: null,
controlAffinity: ListTileControlAffinity.leading,
secondary: IconButton(
icon: Icon(Icons.open_in_new),
onPressed: _openTOS,
),
),
SizedBox(height: 10),
// Submit button
Center(
child: ElevatedButton(
onPressed: _submitForm,
child: Text(tr("Create account")!),
),
),
],
),
),
),
);
}
void _updateUI() {
setState(() {
// Nothing yet
});
}
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: _passwordInputKey.currentState!.value,
));
setState(() {
_isCreating = false;
});
if (result != CreateAccountResult.SUCCESS) {
_createAccountResult = result;
_showCreateAccountError();
_updateUI();
return;
}
widget.onCreated();
}
void _openTOS() => launch(ServerConfigurationHelper.config!.termsURL);
void _showCreateAccountError() async {
await showCupertinoDialog(
context: context,
builder: (c) => CupertinoAlertDialog(
title: Text(tr("Error while creating your account")!),
content: Text(errorMessage!),
actions: <Widget>[
CupertinoButton(
child: Text(tr("Ok")!.toUpperCase()),
onPressed: () => Navigator.of(context).pop(),
)
],
),
);
}
}
class _InputEntry extends StatelessWidget {
final TextEditingController controller;
final String label;
final VoidCallback onEdited;
final bool isPassword;
final String? error;
final Widget? icon;
final TextInputType? keyboard;
final int? maxLength;
const _InputEntry({
Key? key,
required this.controller,
required this.label,
required this.onEdited,
this.isPassword = false,
this.error,
this.icon,
this.keyboard,
this.maxLength,
}) : assert(controller != null),
assert(label != null),
assert(onEdited != null),
assert(isPassword != null),
super(key: key);
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (s) => onEdited(),
keyboardType: keyboard,
obscureText: isPassword,
maxLength: maxLength,
decoration: InputDecoration(
counterText: "",
alignLabelWithHint: true,
errorText: error,
labelText: label,
icon: icon,
),
);
}
}