From 6e274cae11c9336836ac57c05e0dbda095c58895 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sun, 3 May 2020 14:22:06 +0200 Subject: [PATCH] Check email address --- lib/helpers/account_helper.dart | 18 ++++ lib/models/api_request.dart | 6 ++ lib/ui/routes/login_route.dart | 16 ++++ lib/ui/routes/reset_password_route.dart | 111 ++++++++++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 lib/ui/routes/reset_password_route.dart diff --git a/lib/helpers/account_helper.dart b/lib/helpers/account_helper.dart index e3121ba..56e1e91 100644 --- a/lib/helpers/account_helper.dart +++ b/lib/helpers/account_helper.dart @@ -116,6 +116,24 @@ class AccountHelper { } } + /// Check out whether a given email address exists or not + /// + /// Throws in case of failure + static Future existsMailAccount(String email) async => + (await APIRequest.withoutLogin("account/exists_email") + .addString("email", email) + .execWithThrow()) + .getObject()["exists"]; + + /// Check out whether security questions have been set for an account or not + /// + /// Throws in case of failure + static Future hasSecurityQuestions(String email) async => + (await APIRequest.withoutLogin("account/has_security_questions") + .addString("email", email) + .execWithThrow()) + .getObject()["defined"]; + /// Get current user ID from the server Future _downloadCurrentUserID() async { final response = await APIRequest( diff --git a/lib/models/api_request.dart b/lib/models/api_request.dart index c357d31..39dde84 100644 --- a/lib/models/api_request.dart +++ b/lib/models/api_request.dart @@ -42,6 +42,12 @@ class APIRequest { if (args == null) this.args = Map(); } + APIRequest.withoutLogin(this.uri, {this.args}) + : needLogin = false, + assert(uri != null) { + if (args == null) this.args = Map(); + } + APIRequest addString(String name, String value) { args[name] = value; return this; diff --git a/lib/ui/routes/login_route.dart b/lib/ui/routes/login_route.dart index 39e8680..1604185 100644 --- a/lib/ui/routes/login_route.dart +++ b/lib/ui/routes/login_route.dart @@ -1,6 +1,7 @@ import 'package:comunic/helpers/account_helper.dart'; import 'package:comunic/models/authentication_details.dart'; import 'package:comunic/ui/routes/create_account_route.dart'; +import 'package:comunic/ui/routes/reset_password_route.dart'; import 'package:comunic/ui/widgets/init_widget.dart'; import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; @@ -65,6 +66,11 @@ class _LoginRouteState extends State { .push(MaterialPageRoute(builder: (c) => CreateAccountRoute())); } + void _openResetPasswordPage() { + Navigator.of(context) + .push(MaterialPageRoute(builder: (c) => ResetPasswordRoute())); + } + /// Build error card Widget _buildErrorCard() { if (_authResult == null) return null; @@ -134,6 +140,16 @@ class _LoginRouteState extends State { ), onTap: () => _openCreateAccountPage(), ), + + Container(height: 10), + + InkWell( + child: Text( + tr("Password forgotten"), + style: TextStyle(color: Colors.blue), + ), + onTap: _openResetPasswordPage, + ), ], ), ), diff --git a/lib/ui/routes/reset_password_route.dart b/lib/ui/routes/reset_password_route.dart new file mode 100644 index 0000000..c06d550 --- /dev/null +++ b/lib/ui/routes/reset_password_route.dart @@ -0,0 +1,111 @@ +import 'package:comunic/helpers/account_helper.dart'; +import 'package:comunic/ui/widgets/safe_state.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'; + +/// Reset password route +/// +/// @author Pierre Hubert + +class ResetPasswordRoute extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(tr("Password forgotten")), + ), + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 300), + child: _ResetPasswordBody(), + ), + ), + ); + } +} + +class _ResetPasswordBody extends StatefulWidget { + @override + _ResetPasswordBodyState createState() => _ResetPasswordBodyState(); +} + +class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> { + var _loading = false; + + /// Step 1 - check email address + String _emailAddress; + final _emailController = TextEditingController(); + + String get _inputEmail => _emailController.text; + + bool get _isEmailValid => + _inputEmail.isNotEmpty && validateEmail(_inputEmail); + + /// Step 2 - Offer options + bool _hasSecurityQuestions; + + void _setLoading(bool loading) => setState(() => _loading = loading); + + @override + Widget build(BuildContext context) { + if (_loading) return buildCenteredProgressBar(); + + if (_emailAddress == null) return _buildEnterEmailAddressScreen(); + } + + Widget _buildEnterEmailAddressScreen() { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: _emailController, + onChanged: (s) => setState(() {}), + onSubmitted: _isEmailValid ? (s) => _checkEmail() : null, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + icon: Icon(Icons.email), + alignLabelWithHint: true, + labelText: tr("Email address..."), + suffixIcon: IconButton( + icon: Icon(Icons.check), + onPressed: _isEmailValid ? _checkEmail : null, + ), + errorText: _inputEmail.isEmpty || _isEmailValid + ? null + : tr("Invalid email address!"), + ), + ), + ], + ); + } + + /// Check given email address + void _checkEmail() async { + try { + _setLoading(true); + + // Check if email address exists or not + if (!await AccountHelper.existsMailAccount(_inputEmail)) { + _setLoading(false); + showSimpleSnack(context, tr("Specified email address was not found!")); + return; + } + + _hasSecurityQuestions = + await AccountHelper.hasSecurityQuestions(_inputEmail); + + // We retain email address only if everything went well + _emailAddress = _inputEmail; + _setLoading(false); + } catch (e, s) { + print("Could not check given email! $e\n$s"); + showSimpleSnack( + context, tr("An error occurred while checking your options !")); + _setLoading(false); + } + } +}