import 'package:comunic/helpers/account_helper.dart'; import 'package:comunic/helpers/settings_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/models/security_settings.dart'; import 'package:comunic/ui/dialogs/input_new_password_dialog.dart'; import 'package:comunic/ui/dialogs/input_user_password_dialog.dart'; import 'package:comunic/ui/widgets/dialogs/auto_sized_dialog_content_widget.dart'; import 'package:comunic/ui/widgets/new_password_input_widget.dart'; import 'package:comunic/ui/widgets/settings/header_spacer_section.dart'; import 'package:comunic/utils/account_utils.dart'; import 'package:comunic/utils/flutter_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart'; /// Account security settings /// /// @author Pierre HUBERT class AccountSecuritySettingsScreen extends StatefulWidget { @override _AccountSecuritySettingsScreenState createState() => _AccountSecuritySettingsScreenState(); } class _AccountSecuritySettingsScreenState extends State { @override Widget build(BuildContext context) { return SettingsList( sections: [ HeadSpacerSection(), SettingsSection( title: tr("Password"), tiles: [ SettingsTile( title: tr("Change password"), onPressed: (_) => _changePassword(), ), SettingsTile( title: tr("Change your security questions"), subtitle: isIOS ? null : tr( "Your security questions can be used to recover an access to your account when you loose your password..."), onPressed: (_) => _changeSecurityQuestions(), subtitleMaxLines: 3, ), SettingsTile( title: tr("Disconnect all your devices"), subtitle: isIOS ? null : tr( "Disconnect all your devices from Comunic, including the current one. Use this option if one of the device you use for Comunic was stolen."), onPressed: (_) => _disconnectAllDevices(), subtitleMaxLines: 6, ), ], ) ], ); } /// Change current user password void _changePassword() async { try { final currEmail = await AccountHelper.getCurrentAccountEmailAddress(); final currUser = await UsersHelper().getSingleWithThrow(userID()); final currPassword = await showUserPasswordDialog(context); if (currPassword == null) return; final newPassword = await showInputNewPassword( context: context, userInfo: UserInfoForPassword( firstName: currUser.firstName, lastName: currUser.lastName, email: currEmail, ), ); if (newPassword == null) return; await SettingsHelper.changePassword(currPassword, newPassword); showSimpleSnack( context, tr("Your password has been successfully changed!")!); } catch (e, stack) { print("Could not update current user password! $e\n$stack"); showSimpleSnack(context, tr("Could not update password!")!); } } /// Change security questions void _changeSecurityQuestions() async { try { final password = await showUserPasswordDialog(context); if (password == null) return; final settings = await SettingsHelper.getSecuritySettings(password); final newSettings = await showDialog( context: context, builder: (c) => _SecurityQuestionsDialog(settings: settings)); if (newSettings == null) return; await SettingsHelper.setSecuritySettings(password, newSettings); showSimpleSnack(context, tr("You security questions have been successfully updated!")!); } catch (e, stack) { print("Could not update security questions!$e\n$stack"); showSimpleSnack(context, tr("Could not update security questions!")!); } } /// Disconnect all devices void _disconnectAllDevices() async { try { if (!await showConfirmDialog( context: context, message: tr( "Do you really want to disconnect all your devices from Comunic ?"))) return; await AccountHelper.disconnectAllDevices(); } catch (e, stack) { print("Could not disconnect user on all devices! $e\n$stack"); showSimpleSnack( context, tr("Could not disconnect you from all your devices!")!); } } } class _SecurityQuestionsDialog extends StatefulWidget { final SecuritySettings settings; const _SecurityQuestionsDialog({Key? key, required this.settings}) : super(key: key); @override __SecurityQuestionsDialogState createState() => __SecurityQuestionsDialogState(); } class __SecurityQuestionsDialogState extends State<_SecurityQuestionsDialog> { late TextEditingController _controllerQuestion1; late TextEditingController _controllerAnswer1; late TextEditingController _controllerQuestion2; late TextEditingController _controllerAnswer2; SecuritySettings get _newSettings => SecuritySettings( securityQuestion1: _controllerQuestion1.text, securityAnswer1: _controllerAnswer1.text, securityQuestion2: _controllerQuestion2.text, securityAnswer2: _controllerAnswer2.text, ); @override void initState() { _controllerQuestion1 = TextEditingController(text: widget.settings.securityQuestion1); _controllerAnswer1 = TextEditingController(text: widget.settings.securityAnswer1); _controllerQuestion2 = TextEditingController(text: widget.settings.securityQuestion2); _controllerAnswer2 = TextEditingController(text: widget.settings.securityAnswer2); super.initState(); } @override Widget build(BuildContext context) { return AlertDialog( title: Text(tr("Update security questions")!), content: AutoSizeDialogContentWidget(child: _buildContent()), actions: [ MaterialButton( onPressed: () => Navigator.of(context).pop(), child: Text(tr("Cancel")!.toUpperCase()), ), MaterialButton( onPressed: () => Navigator.of(context).pop(_newSettings), child: Text(tr("Update")!.toUpperCase()), ) ], ); } Widget _buildContent() { return Column( children: [ Text(tr( "Note: Your two questions and answers MUST be completed in order to be able to recover your account using your security questions!")!), _buildTextField( controller: _controllerQuestion1, label: tr("Question 1")), _buildTextField(controller: _controllerAnswer1, label: tr("Answer 1")), _buildTextField( controller: _controllerQuestion2, label: tr("Question 2")), _buildTextField(controller: _controllerAnswer2, label: tr("Answer 2")), ], ); } Widget _buildTextField({ required TextEditingController controller, required String? label, }) { return TextField( controller: controller, onChanged: (s) => setState(() {}), decoration: InputDecoration( alignLabelWithHint: true, labelText: label, errorText: controller.text.isNotEmpty && controller.text.length < 5 ? tr("Unsafe value!") : null, ), ); } }