Compare commits

..

No commits in common. "94073fe1f152e26480715f18374f43568df944e5" and "847bf68f853c1cc34d365d91eda2c5098740dee1" have entirely different histories.

13 changed files with 605 additions and 755 deletions

View File

@ -1,8 +1 @@
const String addMountTitle = 'New Mount Settings';
const String appTitle = 'Repertory Management Portal'; const String appTitle = 'Repertory Management Portal';
const databaseTypeList = ['rocksdb', 'sqlite'];
const downloadTypeList = ['default', 'direct', 'ring_buffer'];
const eventLevelList = ['critical', 'error', 'warn', 'info', 'debug', 'trace'];
const protocolTypeList = ['http', 'https'];
const providerTypeList = ['Encrypt', 'Remote', 'S3', 'Sia'];
const ringBufferSizeList = ['128', '256', '512', '1024', '2048'];

View File

@ -4,7 +4,6 @@ String formatMountName(String type, String name) {
if (type == 'remote') { if (type == 'remote') {
return name.replaceAll('_', ':'); return name.replaceAll('_', ':');
} }
return name; return name;
} }
@ -12,7 +11,6 @@ String getBaseUri() {
if (kDebugMode || !kIsWeb) { if (kDebugMode || !kIsWeb) {
return 'http://127.0.0.1:30000'; return 'http://127.0.0.1:30000';
} }
return Uri.base.origin; return Uri.base.origin;
} }
@ -27,37 +25,3 @@ String initialCaps(String txt) {
return txt[0].toUpperCase() + txt.substring(1).toLowerCase(); return txt[0].toUpperCase() + txt.substring(1).toLowerCase();
} }
Map<String, dynamic> createDefaultSettings(String mountType) {
switch (mountType) {
case 'Encrypt':
return {
'EncryptConfig': {'EncryptionToken': '', 'Path': ''},
};
case 'Remote':
return {'EventLevel': 'info'};
case 'S3':
return {
'S3Config': {
'AccessKey': '',
'Bucket': '',
'Region': 'any',
'SecretKey': '',
'URL': '',
'UsePathStyle': false,
'UseRegionInURL': false,
},
};
case 'Sia':
return {
'HostConfig': {
'ApiPassword': '',
'ApiPort': '9980',
'HostNameOrIp': 'localhost',
},
'SiaConfig': {'Bucket': 'default'},
};
}
return {};
}

View File

@ -4,9 +4,9 @@ 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/screens/add_mount_screen.dart'; import 'package:repertory/widgets/add_mount_widget.dart';
import 'package:repertory/screens/edit_mount_screen.dart'; import 'package:repertory/widgets/mount_list_widget.dart';
import 'package:repertory/screens/home_screen.dart'; import 'package:repertory/widgets/mount_settings.dart';
void main() { void main() {
runApp( runApp(
@ -14,14 +14,9 @@ void main() {
); );
} }
class MyApp extends StatefulWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override @override
Widget build(context) { Widget build(context) {
return MaterialApp( return MaterialApp(
@ -46,23 +41,23 @@ class _MyAppState extends State<MyApp> {
), ),
), ),
initialRoute: '/', initialRoute: '/',
routes: { routes: {'/': (context) => const MyHomePage(title: constants.appTitle)},
'/': (context) => const HomeScreen(title: constants.appTitle),
'/add':
(context) => const AddMountScreen(title: constants.addMountTitle),
},
onGenerateRoute: (settings) { onGenerateRoute: (settings) {
if (settings.name != '/edit') { if (settings.name != '/settings') {
return null; return null;
} }
final mount = settings.arguments as Mount; final mount = settings.arguments as Mount;
return MaterialPageRoute( return MaterialPageRoute(
builder: (context) { builder: (context) {
return EditMountScreen( return Scaffold(
mount: mount, appBar: AppBar(
title: backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(
'${initialCaps(mount.type)} [${formatMountName(mount.type, mount.name)}] Settings', '${initialCaps(mount.type)} [${formatMountName(mount.type, mount.name)}] Settings',
),
),
body: MountSettingsWidget(mount: mount),
); );
}, },
); );
@ -70,3 +65,152 @@ class _MyAppState extends State<MyApp> {
); );
} }
} }
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({super.key, required this.title});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _allowAdd = true;
String? _apiPassword;
String? _apiPort;
String? _bucket;
String? _encryptionToken;
String? _hostNameOrIp;
String _mountType = 'Encrypt';
String _mountName = "";
String? _path;
void _resetData() {
_apiPassword = null;
_apiPort = null;
_bucket = null;
_encryptionToken = null;
_hostNameOrIp = null;
_mountName = "";
_mountType = 'Encrypt';
_path = null;
}
void _updateData(String name, String? value) {
switch (name) {
case 'ApiPassword':
_apiPassword = value ?? '';
return;
case 'ApiPort':
_apiPort = value ?? '';
return;
case 'Bucket':
_bucket = value ?? '';
return;
case 'EncryptionToken':
_encryptionToken = value ?? '';
return;
case 'HostNameOrIp':
_hostNameOrIp = value ?? '';
return;
case 'Name':
_mountName = value ?? '';
return;
case 'Provider':
_mountType = value ?? 'Encrypt';
return;
case 'Path':
_path = value ?? '';
return;
}
}
@override
Widget build(context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
leading: const Icon(Icons.storage),
title: Text(widget.title),
),
body: MountListWidget(),
floatingActionButton: FloatingActionButton(
onPressed:
_allowAdd
? () async => showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Add Mount'),
content: Consumer<MountList>(
builder: (_, MountList mountList, __) {
return AddMountWidget(
mountType: _mountType,
onDataChanged: _updateData,
);
},
),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () {
_resetData();
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('OK'),
onPressed: () {
setState(() => _allowAdd = false);
Provider.of<MountList>(context, listen: false)
.add(
_mountType,
_mountName,
apiPassword: _apiPassword,
apiPort: _apiPort,
bucket: _bucket,
encryptionToken: _encryptionToken,
hostNameOrIp: _hostNameOrIp,
path: _path,
)
.then((_) {
_resetData();
setState(() {
_allowAdd = true;
});
})
.catchError((_) {
setState(() => _allowAdd = true);
});
Navigator.of(context).pop();
},
),
],
);
},
)
: null,
tooltip: 'Add Mount',
child: const Icon(Icons.add),
),
);
}
@override
void setState(VoidCallback fn) {
if (!mounted) {
return;
}
super.setState(fn);
}
}

