2020-05-01 07:42:01 +00:00
|
|
|
import 'package:comunic/helpers/account_helper.dart';
|
2020-04-30 16:19:01 +00:00
|
|
|
import 'package:comunic/helpers/settings_helper.dart';
|
2021-02-18 17:58:47 +00:00
|
|
|
import 'package:comunic/helpers/users_helper.dart';
|
2020-05-01 07:02:15 +00:00
|
|
|
import 'package:comunic/models/security_settings.dart';
|
2020-04-30 16:19:01 +00:00
|
|
|
import 'package:comunic/ui/dialogs/input_new_password_dialog.dart';
|
2020-04-30 11:32:22 +00:00
|
|
|
import 'package:comunic/ui/dialogs/input_user_password_dialog.dart';
|
2020-05-02 13:32:06 +00:00
|
|
|
import 'package:comunic/ui/widgets/dialogs/auto_sized_dialog_content_widget.dart';
|
2021-02-18 17:58:47 +00:00
|
|
|
import 'package:comunic/ui/widgets/new_password_input_widget.dart';
|
2021-02-12 21:36:22 +00:00
|
|
|
import 'package:comunic/ui/widgets/settings/header_spacer_section.dart';
|
2021-02-18 17:58:47 +00:00
|
|
|
import 'package:comunic/utils/account_utils.dart';
|
2021-04-27 07:44:11 +00:00
|
|
|
import 'package:comunic/utils/flutter_utils.dart';
|
2020-04-29 15:28:47 +00:00
|
|
|
import 'package:comunic/utils/intl_utils.dart';
|
2020-04-30 16:19:01 +00:00
|
|
|
import 'package:comunic/utils/ui_utils.dart';
|
2020-04-29 15:28:47 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2020-04-30 11:32:22 +00:00
|
|
|
import 'package:settings_ui/settings_ui.dart';
|
2020-04-29 15:28:47 +00:00
|
|
|
|
|
|
|
/// Account security settings
|
|
|
|
///
|
|
|
|
/// @author Pierre HUBERT
|
|
|
|
|
2020-05-13 16:12:45 +00:00
|
|
|
class AccountSecuritySettingsScreen extends StatefulWidget {
|
2020-04-29 15:28:47 +00:00
|
|
|
@override
|
2020-05-13 16:12:45 +00:00
|
|
|
_AccountSecuritySettingsScreenState createState() =>
|
|
|
|
_AccountSecuritySettingsScreenState();
|
2020-04-30 11:32:22 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 16:12:45 +00:00
|
|
|
class _AccountSecuritySettingsScreenState
|
|
|
|
extends State<AccountSecuritySettingsScreen> {
|
2020-04-30 11:32:22 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return SettingsList(
|
|
|
|
sections: [
|
2021-02-12 21:36:22 +00:00
|
|
|
HeadSpacerSection(),
|
2020-04-30 11:32:22 +00:00
|
|
|
SettingsSection(
|
|
|
|
title: tr("Password"),
|
|
|
|
tiles: [
|
|
|
|
SettingsTile(
|
|
|
|
title: tr("Change password"),
|
2021-02-12 21:36:22 +00:00
|
|
|
onPressed: (_) => _changePassword(),
|
2020-04-30 11:32:22 +00:00
|
|
|
),
|
2020-05-01 07:02:15 +00:00
|
|
|
SettingsTile(
|
|
|
|
title: tr("Change your security questions"),
|
2021-04-27 07:44:11 +00:00
|
|
|
subtitle: isIOS
|
|
|
|
? null
|
|
|
|
: tr(
|
|
|
|
"Your security questions can be used to recover an access to your account when you loose your password..."),
|
2021-02-12 21:36:22 +00:00
|
|
|
onPressed: (_) => _changeSecurityQuestions(),
|
2021-04-17 06:40:33 +00:00
|
|
|
subtitleMaxLines: 3,
|
2020-05-01 07:02:15 +00:00
|
|
|
),
|
2020-05-01 07:42:01 +00:00
|
|
|
SettingsTile(
|
|
|
|
title: tr("Disconnect all your devices"),
|
2021-04-27 07:44:11 +00:00
|
|
|
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."),
|
2021-02-12 21:36:22 +00:00
|
|
|
onPressed: (_) => _disconnectAllDevices(),
|
2021-04-17 06:40:33 +00:00
|
|
|
subtitleMaxLines: 6,
|
2020-05-01 07:42:01 +00:00
|
|
|
),
|
2020-04-30 11:32:22 +00:00
|
|
|
],
|
|
|
|
)
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Change current user password
|
|
|
|
void _changePassword() async {
|
2020-04-30 16:19:01 +00:00
|
|
|
try {
|
2021-02-18 17:58:47 +00:00
|
|
|
final currEmail = await AccountHelper.getCurrentAccountEmailAddress();
|
|
|
|
final currUser = await UsersHelper().getSingleWithThrow(userID());
|
|
|
|
|
2020-04-30 16:19:01 +00:00
|
|
|
final currPassword = await showUserPasswordDialog(context);
|
|
|
|
|
|
|
|
if (currPassword == null) return;
|
|
|
|
|
2021-02-18 17:58:47 +00:00
|
|
|
final newPassword = await showInputNewPassword(
|
|
|
|
context: context,
|
|
|
|
userInfo: UserInfoForPassword(
|
|
|
|
firstName: currUser.firstName,
|
|
|
|
lastName: currUser.lastName,
|
|
|
|
email: currEmail,
|
|
|
|
),
|
|
|
|
);
|
2020-04-30 16:19:01 +00:00
|
|
|
|
|
|
|
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!"));
|
|
|
|
}
|
2020-04-30 11:32:22 +00:00
|
|
|
}
|
2020-05-01 07:02:15 +00:00
|
|
|
|
|
|
|
/// 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<SecuritySettings>(
|
|
|
|
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!"));
|
|
|
|
}
|
|
|
|
}
|
2020-05-01 07:42:01 +00:00
|
|
|
|
|
|
|
/// 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!"));
|
|
|
|
}
|
|
|
|
}
|
2020-05-01 07:02:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class _SecurityQuestionsDialog extends StatefulWidget {
|
|
|
|
final SecuritySettings settings;
|
|
|
|
|
|
|
|
const _SecurityQuestionsDialog({Key key, @required this.settings})
|
|
|
|
: assert(settings != null),
|
|
|
|
super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
__SecurityQuestionsDialogState createState() =>
|
|
|
|
__SecurityQuestionsDialogState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class __SecurityQuestionsDialogState extends State<_SecurityQuestionsDialog> {
|
|
|
|
TextEditingController _controllerQuestion1;
|
|
|
|
TextEditingController _controllerAnswer1;
|
|
|
|
TextEditingController _controllerQuestion2;
|
|
|
|
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: <Widget>[
|
|
|
|
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: <Widget>[
|
|
|
|
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,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2020-04-30 11:32:22 +00:00
|
|
|
}
|