diff --git a/lib/helpers/settings_helper.dart b/lib/helpers/settings_helper.dart index 3774752..de669e4 100644 --- a/lib/helpers/settings_helper.dart +++ b/lib/helpers/settings_helper.dart @@ -1,7 +1,9 @@ import 'dart:io'; +import 'package:comunic/enums/user_page_visibility.dart'; import 'package:comunic/models/account_image_settings.dart'; import 'package:comunic/models/api_request.dart'; +import 'package:comunic/models/general_settings.dart'; /// Settings helper /// @@ -14,6 +16,54 @@ const _APIAccountImageVisibilityAPILevels = { }; class SettingsHelper { + /// Get & return general user settings + static Future getGeneralSettings() async { + final response = + (await APIRequest(uri: "settings/get_general", needLogin: true).exec()) + .assertOk() + .getObject(); + + return GeneralSettings( + email: response["email"], + firstName: response["firstName"], + lastName: response["lastName"], + pageVisibility: response["is_open"] + ? UserPageVisibility.OPEN + : response["is_public"] + ? UserPageVisibility.PUBLIC + : UserPageVisibility.PRIVATE, + allowComments: response["allow_comments"], + allowPostsFromFriends: response["allow_posts_from_friends"], + allowComunicEmails: response["allow_comunic_mails"], + publicFriendsList: response["public_friends_list"], + virtualDirectory: response["virtual_directory"], + personnalWebsite: response["personnal_website"], + publicNote: response["publicNote"], + ); + } + + /// Apply new general settings + static Future updateGeneralSettings(GeneralSettings settings) async { + (await APIRequest(uri: "settings/set_general", needLogin: true, args: { + "firstName": settings.firstName, + "lastName": settings.lastName, + "allow_comunic_mails": settings.allowComunicEmails ? "true" : "false", + "isPublic": settings.pageVisibility == UserPageVisibility.PUBLIC + ? "true" + : "false", + "isOpen": + settings.pageVisibility == UserPageVisibility.OPEN ? "true" : "false", + "allowComments": settings.allowComments ? "true" : "false", + "allowPostsFromFriends": + settings.allowPostsFromFriends ? "true" : "false", + "publicFriendsList": settings.publicFriendsList ? "true" : "false", + "personnalWebsite": settings.personnalWebsite, + "virtualDirectory": settings.virtualDirectory, + "publicNote": settings.publicNote, + }).exec()) + .assertOk(); + } + /// Get & return account image settings static Future getAccountImageSettings() async { final response = diff --git a/lib/models/general_settings.dart b/lib/models/general_settings.dart new file mode 100644 index 0000000..5326810 --- /dev/null +++ b/lib/models/general_settings.dart @@ -0,0 +1,44 @@ +import 'package:comunic/enums/user_page_visibility.dart'; +import 'package:flutter/material.dart'; + +/// General settings +/// +/// @author Pierre Hubert + +class GeneralSettings { + final String email; + String firstName; + String lastName; + final UserPageVisibility pageVisibility; + final bool allowComments; + final bool allowPostsFromFriends; + final bool allowComunicEmails; + final bool publicFriendsList; + final String virtualDirectory; + final String personnalWebsite; + final String publicNote; + + GeneralSettings({ + @required this.email, + @required this.firstName, + @required this.lastName, + @required this.pageVisibility, + @required this.allowComments, + @required this.allowPostsFromFriends, + @required this.allowComunicEmails, + @required this.publicFriendsList, + @required this.virtualDirectory, + @required this.personnalWebsite, + @required this.publicNote, + }) : assert(email != null), + assert(firstName != null), + assert(lastName != null), + assert(pageVisibility != null), + assert(allowComments != null), + assert(allowPostsFromFriends != null), + assert(allowComunicEmails), + assert(publicFriendsList != null), + assert(virtualDirectory != null), + assert(personnalWebsite != null), + assert(publicNote != null); +} diff --git a/lib/ui/dialogs/single_input_dialog.dart b/lib/ui/dialogs/single_input_dialog.dart index 63fd5c5..8359658 100644 --- a/lib/ui/dialogs/single_input_dialog.dart +++ b/lib/ui/dialogs/single_input_dialog.dart @@ -10,6 +10,7 @@ class SingleInputDialog extends StatefulWidget { final String label; final bool Function(String) checkInput; final String errorMessage; + final bool canBeEmpty; const SingleInputDialog({ Key key, @@ -19,8 +20,8 @@ class SingleInputDialog extends StatefulWidget { @required this.label, @required this.checkInput, @required this.errorMessage, + this.canBeEmpty = false, }) : assert(title != null), - assert(icon != null), assert(label != null), assert(checkInput != null), assert(errorMessage != null), @@ -34,7 +35,8 @@ class __InputURLDialogState extends State { TextEditingController _controller; bool get _isValid => - _controller.text.isNotEmpty && widget.checkInput(_controller.text); + (_controller.text.isNotEmpty || widget.canBeEmpty) && + widget.checkInput(_controller.text); @override void initState() { @@ -50,7 +52,7 @@ class __InputURLDialogState extends State { controller: _controller, onChanged: (s) => setState(() {}), decoration: InputDecoration( - icon: Icon(widget.icon), + icon: widget.icon == null ? null : Icon(widget.icon), alignLabelWithHint: true, labelText: widget.label, errorText: _controller.text.isNotEmpty && !_isValid diff --git a/lib/ui/routes/account_settings/account_settings_route.dart b/lib/ui/routes/account_settings/account_settings_route.dart index a35759e..8fbec61 100644 --- a/lib/ui/routes/account_settings/account_settings_route.dart +++ b/lib/ui/routes/account_settings/account_settings_route.dart @@ -1,4 +1,5 @@ import 'package:comunic/ui/routes/account_settings/account_image_settings.dart'; +import 'package:comunic/ui/routes/account_settings/general_account_settings.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:flutter/material.dart'; import 'package:settings_ui/settings_ui.dart'; @@ -32,6 +33,14 @@ class __AccountSettingsBodyState extends State<_AccountSettingsBody> { SettingsSection( title: tr("Account settings"), tiles: [ + + SettingsTile( + title: tr("General settings"), + subtitle: tr("Configure the main settings of your account"), + leading: Icon(Icons.settings), + onTap: () => _openSection(GeneralAccountSettingsScreen()), + ), + SettingsTile( title: tr("Account image"), subtitle: tr("Customize your account image"), diff --git a/lib/ui/routes/account_settings/general_account_settings.dart b/lib/ui/routes/account_settings/general_account_settings.dart new file mode 100644 index 0000000..d399e1c --- /dev/null +++ b/lib/ui/routes/account_settings/general_account_settings.dart @@ -0,0 +1,103 @@ +import 'package:comunic/helpers/settings_helper.dart'; +import 'package:comunic/models/general_settings.dart'; +import 'package:comunic/ui/widgets/async_screen_widget.dart'; +import 'package:comunic/ui/widgets/settings/text_settings_edit_tile.dart'; +import 'package:comunic/utils/account_utils.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:settings_ui/settings_ui.dart'; + +/// General account settings +/// +/// @author Pierre HUBERT + +class GeneralAccountSettingsScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("General settings"), + ), + body: _GeneralAccountSettingsBody(), + ); + } +} + +class _GeneralAccountSettingsBody extends StatefulWidget { + @override + __GeneralAccountSettingsBodyState createState() => + __GeneralAccountSettingsBodyState(); +} + +class __GeneralAccountSettingsBodyState + extends State<_GeneralAccountSettingsBody> { + GeneralSettings _settings; + + final _key = GlobalKey(); + + @override + Widget build(BuildContext context) { + return AsyncScreenWidget( + key: _key, + onReload: () async => + _settings = await SettingsHelper.getGeneralSettings(), + onBuild: _buildSettings, + errorMessage: tr("Could not load general settings!")); + } + + Widget _buildSettings() { + return SettingsList( + sections: [ + SettingsSection( + title: tr("Main account information"), + tiles: _mainInformationTiles(), + ) + ], + ); + } + + List _mainInformationTiles() { + return [ + SettingsTile( + title: tr("User ID"), + subtitle: "${userID()}", + ), + SettingsTile( + title: tr("Email address"), + subtitle: _settings.email, + ), + + // First name + TextEditSettingsTile( + title: tr("First name"), + currValue: _settings.firstName, + onChanged: (s) { + _settings.firstName = s; + _updateSettings(); + }, + ), + + // Last name + TextEditSettingsTile( + title: tr("Last name"), + currValue: _settings.lastName, + onChanged: (s) { + _settings.lastName = s; + _updateSettings(); + }, + ), + ]; + } + + /// Apply new settings + Future _updateSettings() async { + try { + await SettingsHelper.updateGeneralSettings(_settings); + } catch (e, stack) { + print("Error while updating settings! $e/n$stack"); + showSimpleSnack(context, tr("Could not update general settings!")); + } + _key.currentState.refresh(); + } +} diff --git a/lib/ui/widgets/settings/text_settings_edit_tile.dart b/lib/ui/widgets/settings/text_settings_edit_tile.dart new file mode 100644 index 0000000..80664cf --- /dev/null +++ b/lib/ui/widgets/settings/text_settings_edit_tile.dart @@ -0,0 +1,60 @@ +import 'package:comunic/ui/dialogs/single_input_dialog.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:settings_ui/settings_ui.dart'; + +/// Text edit settings tile +/// +/// @author Pierre HUBERT + +bool _defaultCheck(String s) => s.isNotEmpty; + +class TextEditSettingsTile extends SettingsTile { + final String title; + final String currValue; + final void Function(String) onChanged; + final bool Function(String) checkInput; + final bool allowEmptyValues; + + bool get readOnly => onChanged == null; + + const TextEditSettingsTile({ + Key key, + @required this.title, + @required this.currValue, + @required this.onChanged, + this.checkInput = _defaultCheck, + this.allowEmptyValues = false, + }) : assert(title != null), + assert(currValue != null), + assert(checkInput != null), + assert(allowEmptyValues != null); + + @override + Widget build(BuildContext context) { + return SettingsTile( + title: title, + subtitle: currValue, + onTap: readOnly ? null : () => _changeValue(context), + ); + } + + void _changeValue(BuildContext context) async { + final value = await showDialog( + context: context, + builder: (b) => SingleInputDialog( + title: title, + icon: null, + initialValue: currValue, + label: title, + checkInput: checkInput, + errorMessage: tr("Invalid value!"), + canBeEmpty: allowEmptyValues, + ), + ); + + if (value == null) return; + + onChanged(value); + } +}