mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
new: server tag
This commit is contained in:
@@ -108,24 +108,30 @@ abstract class S {
|
|||||||
/// **'Thanks to the following people who participated in.'**
|
/// **'Thanks to the following people who participated in.'**
|
||||||
String get aboutThanks;
|
String get aboutThanks;
|
||||||
|
|
||||||
|
/// No description provided for @add.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Add'**
|
||||||
|
String get add;
|
||||||
|
|
||||||
/// No description provided for @addAServer.
|
/// No description provided for @addAServer.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'add a server'**
|
/// **'add a server'**
|
||||||
String get addAServer;
|
String get addAServer;
|
||||||
|
|
||||||
/// No description provided for @addOne.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Add one'**
|
|
||||||
String get addOne;
|
|
||||||
|
|
||||||
/// No description provided for @addPrivateKey.
|
/// No description provided for @addPrivateKey.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Add private key'**
|
/// **'Add private key'**
|
||||||
String get addPrivateKey;
|
String get addPrivateKey;
|
||||||
|
|
||||||
|
/// No description provided for @all.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'All'**
|
||||||
|
String get all;
|
||||||
|
|
||||||
/// No description provided for @alreadyLastDir.
|
/// No description provided for @alreadyLastDir.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -10,14 +10,17 @@ class SDe extends S {
|
|||||||
@override
|
@override
|
||||||
String get aboutThanks => 'Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n';
|
String get aboutThanks => 'Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get add => 'Neu';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addAServer => 'Server hinzufügen';
|
String get addAServer => 'Server hinzufügen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addOne => 'Hinzufügen';
|
String get addPrivateKey => 'Private key hinzufügen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addPrivateKey => 'Private key hinzufügen';
|
String get all => 'Alle';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get alreadyLastDir => 'Bereits im letzten Verzeichnis.';
|
String get alreadyLastDir => 'Bereits im letzten Verzeichnis.';
|
||||||
|
|||||||
@@ -10,14 +10,17 @@ class SEn extends S {
|
|||||||
@override
|
@override
|
||||||
String get aboutThanks => 'Thanks to the following people who participated in.';
|
String get aboutThanks => 'Thanks to the following people who participated in.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get add => 'Add';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addAServer => 'add a server';
|
String get addAServer => 'add a server';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addOne => 'Add one';
|
String get addPrivateKey => 'Add private key';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addPrivateKey => 'Add private key';
|
String get all => 'All';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get alreadyLastDir => 'Already in last directory.';
|
String get alreadyLastDir => 'Already in last directory.';
|
||||||
|
|||||||
@@ -10,14 +10,17 @@ class SZh extends S {
|
|||||||
@override
|
@override
|
||||||
String get aboutThanks => '感谢以下参与的各位。';
|
String get aboutThanks => '感谢以下参与的各位。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get add => '新增';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addAServer => '添加服务器';
|
String get addAServer => '添加服务器';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addOne => '前去新增';
|
String get addPrivateKey => '添加一个私钥';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addPrivateKey => '添加一个私钥';
|
String get all => '所有';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get alreadyLastDir => '已经是最上层目录了';
|
String get alreadyLastDir => '已经是最上层目录了';
|
||||||
@@ -614,14 +617,17 @@ class SZhTw extends SZh {
|
|||||||
@override
|
@override
|
||||||
String get aboutThanks => '感謝以下參與的各位。';
|
String get aboutThanks => '感謝以下參與的各位。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get add => '新增';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addAServer => '新增服務器';
|
String get addAServer => '新增服務器';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addOne => '前去新增';
|
String get addPrivateKey => '新增一個私鑰';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addPrivateKey => '新增一個私鑰';
|
String get all => '所有';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get alreadyLastDir => '已經是最上層目錄了';
|
String get alreadyLastDir => '已經是最上層目錄了';
|
||||||
|
|||||||
@@ -19,4 +19,16 @@ extension StringOrderX on StringOrder {
|
|||||||
if (index == -1) return;
|
if (index == -1) return;
|
||||||
this[index] = newId;
|
this[index] = newId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int index(String id) {
|
||||||
|
return indexOf(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveById(String oid, String nid, StoreProperty property) {
|
||||||
|
final index = indexOf(oid);
|
||||||
|
if (index == -1) return;
|
||||||
|
final newIndex = indexOf(nid);
|
||||||
|
if (newIndex == -1) return;
|
||||||
|
move(index, newIndex, property);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ void showSnippetDialog(
|
|||||||
context.pop();
|
context.pop();
|
||||||
AppRoute(const SnippetEditPage(), 'edit snippet').go(context);
|
AppRoute(const SnippetEditPage(), 'edit snippet').go(context);
|
||||||
},
|
},
|
||||||
child: Text(s.addOne),
|
child: Text(s.add),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class ServerPrivateInfo {
|
|||||||
late String pwd;
|
late String pwd;
|
||||||
@HiveField(5)
|
@HiveField(5)
|
||||||
String? pubKeyId;
|
String? pubKeyId;
|
||||||
|
@HiveField(6)
|
||||||
|
List<String>? tags;
|
||||||
|
|
||||||
late String id;
|
late String id;
|
||||||
|
|
||||||
@@ -26,6 +28,7 @@ class ServerPrivateInfo {
|
|||||||
required this.user,
|
required this.user,
|
||||||
required this.pwd,
|
required this.pwd,
|
||||||
this.pubKeyId,
|
this.pubKeyId,
|
||||||
|
this.tags,
|
||||||
}) : id = '$user@$ip:$port';
|
}) : id = '$user@$ip:$port';
|
||||||
|
|
||||||
ServerPrivateInfo.fromJson(Map<String, dynamic> json) {
|
ServerPrivateInfo.fromJson(Map<String, dynamic> json) {
|
||||||
@@ -36,6 +39,7 @@ class ServerPrivateInfo {
|
|||||||
pwd = json["authorization"].toString();
|
pwd = json["authorization"].toString();
|
||||||
pubKeyId = json["pubKeyId"]?.toString();
|
pubKeyId = json["pubKeyId"]?.toString();
|
||||||
id = '$user@$ip:$port';
|
id = '$user@$ip:$port';
|
||||||
|
tags = json["tags"]?.cast<String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@@ -46,6 +50,11 @@ class ServerPrivateInfo {
|
|||||||
data["user"] = user;
|
data["user"] = user;
|
||||||
data["authorization"] = pwd;
|
data["authorization"] = pwd;
|
||||||
data["pubKeyId"] = pubKeyId;
|
data["pubKeyId"] = pubKeyId;
|
||||||
|
data["tags"] = tags;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool shouldReconnect(ServerPrivateInfo old) {
|
||||||
|
return id != id || pwd != old.pwd || pubKeyId != old.pubKeyId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,14 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
user: fields[3] as String,
|
user: fields[3] as String,
|
||||||
pwd: fields[4] as String,
|
pwd: fields[4] as String,
|
||||||
pubKeyId: fields[5] as String?,
|
pubKeyId: fields[5] as String?,
|
||||||
|
tags: (fields[6] as List?)?.cast<String>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, ServerPrivateInfo obj) {
|
void write(BinaryWriter writer, ServerPrivateInfo obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(6)
|
..writeByte(7)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.name)
|
..write(obj.name)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
@@ -41,7 +42,9 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
|||||||
..writeByte(4)
|
..writeByte(4)
|
||||||
..write(obj.pwd)
|
..write(obj.pwd)
|
||||||
..writeByte(5)
|
..writeByte(5)
|
||||||
..write(obj.pubKeyId);
|
..write(obj.pubKeyId)
|
||||||
|
..writeByte(6)
|
||||||
|
..write(obj.tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -8,20 +8,16 @@ class Snippet {
|
|||||||
late String name;
|
late String name;
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
late String script;
|
late String script;
|
||||||
@HiveField(2)
|
Snippet(this.name, this.script);
|
||||||
List<String>? tags;
|
|
||||||
Snippet(this.name, this.script, {this.tags});
|
|
||||||
|
|
||||||
Snippet.fromJson(Map<String, dynamic> json) {
|
Snippet.fromJson(Map<String, dynamic> json) {
|
||||||
name = json['name'].toString();
|
name = json['name'].toString();
|
||||||
script = json['script'].toString();
|
script = json['script'].toString();
|
||||||
tags = json['tags'].cast<String>();
|
|
||||||
}
|
}
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final data = <String, dynamic>{};
|
final data = <String, dynamic>{};
|
||||||
data['name'] = name;
|
data['name'] = name;
|
||||||
data['script'] = script;
|
data['script'] = script;
|
||||||
data['tags'] = tags;
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,20 +19,17 @@ class SnippetAdapter extends TypeAdapter<Snippet> {
|
|||||||
return Snippet(
|
return Snippet(
|
||||||
fields[0] as String,
|
fields[0] as String,
|
||||||
fields[1] as String,
|
fields[1] as String,
|
||||||
tags: (fields[2] as List?)?.cast<String>(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, Snippet obj) {
|
void write(BinaryWriter writer, Snippet obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(3)
|
..writeByte(2)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.name)
|
..write(obj.name)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
..write(obj.script)
|
..write(obj.script);
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.tags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ typedef ServersMap = Map<String, Server>;
|
|||||||
class ServerProvider extends BusyProvider {
|
class ServerProvider extends BusyProvider {
|
||||||
final ServersMap _servers = {};
|
final ServersMap _servers = {};
|
||||||
ServersMap get servers => _servers;
|
ServersMap get servers => _servers;
|
||||||
final StringOrder serverOrder = [];
|
final StringOrder _serverOrder = [];
|
||||||
|
StringOrder get serverOrder => _serverOrder;
|
||||||
|
final List<String> _tags = [];
|
||||||
|
List<String> get tags => _tags;
|
||||||
|
|
||||||
final _limiter = TryLimiter();
|
final _limiter = TryLimiter();
|
||||||
|
|
||||||
@@ -42,23 +45,38 @@ class ServerProvider extends BusyProvider {
|
|||||||
}
|
}
|
||||||
final serverOrder_ = _settingStore.serverOrder.fetch();
|
final serverOrder_ = _settingStore.serverOrder.fetch();
|
||||||
if (serverOrder_ != null) {
|
if (serverOrder_ != null) {
|
||||||
serverOrder.addAll(serverOrder_.toSet());
|
_serverOrder.addAll(serverOrder_.toSet());
|
||||||
if (serverOrder.length != infos.length) {
|
if (_serverOrder.length != infos.length) {
|
||||||
final missed = infos
|
final missed = infos
|
||||||
.where(
|
.where(
|
||||||
(e) => !serverOrder.contains(e.id),
|
(e) => !_serverOrder.contains(e.id),
|
||||||
)
|
)
|
||||||
.map((e) => e.id);
|
.map((e) => e.id);
|
||||||
serverOrder.addAll(missed);
|
_serverOrder.addAll(missed);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
serverOrder.addAll(_servers.keys);
|
_serverOrder.addAll(_servers.keys);
|
||||||
}
|
}
|
||||||
_settingStore.serverOrder.put(serverOrder);
|
_settingStore.serverOrder.put(_serverOrder);
|
||||||
|
_updateTags();
|
||||||
setBusyState(false);
|
setBusyState(false);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateTags() {
|
||||||
|
_tags.clear();
|
||||||
|
for (final s in _servers.values) {
|
||||||
|
if (s.spi.tags == null) continue;
|
||||||
|
for (final t in s.spi.tags!) {
|
||||||
|
if (!_tags.contains(t)) {
|
||||||
|
_tags.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tags.sort();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
Server genServer(ServerPrivateInfo spi) {
|
Server genServer(ServerPrivateInfo spi) {
|
||||||
return Server(spi, initStatus, null, ServerState.disconnected);
|
return Server(spi, initStatus, null, ServerState.disconnected);
|
||||||
}
|
}
|
||||||
@@ -81,8 +99,7 @@ class ServerProvider extends BusyProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startAutoRefresh() async {
|
Future<void> startAutoRefresh() async {
|
||||||
final duration =
|
final duration = _settingStore.serverStatusUpdateInterval.fetch()!;
|
||||||
locator<SettingStore>().serverStatusUpdateInterval.fetch()!;
|
|
||||||
if (duration == 0) return;
|
if (duration == 0) return;
|
||||||
stopAutoRefresh();
|
stopAutoRefresh();
|
||||||
_timer = Timer.periodic(Duration(seconds: duration), (_) async {
|
_timer = Timer.periodic(Duration(seconds: duration), (_) async {
|
||||||
@@ -126,15 +143,17 @@ class ServerProvider extends BusyProvider {
|
|||||||
_servers[spi.id] = genServer(spi);
|
_servers[spi.id] = genServer(spi);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_serverStore.put(spi);
|
_serverStore.put(spi);
|
||||||
serverOrder.add(spi.id);
|
_serverOrder.add(spi.id);
|
||||||
_settingStore.serverOrder.put(serverOrder);
|
_settingStore.serverOrder.put(_serverOrder);
|
||||||
|
_updateTags();
|
||||||
refreshData(spi: spi);
|
refreshData(spi: spi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void delServer(String id) {
|
void delServer(String id) {
|
||||||
_servers.remove(id);
|
_servers.remove(id);
|
||||||
serverOrder.remove(id);
|
_serverOrder.remove(id);
|
||||||
_settingStore.serverOrder.put(serverOrder);
|
_settingStore.serverOrder.put(_serverOrder);
|
||||||
|
_updateTags();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_serverStore.delete(id);
|
_serverStore.delete(id);
|
||||||
}
|
}
|
||||||
@@ -143,13 +162,27 @@ class ServerProvider extends BusyProvider {
|
|||||||
ServerPrivateInfo old,
|
ServerPrivateInfo old,
|
||||||
ServerPrivateInfo newSpi,
|
ServerPrivateInfo newSpi,
|
||||||
) async {
|
) async {
|
||||||
_servers.remove(old.id);
|
if (old != newSpi) {
|
||||||
_serverStore.update(old, newSpi);
|
_serverStore.update(old, newSpi);
|
||||||
_servers[newSpi.id] = genServer(newSpi);
|
_servers[old.id]?.spi = newSpi;
|
||||||
_servers[newSpi.id]?.client = await genClient(newSpi);
|
|
||||||
serverOrder.update(old.id, newSpi.id);
|
if (newSpi.id != old.id) {
|
||||||
_settingStore.serverOrder.put(serverOrder);
|
_servers[newSpi.id] = _servers[old.id]!;
|
||||||
await refreshData(spi: newSpi);
|
_servers[newSpi.id]?.spi = newSpi;
|
||||||
|
_servers.remove(old.id);
|
||||||
|
_serverOrder.update(old.id, newSpi.id);
|
||||||
|
_settingStore.serverOrder.put(_serverOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only reconnect if neccessary
|
||||||
|
if (newSpi.shouldReconnect(old)) {
|
||||||
|
_servers[newSpi.id]?.client = await genClient(newSpi);
|
||||||
|
refreshData(spi: newSpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only update if [spi.tags] changed
|
||||||
|
_updateTags();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getData(ServerPrivateInfo spi) async {
|
Future<void> _getData(ServerPrivateInfo spi) async {
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
"@@locale": "de",
|
"@@locale": "de",
|
||||||
"about": "Über",
|
"about": "Über",
|
||||||
"aboutThanks": "Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n",
|
"aboutThanks": "Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n",
|
||||||
|
"add": "Neu",
|
||||||
"addAServer": "Server hinzufügen",
|
"addAServer": "Server hinzufügen",
|
||||||
"addOne": "Hinzufügen",
|
|
||||||
"addPrivateKey": "Private key hinzufügen",
|
"addPrivateKey": "Private key hinzufügen",
|
||||||
|
"all": "Alle",
|
||||||
"alreadyLastDir": "Bereits im letzten Verzeichnis.",
|
"alreadyLastDir": "Bereits im letzten Verzeichnis.",
|
||||||
"appPrimaryColor": "Farbschema",
|
"appPrimaryColor": "Farbschema",
|
||||||
"attention": "Achtung",
|
"attention": "Achtung",
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"aboutThanks": "Thanks to the following people who participated in.",
|
"aboutThanks": "Thanks to the following people who participated in.",
|
||||||
|
"add": "Add",
|
||||||
"addAServer": "add a server",
|
"addAServer": "add a server",
|
||||||
"addOne": "Add one",
|
|
||||||
"addPrivateKey": "Add private key",
|
"addPrivateKey": "Add private key",
|
||||||
|
"all": "All",
|
||||||
"alreadyLastDir": "Already in last directory.",
|
"alreadyLastDir": "Already in last directory.",
|
||||||
"appPrimaryColor": "App primary color",
|
"appPrimaryColor": "App primary color",
|
||||||
"attention": "Attention",
|
"attention": "Attention",
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
"@@locale": "zh",
|
"@@locale": "zh",
|
||||||
"about": "关于",
|
"about": "关于",
|
||||||
"aboutThanks": "感谢以下参与的各位。",
|
"aboutThanks": "感谢以下参与的各位。",
|
||||||
|
"add": "新增",
|
||||||
"addAServer": "添加服务器",
|
"addAServer": "添加服务器",
|
||||||
"addOne": "前去新增",
|
|
||||||
"addPrivateKey": "添加一个私钥",
|
"addPrivateKey": "添加一个私钥",
|
||||||
|
"all": "所有",
|
||||||
"alreadyLastDir": "已经是最上层目录了",
|
"alreadyLastDir": "已经是最上层目录了",
|
||||||
"appPrimaryColor": "App主要色",
|
"appPrimaryColor": "App主要色",
|
||||||
"attention": "注意",
|
"attention": "注意",
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
"@@locale": "zh_TW",
|
"@@locale": "zh_TW",
|
||||||
"about": "關於",
|
"about": "關於",
|
||||||
"aboutThanks": "感謝以下參與的各位。",
|
"aboutThanks": "感謝以下參與的各位。",
|
||||||
|
"add": "新增",
|
||||||
"addAServer": "新增服務器",
|
"addAServer": "新增服務器",
|
||||||
"addOne": "前去新增",
|
|
||||||
"addPrivateKey": "新增一個私鑰",
|
"addPrivateKey": "新增一個私鑰",
|
||||||
|
"all": "所有",
|
||||||
"alreadyLastDir": "已經是最上層目錄了",
|
"alreadyLastDir": "已經是最上層目錄了",
|
||||||
"appPrimaryColor": "主要色調",
|
"appPrimaryColor": "主要色調",
|
||||||
"attention": "注意",
|
"attention": "注意",
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip: _s.convert,
|
tooltip: _s.convert,
|
||||||
heroTag: 'convert fab',
|
|
||||||
child: const Icon(Icons.send),
|
child: const Icon(Icons.send),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ class _PingPageState extends State<PingPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
heroTag: 'ping fab',
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
try {
|
try {
|
||||||
doPing();
|
doPing();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import '../../../data/provider/server.dart';
|
|||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../../data/store/private_key.dart';
|
import '../../../data/store/private_key.dart';
|
||||||
import '../../../locator.dart';
|
import '../../../locator.dart';
|
||||||
|
import '../../widget/tag.dart';
|
||||||
import '../private_key/edit.dart';
|
import '../private_key/edit.dart';
|
||||||
|
|
||||||
class ServerEditPage extends StatefulWidget {
|
class ServerEditPage extends StatefulWidget {
|
||||||
@@ -43,6 +44,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
bool usePublicKey = false;
|
bool usePublicKey = false;
|
||||||
int? _pubKeyIndex;
|
int? _pubKeyIndex;
|
||||||
PrivateKeyInfo? _keyInfo;
|
PrivateKeyInfo? _keyInfo;
|
||||||
|
List<String> _tags = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -143,6 +145,13 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
icon: Icons.account_box,
|
icon: Icons.account_box,
|
||||||
hint: 'root',
|
hint: 'root',
|
||||||
),
|
),
|
||||||
|
TagEditor(
|
||||||
|
tags: _tags,
|
||||||
|
onChanged: (p0) => setState(() {
|
||||||
|
_tags = p0;
|
||||||
|
}),
|
||||||
|
s: _s,
|
||||||
|
),
|
||||||
width7,
|
width7,
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -262,6 +271,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
user: _usernameController.text,
|
user: _usernameController.text,
|
||||||
pwd: authorization,
|
pwd: authorization,
|
||||||
pubKeyId: usePublicKey ? _keyInfo!.id : null,
|
pubKeyId: usePublicKey ? _keyInfo!.id : null,
|
||||||
|
tags: _tags,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.spi == null) {
|
if (widget.spi == null) {
|
||||||
@@ -300,6 +310,9 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
} else {
|
} else {
|
||||||
usePublicKey = true;
|
usePublicKey = true;
|
||||||
}
|
}
|
||||||
|
if (widget.spi?.tags != null) {
|
||||||
|
_tags = widget.spi!.tags!;
|
||||||
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:toolbox/core/extension/navigator.dart';
|
import 'package:toolbox/core/extension/navigator.dart';
|
||||||
import 'package:toolbox/core/extension/order.dart';
|
import 'package:toolbox/core/extension/order.dart';
|
||||||
import 'package:toolbox/core/utils/misc.dart';
|
import 'package:toolbox/core/utils/misc.dart';
|
||||||
|
import 'package:toolbox/view/widget/fade_in.dart';
|
||||||
|
|
||||||
import '../../../core/route.dart';
|
import '../../../core/route.dart';
|
||||||
import '../../../core/utils/ui.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
@@ -45,6 +46,8 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
late SettingStore _settingStore;
|
late SettingStore _settingStore;
|
||||||
late S _s;
|
late S _s;
|
||||||
|
|
||||||
|
String? _tag;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -71,18 +74,65 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
'Add server info page',
|
'Add server info page',
|
||||||
).go(context),
|
).go(context),
|
||||||
tooltip: _s.addAServer,
|
tooltip: _s.addAServer,
|
||||||
heroTag: 'server page fab',
|
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildTagsSwitcher(ServerProvider pro) {
|
||||||
|
if (pro.tags.isEmpty) return placeholder;
|
||||||
|
final items = <String?>[null, ...pro.tags];
|
||||||
|
return Container(
|
||||||
|
height: 37,
|
||||||
|
width: _media.size.width,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemBuilder: (context, index) => _buildTagItem(items[index]),
|
||||||
|
itemCount: items.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTagItem(String? tag) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 4, right: 5, bottom: 9),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_tag = tag;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
|
||||||
|
color: primaryColor.withAlpha(20),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 2.7),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
tag == null ? _s.all : '#$tag',
|
||||||
|
style: TextStyle(
|
||||||
|
color: _tag == tag ? null : _theme.disabledColor,
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async =>
|
onRefresh: () async =>
|
||||||
await _serverProvider.refreshData(onlyFailed: true),
|
await _serverProvider.refreshData(onlyFailed: true),
|
||||||
child: Consumer<ServerProvider>(
|
child: Consumer<ServerProvider>(
|
||||||
builder: (_, pro, __) {
|
builder: (_, pro, __) {
|
||||||
|
if (!pro.tags.contains(_tag)) {
|
||||||
|
_tag = null;
|
||||||
|
}
|
||||||
if (pro.serverOrder.isEmpty) {
|
if (pro.serverOrder.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -91,21 +141,29 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ReorderableListView(
|
final filtered = pro.serverOrder
|
||||||
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
|
.where((e) => pro.servers.containsKey(e))
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
.where((e) =>
|
||||||
onReorder: (oldIndex, newIndex) => setState(() {
|
_tag == null ||
|
||||||
pro.serverOrder.move(
|
(pro.servers[e]?.spi.tags?.contains(_tag) ?? false))
|
||||||
oldIndex,
|
.toList();
|
||||||
newIndex,
|
return FadeIn(
|
||||||
_settingStore.serverOrder,
|
key: ValueKey(_tag),
|
||||||
);
|
child: ReorderableListView(
|
||||||
}),
|
header: _buildTagsSwitcher(pro),
|
||||||
children: pro.serverOrder
|
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
|
||||||
.where((e) => pro.servers.containsKey(e))
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
.map((e) => _buildEachServerCard(pro.servers[e]))
|
onReorder: (oldIndex, newIndex) => setState(() {
|
||||||
.toList(),
|
pro.serverOrder.moveById(
|
||||||
);
|
filtered[oldIndex],
|
||||||
|
filtered[newIndex],
|
||||||
|
_settingStore.serverOrder,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
children: filtered
|
||||||
|
.map((e) => _buildEachServerCard(pro.servers[e]))
|
||||||
|
.toList(),
|
||||||
|
));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
_onItemPress(context, file, true);
|
_onItemPress(context, file, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () => _onItemPress(context, file, false),
|
onLongPress: () => _onItemPress(context, file, !isDir),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:toolbox/core/extension/navigator.dart';
|
import 'package:toolbox/core/extension/navigator.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import 'package:toolbox/view/widget/input_field.dart';
|
||||||
import 'package:toolbox/view/widget/tag.dart';
|
|
||||||
|
|
||||||
import '../../../core/utils/ui.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import '../../../data/model/server/snippet.dart';
|
import '../../../data/model/server/snippet.dart';
|
||||||
@@ -29,8 +28,6 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
late SnippetProvider _provider;
|
late SnippetProvider _provider;
|
||||||
late S _s;
|
late S _s;
|
||||||
|
|
||||||
var _tags = <String>[];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -76,7 +73,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
showSnackBar(context, Text(_s.fieldMustNotEmpty));
|
showSnackBar(context, Text(_s.fieldMustNotEmpty));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final snippet = Snippet(name, script, tags: _tags);
|
final snippet = Snippet(name, script);
|
||||||
if (widget.snippet != null) {
|
if (widget.snippet != null) {
|
||||||
_provider.update(widget.snippet!, snippet);
|
_provider.update(widget.snippet!, snippet);
|
||||||
} else {
|
} else {
|
||||||
@@ -107,13 +104,6 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
label: _s.snippet,
|
label: _s.snippet,
|
||||||
icon: Icons.code,
|
icon: Icons.code,
|
||||||
),
|
),
|
||||||
TagEditor(
|
|
||||||
tags: widget.snippet?.tags ?? [],
|
|
||||||
onChanged: (p0) => setState(() {
|
|
||||||
_tags = p0;
|
|
||||||
}),
|
|
||||||
s: _s.tag,
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -123,7 +113,6 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
if (widget.snippet != null) {
|
if (widget.snippet != null) {
|
||||||
_nameController.text = widget.snippet!.name;
|
_nameController.text = widget.snippet!.name;
|
||||||
_scriptController.text = widget.snippet!.script;
|
_scriptController.text = widget.snippet!.script;
|
||||||
_tags = widget.snippet!.tags ?? [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
// import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
// class SnippetGroupOrderPage extends StatelessWidget {
|
|
||||||
// final
|
|
||||||
// }
|
|
||||||
@@ -1,16 +1,22 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import 'package:toolbox/view/widget/input_field.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
|
||||||
|
import '../../core/utils/ui.dart';
|
||||||
import '../../data/res/color.dart';
|
import '../../data/res/color.dart';
|
||||||
|
|
||||||
class TagEditor extends StatelessWidget {
|
class TagEditor extends StatelessWidget {
|
||||||
final List<String> tags;
|
final List<String> tags;
|
||||||
final String s;
|
final S s;
|
||||||
final void Function(List<String>)? onChanged;
|
final void Function(List<String>)? onChanged;
|
||||||
|
|
||||||
const TagEditor(
|
const TagEditor({
|
||||||
{super.key, required this.tags, this.onChanged, required this.s});
|
super.key,
|
||||||
|
required this.tags,
|
||||||
|
this.onChanged,
|
||||||
|
required this.s,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -38,7 +44,7 @@ class TagEditor extends StatelessWidget {
|
|||||||
List<String> tags,
|
List<String> tags,
|
||||||
Function(String) onTagDelete,
|
Function(String) onTagDelete,
|
||||||
) {
|
) {
|
||||||
if (tags.isEmpty) return Text(s);
|
if (tags.isEmpty) return Text(s.tag);
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -88,28 +94,24 @@ class TagEditor extends StatelessWidget {
|
|||||||
void Function(List<String>)? onChanged,
|
void Function(List<String>)? onChanged,
|
||||||
) {
|
) {
|
||||||
final textEditingController = TextEditingController();
|
final textEditingController = TextEditingController();
|
||||||
showDialog(
|
showRoundDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
title: Text(s.add),
|
||||||
return AlertDialog(
|
child: Input(
|
||||||
title: const Text('Add Tag'),
|
controller: textEditingController,
|
||||||
content: Input(
|
hint: s.tag,
|
||||||
controller: textEditingController,
|
),
|
||||||
hint: 'Tag',
|
actions: [
|
||||||
),
|
TextButton(
|
||||||
actions: [
|
onPressed: () {
|
||||||
TextButton(
|
final tag = textEditingController.text;
|
||||||
onPressed: () {
|
tags.add(tag.trim());
|
||||||
final tag = textEditingController.text;
|
onChanged?.call(tags);
|
||||||
tags.add(tag.trim());
|
Navigator.pop(context);
|
||||||
onChanged?.call(tags);
|
},
|
||||||
Navigator.pop(context);
|
child: Text(s.add),
|
||||||
},
|
),
|
||||||
child: const Text('Add'),
|
],
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user