Snippet support import/export.

This commit is contained in:
Junyuan Feng
2022-04-07 20:00:06 +08:00
parent fa73c4feee
commit 4274e8bed1
5 changed files with 128 additions and 13 deletions

View File

@@ -29,7 +29,8 @@ class ServerTabMenuItems {
static const sftp = MenuItem(text: 'SFTP', icon: Icons.insert_drive_file); static const sftp = MenuItem(text: 'SFTP', icon: Icons.insert_drive_file);
static const snippet = MenuItem(text: 'Snippet', icon: Icons.label); static const snippet = MenuItem(text: 'Snippet', icon: Icons.label);
static const apt = MenuItem(text: 'Apt/Yum', icon: Icons.system_security_update); static const apt =
MenuItem(text: 'Apt/Yum', icon: Icons.system_security_update);
static const docker = MenuItem(text: 'Docker', icon: Icons.view_agenda); static const docker = MenuItem(text: 'Docker', icon: Icons.view_agenda);
static const edit = MenuItem(text: 'Edit', icon: Icons.edit); static const edit = MenuItem(text: 'Edit', icon: Icons.edit);
} }

View File

@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:toolbox/core/provider_base.dart'; import 'package:toolbox/core/provider_base.dart';
import 'package:toolbox/data/model/server/snippet.dart'; import 'package:toolbox/data/model/server/snippet.dart';
import 'package:toolbox/data/store/snippet.dart'; import 'package:toolbox/data/store/snippet.dart';
@@ -11,22 +13,34 @@ class SnippetProvider extends BusyProvider {
_snippets = locator<SnippetStore>().fetch(); _snippets = locator<SnippetStore>().fetch();
} }
void addInfo(Snippet snippet) { void add(Snippet snippet) {
if (have(snippet)) return;
_snippets.add(snippet); _snippets.add(snippet);
locator<SnippetStore>().put(snippet); locator<SnippetStore>().put(snippet);
notifyListeners(); notifyListeners();
} }
void delInfo(Snippet snippet) { void del(Snippet snippet) {
_snippets.removeWhere((e) => e.name == snippet.name); if (!have(snippet)) return;
_snippets.removeAt(index(snippet));
locator<SnippetStore>().delete(snippet); locator<SnippetStore>().delete(snippet);
notifyListeners(); notifyListeners();
} }
void updateInfo(Snippet old, Snippet newOne) { int index(Snippet snippet) {
final idx = _snippets.indexWhere((e) => e.name == old.name); return _snippets.indexWhere((e) => e.name == snippet.name);
_snippets[idx] = newOne; }
bool have(Snippet snippet) {
return index(snippet) != -1;
}
void update(Snippet old, Snippet newOne) {
if (!have(old)) return;
_snippets[index(old)] = newOne;
locator<SnippetStore>().update(old, newOne); locator<SnippetStore>().update(old, newOne);
notifyListeners(); notifyListeners();
} }
String get export => json.encode(snippets);
} }

View File

@@ -2,9 +2,9 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 110; static const int build = 111;
static const String engine = static const String engine =
"Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision c860cba910 (12 days ago) • 2022-03-25 00:23:12 -0500\nEngine • revision 57d3bac3dd\nTools • Dart 2.16.2 • DevTools 2.9.2\n"; "Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision c860cba910 (13 days ago) • 2022-03-25 00:23:12 -0500\nEngine • revision 57d3bac3dd\nTools • Dart 2.16.2 • DevTools 2.9.2\n";
static const String buildAt = "2022-04-06 13:48:27.717376"; static const String buildAt = "2022-04-07 19:53:12.283804";
static const int modifications = 0; static const int modifications = 0;
} }

View File

@@ -35,7 +35,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
widget.snippet != null widget.snippet != null
? IconButton( ? IconButton(
onPressed: () { onPressed: () {
_provider.delInfo(widget.snippet!); _provider.del(widget.snippet!);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
icon: const Icon(Icons.delete)) icon: const Icon(Icons.delete))
@@ -71,9 +71,9 @@ class _SnippetEditPageState extends State<SnippetEditPage>
} }
final snippet = Snippet(name, script); final snippet = Snippet(name, script);
if (widget.snippet != null) { if (widget.snippet != null) {
_provider.updateInfo(widget.snippet!, snippet); _provider.update(widget.snippet!, snippet);
} else { } else {
_provider.addInfo(snippet); _provider.add(snippet);
} }
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },

View File

@@ -1,3 +1,4 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/core/route.dart'; import 'package:toolbox/core/route.dart';
@@ -23,12 +24,20 @@ class SnippetListPage extends StatefulWidget {
class _SnippetListPageState extends State<SnippetListPage> { class _SnippetListPageState extends State<SnippetListPage> {
late ServerPrivateInfo _selectedIndex; late ServerPrivateInfo _selectedIndex;
final _importFieldController = TextEditingController();
final _exportFieldController = TextEditingController();
final _textStyle = TextStyle(color: primaryColor); final _textStyle = TextStyle(color: primaryColor);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Snippet List'), title: const Text('Snippet List'),
actions: [
IconButton(
onPressed: () => _showImportExport(),
icon: const Icon(Icons.import_export)),
],
), ),
body: _buildBody(), body: _buildBody(),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
@@ -39,6 +48,97 @@ class _SnippetListPageState extends State<SnippetListPage> {
); );
} }
Future<void> _showImportExport() async {
await showRoundDialog(
context,
'Choose',
Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text('Import'),
leading: const Icon(Icons.download),
onTap: () => _showImportDialog(),
),
ListTile(
title: const Text('Export'),
leading: const Icon(Icons.file_upload),
onTap: () => _showExportDialog(),
),
],
),
[]);
}
Future<void> _showExportDialog() async {
Navigator.of(context).pop();
_exportFieldController.text = locator<SnippetProvider>().export;
await showRoundDialog(
context,
'Export',
TextField(
decoration: const InputDecoration(
labelText: 'JSON',
),
maxLines: 3,
controller: _exportFieldController,
),
[
TextButton(
child: const Text('OK'),
onPressed: () => Navigator.pop(context),
),
]);
}
Future<void> _showImportDialog() async {
Navigator.of(context).pop();
await showRoundDialog(
context,
'Import',
TextField(
decoration: const InputDecoration(
labelText: 'Url or JSON',
),
maxLines: 2,
controller: _importFieldController,
),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel')),
TextButton(
onPressed: () async =>
await _import(_importFieldController.text.trim()),
child: const Text('GO'),
)
]);
}
Future<void> _import(String text) async {
if (text.isEmpty) {
showSnackBar(context, const Text('field can not be empty'));
return;
}
final snippetProvider = locator<SnippetProvider>();
if (text.startsWith('http')) {
final resp = await Dio().get(text);
if (resp.statusCode != 200) {
showSnackBar(
context, Text('request failed, status code: ${resp.statusCode}'));
return;
}
for (final snippet in getSnippetList(resp.data)) {
snippetProvider.add(snippet);
}
} else {
for (final snippet in getSnippetList(text)) {
snippetProvider.add(snippet);
}
}
Navigator.of(context).pop();
}
Widget _buildBody() { Widget _buildBody() {
return Consumer<SnippetProvider>( return Consumer<SnippetProvider>(
builder: (_, key, __) { builder: (_, key, __) {