View File

@ -7,10 +7,7 @@ import 'package:repertory/types/mount_config.dart';
class Mount with ChangeNotifier { class Mount with ChangeNotifier {
final MountConfig mountConfig; final MountConfig mountConfig;
Mount(this.mountConfig, {isAdd = false}) { Mount(this.mountConfig) {
if (isAdd) {
return;
}
refresh(); refresh();
} }
@ -20,60 +17,48 @@ class Mount with ChangeNotifier {
String get type => mountConfig.type; String get type => mountConfig.type;
Future<void> _fetch() async { Future<void> _fetch() async {
try { final response = await http.get(
final response = await http.get( Uri.parse(
Uri.parse( Uri.encodeFull('${getBaseUri()}/api/v1/mount?name=$name&type=$type'),
Uri.encodeFull('${getBaseUri()}/api/v1/mount?name=$name&type=$type'), ),
), );
);
if (response.statusCode != 200) { if (response.statusCode != 200) {
return; return;
}
mountConfig.updateSettings(jsonDecode(response.body));
notifyListeners();
} catch (e) {
debugPrint('$e');
} }
mountConfig.updateSettings(jsonDecode(response.body));
notifyListeners();
} }
Future<void> _fetchStatus() async { Future<void> _fetchStatus() async {
try { final response = await http.get(
final response = await http.get( Uri.parse(
Uri.parse( Uri.encodeFull(
Uri.encodeFull( '${getBaseUri()}/api/v1/mount_status?name=$name&type=$type',
'${getBaseUri()}/api/v1/mount_status?name=$name&type=$type',
),
), ),
); ),
);
if (response.statusCode != 200) { if (response.statusCode != 200) {
return; return;
}
mountConfig.updateStatus(jsonDecode(response.body));
notifyListeners();
} catch (e) {
debugPrint('$e');
} }
mountConfig.updateStatus(jsonDecode(response.body));
notifyListeners();
} }
Future<void> mount(bool unmount, {String? location}) async { Future<void> mount(bool unmount, {String? location}) async {
try { await http.post(
await http.post( Uri.parse(
Uri.parse( Uri.encodeFull(
Uri.encodeFull( '${getBaseUri()}/api/v1/mount?unmount=$unmount&name=$name&type=$type&location=$location',
'${getBaseUri()}/api/v1/mount?unmount=$unmount&name=$name&type=$type&location=$location',
),
), ),
); ),
);
return refresh(); return refresh();
} catch (e) {
debugPrint('$e');
}
} }
Future<void> refresh() async { Future<void> refresh() async {
@ -82,40 +67,30 @@ class Mount with ChangeNotifier {
} }
Future<void> setValue(String key, String value) async { Future<void> setValue(String key, String value) async {
try { await http.put(
await http.put( Uri.parse(
Uri.parse( Uri.encodeFull(
Uri.encodeFull( '${getBaseUri()}/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',
),
), ),
); ),
);
return refresh(); return refresh();
} catch (e) {
debugPrint('$e');
}
} }
Future<String?> getMountLocation() async { Future<String?> getMountLocation() async {
try { final response = await http.get(
final response = await http.get( Uri.parse(
Uri.parse( Uri.encodeFull(
Uri.encodeFull( '${getBaseUri()}/api/v1/get_mount_location?name=$name&type=$type',
'${getBaseUri()}/api/v1/get_mount_location?name=$name&type=$type',
),
), ),
); ),
);
if (response.statusCode != 200) { if (response.statusCode != 200) {
return null; return null;
}
return jsonDecode(response.body)['Location'] as String;
} catch (e) {
debugPrint('$e');
} }
return null; return jsonDecode(response.body)['Location'] as String;
} }
} }

View File

@ -17,14 +17,11 @@ class MountList with ChangeNotifier {
UnmodifiableListView<MountConfig>(_mountList); UnmodifiableListView<MountConfig>(_mountList);
Future<void> _fetch() async { Future<void> _fetch() async {
try { final response = await http.get(
final response = await http.get( Uri.parse('${getBaseUri()}/api/v1/mount_list'),
Uri.parse('${getBaseUri()}/api/v1/mount_list'), );
);
if (response.statusCode != 200) { if (response.statusCode == 200) {
return;
}
List<MountConfig> nextList = []; List<MountConfig> nextList = [];
jsonDecode(response.body).forEach((key, value) { jsonDecode(response.body).forEach((key, value) {
@ -36,8 +33,7 @@ class MountList with ChangeNotifier {
_mountList = nextList; _mountList = nextList;
notifyListeners(); notifyListeners();
} catch (e) { return;
debugPrint('$e');
} }
} }
@ -54,22 +50,25 @@ class MountList with ChangeNotifier {
Future<void> add( Future<void> add(
String type, String type,
String name, String name, {
Map<String, dynamic> mountConfig, String? apiPassword,
) async { String? apiPort,
try { String? bucket,
await http.post( String? encryptionToken,
Uri.parse( String? hostNameOrIp,
Uri.encodeFull( String? path,
'${getBaseUri()}/api/v1/add_mount?name=$name&type=$type&config=${jsonEncode(mountConfig)}', }) async {
), await http.post(
Uri.parse(
Uri.encodeFull(
'${getBaseUri()}/api/v1/add_mount?name=$name&type=$type&bucket=$bucket'
'&path=$path&apiPassword=$apiPassword&apiPort=$apiPort&hostNameOrIp=$hostNameOrIp'
'&encryptionToken=$encryptionToken',
), ),
); ),
);
return _fetch(); return _fetch();
} catch (e) {
debugPrint('$e');
}
} }
void remove(String name) { void remove(String name) {

View File

@ -1,165 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:repertory/constants.dart';
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/mount_settings.dart';
class AddMountScreen extends StatefulWidget {
final String title;
const AddMountScreen({super.key, required this.title});
@override
State<AddMountScreen> createState() => _AddMountScreenState();
}
class _AddMountScreenState extends State<AddMountScreen> {
static const _padding = 15.0;
Mount? _mount;
final _mountNameController = TextEditingController();
String _mountType = "";
final Map<String, Map<String, dynamic>> _settings = {
"": {},
"Encrypt": createDefaultSettings("Encrypt"),
"Remote": createDefaultSettings("Remote"),
"S3": createDefaultSettings("S3"),
"Sia": createDefaultSettings("Sia"),
};
bool _showAdvanced = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
actions: [
Row(
children: [
const Text("Advanced"),
IconButton(
icon: Icon(_showAdvanced ? Icons.toggle_on : Icons.toggle_off),
onPressed: () => setState(() => _showAdvanced = !_showAdvanced),
),
],
),
],
),
body: Padding(
padding: const EdgeInsets.all(_padding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(_padding),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text('Provider Type'),
const SizedBox(width: _padding),
DropdownButton<String>(
value: _mountType,
onChanged: (mountType) => _handleChange(mountType ?? ''),
items:
providerTypeList.map<DropdownMenuItem<String>>((
item,
) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
],
),
),
),
if (_mountType.isNotEmpty) const SizedBox(height: _padding),
if (_mountType.isNotEmpty)
Card(
child: Padding(
padding: const EdgeInsets.all(_padding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text('Configuration Name'),
const SizedBox(width: _padding),
TextField(
autofocus: true,
controller: _mountNameController,
keyboardType: TextInputType.text,
onChanged: (_) => _handleChange(_mountType),
),
],
),
),
),
if (_mount != null)
Expanded(
child: Card(
child: Padding(
padding: const EdgeInsets.all(_padding),
child: MountSettingsWidget(
isAdd: true,
mount: _mount!,
settings: _settings[_mountType]!,
showAdvanced: _showAdvanced,
),
),
),
),
if (_mount != null)
ElevatedButton.icon(
onPressed: () {
Provider.of<MountList>(context, listen: false).add(
_mountType,
_mountNameController.text,
_settings[_mountType]!,
);
Navigator.pop(context);
},
label: const Text('Add'),
icon: Icon(Icons.add),
),
],
),
),
);
}
void _handleChange(String mountType) {
setState(() {
_mountType = mountType;
_mount =
(_mountNameController.text.isEmpty)
? null
: Mount(
MountConfig(
name: _mountNameController.text,
settings: _settings[mountType],
type: mountType,
),
isAdd: true,
);
});
}
@override
void setState(VoidCallback fn) {
if (!mounted) {
return;
}
super.setState(fn);
}
}

View File

@ -1,53 +0,0 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:repertory/models/mount.dart';
import 'package:repertory/widgets/mount_settings.dart';
class EditMountScreen extends StatefulWidget {
final Mount mount;
final String title;
const EditMountScreen({super.key, required this.mount, required this.title});
@override
State<EditMountScreen> createState() => _EditMountScreenState();
}
class _EditMountScreenState extends State<EditMountScreen> {
bool _showAdvanced = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
actions: [
Row(
children: [
const Text("Advanced"),
IconButton(
icon: Icon(_showAdvanced ? Icons.toggle_on : Icons.toggle_off),
onPressed: () => setState(() => _showAdvanced = !_showAdvanced),
),
],
),
],
),
body: MountSettingsWidget(
mount: widget.mount,
settings: jsonDecode(jsonEncode(widget.mount.mountConfig.settings)),
showAdvanced: _showAdvanced,
),
);
}
@override
void setState(VoidCallback fn) {
if (!mounted) {
return;
}
super.setState(fn);
}
}

View File

@ -1,38 +0,0 @@
import 'package:flutter/material.dart';
import 'package:repertory/widgets/mount_list_widget.dart';
class HomeScreen extends StatefulWidget {
final String title;
const HomeScreen({super.key, required this.title});
@override
State<HomeScreen> createState() => _HomeScreeState();
}
class _HomeScreeState extends State<HomeScreen> {
@override
Widget build(context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
leading: const Icon(Icons.storage),
title: Text(widget.title),
),
body: MountListWidget(),
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pushNamed(context, '/add'),
tooltip: 'Add Mount',
child: const Icon(Icons.add),
),
);
}
@override
void setState(VoidCallback fn) {
if (!mounted) {
return;
}
super.setState(fn);
}
}

