mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
opt.: TagsEditor & Btn
This commit is contained in:
@@ -31,7 +31,7 @@ Linux / Windows | [GitHub](https://github.com/lollipopkit/flutter_server_box/rel
|
||||
## 🔖 Feature
|
||||
- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Process`...
|
||||
- Platform specific: `Bio auth`、`Msg push`、`Home widget`、`watchOS App`...
|
||||
- English, 简体中文; Deutsch [@its-tom](https://github.com/its-tom), 繁體中文 [@kalashnikov](https://github.com/kalashnikov), Indonesian [@azkadev](https://github.com/azkadev), Français [@FrancXPT](https://github.com/FrancXPT), Dutch [@QazCetelic](https://github.com/QazCetelic); Español, Русский язык, Português, 日本語 (Generated by GPT)
|
||||
- English, 简体中文; Deutsch [@its-tom](https://github.com/its-tom), 繁體中文 [@kalashnikov](https://github.com/kalashnikov), Indonesian [@azkadev](https://github.com/azkadev), Français [@FrancXPT](https://github.com/FrancXPT), Dutch [@QazCetelic](https://github.com/QazCetelic), Türkçe [@mikropsoft](https://github.com/mikropsoft); Español, Русский язык, Português, 日本語 (Generated by GPT)
|
||||
|
||||
|
||||
## 🏙️ ScreenShots
|
||||
|
||||
@@ -33,7 +33,7 @@ Linux / Windows | [GitHub](https://github.com/lollipopkit/flutter_server_box/rel
|
||||
- 本地化
|
||||
- English, 简体中文
|
||||
- Español, Русский язык, Português, 日本語 (Generated by GPT)
|
||||
- Deutsch (@its-tom) / 繁體中文 (@kalashnikov) / Indonesian (@azkadev) / Français (@FrancXPT) / Dutch (@QazCetelic)
|
||||
- Deutsch [@its-tom](https://github.com/its-tom), 繁體中文 [@kalashnikov](https://github.com/kalashnikov), Indonesian [@azkadev](https://github.com/azkadev), Français [@FrancXPT](https://github.com/FrancXPT), Dutch [@QazCetelic](https://github.com/QazCetelic), Türkçe [@mikropsoft](https://github.com/mikropsoft);
|
||||
|
||||
|
||||
## 🏙️ 截屏
|
||||
|
||||
@@ -26,8 +26,8 @@ class ServerProvider extends ChangeNotifier {
|
||||
Iterable<Server> get servers => _servers.values;
|
||||
final List<String> _serverOrder = [];
|
||||
List<String> get serverOrder => _serverOrder;
|
||||
final _tags = ValueNotifier(<String>[]);
|
||||
ValueNotifier<List<String>> get tags => _tags;
|
||||
final _tags = ValueNotifier(<String>{});
|
||||
ValueNotifier<Set<String>> get tags => _tags;
|
||||
|
||||
Timer? _timer;
|
||||
|
||||
@@ -85,7 +85,6 @@ class ServerProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void _updateTags() {
|
||||
_tags.value.clear();
|
||||
for (final s in _servers.values) {
|
||||
if (s.spi.tags == null) continue;
|
||||
for (final t in s.spi.tags!) {
|
||||
@@ -94,21 +93,7 @@ class ServerProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
_tags.value.sort();
|
||||
_tags.notifyListeners();
|
||||
}
|
||||
|
||||
void renameTag(String old, String new_) {
|
||||
for (final s in _servers.values) {
|
||||
if (s.spi.tags == null) continue;
|
||||
for (var i = 0; i < s.spi.tags!.length; i++) {
|
||||
if (s.spi.tags![i] == old) {
|
||||
s.spi.tags![i] = new_;
|
||||
}
|
||||
}
|
||||
Stores.server.update(s.spi, s.spi);
|
||||
}
|
||||
_updateTags();
|
||||
_tags.value = (_tags.value.toList()..sort()).toSet();
|
||||
}
|
||||
|
||||
Server genServer(ServerPrivateInfo spi) {
|
||||
|
||||
@@ -9,8 +9,7 @@ class SnippetProvider extends ChangeNotifier {
|
||||
late List<Snippet> _snippets;
|
||||
List<Snippet> get snippets => _snippets;
|
||||
|
||||
final _tags = ValueNotifier(<String>[]);
|
||||
ValueNotifier<List<String>> get tags => _tags;
|
||||
final tags = ValueNotifier(<String>{});
|
||||
|
||||
void load() {
|
||||
_snippets = Stores.snippet.fetch();
|
||||
@@ -29,16 +28,14 @@ class SnippetProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void _updateTags() {
|
||||
_tags.value.clear();
|
||||
final tags = <String>{};
|
||||
final tags_ = <String>{};
|
||||
for (final s in _snippets) {
|
||||
if (s.tags?.isEmpty ?? true) {
|
||||
continue;
|
||||
final t = s.tags;
|
||||
if (t != null) {
|
||||
tags_.addAll(t);
|
||||
}
|
||||
tags.addAll(s.tags!);
|
||||
}
|
||||
_tags.value.addAll(tags);
|
||||
_tags.notifyListeners();
|
||||
tags.value = tags_;
|
||||
}
|
||||
|
||||
void add(Snippet snippet) {
|
||||
|
||||
@@ -48,8 +48,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
final _pveIgnoreCert = ValueNotifier(false);
|
||||
final _env = <String, String>{}.vn;
|
||||
final _customCmds = <String, String>{}.vn;
|
||||
|
||||
var _tags = <String>[];
|
||||
final _tags = <String>{}.vn;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -170,12 +169,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
hint: 'root',
|
||||
suggestion: false,
|
||||
),
|
||||
TagEditor(
|
||||
tags: _tags,
|
||||
onChanged: (p0) => _tags = p0,
|
||||
allTags: [...Pros.server.tags.value],
|
||||
onRenameTag: Pros.server.renameTag,
|
||||
),
|
||||
TagTile(tags: _tags, allTags: Pros.server.tags.value).cardx,
|
||||
ListTile(
|
||||
title: Text(l10n.autoConnect),
|
||||
trailing: ListenableBuilder(
|
||||
@@ -436,7 +430,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
|
||||
List<Widget> _buildWOLs() {
|
||||
return [
|
||||
const Text('Wake On LAN', style: UIs.text13Grey),
|
||||
const Text('Wake On LAN (beta)', style: UIs.text13Grey),
|
||||
UIs.height7,
|
||||
ListTile(
|
||||
leading: const Icon(BoxIcons.bxs_help_circle),
|
||||
@@ -455,7 +449,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
controller: _wolIpCtrl,
|
||||
type: TextInputType.text,
|
||||
label: 'IP ${l10n.addr}',
|
||||
icon: Icons.network_cell,
|
||||
icon: ZondIcons.network,
|
||||
hint: '192.168.1.x',
|
||||
suggestion: false,
|
||||
),
|
||||
@@ -590,7 +584,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
keyId: _keyIdx.value != null
|
||||
? Pros.key.pkis.elementAt(_keyIdx.value!).id
|
||||
: null,
|
||||
tags: _tags,
|
||||
tags: _tags.value.isEmpty ? null : _tags.value.toList(),
|
||||
alterUrl: _altUrlController.text.selfIfNotNullEmpty,
|
||||
autoConnect: _autoConnect.value,
|
||||
jumpId: _jumpServer.value,
|
||||
@@ -616,7 +610,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
context.showRoundDialog(
|
||||
title: l10n.attention,
|
||||
child: SimpleMarkdown(data: l10n.writeScriptTip),
|
||||
actions: Btns.oks(onTap: () => context.pop(true)),
|
||||
actions: [Btn.ok()],
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
@@ -648,7 +642,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
}
|
||||
|
||||
/// List in dart is passed by pointer, so you need to copy it here
|
||||
_tags.addAll(spi.tags ?? []);
|
||||
_tags.value = spi.tags?.toSet() ?? {};
|
||||
|
||||
_altUrlController.text = spi.alterUrl ?? '';
|
||||
_autoConnect.value = spi.autoConnect ?? true;
|
||||
|
||||
@@ -1251,7 +1251,8 @@ class _SettingPageState extends State<SettingPage> {
|
||||
),
|
||||
onTap: () async {
|
||||
final ctrl = TextEditingController(text: val);
|
||||
void onSave(String s) {
|
||||
void onSave() {
|
||||
final s = ctrl.text.trim();
|
||||
_setting.sftpEditor.put(s);
|
||||
context.pop();
|
||||
}
|
||||
@@ -1265,9 +1266,9 @@ class _SettingPageState extends State<SettingPage> {
|
||||
hint: '\$EDITOR / vim / nano ...',
|
||||
icon: Icons.edit,
|
||||
suggestion: false,
|
||||
onSubmitted: onSave,
|
||||
onSubmitted: (_) => onSave(),
|
||||
),
|
||||
actions: Btns.oks(onTap: () => onSave(ctrl.text)),
|
||||
actions: [Btn.ok(onTap: (_) => onSave())],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2,10 +2,9 @@ import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:server_box/core/extension/context/locale.dart';
|
||||
import 'package:server_box/data/model/server/snippet.dart';
|
||||
import 'package:server_box/data/res/provider.dart';
|
||||
|
||||
import '../../../data/model/server/snippet.dart';
|
||||
|
||||
class SnippetEditPage extends StatefulWidget {
|
||||
const SnippetEditPage({super.key, this.snippet});
|
||||
|
||||
@@ -22,7 +21,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
final _noteController = TextEditingController();
|
||||
final _scriptNode = FocusNode();
|
||||
final _autoRunOn = ValueNotifier(<String>[]);
|
||||
final _tags = ValueNotifier(<String>[]);
|
||||
final _tags = <String>{}.vn;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -36,7 +35,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
title: Text(l10n.edit, style: UIs.text18),
|
||||
title: Text(l10n.edit),
|
||||
actions: _buildAppBarActions(),
|
||||
),
|
||||
body: _buildBody(),
|
||||
@@ -45,9 +44,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
}
|
||||
|
||||
List<Widget>? _buildAppBarActions() {
|
||||
if (widget.snippet == null) {
|
||||
return null;
|
||||
}
|
||||
if (widget.snippet == null) return null;
|
||||
return [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
@@ -89,7 +86,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
final snippet = Snippet(
|
||||
name: name,
|
||||
script: script,
|
||||
tags: _tags.value.isEmpty ? null : _tags.value,
|
||||
tags: _tags.value.isEmpty ? null : _tags.value.toList(),
|
||||
note: note.isEmpty ? null : note,
|
||||
autoRunOn: _autoRunOn.value.isEmpty ? null : _autoRunOn.value,
|
||||
);
|
||||
@@ -125,21 +122,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
icon: Icons.note,
|
||||
suggestion: true,
|
||||
),
|
||||
ValBuilder(
|
||||
listenable: _tags,
|
||||
builder: (vals) {
|
||||
return TagEditor(
|
||||
tags: _tags.value,
|
||||
onChanged: (p0) => setState(() {
|
||||
_tags.value = p0;
|
||||
}),
|
||||
allTags: [...Pros.snippet.tags.value],
|
||||
onRenameTag: (old, n) => setState(() {
|
||||
Pros.snippet.renameTag(old, n);
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
TagTile(tags: _tags, allTags: Pros.snippet.tags.value).cardx,
|
||||
Input(
|
||||
controller: _scriptController,
|
||||
node: _scriptNode,
|
||||
@@ -167,7 +150,10 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
.map((e) => Pros.server.pick(id: e)?.spi.name ?? e)
|
||||
.join(', ');
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.settings_remote, size: 19),
|
||||
leading: const Padding(
|
||||
padding: EdgeInsets.only(left: 5),
|
||||
child: Icon(Icons.settings_remote, size: 19),
|
||||
),
|
||||
title: Text(l10n.autoRun),
|
||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||
subtitle: subtitle == null
|
||||
@@ -232,7 +218,7 @@ ${l10n.forExample}:
|
||||
}
|
||||
|
||||
if (snippet.tags != null) {
|
||||
_tags.value = snippet.tags!;
|
||||
_tags.value = snippet.tags!.toSet();
|
||||
}
|
||||
|
||||
if (snippet.autoRunOn != null) {
|
||||
|
||||
@@ -100,16 +100,8 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
maxLines: 3,
|
||||
style: UIs.textGrey,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
AppRoutes.snippetEdit(snippet: snippet).go(context),
|
||||
icon: const Icon(Icons.edit),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||
onTap: () => AppRoutes.snippetEdit(snippet: snippet).go(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
var newPerm = perm.copyWith();
|
||||
final ok = await context.showRoundDialog(
|
||||
child: UnixPermEditor(perm: perm, onChanged: (p) => newPerm = p),
|
||||
actions: Btns.oks(onTap: () => context.pop(true)),
|
||||
actions: [Btn.ok(onTap: (context) => context.pop(true))],
|
||||
);
|
||||
|
||||
final permStr = newPerm.perm;
|
||||
@@ -752,7 +752,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
context.showRoundDialog(
|
||||
title: l10n.error,
|
||||
child: Text('Unsupport file: ${name.filename}'),
|
||||
actions: Btns.oks(onTap: () => context.pop()),
|
||||
actions: [Btn.ok()],
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -760,11 +760,10 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
final confirm = await context.showRoundDialog(
|
||||
title: l10n.attention,
|
||||
child: SimpleMarkdown(data: '```sh\n$cmd\n```'),
|
||||
actions: Btns.okCancels(
|
||||
onTapOk: () => context.pop(true),
|
||||
onTapCancel: () => context.pop(false),
|
||||
red: true,
|
||||
),
|
||||
actions: [
|
||||
Btn.cancel(onTap: (c) => c.pop(false)),
|
||||
Btn.ok(onTap: (c) => c.pop(true), red: true),
|
||||
],
|
||||
);
|
||||
if (confirm != true) return;
|
||||
|
||||
|
||||
@@ -385,8 +385,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "v1.0.90"
|
||||
resolved-ref: "9f17bfce9f6cc9e071c40648bcac8d94e2dee919"
|
||||
ref: "v1.0.94"
|
||||
resolved-ref: b4e431b7131605fa266be80a016165562b5437eb
|
||||
url: "https://github.com/lppcg/fl_lib"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
|
||||
@@ -62,7 +62,7 @@ dependencies:
|
||||
fl_lib:
|
||||
git:
|
||||
url: https://github.com/lppcg/fl_lib
|
||||
ref: v1.0.90
|
||||
ref: v1.0.94
|
||||
|
||||
dependency_overrides:
|
||||
# dartssh2:
|
||||
|
||||
Reference in New Issue
Block a user