From 10829fc9d9929ea96b3a7be163a7474882531177 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 24 Mar 2025 11:23:36 -0500 Subject: [PATCH] return available drive letters on windows --- repertory/repertory/include/ui/handlers.hpp | 2 + repertory/repertory/src/ui/handlers.cpp | 33 ++++++++ web/repertory/lib/models/mount.dart | 28 +++++++ web/repertory/lib/widgets/mount_widget.dart | 85 ++++++++++++++------- 4 files changed, 119 insertions(+), 29 deletions(-) diff --git a/repertory/repertory/include/ui/handlers.hpp b/repertory/repertory/include/ui/handlers.hpp index 31ce7d59..27d07c0a 100644 --- a/repertory/repertory/include/ui/handlers.hpp +++ b/repertory/repertory/include/ui/handlers.hpp @@ -74,6 +74,8 @@ private: [[nodiscard]] auto data_directory_exists(provider_type prov, std::string_view name) const -> bool; + void handle_get_available_locations(httplib::Response &res) const; + void handle_get_mount(const httplib::Request &req, httplib::Response &res) const; diff --git a/repertory/repertory/src/ui/handlers.cpp b/repertory/repertory/src/ui/handlers.cpp index a39b1a3a..c8192525 100644 --- a/repertory/repertory/src/ui/handlers.cpp +++ b/repertory/repertory/src/ui/handlers.cpp @@ -168,6 +168,10 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server) : http_error_codes::internal_error; }); + server->Get("/api/v1/locations", [this](auto && /* req */, auto &&res) { + handle_get_available_locations(res); + }); + server->Get("/api/v1/mount", [this](auto &&req, auto &&res) { handle_get_mount(req, res); }); @@ -294,6 +298,35 @@ auto handlers::data_directory_exists(provider_type prov, return ret; } +void handlers::handle_get_available_locations(httplib::Response &res) const { +#if defined(_WIN32) + constexpr const std::array letters{ + "A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:", + "J:", "K:", "L:", "M:", "N:", "O:", "P:", "Q:", "R:", + "S:", "T:", "U:", "V:", "W:", "X:", "Y:", "Z:", + }; + + auto available = std::accumulate( + letters.begin(), letters.end(), std::vector(), + [](auto &&vec, auto &&letter) -> std::vector { + if (utils::file::directory{utils::path::combine(letter, {"\\"})} + .exists()) { + return vec; + } + + vec.emplace_back(letter); + return vec; + }); + + res.set_content(nlohmann::json(available).dump(), "application/json"); +#else // !defined(_WIN32) + res.set_content(nlohmann::json(std::vector()).dump(), + "application/json"); +#endif // defined(_WIN32) + + res.status = http_error_codes::ok; +} + void handlers::handle_get_mount(const httplib::Request &req, httplib::Response &res) const { REPERTORY_USES_FUNCTION_NAME(); diff --git a/web/repertory/lib/models/mount.dart b/web/repertory/lib/models/mount.dart index 81bed08a..8a4dfc87 100644 --- a/web/repertory/lib/models/mount.dart +++ b/web/repertory/lib/models/mount.dart @@ -101,6 +101,34 @@ class Mount with ChangeNotifier { } } + Future> getAvailableLocations() async { + try { + final auth = await _auth.createAuth(); + final response = await http.get( + Uri.parse( + Uri.encodeFull( + '${getBaseUri()}/api/v1/mount_location?auth=$auth&name=$name&type=$type', + ), + ), + ); + + if (response.statusCode == 401) { + _auth.logoff(); + return []; + } + + if (response.statusCode != 200) { + return []; + } + + return jsonDecode(response.body) as List; + } catch (e) { + debugPrint('$e'); + } + + return []; + } + Future getMountLocation() async { try { final auth = await _auth.createAuth(); diff --git a/web/repertory/lib/widgets/mount_widget.dart b/web/repertory/lib/widgets/mount_widget.dart index 80d9079a..3f19751e 100644 --- a/web/repertory/lib/widgets/mount_widget.dart +++ b/web/repertory/lib/widgets/mount_widget.dart @@ -149,39 +149,66 @@ class _MountWidgetState extends State { return location; } + final available = await mount.getAvailableLocations(); + String? currentLocation; return await showDialog( context: context, builder: (context) { - return AlertDialog( - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () => Navigator.of(context).pop(null), - ), - TextButton( - child: const Text('OK'), - onPressed: () { - final result = getSettingValidators('Path').firstWhereOrNull( - (validator) => !validator(currentLocation ?? ''), - ); - if (result != null) { - return displayErrorMessage( - context, - "Mount location is not valid", - ); - } - Navigator.of(context).pop(currentLocation); - }, - ), - ], - content: TextField( - autofocus: true, - controller: TextEditingController(text: currentLocation), - inputFormatters: [FilteringTextInputFormatter.deny(RegExp(r'\s'))], - onChanged: (value) => currentLocation = value, - ), - title: const Text('Set Mount Location'), + return StatefulBuilder( + builder: (context, setState) { + return AlertDialog( + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () => Navigator.of(context).pop(null), + ), + TextButton( + child: const Text('OK'), + onPressed: () { + final result = getSettingValidators( + 'Path', + ).firstWhereOrNull( + (validator) => !validator(currentLocation ?? ''), + ); + if (result != null) { + return displayErrorMessage( + context, + "Mount location is not valid", + ); + } + Navigator.of(context).pop(currentLocation); + }, + ), + ], + content: + available.isEmpty + ? TextField( + autofocus: true, + controller: TextEditingController( + text: currentLocation, + ), + inputFormatters: [ + FilteringTextInputFormatter.deny(RegExp(r'\s')), + ], + onChanged: + (value) => setState(() => currentLocation = value), + ) + : DropdownButton( + value: currentLocation, + onChanged: + (value) => setState(() => currentLocation = value), + items: + available.map>((item) { + return DropdownMenuItem( + value: item, + child: Text(item), + ); + }).toList(), + ), + title: const Text('Set Mount Location'), + ); + }, ); }, );