View File

@ -7,13 +7,7 @@ class MountConfig {
Map<String, dynamic> _settings = {}; Map<String, dynamic> _settings = {};
IconData _state = Icons.toggle_off; IconData _state = Icons.toggle_off;
final String _type; final String _type;
MountConfig({required name, required type, Map<String, dynamic>? settings}) MountConfig({required name, required type}) : _name = name, _type = type;
: _name = name,
_type = type {
if (settings != null) {
_settings = settings;
}
}
String get name => _name; String get name => _name;
String get path => _path; String get path => _path;

View File

@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
class AddMountWidget extends StatefulWidget {
final String mountType;
final void Function(String name, String? value) onDataChanged;
const AddMountWidget({
super.key,
required this.mountType,
required this.onDataChanged,
});
@override
State<AddMountWidget> createState() => _AddMountWidgetState();
}
class _AddMountWidgetState extends State<AddMountWidget> {
static const _items = <String>['Encrypt', 'Remote', 'S3', 'Sia'];
static const _padding = 15.0;
late String _mountType;
List<Widget> _createTextField(
String title,
String dataName, {
String? value,
}) {
if (value != null) {
widget.onDataChanged(dataName, value);
}
return [
const SizedBox(height: _padding),
Text(
title,
textAlign: TextAlign.left,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
TextField(
autofocus: dataName == 'Name',
decoration: InputDecoration(),
onChanged: (value) => widget.onDataChanged(dataName, value),
controller: TextEditingController(text: value),
),
];
}
@override
Widget build(BuildContext context) {
final mountTypeLower = _mountType.toLowerCase();
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Provider Type',
textAlign: TextAlign.left,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: _padding),
DropdownButton<String>(
value: _mountType,
onChanged: (value) {
widget.onDataChanged('Provider', value);
setState(() {
_mountType = value ?? "";
});
},
items:
_items.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
],
),
if (mountTypeLower != 'remote')
..._createTextField('Configuration Name', 'Name'),
if (mountTypeLower == 'encrypt') ..._createTextField('Path', 'Path'),
if (mountTypeLower == 'sia')
..._createTextField('ApiPassword', 'ApiPassword'),
if (mountTypeLower == 's3' || mountTypeLower == 'sia')
..._createTextField(
'Bucket',
'Bucket',
value: mountTypeLower == 'sia' ? 'default' : null,
),
if (mountTypeLower == 'remote' || mountTypeLower == 'sia')
..._createTextField(
'HostNameOrIp',
'HostNameOrIp',
value: 'localhost',
),
if (mountTypeLower == 'remote' || mountTypeLower == 'sia')
..._createTextField(
'ApiPort',
'ApiPort',
value: mountTypeLower == 'sia' ? '9980' : null,
),
if (mountTypeLower == 'encrypt' || mountTypeLower == 'remote')
..._createTextField('EncryptionToken', 'EncryptionToken'),
],
);
}
@override
void initState() {
_mountType = widget.mountType;
super.initState();
}
}

