From e78d526bbbc14c352b5a9fca2bc1b5a42b97b48f Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sun, 3 May 2020 15:35:07 +0200 Subject: [PATCH] Show security questions --- lib/helpers/account_helper.dart | 14 ++++- lib/ui/routes/reset_password_route.dart | 79 +++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/lib/helpers/account_helper.dart b/lib/helpers/account_helper.dart index 56e1e91..a4c58e2 100644 --- a/lib/helpers/account_helper.dart +++ b/lib/helpers/account_helper.dart @@ -130,10 +130,20 @@ class AccountHelper { /// Throws in case of failure static Future hasSecurityQuestions(String email) async => (await APIRequest.withoutLogin("account/has_security_questions") - .addString("email", email) - .execWithThrow()) + .addString("email", email) + .execWithThrow()) .getObject()["defined"]; + /// Get the security questions of the user + /// + /// Throws in case of failure + static Future> getSecurityQuestions(String email) async => + ((await APIRequest.withoutLogin("account/get_security_questions") + .addString("email", email) + .execWithThrow()) + .getObject()["questions"]) + .cast(); + /// Get current user ID from the server Future _downloadCurrentUserID() async { final response = await APIRequest( diff --git a/lib/ui/routes/reset_password_route.dart b/lib/ui/routes/reset_password_route.dart index df08435..0946412 100644 --- a/lib/ui/routes/reset_password_route.dart +++ b/lib/ui/routes/reset_password_route.dart @@ -6,6 +6,7 @@ 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:flutter/rendering.dart'; /// Reset password route /// @@ -21,14 +22,14 @@ class ResetPasswordRoute extends StatelessWidget { body: Center( child: ConstrainedBox( constraints: BoxConstraints(maxWidth: 300), - child: _ResetPasswordBody(), + child: SingleChildScrollView(child: _ResetPasswordBody()), ), ), ); } } -enum _SelectedOption { NONE } +enum _SelectedOption { NONE, SECURITY_QUESTIONS } class _ResetPasswordBody extends StatefulWidget { @override @@ -54,6 +55,10 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> { void _setLoading(bool loading) => setState(() => _loading = loading); + /// Step 3b - Answer security questions + List _questions; + var _questionsControllers = List(); + @override Widget build(BuildContext context) { if (_loading) return buildCenteredProgressBar(); @@ -63,6 +68,9 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> { switch (_selectedOption) { case _SelectedOption.NONE: return _buildOptionsScreen(); + + case _SelectedOption.SECURITY_QUESTIONS: + return _buildSecurityQuestionsScreen(); } } @@ -126,13 +134,20 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text(tr("Here are your options to reset your account:")), - Container(height: 15), + _Spacer(), OutlineButton.icon( onPressed: _openSendEmailDialog, icon: Icon(Icons.email), label: Text(tr("Send us an email to ask for help")), ), - Container(height: 15), + _Spacer(visible: _hasSecurityQuestions), + _hasSecurityQuestions + ? OutlineButton.icon( + onPressed: _loadSecurityQuestions, + icon: Icon(Icons.help_outline), + label: Text(tr("Answer your security questions")), + ) + : Container(), ], ); @@ -146,4 +161,60 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> { actions: [CancelDialogButton()], )); } + + /// Load security questions + void _loadSecurityQuestions() async { + _setLoading(true); + try { + _questions = await AccountHelper.getSecurityQuestions(_emailAddress); + + _questionsControllers = + List.generate(_questions.length, (i) => TextEditingController()); + _selectedOption = _SelectedOption.SECURITY_QUESTIONS; + } catch (e, s) { + print("Could not load security questions! $e\n$s"); + showSimpleSnack(context, tr("Could not load your security questions!")); + } + _setLoading(false); + } + + /// Show a screen to prompt security questions of the user + Widget _buildSecurityQuestionsScreen() { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [] + ..add(Text(tr("Please answer now your security questions:"))) + ..add(_Spacer()) + ..addAll(List.generate(_questions.length, _buildSecurityQuestionField)) + ..add(_Spacer()) + ..add(OutlineButton( + onPressed: null, + child: Text(tr("Submit")), + )), + ); + } + + Widget _buildSecurityQuestionField(int id) => TextField( + controller: _questionsControllers[id], + decoration: InputDecoration( + alignLabelWithHint: true, + labelText: _questions[id], + ), + ); +} + +class _Spacer extends StatelessWidget { + final bool visible; + + const _Spacer({Key key, this.visible = true}) + : assert(visible != null), + super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: visible ? 35 : null, + ); + } }