diff --git a/repertory/repertory/include/ui/handlers.hpp b/repertory/repertory/include/ui/handlers.hpp index 4ef92302..2cf091b5 100644 --- a/repertory/repertory/include/ui/handlers.hpp +++ b/repertory/repertory/include/ui/handlers.hpp @@ -63,6 +63,8 @@ private: void handle_get_mount_status(auto &&req, auto &&res) const; + void handle_get_settings(auto &&res) const; + void handle_post_add_mount(auto &&req, auto &&res) const; void handle_post_mount(auto &&req, auto &&res) const; diff --git a/repertory/repertory/include/ui/mgmt_app_config.hpp b/repertory/repertory/include/ui/mgmt_app_config.hpp index 84dd4748..48e8886a 100644 --- a/repertory/repertory/include/ui/mgmt_app_config.hpp +++ b/repertory/repertory/include/ui/mgmt_app_config.hpp @@ -42,6 +42,8 @@ private: void save() const; public: + [[nodiscard]] auto to_json() const -> nlohmann::json; + [[nodiscard]] auto get_api_password() const -> std::string { return api_password_; } diff --git a/repertory/repertory/src/ui/handlers.cpp b/repertory/repertory/src/ui/handlers.cpp index 004ec94a..a4d7a593 100644 --- a/repertory/repertory/src/ui/handlers.cpp +++ b/repertory/repertory/src/ui/handlers.cpp @@ -102,6 +102,11 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server) handle_get_mount_status(req, res); }); + server->Get("/api/v1/settings", + [this](const httplib::Request & /* req */, auto &&res) { + handle_get_settings(res); + }); + server->Post("/api/v1/add_mount", [this](auto &&req, auto &&res) { handle_post_add_mount(req, res); }); @@ -307,6 +312,11 @@ void handlers::handle_get_mount_status(auto &&req, auto &&res) const { res.status = http_error_codes::ok; } +void handlers::handle_get_settings(auto &&res) const { + res.set_content(config_->to_json().dump(), "application/json"); + res.status = http_error_codes::ok; +} + void handlers::handle_post_add_mount(auto &&req, auto &&res) const { auto name = req.get_param_value("name"); auto prov = provider_type_from_string(req.get_param_value("type")); diff --git a/repertory/repertory/src/ui/mgmt_app_config.cpp b/repertory/repertory/src/ui/mgmt_app_config.cpp index 81aab7db..21bc842a 100644 --- a/repertory/repertory/src/ui/mgmt_app_config.cpp +++ b/repertory/repertory/src/ui/mgmt_app_config.cpp @@ -59,7 +59,7 @@ namespace { return map_of_maps; } -[[nodiscard]] auto to_json(const auto &map_of_maps) -> nlohmann::json { +[[nodiscard]] auto map_to_json(const auto &map_of_maps) -> nlohmann::json { auto json = nlohmann::json::object(); for (const auto &[prov, map] : map_of_maps) { for (const auto &[key, value] : map) { @@ -134,12 +134,7 @@ void mgmt_app_config::save() const { return; } - nlohmann::json data; - data[JSON_API_PASSWORD] = api_password_; - data[JSON_API_PORT] = api_port_; - data[JSON_API_USER] = api_user_; - data[JSON_MOUNT_LOCATIONS] = to_json(locations_); - if (utils::file::write_json_file(config_file, data)) { + if (utils::file::write_json_file(config_file, to_json())) { return; } @@ -181,4 +176,13 @@ void mgmt_app_config::set_mount_location(provider_type prov, save(); } + +auto mgmt_app_config::to_json() const -> nlohmann::json { + nlohmann::json data; + data[JSON_API_PASSWORD] = api_password_; + data[JSON_API_PORT] = api_port_; + data[JSON_API_USER] = api_user_; + data[JSON_MOUNT_LOCATIONS] = map_to_json(locations_); + return data; +} } // namespace repertory::ui diff --git a/web/repertory/lib/helpers.dart b/web/repertory/lib/helpers.dart index 7dc36be0..090a62c4 100644 --- a/web/repertory/lib/helpers.dart +++ b/web/repertory/lib/helpers.dart @@ -125,7 +125,7 @@ String getBaseUri() { String? getSettingDescription(String settingPath) { switch (settingPath) { case 'ApiPassword': - return "'repertory' REST API password"; + return "HTTP basic authentication password"; case 'HostConfig.ApiPassword': return "RENTERD_API_PASSWORD"; default: diff --git a/web/repertory/lib/main.dart b/web/repertory/lib/main.dart index c45e91ce..0ac99633 100644 --- a/web/repertory/lib/main.dart +++ b/web/repertory/lib/main.dart @@ -6,6 +6,7 @@ import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount_list.dart'; import 'package:repertory/screens/add_mount_screen.dart'; import 'package:repertory/screens/edit_mount_screen.dart'; +import 'package:repertory/screens/edit_settings_screen.dart'; import 'package:repertory/screens/home_screen.dart'; void main() { @@ -50,6 +51,8 @@ class _MyAppState extends State { '/': (context) => const HomeScreen(title: constants.appTitle), '/add': (context) => const AddMountScreen(title: constants.addMountTitle), + '/settings': + (context) => const EditSettingsScreen(title: constants.appTitle), }, onGenerateRoute: (settings) { if (settings.name != '/edit') { diff --git a/web/repertory/lib/screens/edit_settings_screen.dart b/web/repertory/lib/screens/edit_settings_screen.dart new file mode 100644 index 00000000..d6db0830 --- /dev/null +++ b/web/repertory/lib/screens/edit_settings_screen.dart @@ -0,0 +1,68 @@ +import 'dart:convert' show jsonDecode; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:repertory/helpers.dart'; +import 'package:repertory/widgets/ui_settings.dart'; + +class EditSettingsScreen extends StatefulWidget { + final String title; + const EditSettingsScreen({super.key, required this.title}); + + @override + State createState() => _EditSettingsScreenState(); +} + +class _EditSettingsScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text(widget.title), + ), + body: FutureBuilder( + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center(child: CircularProgressIndicator()); + } + + return UISettingsWidget( + settings: snapshot.requireData, + showAdvanced: false, + ); + }, + future: _grabSettings(), + initialData: {}, + ), + ); + } + + Future> _grabSettings() async { + try { + final response = await http.get( + Uri.parse('${getBaseUri()}/api/v1/settings'), + ); + + if (response.statusCode != 200) { + return {}; + } + + return jsonDecode(response.body); + } catch (e) { + debugPrint('$e'); + } + + return {}; + } + + // UISettingsWidget(settings: {}, showAdvanced: false), + @override + void setState(VoidCallback fn) { + if (!mounted) { + return; + } + + super.setState(fn); + } +} diff --git a/web/repertory/lib/screens/home_screen.dart b/web/repertory/lib/screens/home_screen.dart index f5fd50f7..0773888a 100644 --- a/web/repertory/lib/screens/home_screen.dart +++ b/web/repertory/lib/screens/home_screen.dart @@ -16,7 +16,10 @@ class _HomeScreeState extends State { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, - leading: const Icon(Icons.storage), + leading: IconButton( + onPressed: () => Navigator.pushNamed(context, '/settings'), + icon: const Icon(Icons.storage), + ), title: Text(widget.title), ), body: Padding( diff --git a/web/repertory/lib/settings.dart b/web/repertory/lib/settings.dart new file mode 100644 index 00000000..b985395f --- /dev/null +++ b/web/repertory/lib/settings.dart @@ -0,0 +1,361 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:repertory/constants.dart' as constants; +import 'package:repertory/helpers.dart' show Validator, displayErrorMessage; +import 'package:settings_ui/settings_ui.dart'; + +void createBooleanSetting( + context, + List list, + Map settings, + String key, + value, + bool isAdvanced, + bool showAdvanced, + widget, + Function setState, { + String? description, +}) { + if (!isAdvanced || showAdvanced) { + list.add( + SettingsTile.switchTile( + leading: const Icon(Icons.quiz), + title: createSettingTitle(context, key, description), + initialValue: (value as bool), + onPressed: + (_) => setState(() { + settings[key] = !value; + widget.onChanged?.call(widget.settings); + }), + onToggle: (bool nextValue) { + setState(() { + settings[key] = nextValue; + widget.onChanged?.call(widget.settings); + }); + }, + ), + ); + } +} + +void createIntListSetting( + context, + List list, + Map settings, + String key, + value, + List valueList, + defaultValue, + IconData icon, + bool isAdvanced, + bool showAdvanced, + widget, + Function setState, { + String? description, +}) { + if (!isAdvanced || widget.showAdvanced) { + list.add( + SettingsTile.navigation( + title: createSettingTitle(context, key, description), + leading: Icon(icon), + value: DropdownButton( + value: value.toString(), + onChanged: (newValue) { + setState(() { + settings[key] = int.parse(newValue ?? defaultValue.toString()); + widget.onChanged?.call(widget.settings); + }); + }, + items: + valueList.map>((item) { + return DropdownMenuItem(value: item, child: Text(item)); + }).toList(), + ), + ), + ); + } +} + +void createIntSetting( + context, + List list, + Map settings, + String key, + value, + bool isAdvanced, + bool showAdvanced, + widget, + Function setState, { + String? description, + List validators = const [], +}) { + if (!isAdvanced || widget.showAdvanced) { + list.add( + SettingsTile.navigation( + leading: const Icon(Icons.onetwothree), + title: createSettingTitle(context, key, description), + value: Text(value.toString()), + onPressed: (_) { + String updatedValue = value.toString(); + showDialog( + context: context, + builder: (context) { + return AlertDialog( + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: const Text('OK'), + onPressed: () { + final result = validators.firstWhereOrNull( + (validator) => !validator(updatedValue), + ); + if (result != null) { + return displayErrorMessage( + context, + "Setting '$key' is not valid", + ); + } + setState(() { + settings[key] = int.parse(updatedValue); + widget.onChanged?.call(widget.settings); + }); + Navigator.of(context).pop(); + }, + ), + ], + content: TextField( + autofocus: true, + controller: TextEditingController(text: updatedValue), + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + keyboardType: TextInputType.number, + onChanged: (nextValue) => updatedValue = nextValue, + ), + title: createSettingTitle(context, key, description), + ); + }, + ); + }, + ), + ); + } +} + +void createPasswordSetting( + context, + List list, + Map settings, + String key, + value, + bool isAdvanced, + bool showAdvanced, + widget, + Function setState, { + String? description, + List validators = const [], +}) { + if (!isAdvanced || widget.showAdvanced) { + list.add( + SettingsTile.navigation( + leading: const Icon(Icons.password), + title: createSettingTitle(context, key, description), + value: Text('*' * (value as String).length), + onPressed: (_) { + String updatedValue1 = value; + String updatedValue2 = value; + showDialog( + context: context, + builder: (context) { + return AlertDialog( + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: const Text('OK'), + onPressed: () { + if (updatedValue1 != updatedValue2) { + return displayErrorMessage( + context, + "Setting '$key' does not match", + ); + } + + final result = validators.firstWhereOrNull( + (validator) => !validator(updatedValue1), + ); + if (result != null) { + return displayErrorMessage( + context, + "Setting '$key' is not valid", + ); + } + + setState(() { + settings[key] = updatedValue1; + widget.onChanged?.call(widget.settings); + }); + Navigator.of(context).pop(); + }, + ), + ], + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + autofocus: true, + controller: TextEditingController(text: updatedValue1), + obscureText: true, + obscuringCharacter: '*', + onChanged: (value) => updatedValue1 = value, + ), + const SizedBox(height: constants.padding), + TextField( + autofocus: false, + controller: TextEditingController(text: updatedValue2), + obscureText: true, + obscuringCharacter: '*', + onChanged: (value) => updatedValue2 = value, + ), + ], + ), + title: createSettingTitle(context, key, description), + ); + }, + ); + }, + ), + ); + } +} + +Widget createSettingTitle(context, String key, String? description) { + if (description == null) { + return Text(key); + } + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(key, textAlign: TextAlign.start), + Text( + description, + style: Theme.of(context).textTheme.titleSmall, + textAlign: TextAlign.start, + ), + ], + ); +} + +void createStringListSetting( + context, + List list, + Map settings, + String key, + value, + List valueList, + IconData icon, + bool isAdvanced, + bool showAdvanced, + widget, + Function setState, { + String? description, +}) { + if (!isAdvanced || widget.showAdvanced) { + list.add( + SettingsTile.navigation( + title: createSettingTitle(context, key, description), + leading: Icon(icon), + value: DropdownButton( + value: value, + onChanged: + (newValue) => setState(() { + settings[key] = newValue; + widget.onChanged?.call(widget.settings); + }), + items: + valueList.map>((item) { + return DropdownMenuItem(value: item, child: Text(item)); + }).toList(), + ), + ), + ); + } +} + +void createStringSetting( + context, + List list, + Map settings, + String key, + value, + IconData icon, + bool isAdvanced, + bool showAdvanced, + widget, + Function setState, { + String? description, + List validators = const [], +}) { + if (!isAdvanced || widget.showAdvanced) { + list.add( + SettingsTile.navigation( + leading: Icon(icon), + title: createSettingTitle(context, key, description), + value: Text(value), + onPressed: (_) { + String updatedValue = value; + showDialog( + context: context, + builder: (context) { + return AlertDialog( + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: const Text('OK'), + onPressed: () { + final result = validators.firstWhereOrNull( + (validator) => !validator(updatedValue), + ); + if (result != null) { + return displayErrorMessage( + context, + "Setting '$key' is not valid", + ); + } + setState(() { + settings[key] = updatedValue; + widget.onChanged?.call(widget.settings); + }); + Navigator.of(context).pop(); + }, + ), + ], + content: TextField( + autofocus: true, + controller: TextEditingController(text: updatedValue), + inputFormatters: [ + FilteringTextInputFormatter.deny(RegExp(r'\s')), + ], + onChanged: (value) => updatedValue = value, + ), + title: createSettingTitle(context, key, description), + ); + }, + ); + }, + ), + ); + } +} diff --git a/web/repertory/lib/widgets/mount_settings.dart b/web/repertory/lib/widgets/mount_settings.dart index 0395c90e..fbd9ff11 100644 --- a/web/repertory/lib/widgets/mount_settings.dart +++ b/web/repertory/lib/widgets/mount_settings.dart @@ -1,16 +1,12 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:repertory/constants.dart' as constants; import 'package:repertory/helpers.dart' - show - Validator, - displayErrorMessage, - getSettingDescription, - getSettingValidators; + show getSettingDescription, getSettingValidators; import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount_list.dart'; +import 'package:repertory/settings.dart'; import 'package:settings_ui/settings_ui.dart'; class MountSettingsWidget extends StatefulWidget { @@ -33,343 +29,6 @@ class MountSettingsWidget extends StatefulWidget { } class _MountSettingsWidgetState extends State { - Widget _createTitle(String key, String? description) { - if (description == null) { - return Text(key); - } - - return Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(key, textAlign: TextAlign.start), - Text( - description, - style: Theme.of(context).textTheme.titleSmall, - textAlign: TextAlign.start, - ), - ], - ); - } - - void _addBooleanSetting( - list, - root, - key, - value, - isAdvanced, { - String? description, - }) { - if (!isAdvanced || widget.showAdvanced) { - list.add( - SettingsTile.switchTile( - leading: const Icon(Icons.quiz), - title: _createTitle(key, description), - initialValue: (value as bool), - onPressed: - (_) => setState(() { - root[key] = !value; - widget.onChanged?.call(widget.settings); - }), - onToggle: (bool nextValue) { - setState(() { - root[key] = nextValue; - widget.onChanged?.call(widget.settings); - }); - }, - ), - ); - } - } - - void _addIntSetting( - list, - root, - key, - value, - isAdvanced, { - String? description, - List validators = const [], - }) { - if (!isAdvanced || widget.showAdvanced) { - list.add( - SettingsTile.navigation( - leading: const Icon(Icons.onetwothree), - title: _createTitle(key, description), - value: Text(value.toString()), - onPressed: (_) { - String updatedValue = value.toString(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () => Navigator.of(context).pop(), - ), - TextButton( - child: const Text('OK'), - onPressed: () { - final result = validators.firstWhereOrNull( - (validator) => !validator(updatedValue), - ); - if (result != null) { - return displayErrorMessage( - context, - "Setting '$key' is not valid", - ); - } - setState(() { - root[key] = int.parse(updatedValue); - widget.onChanged?.call(widget.settings); - }); - Navigator.of(context).pop(); - }, - ), - ], - content: TextField( - autofocus: true, - controller: TextEditingController(text: updatedValue), - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - keyboardType: TextInputType.number, - onChanged: (nextValue) => updatedValue = nextValue, - ), - title: _createTitle(key, description), - ); - }, - ); - }, - ), - ); - } - } - - void _addIntListSetting( - list, - root, - key, - value, - List valueList, - defaultValue, - icon, - isAdvanced, { - String? description, - }) { - if (!isAdvanced || widget.showAdvanced) { - list.add( - SettingsTile.navigation( - title: _createTitle(key, description), - leading: Icon(icon), - value: DropdownButton( - value: value.toString(), - onChanged: (newValue) { - setState(() { - root[key] = int.parse(newValue ?? defaultValue.toString()); - widget.onChanged?.call(widget.settings); - }); - }, - items: - valueList.map>((item) { - return DropdownMenuItem( - value: item, - child: Text(item), - ); - }).toList(), - ), - ), - ); - } - } - - void _addPasswordSetting( - list, - root, - key, - value, - isAdvanced, { - String? description, - List validators = const [], - }) { - if (!isAdvanced || widget.showAdvanced) { - list.add( - SettingsTile.navigation( - leading: const Icon(Icons.password), - title: _createTitle(key, description), - value: Text('*' * (value as String).length), - onPressed: (_) { - String updatedValue1 = value; - String updatedValue2 = value; - showDialog( - context: context, - builder: (context) { - return AlertDialog( - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () => Navigator.of(context).pop(), - ), - TextButton( - child: const Text('OK'), - onPressed: () { - if (updatedValue1 != updatedValue2) { - return displayErrorMessage( - context, - "Setting '$key' does not match", - ); - } - - final result = validators.firstWhereOrNull( - (validator) => !validator(updatedValue1), - ); - if (result != null) { - return displayErrorMessage( - context, - "Setting '$key' is not valid", - ); - } - - setState(() { - root[key] = updatedValue1; - widget.onChanged?.call(widget.settings); - }); - Navigator.of(context).pop(); - }, - ), - ], - content: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - autofocus: true, - controller: TextEditingController(text: updatedValue1), - obscureText: true, - obscuringCharacter: '*', - onChanged: (value) => updatedValue1 = value, - ), - const SizedBox(height: constants.padding), - TextField( - autofocus: false, - controller: TextEditingController(text: updatedValue2), - obscureText: true, - obscuringCharacter: '*', - onChanged: (value) => updatedValue2 = value, - ), - ], - ), - title: _createTitle(key, description), - ); - }, - ); - }, - ), - ); - } - } - - void _addStringListSetting( - list, - root, - key, - value, - List valueList, - icon, - isAdvanced, { - String? description, - }) { - if (!isAdvanced || widget.showAdvanced) { - list.add( - SettingsTile.navigation( - title: _createTitle(key, description), - leading: Icon(icon), - value: DropdownButton( - value: value, - onChanged: - (newValue) => setState(() { - root[key] = newValue; - widget.onChanged?.call(widget.settings); - }), - items: - valueList.map>((item) { - return DropdownMenuItem( - value: item, - child: Text(item), - ); - }).toList(), - ), - ), - ); - } - } - - void _addStringSetting( - list, - root, - key, - value, - icon, - isAdvanced, { - String? description, - List validators = const [], - }) { - if (!isAdvanced || widget.showAdvanced) { - list.add( - SettingsTile.navigation( - leading: Icon(icon), - title: _createTitle(key, description), - value: Text(value), - onPressed: (_) { - String updatedValue = value; - showDialog( - context: context, - builder: (context) { - return AlertDialog( - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () => Navigator.of(context).pop(), - ), - TextButton( - child: const Text('OK'), - onPressed: () { - final result = validators.firstWhereOrNull( - (validator) => !validator(updatedValue), - ); - if (result != null) { - return displayErrorMessage( - context, - "Setting '$key' is not valid", - ); - } - setState(() { - root[key] = updatedValue; - widget.onChanged?.call(widget.settings); - }); - Navigator.of(context).pop(); - }, - ), - ], - content: TextField( - autofocus: true, - controller: TextEditingController(text: updatedValue), - inputFormatters: [ - FilteringTextInputFormatter.deny(RegExp(r'\s')), - ], - onChanged: (value) => updatedValue = value, - ), - title: _createTitle(key, description), - ); - }, - ); - }, - ), - ); - } - } - @override Widget build(BuildContext context) { List commonSettings = []; @@ -384,12 +43,16 @@ class _MountSettingsWidgetState extends State { switch (key) { case 'ApiPassword': { - _addPasswordSetting( + createPasswordSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -397,12 +60,16 @@ class _MountSettingsWidgetState extends State { break; case 'ApiPort': { - _addIntSetting( + createIntSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -410,13 +77,17 @@ class _MountSettingsWidgetState extends State { break; case 'ApiUser': { - _addStringSetting( + createStringSetting( + context, commonSettings, widget.settings, key, value, Icons.person, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -424,7 +95,8 @@ class _MountSettingsWidgetState extends State { break; case 'DatabaseType': { - _addStringListSetting( + createStringListSetting( + context, commonSettings, widget.settings, key, @@ -432,18 +104,25 @@ class _MountSettingsWidgetState extends State { constants.databaseTypeList, Icons.dataset, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } break; case 'DownloadTimeoutSeconds': { - _addIntSetting( + createIntSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -451,31 +130,40 @@ class _MountSettingsWidgetState extends State { break; case 'EnableDownloadTimeout': { - _addBooleanSetting( + createBooleanSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } break; case 'EnableDriveEvents': { - _addBooleanSetting( + createBooleanSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } break; case 'EventLevel': { - _addStringListSetting( + createStringListSetting( + context, commonSettings, widget.settings, key, @@ -483,18 +171,25 @@ class _MountSettingsWidgetState extends State { constants.eventLevelList, Icons.event, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } break; case 'EvictionDelayMinutes': { - _addIntSetting( + createIntSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -502,24 +197,32 @@ class _MountSettingsWidgetState extends State { break; case 'EvictionUseAccessedTime': { - _addBooleanSetting( + createBooleanSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } break; case 'MaxCacheSizeBytes': { - _addIntSetting( + createIntSetting( + context, commonSettings, widget.settings, key, value, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -527,12 +230,16 @@ class _MountSettingsWidgetState extends State { break; case 'MaxUploadCount': { - _addIntSetting( + createIntSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -540,12 +247,16 @@ class _MountSettingsWidgetState extends State { break; case 'OnlineCheckRetrySeconds': { - _addIntSetting( + createIntSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -553,7 +264,8 @@ class _MountSettingsWidgetState extends State { break; case 'PreferredDownloadType': { - _addStringListSetting( + createStringListSetting( + context, commonSettings, widget.settings, key, @@ -561,18 +273,25 @@ class _MountSettingsWidgetState extends State { constants.downloadTypeList, Icons.download, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } break; case 'RetryReadCount': { - _addIntSetting( + createIntSetting( + context, commonSettings, widget.settings, key, value, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), validators: getSettingValidators(key), ); @@ -580,7 +299,8 @@ class _MountSettingsWidgetState extends State { break; case 'RingBufferFileSize': { - _addIntListSetting( + createIntListSetting( + context, commonSettings, widget.settings, key, @@ -589,6 +309,9 @@ class _MountSettingsWidgetState extends State { 512, Icons.animation, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } @@ -622,13 +345,17 @@ class _MountSettingsWidgetState extends State { { value.forEach((subKey, subValue) { if (subKey == 'Bucket') { - _addStringSetting( + createStringSetting( + context, siaConfigSettings, widget.settings[key], subKey, subValue, Icons.folder, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: [ ...getSettingValidators('$key.$subKey'), @@ -697,13 +424,17 @@ class _MountSettingsWidgetState extends State { switch (subKey) { case 'AgentString': { - _addStringSetting( + createStringSetting( + context, hostConfigSettings, widget.settings[key], subKey, subValue, Icons.support_agent, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -711,12 +442,16 @@ class _MountSettingsWidgetState extends State { break; case 'ApiPassword': { - _addPasswordSetting( + createPasswordSetting( + context, hostConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -724,12 +459,16 @@ class _MountSettingsWidgetState extends State { break; case 'ApiPort': { - _addIntSetting( + createIntSetting( + context, hostConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -737,13 +476,17 @@ class _MountSettingsWidgetState extends State { break; case 'ApiUser': { - _addStringSetting( + createStringSetting( + context, hostConfigSettings, widget.settings[key], subKey, subValue, Icons.person, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -751,13 +494,17 @@ class _MountSettingsWidgetState extends State { break; case 'HostNameOrIp': { - _addStringSetting( + createStringSetting( + context, hostConfigSettings, widget.settings[key], subKey, subValue, Icons.computer, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -765,13 +512,17 @@ class _MountSettingsWidgetState extends State { break; case 'Path': { - _addStringSetting( + createStringSetting( + context, hostConfigSettings, widget.settings[key], subKey, subValue, Icons.route, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -779,7 +530,8 @@ class _MountSettingsWidgetState extends State { break; case 'Protocol': { - _addStringListSetting( + createStringListSetting( + context, hostConfigSettings, widget.settings[key], subKey, @@ -787,18 +539,25 @@ class _MountSettingsWidgetState extends State { constants.protocolTypeList, Icons.http, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription(key), ); } break; case 'TimeoutMs': { - _addIntSetting( + createIntSetting( + context, hostConfigSettings, widget.settings[key], subKey, subValue, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -817,12 +576,16 @@ class _MountSettingsWidgetState extends State { switch (subKey) { case 'EncryptionToken': { - _addPasswordSetting( + createPasswordSetting( + context, encryptConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -830,13 +593,17 @@ class _MountSettingsWidgetState extends State { break; case 'Path': { - _addStringSetting( + createStringSetting( + context, encryptConfigSettings, widget.settings[key], subKey, subValue, Icons.folder, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -878,13 +645,17 @@ class _MountSettingsWidgetState extends State { switch (subKey) { case 'AccessKey': { - _addStringSetting( + createStringSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, Icons.key, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -892,13 +663,17 @@ class _MountSettingsWidgetState extends State { break; case 'Bucket': { - _addStringSetting( + createStringSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, Icons.folder, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: [ ...getSettingValidators('$key.$subKey'), @@ -917,12 +692,16 @@ class _MountSettingsWidgetState extends State { break; case 'EncryptionToken': { - _addPasswordSetting( + createPasswordSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -930,13 +709,17 @@ class _MountSettingsWidgetState extends State { break; case 'Region': { - _addStringSetting( + createStringSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, Icons.map, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -944,12 +727,16 @@ class _MountSettingsWidgetState extends State { break; case 'SecretKey': { - _addPasswordSetting( + createPasswordSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -957,12 +744,16 @@ class _MountSettingsWidgetState extends State { break; case 'TimeoutMs': { - _addIntSetting( + createIntSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -970,13 +761,17 @@ class _MountSettingsWidgetState extends State { break; case 'URL': { - _addStringSetting( + createStringSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, Icons.http, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -984,24 +779,32 @@ class _MountSettingsWidgetState extends State { break; case 'UsePathStyle': { - _addBooleanSetting( + createBooleanSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), ); } break; case 'UseRegionInURL': { - _addBooleanSetting( + createBooleanSetting( + context, s3ConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), ); } @@ -1020,12 +823,16 @@ class _MountSettingsWidgetState extends State { case 'Enable': { List tempSettings = []; - _addBooleanSetting( + createBooleanSetting( + context, tempSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), ); remoteMountSettings.insertAll(0, tempSettings); @@ -1033,12 +840,16 @@ class _MountSettingsWidgetState extends State { break; case 'ApiPort': { - _addIntSetting( + createIntSetting( + context, remoteMountSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1046,12 +857,16 @@ class _MountSettingsWidgetState extends State { break; case 'ClientPoolSize': { - _addIntSetting( + createIntSetting( + context, remoteMountSettings, widget.settings[key], subKey, subValue, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1059,12 +874,16 @@ class _MountSettingsWidgetState extends State { break; case 'EncryptionToken': { - _addPasswordSetting( + createPasswordSetting( + context, remoteMountSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1083,12 +902,16 @@ class _MountSettingsWidgetState extends State { switch (subKey) { case 'ApiPort': { - _addIntSetting( + createIntSetting( + context, remoteConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1096,12 +919,16 @@ class _MountSettingsWidgetState extends State { break; case 'EncryptionToken': { - _addPasswordSetting( + createPasswordSetting( + context, remoteConfigSettings, widget.settings[key], subKey, subValue, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1109,13 +936,17 @@ class _MountSettingsWidgetState extends State { break; case 'HostNameOrIp': { - _addStringSetting( + createStringSetting( + context, remoteConfigSettings, widget.settings[key], subKey, subValue, Icons.computer, false, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1123,12 +954,16 @@ class _MountSettingsWidgetState extends State { break; case 'MaxConnections': { - _addIntSetting( + createIntSetting( + context, remoteConfigSettings, widget.settings[key], subKey, subValue, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1136,12 +971,16 @@ class _MountSettingsWidgetState extends State { break; case 'ReceiveTimeoutMs': { - _addIntSetting( + createIntSetting( + context, remoteConfigSettings, widget.settings[key], subKey, subValue, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1149,12 +988,16 @@ class _MountSettingsWidgetState extends State { break; case 'SendTimeoutMs': { - _addIntSetting( + createIntSetting( + context, remoteConfigSettings, widget.settings[key], subKey, subValue, true, + widget.showAdvanced, + widget, + setState, description: getSettingDescription('$key.$subKey'), validators: getSettingValidators('$key.$subKey'), ); @@ -1163,4 +1006,13 @@ class _MountSettingsWidgetState extends State { } }); } + + @override + void setState(VoidCallback fn) { + if (!mounted) { + return; + } + + super.setState(fn); + } } diff --git a/web/repertory/lib/widgets/ui_settings.dart b/web/repertory/lib/widgets/ui_settings.dart new file mode 100644 index 00000000..4ee22eda --- /dev/null +++ b/web/repertory/lib/widgets/ui_settings.dart @@ -0,0 +1,105 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:repertory/constants.dart' as constants; +import 'package:repertory/helpers.dart' + show getSettingDescription, getSettingValidators; +import 'package:repertory/models/mount.dart'; +import 'package:repertory/models/mount_list.dart'; +import 'package:repertory/settings.dart'; +import 'package:settings_ui/settings_ui.dart'; + +class UISettingsWidget extends StatefulWidget { + final bool showAdvanced; + final Function? onChanged; + final Map settings; + const UISettingsWidget({ + super.key, + this.onChanged, + required this.settings, + required this.showAdvanced, + }); + + @override + State createState() => _UISettingsWidgetState(); +} + +class _UISettingsWidgetState extends State { + @override + Widget build(BuildContext context) { + List commonSettings = []; + + widget.settings.forEach((key, value) { + switch (key) { + case 'ApiPassword': + { + createPasswordSetting( + context, + commonSettings, + widget.settings, + key, + value, + false, + widget.showAdvanced, + widget, + setState, + description: getSettingDescription(key), + validators: getSettingValidators(key), + ); + } + break; + case 'ApiPort': + { + createIntSetting( + context, + commonSettings, + widget.settings, + key, + value, + true, + widget.showAdvanced, + widget, + setState, + description: getSettingDescription(key), + validators: getSettingValidators(key), + ); + } + break; + case 'ApiUser': + { + createStringSetting( + context, + commonSettings, + widget.settings, + key, + value, + Icons.person, + true, + widget.showAdvanced, + widget, + setState, + description: getSettingDescription(key), + validators: getSettingValidators(key), + ); + } + break; + } + }); + + return SettingsList( + shrinkWrap: false, + sections: [ + SettingsSection(title: const Text('Settings'), tiles: commonSettings), + ], + ); + } + + @override + void setState(VoidCallback fn) { + if (!mounted) { + return; + } + + super.setState(fn); + } +}