View File

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

View File

@ -1,102 +1,87 @@
import 'dart:convert';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:repertory/constants.dart';
import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount.dart';
import 'package:settings_ui/settings_ui.dart'; import 'package:settings_ui/settings_ui.dart';
class MountSettingsWidget extends StatefulWidget { class MountSettingsWidget extends StatefulWidget {
final bool isAdd;
final bool showAdvanced;
final Mount mount; final Mount mount;
final Function? onChanged; const MountSettingsWidget({super.key, required this.mount});
final Map<String, dynamic> settings;
const MountSettingsWidget({
super.key,
this.isAdd = false,
required this.mount,
this.onChanged,
required this.settings,
required this.showAdvanced,
});
@override @override
State<MountSettingsWidget> createState() => _MountSettingsWidgetState(); State<MountSettingsWidget> createState() => _MountSettingsWidgetState();
} }
class _MountSettingsWidgetState extends State<MountSettingsWidget> { class _MountSettingsWidgetState extends State<MountSettingsWidget> {
void _addBooleanSetting(list, root, key, value, isAdvanced) { Map<String, dynamic> _settings = {};
if (!isAdvanced || widget.showAdvanced) {
list.add( void _addBooleanSetting(list, root, key, value) {
SettingsTile.switchTile( list.add(
leading: Icon(Icons.quiz), SettingsTile.switchTile(
title: Text(key), leading: Icon(Icons.quiz),
initialValue: (value as bool), title: Text(key),
onPressed: (_) { initialValue: (value as bool),
setState(() { onPressed: (_) {
root[key] = !value; setState(() {
widget.onChanged?.call(widget.settings); root[key] = !value;
}); });
}, },
onToggle: (bool nextValue) { onToggle: (bool nextValue) {
setState(() { setState(() {
root[key] = nextValue; root[key] = nextValue;
widget.onChanged?.call(widget.settings); });
}); },
}, ),
), );
);
}
} }
void _addIntSetting(list, root, key, value, isAdvanced) { void _addIntSetting(list, root, key, value) {
if (!isAdvanced || widget.showAdvanced) { list.add(
list.add( SettingsTile.navigation(
SettingsTile.navigation( leading: Icon(Icons.onetwothree),
leading: Icon(Icons.onetwothree), title: Text(key),
title: Text(key), value: Text(value.toString()),
value: Text(value.toString()), onPressed: (_) {
onPressed: (_) { String updatedValue = value.toString();
String updatedValue = value.toString(); showDialog(
showDialog( context: context,
context: context, builder: (context) {
builder: (context) { return AlertDialog(
return AlertDialog( actions: [
actions: [ TextButton(
TextButton( child: Text('Cancel'),
child: Text('Cancel'), onPressed: () {
onPressed: () { Navigator.of(context).pop();
Navigator.of(context).pop();
},
),
TextButton(
child: Text('OK'),
onPressed: () {
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: Text(key), TextButton(
); child: Text('OK'),
}, onPressed: () {
); setState(() {
}, root[key] = int.parse(updatedValue);
), });
); Navigator.of(context).pop();
} },
),
],
content: TextField(
autofocus: true,
controller: TextEditingController(text: updatedValue),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
keyboardType: TextInputType.number,
onChanged: (nextValue) {
updatedValue = nextValue;
},
),
title: Text(key),
);
},
);
},
),
);
} }
void _addIntListSetting( void _addIntListSetting(
@ -107,127 +92,101 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
List<String> valueList, List<String> valueList,
defaultValue, defaultValue,
icon, icon,
isAdvanced,
) { ) {
if (!isAdvanced || widget.showAdvanced) { list.add(
list.add( SettingsTile.navigation(
SettingsTile.navigation( title: Text(key),
title: Text(key), leading: Icon(icon),
leading: Icon(icon), value: DropdownButton<String>(
value: DropdownButton<String>( value: value.toString(),
value: value.toString(), onChanged: (newValue) {
onChanged: (newValue) { setState(() {
setState(() { root[key] = int.parse(newValue ?? defaultValue.toString());
root[key] = int.parse(newValue ?? defaultValue.toString()); });
widget.onChanged?.call(widget.settings); },
}); items:
}, valueList.map<DropdownMenuItem<String>>((item) {
items: return DropdownMenuItem<String>(value: item, child: Text(item));
valueList.map<DropdownMenuItem<String>>((item) { }).toList(),
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
), ),
); ),
} );
} }
void _addListSetting( void _addListSetting(list, root, key, value, List<String> valueList, icon) {
list, list.add(
root, SettingsTile.navigation(
key, title: Text(key),
value, leading: Icon(icon),
List<String> valueList, value: DropdownButton<String>(
icon, value: value,
isAdvanced, onChanged: (newValue) {
) { setState(() {
if (!isAdvanced || widget.showAdvanced) { root[key] = newValue;
list.add( });
SettingsTile.navigation( },
title: Text(key), items:
leading: Icon(icon), valueList.map<DropdownMenuItem<String>>((item) {
value: DropdownButton<String>( return DropdownMenuItem<String>(value: item, child: Text(item));
value: value, }).toList(),
onChanged: (newValue) {
setState(() {
root[key] = newValue;
widget.onChanged?.call(widget.settings);
});
},
items:
valueList.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
), ),
); ),
} );
} }
void _addPasswordSetting(list, root, key, value, isAdvanced) { void _addPasswordSetting(list, root, key, value) {
if (!isAdvanced || widget.showAdvanced) { list.add(
list.add( SettingsTile.navigation(
SettingsTile.navigation( leading: Icon(Icons.password),
leading: Icon(Icons.password), title: Text(key),
title: Text(key), value: Text('*' * (value as String).length),
value: Text('*' * (value as String).length), ),
), );
);
}
} }
void _addStringSetting(list, root, key, value, icon, isAdvanced) { void _addStringSetting(list, root, key, value, icon) {
if (!isAdvanced || widget.showAdvanced) { list.add(
list.add( SettingsTile.navigation(
SettingsTile.navigation( leading: Icon(icon),
leading: Icon(icon), title: Text(key),
title: Text(key), value: Text(value),
value: Text(value), onPressed: (_) {
onPressed: (_) { String updatedValue = value;
String updatedValue = value; showDialog(
showDialog( context: context,
context: context, builder: (context) {
builder: (context) { return AlertDialog(
return AlertDialog( actions: [
actions: [ TextButton(
TextButton( child: Text('Cancel'),
child: Text('Cancel'), onPressed: () {
onPressed: () { Navigator.of(context).pop();
Navigator.of(context).pop();
},
),
TextButton(
child: Text('OK'),
onPressed: () {
setState(() {
root[key] = updatedValue;
widget.onChanged?.call(widget.settings);
});
Navigator.of(context).pop();
},
),
],
content: TextField(
autofocus: true,
controller: TextEditingController(text: updatedValue),
onChanged: (value) {
updatedValue = value;
}, },
), ),
title: Text(key), TextButton(
); child: Text('OK'),
}, onPressed: () {
); setState(() {
}, root[key] = updatedValue;
), });
); Navigator.of(context).pop();
} },
),
],
content: TextField(
autofocus: true,
controller: TextEditingController(text: updatedValue),
onChanged: (value) {
updatedValue = value;
},
),
title: Text(key),
);
},
);
},
),
);
} }
@override @override
@ -240,97 +199,77 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
List<SettingsTile> s3ConfigSettings = []; List<SettingsTile> s3ConfigSettings = [];
List<SettingsTile> siaConfigSettings = []; List<SettingsTile> siaConfigSettings = [];
widget.settings.forEach((key, value) { _settings.forEach((key, value) {
if (key == 'ApiAuth') { if (key == 'ApiAuth') {
_addPasswordSetting(commonSettings, widget.settings, key, value, true); _addPasswordSetting(commonSettings, _settings, key, value);
} else if (key == 'ApiPort') { } else if (key == 'ApiPort') {
_addIntSetting(commonSettings, widget.settings, key, value, true); _addIntSetting(commonSettings, _settings, key, value);
} else if (key == 'ApiUser') { } else if (key == 'ApiUser') {
_addStringSetting( _addStringSetting(commonSettings, _settings, key, value, Icons.person);
commonSettings,
widget.settings,
key,
value,
Icons.person,
true,
);
} else if (key == 'DatabaseType') { } else if (key == 'DatabaseType') {
_addListSetting( _addListSetting(commonSettings, _settings, key, value, [
commonSettings, 'rocksdb',
widget.settings, 'sqlite',
key, ], Icons.dataset);
value,
databaseTypeList,
Icons.dataset,
true,
);
} else if (key == 'DownloadTimeoutSeconds') { } else if (key == 'DownloadTimeoutSeconds') {
_addIntSetting(commonSettings, widget.settings, key, value, true); _addIntSetting(commonSettings, _settings, key, value);
} else if (key == 'EnableDownloadTimeout') { } else if (key == 'EnableDownloadTimeout') {
_addBooleanSetting(commonSettings, widget.settings, key, value, true); _addBooleanSetting(commonSettings, _settings, key, value);
} else if (key == 'EnableDriveEvents') { } else if (key == 'EnableDriveEvents') {
_addBooleanSetting(commonSettings, widget.settings, key, value, true); _addBooleanSetting(commonSettings, _settings, key, value);
} else if (key == 'EventLevel') { } else if (key == 'EventLevel') {
_addListSetting( _addListSetting(commonSettings, _settings, key, value, [
commonSettings, 'critical',
widget.settings, 'error',
key, 'warn',
value, 'info',
eventLevelList, 'debug',
Icons.event, 'trace',
false, ], Icons.event);
);
} else if (key == 'EvictionDelayMinutes') { } else if (key == 'EvictionDelayMinutes') {
_addIntSetting(commonSettings, widget.settings, key, value, true); _addIntSetting(commonSettings, _settings, key, value);
} else if (key == 'EvictionUseAccessedTime') { } else if (key == 'EvictionUseAccessedTime') {
_addBooleanSetting(commonSettings, widget.settings, key, value, true); _addBooleanSetting(commonSettings, _settings, key, value);
} else if (key == 'MaxCacheSizeBytes') { } else if (key == 'MaxCacheSizeBytes') {
_addIntSetting(commonSettings, widget.settings, key, value, false); _addIntSetting(commonSettings, _settings, key, value);
} else if (key == 'MaxUploadCount') { } else if (key == 'MaxUploadCount') {
_addIntSetting(commonSettings, widget.settings, key, value, true); _addIntSetting(commonSettings, _settings, key, value);
} else if (key == 'OnlineCheckRetrySeconds') { } else if (key == 'OnlineCheckRetrySeconds') {
_addIntSetting(commonSettings, widget.settings, key, value, true); _addIntSetting(commonSettings, _settings, key, value);
} else if (key == 'PreferredDownloadType') { } else if (key == 'PreferredDownloadType') {
_addListSetting( _addListSetting(commonSettings, _settings, key, value, [
commonSettings, 'default',
widget.settings, 'direct',
key, 'ring_buffer',
value, ], Icons.download);
downloadTypeList,
Icons.download,
false,
);
} else if (key == 'RetryReadCount') { } else if (key == 'RetryReadCount') {
_addIntSetting(commonSettings, widget.settings, key, value, true); _addIntSetting(commonSettings, _settings, key, value);
} else if (key == 'RingBufferFileSize') { } else if (key == 'RingBufferFileSize') {
_addIntListSetting( _addIntListSetting(
commonSettings, commonSettings,
widget.settings, _settings,
key, key,
value, value,
ringBufferSizeList, ['128', '256', '512', '1024', '2048'],
512, 512,
Icons.animation, Icons.animation,
false,
); );
} else if (key == 'EncryptConfig') { } else if (key == 'EncryptConfig') {
value.forEach((subKey, subValue) { value.forEach((subKey, subValue) {
if (subKey == 'EncryptionToken') { if (subKey == 'EncryptionToken') {
_addPasswordSetting( _addPasswordSetting(
encryptConfigSettings, encryptConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'Path') { } else if (subKey == 'Path') {
_addStringSetting( _addStringSetting(
encryptConfigSettings, encryptConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.folder, Icons.folder,
false,
); );
} }
}); });
@ -339,72 +278,64 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
if (subKey == 'AgentString') { if (subKey == 'AgentString') {
_addStringSetting( _addStringSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.support_agent, Icons.support_agent,
true,
); );
} else if (subKey == 'ApiPassword') { } else if (subKey == 'ApiPassword') {
_addPasswordSetting( _addPasswordSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'ApiPort') { } else if (subKey == 'ApiPort') {
_addIntSetting( _addIntSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'ApiUser') { } else if (subKey == 'ApiUser') {
_addStringSetting( _addStringSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.person, Icons.person,
true,
); );
} else if (subKey == 'HostNameOrIp') { } else if (subKey == 'HostNameOrIp') {
_addStringSetting( _addStringSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.computer, Icons.computer,
false,
); );
} else if (subKey == 'Path') { } else if (subKey == 'Path') {
_addStringSetting( _addStringSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.route, Icons.route,
true,
); );
} else if (subKey == 'Protocol') { } else if (subKey == 'Protocol') {
_addListSetting( _addListSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
protocolTypeList, ['http', 'https'],
Icons.http, Icons.http,
true,
); );
} else if (subKey == 'TimeoutMs') { } else if (subKey == 'TimeoutMs') {
_addIntSetting( _addIntSetting(
hostConfigSettings, hostConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
true,
); );
} }
}); });
@ -413,51 +344,45 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
if (subKey == 'ApiPort') { if (subKey == 'ApiPort') {
_addIntSetting( _addIntSetting(
remoteConfigSettings, remoteConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'EncryptionToken') { } else if (subKey == 'EncryptionToken') {
_addPasswordSetting( _addPasswordSetting(
remoteConfigSettings, remoteConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'HostNameOrIp') { } else if (subKey == 'HostNameOrIp') {
_addStringSetting( _addStringSetting(
remoteConfigSettings, remoteConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.computer, Icons.computer,
false,
); );
} else if (subKey == 'MaxConnections') { } else if (subKey == 'MaxConnections') {
_addIntSetting( _addIntSetting(
remoteConfigSettings, remoteConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
true,
); );
} else if (subKey == 'ReceiveTimeoutMs') { } else if (subKey == 'ReceiveTimeoutMs') {
_addIntSetting( _addIntSetting(
remoteConfigSettings, remoteConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
true,
); );
} else if (subKey == 'SendTimeoutMs') { } else if (subKey == 'SendTimeoutMs') {
_addIntSetting( _addIntSetting(
remoteConfigSettings, remoteConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
true,
); );
} }
}); });
@ -465,37 +390,28 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
value.forEach((subKey, subValue) { value.forEach((subKey, subValue) {
if (subKey == 'Enable') { if (subKey == 'Enable') {
List<SettingsTile> tempSettings = []; List<SettingsTile> tempSettings = [];
_addBooleanSetting( _addBooleanSetting(tempSettings, _settings[key], subKey, subValue);
tempSettings,
widget.settings[key],
subKey,
subValue,
false,
);
remoteMountSettings.insertAll(0, tempSettings); remoteMountSettings.insertAll(0, tempSettings);
} else if (subKey == 'ApiPort') { } else if (subKey == 'ApiPort') {
_addIntSetting( _addIntSetting(
remoteMountSettings, remoteMountSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'ClientPoolSize') { } else if (subKey == 'ClientPoolSize') {
_addIntSetting( _addIntSetting(
remoteMountSettings, remoteMountSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
true,
); );
} else if (subKey == 'EncryptionToken') { } else if (subKey == 'EncryptionToken') {
_addPasswordSetting( _addPasswordSetting(
remoteMountSettings, remoteMountSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} }
}); });
@ -504,77 +420,63 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
if (subKey == 'AccessKey') { if (subKey == 'AccessKey') {
_addPasswordSetting( _addPasswordSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'Bucket') { } else if (subKey == 'Bucket') {
_addStringSetting( _addStringSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.folder, Icons.folder,
false,
); );
} else if (subKey == 'EncryptionToken') { } else if (subKey == 'EncryptionToken') {
_addPasswordSetting( _addPasswordSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'Region') { } else if (subKey == 'Region') {
_addStringSetting( _addStringSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.map, Icons.map,
false,
); );
} else if (subKey == 'SecretKey') { } else if (subKey == 'SecretKey') {
_addPasswordSetting( _addPasswordSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'TimeoutMs') { } else if (subKey == 'TimeoutMs') {
_addIntSetting( _addIntSetting(s3ConfigSettings, _settings[key], subKey, subValue);
s3ConfigSettings,
widget.settings[key],
subKey,
subValue,
true,
);
} else if (subKey == 'URL') { } else if (subKey == 'URL') {
_addStringSetting( _addStringSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.http, Icons.http,
false,
); );
} else if (subKey == 'UsePathStyle') { } else if (subKey == 'UsePathStyle') {
_addBooleanSetting( _addBooleanSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} else if (subKey == 'UseRegionInURL') { } else if (subKey == 'UseRegionInURL') {
_addBooleanSetting( _addBooleanSetting(
s3ConfigSettings, s3ConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
false,
); );
} }
}); });
@ -583,11 +485,10 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
if (subKey == 'Bucket') { if (subKey == 'Bucket') {
_addStringSetting( _addStringSetting(
siaConfigSettings, siaConfigSettings,
widget.settings[key], _settings[key],
subKey, subKey,
subValue, subValue,
Icons.folder, Icons.folder,
false,
); );
} }
}); });
@ -626,40 +527,51 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
SettingsSection( SettingsSection(
title: const Text('Remote Mount'), title: const Text('Remote Mount'),
tiles: tiles:
widget.settings['RemoteMount']['Enable'] as bool _settings['RemoteMount']['Enable'] as bool
? remoteMountSettings ? remoteMountSettings
: [remoteMountSettings[0]], : [remoteMountSettings[0]],
), ),
if (commonSettings.isNotEmpty) SettingsSection(title: const Text('Settings'), tiles: commonSettings),
SettingsSection(title: const Text('Settings'), tiles: commonSettings),
], ],
); );
} }
@override @override
void dispose() { void dispose() {
if (!widget.isAdd) { var settings = widget.mount.mountConfig.settings;
var settings = widget.mount.mountConfig.settings; if (!DeepCollectionEquality().equals(_settings, settings)) {
if (!DeepCollectionEquality().equals(widget.settings, settings)) { _settings.forEach((key, value) {
widget.settings.forEach((key, value) { if (!DeepCollectionEquality().equals(settings[key], value)) {
if (!DeepCollectionEquality().equals(settings[key], value)) { if (value is Map<String, dynamic>) {
if (value is Map<String, dynamic>) { value.forEach((subKey, subValue) {
value.forEach((subKey, subValue) { if (!DeepCollectionEquality().equals(
if (!DeepCollectionEquality().equals( settings[key][subKey],
settings[key][subKey], subValue,
subValue, )) {
)) { widget.mount.setValue('$key.$subKey', subValue.toString());
widget.mount.setValue('$key.$subKey', subValue.toString()); }
} });
}); } else {
} else { widget.mount.setValue(key, value.toString());
widget.mount.setValue(key, value.toString());
}
} }
}); }
} });
} }
super.dispose(); super.dispose();
} }
@override
void initState() {
_settings = jsonDecode(jsonEncode(widget.mount.mountConfig.settings));
super.initState();
}
@override
void setState(VoidCallback fn) {
if (!mounted) {
return;
}
super.setState(fn);
}
} }

View File

@ -38,7 +38,7 @@ class _MountWidgetState extends State<MountWidget> {
leading: IconButton( leading: IconButton(
icon: Icon(Icons.settings, color: textColor), icon: Icon(Icons.settings, color: textColor),
onPressed: () { onPressed: () {
Navigator.pushNamed(context, '/edit', arguments: mount); Navigator.pushNamed(context, '/settings', arguments: mount);
}, },
), ),
subtitle: Column( subtitle: Column(