Compare commits

...

6 Commits

Author SHA1 Message Date
71f3567375 Create management portal in Flutter #39
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
2025-03-04 14:48:37 -06:00
865ed5f0c1 Create management portal in Flutter #39 2025-03-04 14:02:28 -06:00
661f57d599 refactor 2025-03-04 13:34:02 -06:00
badd098fe0 Create management portal in Flutter #39 2025-03-04 13:32:15 -06:00
820617b3ff refactor 2025-03-04 13:06:23 -06:00
717b461eaf Create management portal in Flutter #39 2025-03-04 12:57:59 -06:00
8 changed files with 144 additions and 25 deletions

View File

@ -1,6 +0,0 @@
class DuplicateMountException implements Exception {
final String _name;
const DuplicateMountException({required name}) : _name = name, super();
String get name => _name;
}

View File

@ -1,3 +1,5 @@
import 'package:flutter/foundation.dart';
String formatMountName(String type, String name) { String formatMountName(String type, String name) {
if (type == "remote") { if (type == "remote") {
return name.replaceAll("_", ":"); return name.replaceAll("_", ":");
@ -5,6 +7,13 @@ String formatMountName(String type, String name) {
return name; return name;
} }
String getBaseUri() {
if (kDebugMode) {
return "http://127.0.0.1:30000";
}
return Uri.base.origin;
}
String initialCaps(String txt) { String initialCaps(String txt) {
if (txt.isEmpty) { if (txt.isEmpty) {
return txt; return txt;

View File

@ -4,6 +4,7 @@ import 'package:repertory/constants.dart' as constants;
import 'package:repertory/helpers.dart'; import 'package:repertory/helpers.dart';
import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount.dart';
import 'package:repertory/models/mount_list.dart'; import 'package:repertory/models/mount_list.dart';
import 'package:repertory/widgets/add_mount_widget.dart';
import 'package:repertory/widgets/mount_list_widget.dart'; import 'package:repertory/widgets/mount_list_widget.dart';
import 'package:repertory/widgets/mount_settings.dart'; import 'package:repertory/widgets/mount_settings.dart';
@ -72,6 +73,10 @@ class MyHomePage extends StatefulWidget {
} }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
bool _allowAdd = true;
String _mountType = "S3";
String _mountName = "";
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -85,8 +90,73 @@ class _MyHomePageState extends State<MyHomePage> {
child: MountListWidget(), child: MountListWidget(),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () {}, onPressed:
tooltip: 'Add', _allowAdd
? () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add Mount'),
content: Consumer<MountList>(
builder: (_, mountList, __) {
return AddMountWidget(
allowEncrypt:
!mountList.items.contains(
(item) => item.type == "encrypt",
),
mountName: _mountName,
mountType: _mountType,
onNameChanged:
(mountName) => setState(
() => _mountName = mountName ?? "",
),
onTypeChanged:
(mountType) => setState(
() => _mountType = mountType ?? "S3",
),
);
},
),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () {
setState(() {
_mountType = "S3";
_mountName = "";
});
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('OK'),
onPressed: () {
setState(() => _allowAdd = false);
Provider.of<MountList>(context, listen: false)
.add(_mountType, _mountName)
.then((_) {
setState(() {
_allowAdd = true;
_mountType = "S3";
_mountName = "";
});
})
.catchError((_) {
setState(() => _allowAdd = true);
});
Navigator.of(context).pop();
},
),
],
);
},
);
}
: null,
tooltip: 'Add Mount',
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),
); );

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:repertory/helpers.dart';
import 'package:repertory/types/mount_config.dart'; import 'package:repertory/types/mount_config.dart';
class Mount with ChangeNotifier { class Mount with ChangeNotifier {
@ -18,7 +19,7 @@ class Mount with ChangeNotifier {
Future<void> _fetch() async { Future<void> _fetch() async {
final response = await http.get( final response = await http.get(
Uri.parse( Uri.parse(
Uri.encodeFull('${Uri.base.origin}/api/v1/mount?name=$name&type=$type'), Uri.encodeFull('${getBaseUri()}/api/v1/mount?name=$name&type=$type'),
), ),
); );
@ -35,7 +36,7 @@ class Mount with ChangeNotifier {
final response = await http.get( final response = await http.get(
Uri.parse( Uri.parse(
Uri.encodeFull( Uri.encodeFull(
'${Uri.base.origin}/api/v1/mount_status?name=$name&type=$type', '${getBaseUri()}/api/v1/mount_status?name=$name&type=$type',
), ),
), ),
); );
@ -52,7 +53,7 @@ class Mount with ChangeNotifier {
await http.post( await http.post(
Uri.parse( Uri.parse(
Uri.encodeFull( Uri.encodeFull(
'${Uri.base.origin}/api/v1/mount?unmount=$unmount&name=$name&type=$type&location=$location', '${getBaseUri()}/api/v1/mount?unmount=$unmount&name=$name&type=$type&location=$location',
), ),
), ),
); );
@ -69,7 +70,7 @@ class Mount with ChangeNotifier {
await http.put( await http.put(
Uri.parse( Uri.parse(
Uri.encodeFull( Uri.encodeFull(
'${Uri.base.origin}/api/v1/set_value_by_name?name=$name&type=$type&key=$key&value=$value', '${getBaseUri()}/api/v1/set_value_by_name?name=$name&type=$type&key=$key&value=$value',
), ),
), ),
); );

View File

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:repertory/errors/duplicate_mount_exception.dart'; import 'package:repertory/helpers.dart';
import 'package:repertory/types/mount_config.dart'; import 'package:repertory/types/mount_config.dart';
class MountList with ChangeNotifier { class MountList with ChangeNotifier {
@ -17,7 +17,7 @@ class MountList with ChangeNotifier {
Future<void> _fetch() async { Future<void> _fetch() async {
final response = await http.get( final response = await http.get(
Uri.parse('${Uri.base.origin}/api/v1/mount_list'), Uri.parse('${getBaseUri()}/api/v1/mount_list'),
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
@ -47,16 +47,16 @@ class MountList with ChangeNotifier {
}); });
} }
void add(MountConfig config) { Future<void> add(String type, String name) async {
var item = _mountList.firstWhereOrNull((cfg) => cfg.name == config.name); await http.post(
if (item != null) { Uri.parse(
throw DuplicateMountException(name: config.name); Uri.encodeFull(
} '${getBaseUri()}/api/v1/add_mount?name=$name&type=$type',
),
),
);
_mountList.add(config); return _fetch();
_sort(_mountList);
notifyListeners();
} }
void remove(String name) { void remove(String name) {

View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
class AddMountWidget extends StatelessWidget {
final bool allowEncrypt;
final String mountName;
final String mountType;
final void Function(String? newName) onNameChanged;
final void Function(String? newType) onTypeChanged;
final _items = <String>["S3", "Sia"];
AddMountWidget({
super.key,
required this.allowEncrypt,
required this.mountName,
required this.mountType,
required this.onNameChanged,
required this.onTypeChanged,
}) {
if (allowEncrypt) {
_items.insert(0, "Encrypt");
}
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// DropdownButton<String>(
// value: mountType,
// onChanged: onTypeChanged,
// items:
// _items.map<DropdownMenuItem<String>>((item) {
// return DropdownMenuItem<String>(value: item, child: Text(item));
// }).toList(),
// ),
TextField(
decoration: InputDecoration(labelText: 'Name'),
onChanged: onNameChanged,
),
],
);
}
}

View File

@ -15,7 +15,7 @@ class _MountListWidgetState extends State<MountListWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<MountList>( return Consumer<MountList>(
builder: (context, mountList, widget) { builder: (context, mountList, _) {
return ListView.builder( return ListView.builder(
itemBuilder: (context, idx) { itemBuilder: (context, idx) {
return ChangeNotifierProvider( return ChangeNotifierProvider(

View File

@ -20,7 +20,7 @@ class _MountWidgetState extends State<MountWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
child: Consumer<Mount>( child: Consumer<Mount>(
builder: (context, mount, widget) { builder: (context, mount, _) {
final textColor = Theme.of(context).colorScheme.onSurface; final textColor = Theme.of(context).colorScheme.onSurface;
final subTextColor = final subTextColor =
Theme.of(context).brightness == Brightness.dark Theme.of(context).brightness == Brightness.dark