From b74160bfb3d72ab1b59d047ed83d5e6e632cc180 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 5 Mar 2025 08:17:27 -0600 Subject: [PATCH] Create management portal in Flutter #39 --- CHANGELOG.md | 1 + repertory/repertory/include/cli/help.hpp | 7 +- repertory/repertory/main.cpp | 7 +- web/repertory/.cspell/words.txt | 4 +- web/repertory/lib/main.dart | 65 +++++++++++------ web/repertory/lib/models/mount_list.dart | 10 ++- .../lib/widgets/add_mount_widget.dart | 73 +++++++++++++++---- .../lib/widgets/mount_list_widget.dart | 7 +- web/repertory/lib/widgets/mount_settings.dart | 9 +++ web/repertory/lib/widgets/mount_widget.dart | 9 +++ 10 files changed, 136 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a07d9a22..93acf11b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Changes from v2.0.4-rc * Continue documentation updates +* Require `--name,-na` option for encryption provider ## v2.0.4-rc diff --git a/repertory/repertory/include/cli/help.hpp b/repertory/repertory/include/cli/help.hpp index ff3a2f5a..83ccbbcb 100644 --- a/repertory/repertory/include/cli/help.hpp +++ b/repertory/repertory/include/cli/help.hpp @@ -36,10 +36,9 @@ template inline void help(std::vector args) { std::cout << " -di,--drive_information Display mounted drive " "information" << std::endl; - std::cout - << " -na,--name Unique name for S3 or Sia " - "instance [Required]" - << std::endl; + std::cout << " -na,--name Unique configuration " + "name [Required for Encrypt, S3 and Sia]" + << std::endl; std::cout << " -s3,--s3 Enables S3 mode" << std::endl; std::cout diff --git a/repertory/repertory/main.cpp b/repertory/repertory/main.cpp index c77f3c09..4b8774a3 100644 --- a/repertory/repertory/main.cpp +++ b/repertory/repertory/main.cpp @@ -105,7 +105,8 @@ auto main(int argc, char **argv) -> int { } } } - } else if ((prov == provider_type::s3) || (prov == provider_type::sia)) { + } else if ((prov == provider_type::s3) || (prov == provider_type::sia) || + (prov == provider_type::encrypt)) { std::string data; res = utils::cli::parse_string_option( args, utils::cli::options::name_option, data); @@ -115,9 +116,9 @@ auto main(int argc, char **argv) -> int { if (prov == provider_type::sia) { unique_id = "default"; } else { - std::cerr << "Name of " + std::cerr << "Configuration name for '" << app_config::get_provider_display_name(prov) - << " instance not provided" << std::endl; + << "' was not provided" << std::endl; res = exit_code::invalid_syntax; } } diff --git a/web/repertory/.cspell/words.txt b/web/repertory/.cspell/words.txt index 10d12a3e..2f188ef6 100644 --- a/web/repertory/.cspell/words.txt +++ b/web/repertory/.cspell/words.txt @@ -1,4 +1,6 @@ +autofocus cupertino cupertinoicons fromargb -onetwothree \ No newline at end of file +onetwothree +rocksdb \ No newline at end of file diff --git a/web/repertory/lib/main.dart b/web/repertory/lib/main.dart index 0e245cde..ca256e8f 100644 --- a/web/repertory/lib/main.dart +++ b/web/repertory/lib/main.dart @@ -1,11 +1,9 @@ -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'; import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount_list.dart'; -import 'package:repertory/types/mount_config.dart'; import 'package:repertory/widgets/add_mount_widget.dart'; import 'package:repertory/widgets/mount_list_widget.dart'; import 'package:repertory/widgets/mount_settings.dart'; @@ -20,7 +18,7 @@ class MyApp extends StatelessWidget { const MyApp({super.key}); @override - Widget build(BuildContext context) { + Widget build(context) { return MaterialApp( title: constants.appTitle, theme: ThemeData( @@ -78,11 +76,22 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { bool _allowAdd = true; - String _mountType = "S3"; + String? _apiAuth; + String? _bucket; + String _mountType = "Encrypt"; String _mountName = ""; + String? _path; + + void _resetData() { + _apiAuth = null; + _bucket = null; + _mountType = "Encrypt"; + _mountName = ""; + _path = null; + } @override - Widget build(BuildContext context) { + Widget build(context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, @@ -96,25 +105,20 @@ class _MyHomePageState extends State { ? () { showDialog( context: context, - builder: (BuildContext context) { + builder: (context) { return AlertDialog( title: const Text('Add Mount'), content: Consumer( - builder: (_, mountList, __) { + builder: (_, MountList mountList, __) { return AddMountWidget( - allowEncrypt: - mountList.items.firstWhereOrNull( - (MountConfig item) => - item.type.toLowerCase() == "encrypt", - ) == - null, mountType: _mountType, - onNameChanged: (mountName) { - _mountName = mountName ?? ""; - }, - onTypeChanged: (mountType) { - _mountType = mountType ?? "S3"; - }, + onApiAuthChanged: (apiAuth) => _apiAuth = apiAuth, + onBucketChanged: (bucket) => _bucket = bucket, + onNameChanged: + (mountName) => _mountName = mountName ?? "", + onPathChanged: (path) => _path = path, + onTypeChanged: + (mountType) => _mountType = mountType ?? "S3", ); }, ), @@ -122,8 +126,7 @@ class _MyHomePageState extends State { TextButton( child: const Text('Cancel'), onPressed: () { - _mountType = "S3"; - _mountName = ""; + _resetData(); Navigator.of(context).pop(); }, ), @@ -133,10 +136,15 @@ class _MyHomePageState extends State { setState(() => _allowAdd = false); Provider.of(context, listen: false) - .add(_mountType, _mountName) + .add( + _mountType, + _mountName, + apiAuth: _apiAuth, + bucket: _bucket, + path: _path, + ) .then((_) { - _mountType = "S3"; - _mountName = ""; + _resetData(); setState(() { _allowAdd = true; }); @@ -159,4 +167,13 @@ class _MyHomePageState extends State { ), ); } + + @override + void setState(VoidCallback fn) { + if (!mounted) { + return; + } + + super.setState(fn); + } } diff --git a/web/repertory/lib/models/mount_list.dart b/web/repertory/lib/models/mount_list.dart index 07dadd55..ee8746cf 100644 --- a/web/repertory/lib/models/mount_list.dart +++ b/web/repertory/lib/models/mount_list.dart @@ -48,11 +48,17 @@ class MountList with ChangeNotifier { }); } - Future add(String type, String name) async { + Future add( + String type, + String name, { + String? apiAuth, + String? bucket, + String? path, + }) async { await http.post( Uri.parse( Uri.encodeFull( - '${getBaseUri()}/api/v1/add_mount?name=$name&type=$type', + '${getBaseUri()}/api/v1/add_mount?name=$name&type=$type&bucket=$bucket&path=$path&apiAuth=$apiAuth', ), ), ); diff --git a/web/repertory/lib/widgets/add_mount_widget.dart b/web/repertory/lib/widgets/add_mount_widget.dart index 1da745fd..c4f15b80 100644 --- a/web/repertory/lib/widgets/add_mount_widget.dart +++ b/web/repertory/lib/widgets/add_mount_widget.dart @@ -1,30 +1,30 @@ import 'package:flutter/material.dart'; class AddMountWidget extends StatefulWidget { - final bool allowEncrypt; final String mountType; + final void Function(String? newApiAuth) onApiAuthChanged; + final void Function(String? newBucket) onBucketChanged; final void Function(String? newName) onNameChanged; + final void Function(String? newPath) onPathChanged; final void Function(String? newType) onTypeChanged; - final _items = ["S3", "Sia"]; - - AddMountWidget({ + const AddMountWidget({ super.key, - required this.allowEncrypt, required this.mountType, + required this.onApiAuthChanged, + required this.onBucketChanged, required this.onNameChanged, + required this.onPathChanged, required this.onTypeChanged, - }) { - if (allowEncrypt) { - _items.insert(0, "Encrypt"); - } - } + }); @override State createState() => _AddMountWidgetState(); } class _AddMountWidgetState extends State { + static const _items = ["Encrypt", "S3", "Sia"]; + String? _mountType; @override @@ -35,6 +35,7 @@ class _AddMountWidgetState extends State { @override Widget build(BuildContext context) { + final mountTypeLower = _mountType?.toLowerCase(); return Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, @@ -62,7 +63,7 @@ class _AddMountWidgetState extends State { widget.onTypeChanged(value); }, items: - widget._items.map>((item) { + _items.map>((item) { return DropdownMenuItem( value: item, child: Text(item), @@ -72,20 +73,60 @@ class _AddMountWidgetState extends State { ], ), const SizedBox(height: 10), - if (_mountType?.toLowerCase() != 'encrypt') + Text( + 'Configuration Name', + textAlign: TextAlign.left, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + TextField( + autofocus: true, + decoration: InputDecoration(), + onChanged: widget.onNameChanged, + ), + if (mountTypeLower == 'encrypt') Text( - 'Configuration Name', + 'Path', textAlign: TextAlign.left, style: TextStyle( color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.bold, ), ), - if (_mountType?.toLowerCase() != 'encrypt') + if (mountTypeLower == 'encrypt') TextField( - autofocus: true, decoration: InputDecoration(), - onChanged: widget.onNameChanged, + onChanged: widget.onPathChanged, + ), + if (mountTypeLower == 'sia') + Text( + 'ApiAuth', + textAlign: TextAlign.left, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + if (mountTypeLower == 'sia') + TextField( + decoration: InputDecoration(), + onChanged: widget.onApiAuthChanged, + ), + if (mountTypeLower == 'sia' || mountTypeLower == 's3') + Text( + 'Bucket', + textAlign: TextAlign.left, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + if (mountTypeLower == 'sia' || mountTypeLower == 's3') + TextField( + decoration: InputDecoration(), + onChanged: widget.onBucketChanged, ), ], ); diff --git a/web/repertory/lib/widgets/mount_list_widget.dart b/web/repertory/lib/widgets/mount_list_widget.dart index e6fbb78f..fe5f1033 100644 --- a/web/repertory/lib/widgets/mount_list_widget.dart +++ b/web/repertory/lib/widgets/mount_list_widget.dart @@ -4,14 +4,9 @@ import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount_list.dart'; import 'package:repertory/widgets/mount_widget.dart'; -class MountListWidget extends StatefulWidget { +class MountListWidget extends StatelessWidget { const MountListWidget({super.key}); - @override - State createState() => _MountListWidgetState(); -} - -class _MountListWidgetState extends State { @override Widget build(BuildContext context) { return Consumer( diff --git a/web/repertory/lib/widgets/mount_settings.dart b/web/repertory/lib/widgets/mount_settings.dart index 8ad7e9bb..063a404c 100644 --- a/web/repertory/lib/widgets/mount_settings.dart +++ b/web/repertory/lib/widgets/mount_settings.dart @@ -565,4 +565,13 @@ class _MountSettingsWidgetState extends State { _settings = jsonDecode(jsonEncode(widget.mount.mountConfig.settings)); super.initState(); } + + @override + void setState(VoidCallback fn) { + if (!mounted) { + return; + } + + super.setState(fn); + } } diff --git a/web/repertory/lib/widgets/mount_widget.dart b/web/repertory/lib/widgets/mount_widget.dart index 60c20962..041433a7 100644 --- a/web/repertory/lib/widgets/mount_widget.dart +++ b/web/repertory/lib/widgets/mount_widget.dart @@ -109,4 +109,13 @@ class _MountWidgetState extends State { Provider.of(context, listen: false).refresh(); }); } + + @override + void setState(VoidCallback fn) { + if (!mounted) { + return; + } + + super.setState(fn); + } }