diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index 253e0ff0..6f902a3a 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -214,12 +214,6 @@ abstract class S { /// **'Backup'** String get backup; - /// No description provided for @backupAndRestore. - /// - /// In en, this message translates to: - /// **'Backup and Restore'** - String get backupAndRestore; - /// No description provided for @backupTip. /// /// In en, this message translates to: @@ -1510,6 +1504,12 @@ abstract class S { /// **'Times'** String get times; + /// No description provided for @total. + /// + /// In en, this message translates to: + /// **'Total'** + String get total; + /// No description provided for @traffic. /// /// In en, this message translates to: @@ -1606,6 +1606,12 @@ abstract class S { /// **'No password will be used.'** String get useNoPwd; + /// No description provided for @used. + /// + /// In en, this message translates to: + /// **'Used'** + String get used; + /// No description provided for @user. /// /// In en, this message translates to: diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart index 72fa4715..34e6b9e8 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -63,9 +63,6 @@ class SDe extends S { @override String get backup => 'Backup'; - @override - String get backupAndRestore => 'Backup und Wiederherstellung'; - @override String get backupTip => 'Das Backup wird nur einfach verschlüsselt.\nBitte bewahre die Datei sicher auf.'; @@ -741,6 +738,9 @@ class SDe extends S { @override String get times => 'x'; + @override + String get total => 'Total'; + @override String get traffic => 'Durchflussmenge'; @@ -793,6 +793,9 @@ class SDe extends S { @override String get useNoPwd => 'Es wird kein Passwort verwendet.'; + @override + String get used => 'Gebraucht'; + @override String get user => 'Benutzer'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index 1e3bca24..9a623a4d 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -63,9 +63,6 @@ class SEn extends S { @override String get backup => 'Backup'; - @override - String get backupAndRestore => 'Backup and Restore'; - @override String get backupTip => 'The exported data is simply encrypted. \nPlease keep it safe.'; @@ -741,6 +738,9 @@ class SEn extends S { @override String get times => 'Times'; + @override + String get total => 'Total'; + @override String get traffic => 'Traffic'; @@ -793,6 +793,9 @@ class SEn extends S { @override String get useNoPwd => 'No password will be used.'; + @override + String get used => 'Used'; + @override String get user => 'User'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart index 582c5353..a8f30afa 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_fr.dart @@ -63,9 +63,6 @@ class SFr extends S { @override String get backup => 'Sauvegarder'; - @override - String get backupAndRestore => 'Sauvegarde et restauration'; - @override String get backupTip => 'Les données exportées sont simplement chiffrées. \nVeuillez les conserver en lieu sûr.'; @@ -741,6 +738,9 @@ class SFr extends S { @override String get times => 'Fois'; + @override + String get total => 'Total'; + @override String get traffic => 'Trafic'; @@ -793,6 +793,9 @@ class SFr extends S { @override String get useNoPwd => 'Aucun mot de passe ne sera utilisé.'; + @override + String get used => 'Utilisé'; + @override String get user => 'Utilisateur'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart index ad2d0b15..1fc5a06c 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart @@ -63,9 +63,6 @@ class SId extends S { @override String get backup => 'Cadangan'; - @override - String get backupAndRestore => 'Cadangan dan Pulihkan'; - @override String get backupTip => 'Data yang diekspor hanya dienkripsi.\nTolong jaga keamanannya.'; @@ -741,6 +738,9 @@ class SId extends S { @override String get times => 'Waktu'; + @override + String get total => 'Total'; + @override String get traffic => 'Lalu lintas'; @@ -793,6 +793,9 @@ class SId extends S { @override String get useNoPwd => 'Tidak ada kata sandi yang akan digunakan.'; + @override + String get used => 'Digunakan'; + @override String get user => 'Username'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index bd6c86e5..9f2bc86d 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -63,9 +63,6 @@ class SZh extends S { @override String get backup => '备份'; - @override - String get backupAndRestore => '备份和恢复'; - @override String get backupTip => '导出的数据仅进行了简单加密,请妥善保管。'; @@ -741,6 +738,9 @@ class SZh extends S { @override String get times => '次'; + @override + String get total => '总共'; + @override String get traffic => '流量'; @@ -793,6 +793,9 @@ class SZh extends S { @override String get useNoPwd => '将会使用无密码。'; + @override + String get used => '已用'; + @override String get user => '用户'; @@ -902,9 +905,6 @@ class SZhTw extends SZh { @override String get backup => '備份'; - @override - String get backupAndRestore => '備份和還原'; - @override String get backupTip => '導出的數據僅進行了簡單加密,請妥善保管。'; @@ -1580,6 +1580,9 @@ class SZhTw extends SZh { @override String get times => '次'; + @override + String get total => '總共'; + @override String get traffic => '流量'; @@ -1632,6 +1635,9 @@ class SZhTw extends SZh { @override String get useNoPwd => '将使用無密碼。'; + @override + String get used => '已用'; + @override String get user => '用戶'; diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6ad6fe81..8b93dcd2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -586,7 +586,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -596,7 +596,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -720,7 +720,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -730,7 +730,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -748,7 +748,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -758,7 +758,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -779,7 +779,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -792,7 +792,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; @@ -818,7 +818,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -831,7 +831,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -854,7 +854,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -867,7 +867,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -890,7 +890,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -902,7 +902,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; @@ -931,7 +931,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -943,7 +943,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; @@ -969,7 +969,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 712; + CURRENT_PROJECT_VERSION = 715; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -981,7 +981,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.712; + MARKETING_VERSION = 1.0.715; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; diff --git a/lib/core/route.dart b/lib/core/route.dart index f7b84f1c..2cae0eb6 100644 --- a/lib/core/route.dart +++ b/lib/core/route.dart @@ -5,15 +5,17 @@ import 'package:toolbox/data/model/server/server_private_info.dart'; import 'package:toolbox/view/page/backup.dart'; import 'package:toolbox/view/page/container.dart'; import 'package:toolbox/view/page/home.dart'; +import 'package:toolbox/view/page/iperf.dart'; import 'package:toolbox/view/page/ping.dart'; import 'package:toolbox/view/page/private_key/edit.dart'; import 'package:toolbox/view/page/private_key/list.dart'; import 'package:toolbox/view/page/server/detail.dart'; -import 'package:toolbox/view/page/setting/android.dart'; -import 'package:toolbox/view/page/setting/ios.dart'; +import 'package:toolbox/view/page/setting/platform/android.dart'; +import 'package:toolbox/view/page/setting/platform/ios.dart'; +import 'package:toolbox/view/page/setting/seq/srv_func_seq.dart'; import 'package:toolbox/view/page/snippet/result.dart'; import 'package:toolbox/view/page/ssh/page.dart'; -import 'package:toolbox/view/page/setting/virt_key.dart'; +import 'package:toolbox/view/page/setting/seq/virt_key.dart'; import 'package:toolbox/view/page/storage/local.dart'; import '../data/model/server/snippet.dart'; @@ -24,8 +26,8 @@ import '../view/page/process.dart'; import '../view/page/server/edit.dart'; import '../view/page/server/tab.dart'; import '../view/page/setting/entry.dart'; -import '../view/page/setting/srv_detail_seq.dart'; -import '../view/page/setting/srv_seq.dart'; +import '../view/page/setting/seq/srv_detail_seq.dart'; +import '../view/page/setting/seq/srv_seq.dart'; import '../view/page/snippet/edit.dart'; import '../view/page/snippet/list.dart'; import '../view/page/storage/sftp.dart'; @@ -218,4 +220,12 @@ class AppRoute { ), 'snippet_result'); } + + static AppRoute iperf({Key? key, required ServerPrivateInfo spi}) { + return AppRoute(IPerfPage(key: key, spi: spi), 'iperf'); + } + + static AppRoute serverFuncBtnsOrder({Key? key}) { + return AppRoute(ServerFuncBtnsOrderPage(key: key), 'server_func_btns_seq'); + } } diff --git a/lib/data/model/app/menu/server_func.dart b/lib/data/model/app/menu/server_func.dart index 9768ac42..f022c4b1 100644 --- a/lib/data/model/app/menu/server_func.dart +++ b/lib/data/model/app/menu/server_func.dart @@ -1,30 +1,44 @@ import 'package:flutter/material.dart'; +import 'package:hive_flutter/hive_flutter.dart'; import 'package:toolbox/core/extension/context/locale.dart'; -enum ServerTabMenu { +part 'server_func.g.dart'; + +@HiveType(typeId: 6) +enum ServerFuncBtn { + @HiveField(0) terminal, + @HiveField(1) sftp, + @HiveField(2) container, + @HiveField(3) process, + @HiveField(4) pkg, + @HiveField(5) snippet, + @HiveField(6) + iperf, ; IconData get icon => switch (this) { - ServerTabMenu.sftp => Icons.insert_drive_file, - ServerTabMenu.snippet => Icons.code, - ServerTabMenu.pkg => Icons.system_security_update, - ServerTabMenu.container => Icons.view_agenda, - ServerTabMenu.process => Icons.list_alt_outlined, - ServerTabMenu.terminal => Icons.terminal, - }; + sftp => Icons.insert_drive_file, + snippet => Icons.code, + pkg => Icons.system_security_update, + container => Icons.view_agenda, + process => Icons.list_alt_outlined, + terminal => Icons.terminal, + iperf => Icons.speed, + }; String get toStr => switch (this) { - ServerTabMenu.sftp => 'SFTP', - ServerTabMenu.snippet => l10n.snippet, - ServerTabMenu.pkg => l10n.pkg, - ServerTabMenu.container => l10n.container, - ServerTabMenu.process => l10n.process, - ServerTabMenu.terminal => l10n.terminal, - }; -} \ No newline at end of file + sftp => 'SFTP', + snippet => l10n.snippet, + pkg => l10n.pkg, + container => l10n.container, + process => l10n.process, + terminal => l10n.terminal, + iperf => 'iperf', + }; +} diff --git a/lib/data/model/app/menu/server_func.g.dart b/lib/data/model/app/menu/server_func.g.dart new file mode 100644 index 00000000..cf83cef3 --- /dev/null +++ b/lib/data/model/app/menu/server_func.g.dart @@ -0,0 +1,71 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_func.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class ServerFuncBtnAdapter extends TypeAdapter { + @override + final int typeId = 6; + + @override + ServerFuncBtn read(BinaryReader reader) { + switch (reader.readByte()) { + case 0: + return ServerFuncBtn.terminal; + case 1: + return ServerFuncBtn.sftp; + case 2: + return ServerFuncBtn.container; + case 3: + return ServerFuncBtn.process; + case 4: + return ServerFuncBtn.pkg; + case 5: + return ServerFuncBtn.snippet; + case 6: + return ServerFuncBtn.iperf; + default: + return ServerFuncBtn.terminal; + } + } + + @override + void write(BinaryWriter writer, ServerFuncBtn obj) { + switch (obj) { + case ServerFuncBtn.terminal: + writer.writeByte(0); + break; + case ServerFuncBtn.sftp: + writer.writeByte(1); + break; + case ServerFuncBtn.container: + writer.writeByte(2); + break; + case ServerFuncBtn.process: + writer.writeByte(3); + break; + case ServerFuncBtn.pkg: + writer.writeByte(4); + break; + case ServerFuncBtn.snippet: + writer.writeByte(5); + break; + case ServerFuncBtn.iperf: + writer.writeByte(6); + break; + } + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ServerFuncBtnAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index 395b0bab..05b9e64b 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -140,9 +140,13 @@ class ServerProvider extends ChangeNotifier { static final refreshKey = GlobalKey(); Future startAutoRefresh() async { - final duration = Stores.setting.serverStatusUpdateInterval.fetch(); + var duration = Stores.setting.serverStatusUpdateInterval.fetch(); stopAutoRefresh(); if (duration == 0) return; + if (duration < 0 || duration > 10 || duration == 1) { + duration = 3; + Loggers.app.warning('Invalid duration: $duration, use default 3'); + } refreshKey.currentState?.show(); _timer = Timer.periodic(Duration(seconds: duration), (_) async { await refreshData(); diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 181a2338..7a1240a1 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,9 +2,9 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 712; - static const String engine = "3.16.7"; - static const String buildAt = "2024-01-21 15:41:48"; - static const int modifications = 5; - static const int script = 35; + static const int build = 715; + static const String engine = "3.16.8"; + static const String buildAt = "2024-01-21 18:14:00"; + static const int modifications = 20; + static const int script = 36; } diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index 008c333a..397a02c3 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:toolbox/core/persistant_store.dart'; import 'package:toolbox/core/utils/platform/base.dart'; +import 'package:toolbox/data/model/app/menu/server_func.dart'; import '../model/app/net_view.dart'; import '../res/default.dart'; @@ -208,6 +209,15 @@ class SettingStore extends PersistentStore { /// Whether collapse UI items by default late final collapseUIDefault = property('collapseUIDefault', true); + late final serverFuncBtns = listProperty('serverBtns', [ + ServerFuncBtn.terminal, + ServerFuncBtn.sftp, + ServerFuncBtn.container, + ServerFuncBtn.process, + ServerFuncBtn.pkg, + ServerFuncBtn.snippet, + ]); + // Never show these settings for users // // ------BEGIN------ diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 52cb3617..c7e8b658 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -19,7 +19,6 @@ "autoConnect": "Automatisch verbinden", "autoUpdateHomeWidget": "Home-Widget automatisch aktualisieren", "backup": "Backup", - "backupAndRestore": "Backup und Wiederherstellung", "backupTip": "Das Backup wird nur einfach verschlüsselt.\nBitte bewahre die Datei sicher auf.", "backupVersionNotMatch": "Die Backup-Version stimmt nicht überein.", "battery": "Batterie", @@ -235,6 +234,7 @@ "themeMode": "Themen-Modus", "time": "Zeit", "times": "x", + "total": "Total", "traffic": "Durchflussmenge", "ttl": "ttl", "unknown": "Unbekannt", @@ -250,6 +250,7 @@ "upsideDown": "Upside Down", "urlOrJson": "URL oder JSON", "useNoPwd": "Es wird kein Passwort verwendet.", + "used": "Gebraucht", "user": "Benutzer", "versionHaveUpdate": "Gefunden: v1.0.{build}, klicke zum Aktualisieren", "versionUnknownUpdate": "Aktuell: v1.0.{build}. Klicken Sie hier, um nach Updates zu suchen", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 2aef53cf..723540c6 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -19,7 +19,6 @@ "autoConnect": "Auto connect", "autoUpdateHomeWidget": "Auto update home widget", "backup": "Backup", - "backupAndRestore": "Backup and Restore", "backupTip": "The exported data is simply encrypted. \nPlease keep it safe.", "backupVersionNotMatch": "Backup version is not match.", "battery": "Battery", @@ -235,6 +234,7 @@ "themeMode": "Theme mode", "time": "Time", "times": "Times", + "total": "Total", "traffic": "Traffic", "ttl": "ttl", "unknown": "Unknown", @@ -251,6 +251,7 @@ "uptime": "Uptime", "urlOrJson": "URL or JSON", "useNoPwd": "No password will be used.", + "used": "Used", "user": "User", "versionHaveUpdate": "Found: v1.0.{build}, click to update", "versionUnknownUpdate": "Current: v1.0.{build}, click to check updates", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index f84b7abb..0d53ce0c 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -19,7 +19,6 @@ "autoConnect": "Connexion automatique", "autoUpdateHomeWidget": "Mise à jour automatique du widget d'accueil", "backup": "Sauvegarder", - "backupAndRestore": "Sauvegarde et restauration", "backupTip": "Les données exportées sont simplement chiffrées. \nVeuillez les conserver en lieu sûr.", "backupVersionNotMatch": "La version de sauvegarde ne correspond pas.", "battery": "Batterie", @@ -235,6 +234,7 @@ "themeMode": "Mode du thème", "time": "L'heure", "times": "Fois", + "total": "Total", "traffic": "Trafic", "ttl": "ttl", "unknown": "Inconnu", @@ -251,6 +251,7 @@ "uptime": "Temps de disponibilité", "urlOrJson": "URL ou JSON", "useNoPwd": "Aucun mot de passe ne sera utilisé.", + "used": "Utilisé", "user": "Utilisateur", "versionHaveUpdate": "Trouvé : v1.0.{build}, cliquez pour mettre à jour", "versionUnknownUpdate": "Actuel : v1.0.{build}, cliquez pour vérifier les mises à jour", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 6fab183b..21279726 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -19,7 +19,6 @@ "autoConnect": "Hubungkan otomatis", "autoUpdateHomeWidget": "Widget Rumah Pembaruan Otomatis", "backup": "Cadangan", - "backupAndRestore": "Cadangan dan Pulihkan", "backupTip": "Data yang diekspor hanya dienkripsi.\nTolong jaga keamanannya.", "backupVersionNotMatch": "Versi cadangan tidak cocok.", "battery": "Baterai", @@ -235,6 +234,7 @@ "themeMode": "Mode tema", "time": "Waktu", "times": "Waktu", + "total": "Total", "traffic": "Lalu lintas", "ttl": "ttl", "unknown": "Tidak dikenal", @@ -250,6 +250,7 @@ "upsideDown": "Terbalik", "urlOrJson": "URL atau JSON", "useNoPwd": "Tidak ada kata sandi yang akan digunakan.", + "used": "Digunakan", "user": "Username", "versionHaveUpdate": "Ditemukan: v1.0.{build}, klik untuk memperbarui", "versionUnknownUpdate": "Saat ini: v1.0.{build}. Klik untuk memeriksa pembaruan.", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 804672b2..38236b4e 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -19,7 +19,6 @@ "autoConnect": "自动连接", "autoUpdateHomeWidget": "自动更新桌面小部件", "backup": "备份", - "backupAndRestore": "备份和恢复", "backupTip": "导出的数据仅进行了简单加密,请妥善保管。", "backupVersionNotMatch": "备份版本不匹配,无法恢复", "battery": "电池", @@ -235,6 +234,7 @@ "themeMode": "主题模式", "time": "时间", "times": "次", + "total": "总共", "traffic": "流量", "ttl": "缓存时间", "unknown": "未知", @@ -251,6 +251,7 @@ "uptime": "启动时长", "urlOrJson": "链接或JSON", "useNoPwd": "将会使用无密码。", + "used": "已用", "user": "用户", "versionHaveUpdate": "找到新版本:v1.0.{build}, 点击更新", "versionUnknownUpdate": "当前:v1.0.{build},点击检查更新", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index ae68a933..5db0178d 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -19,7 +19,6 @@ "autoConnect": "自動連接", "autoUpdateHomeWidget": "自動更新桌面小部件", "backup": "備份", - "backupAndRestore": "備份和還原", "backupTip": "導出的數據僅進行了簡單加密,請妥善保管。", "backupVersionNotMatch": "備份版本不匹配,無法還原", "battery": "電池", @@ -235,6 +234,7 @@ "themeMode": "主題模式", "time": "時間", "times": "次", + "total": "總共", "traffic": "流量", "ttl": "緩存時間", "unknown": "未知", @@ -251,6 +251,7 @@ "uptime": "啟動時長", "urlOrJson": "鏈接或JSON", "useNoPwd": "将使用無密碼。", + "used": "已用", "user": "用戶", "versionHaveUpdate": "找到新版本:v1.0.{build}, 點擊更新", "versionUnknownUpdate": "當前:v1.0.{build},點擊檢查更新", diff --git a/lib/main.dart b/lib/main.dart index 26522edf..ac221f52 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,6 +15,7 @@ import 'package:toolbox/core/utils/sync/icloud.dart'; import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/core/utils/sync/webdav.dart'; import 'package:toolbox/core/utils/ui.dart'; +import 'package:toolbox/data/model/app/menu/server_func.dart'; import 'package:toolbox/data/res/logger.dart'; import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/store.dart'; @@ -118,6 +119,7 @@ Future _initDb() async { Hive.registerAdapter(ServerPrivateInfoAdapter()); // 3 Hive.registerAdapter(VirtKeyAdapter()); // 4 Hive.registerAdapter(NetViewTypeAdapter()); // 5 + Hive.registerAdapter(ServerFuncBtnAdapter()); // 6 } void _setupLogger() { diff --git a/lib/view/page/backup.dart b/lib/view/page/backup.dart index e9f989a7..005bd2a2 100644 --- a/lib/view/page/backup.dart +++ b/lib/view/page/backup.dart @@ -33,7 +33,7 @@ class BackupPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar( - title: Text(l10n.backupAndRestore, style: UIs.text18), + title: Text(l10n.backup, style: UIs.text18), ), body: _buildBody(context), ); diff --git a/lib/view/page/home.dart b/lib/view/page/home.dart index 673300ad..2f0da646 100644 --- a/lib/view/page/home.dart +++ b/lib/view/page/home.dart @@ -232,7 +232,7 @@ class _HomePageState extends State ), ListTile( leading: const Icon(Icons.import_export), - title: Text(l10n.backupAndRestore), + title: Text(l10n.backup), onTap: () => AppRoute.backup().go(context), ), ListTile( @@ -286,7 +286,7 @@ ${GithubIds.contributors.map((e) => '[$e](${e.url})').join(' ')} #### Participants ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')} -#### My apps +#### My other apps - [GPT Box](https://github.com/lollipopkit/flutter_gpt_box) ''', ), diff --git a/lib/view/page/iperf.dart b/lib/view/page/iperf.dart new file mode 100644 index 00000000..31fd32c7 --- /dev/null +++ b/lib/view/page/iperf.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:toolbox/core/extension/context/locale.dart'; +import 'package:toolbox/core/extension/context/snackbar.dart'; +import 'package:toolbox/core/route.dart'; +import 'package:toolbox/data/model/server/server_private_info.dart'; +import 'package:toolbox/view/widget/appbar.dart'; +import 'package:toolbox/view/widget/input_field.dart'; + +class IPerfPage extends StatefulWidget { + final ServerPrivateInfo spi; + const IPerfPage({super.key, required this.spi}); + + @override + State createState() => _IPerfPageState(); +} + +class _IPerfPageState extends State { + final _hostCtrl = TextEditingController(); + final _portCtrl = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const CustomAppBar( + title: Text('iperf'), + ), + body: _buildBody(), + floatingActionButton: _buildFAB(), + ); + } + + Widget _buildFAB() { + return FloatingActionButton( + heroTag: 'iperf', + child: const Icon(Icons.send), + onPressed: () { + if (_hostCtrl.text.isEmpty || _portCtrl.text.isEmpty) { + context.showSnackBar(l10n.fieldMustNotEmpty); + return; + } + AppRoute.ssh( + spi: widget.spi, + initCmd: 'iperf -c ${_hostCtrl.text} -p ${_portCtrl.text}', + ).go(context); + }, + ); + } + + Widget _buildBody() { + return ListView( + padding: const EdgeInsets.symmetric(horizontal: 17), + children: [ + Input( + controller: _hostCtrl, + label: l10n.host, + icon: Icons.computer, + ), + Input( + controller: _portCtrl, + label: l10n.port, + type: TextInputType.number, + icon: Icons.numbers, + ), + ], + ); + } +} diff --git a/lib/view/page/server/detail.dart b/lib/view/page/server/detail.dart index 8205f6c5..2a4263a8 100644 --- a/lib/view/page/server/detail.dart +++ b/lib/view/page/server/detail.dart @@ -452,7 +452,7 @@ class _ServerDetailPageState extends State Widget _buildDiskItem(Disk disk, ServerStatus ss) { final (read, write) = ss.diskIO.getSpeed(disk.dev); final text = () { - final use = '${disk.used.kb2Str} / ${disk.size.kb2Str}'; + final use = '${l10n.used} ${disk.avail.kb2Str} / ${disk.size.kb2Str}'; if (read == null || write == null) return use; return '$use\n${l10n.read} $read | ${l10n.write} $write'; }(); diff --git a/lib/view/page/setting/entry.dart b/lib/view/page/setting/entry.dart index e30ef3ff..875e6dd6 100644 --- a/lib/view/page/setting/entry.dart +++ b/lib/view/page/setting/entry.dart @@ -206,7 +206,8 @@ class _SettingPageState extends State { children: [ _buildCollapseUI(), _buildServerFuncBtns(), - _buildSequence(), + _buildServerSeq(), + _buildServerDetailCardSeq(), _buildNetViewType(), _buildUpdateInterval(), _buildMaxRetry(), @@ -985,6 +986,16 @@ class _SettingPageState extends State { } Widget _buildServerFuncBtns() { + return ExpandTile( + title: Text(l10n.serverFuncBtns), + children: [ + _buildServerFuncBtnsSwitch(), + _buildServerFuncBtnsOrder(), + ], + ); + } + + Widget _buildServerFuncBtnsSwitch() { return ListTile( title: Text(l10n.location), subtitle: Text(l10n.moveOutServerFuncBtnsHelp, style: UIs.text13Grey), @@ -992,26 +1003,28 @@ class _SettingPageState extends State { ); } - Widget _buildSequence() { - return ExpandTile( + Widget _buildServerFuncBtnsOrder() { + return ListTile( title: Text(l10n.sequence), - subtitle: Text( - '${l10n.serverOrder} / ${l10n.serverDetailOrder} ...', - style: UIs.textGrey, - ), - children: [ - ListTile( - title: Text(l10n.serverOrder), - trailing: const Icon(Icons.keyboard_arrow_right), - onTap: () => AppRoute.serverOrder().go(context), - ), - ListTile( + trailing: const Icon(Icons.keyboard_arrow_right), + onTap: () => AppRoute.serverFuncBtnsOrder().go(context), + ); + } + + Widget _buildServerSeq() { + return ListTile( + title: Text(l10n.serverOrder), + trailing: const Icon(Icons.keyboard_arrow_right), + onTap: () => AppRoute.serverOrder().go(context), + ); + } + + Widget _buildServerDetailCardSeq() { + return ListTile( title: Text(l10n.serverDetailOrder), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () => AppRoute.serverDetailOrder().go(context), - ), - ], - ); + ); } Widget _buildEditorFontSize() { diff --git a/lib/view/page/setting/android.dart b/lib/view/page/setting/platform/android.dart similarity index 97% rename from lib/view/page/setting/android.dart rename to lib/view/page/setting/platform/android.dart index 5a1362e5..bc7141c6 100644 --- a/lib/view/page/setting/android.dart +++ b/lib/view/page/setting/platform/android.dart @@ -9,7 +9,7 @@ import 'package:toolbox/core/extension/context/snackbar.dart'; import 'package:toolbox/core/utils/platform/auth.dart'; import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/data/res/ui.dart'; -import 'package:toolbox/view/page/setting/platform_pub.dart'; +import 'package:toolbox/view/page/setting/platform/platform_pub.dart'; import 'package:toolbox/view/widget/appbar.dart'; import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/cardx.dart'; diff --git a/lib/view/page/setting/ios.dart b/lib/view/page/setting/platform/ios.dart similarity index 98% rename from lib/view/page/setting/ios.dart rename to lib/view/page/setting/platform/ios.dart index 79ed16ff..2bc3eb4c 100644 --- a/lib/view/page/setting/ios.dart +++ b/lib/view/page/setting/platform/ios.dart @@ -12,7 +12,7 @@ import 'package:toolbox/data/res/logger.dart'; import 'package:toolbox/data/res/misc.dart'; import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/data/res/ui.dart'; -import 'package:toolbox/view/page/setting/platform_pub.dart'; +import 'package:toolbox/view/page/setting/platform/platform_pub.dart'; import 'package:toolbox/view/widget/appbar.dart'; import 'package:toolbox/view/widget/future_widget.dart'; import 'package:toolbox/view/widget/cardx.dart'; diff --git a/lib/view/page/setting/platform_pub.dart b/lib/view/page/setting/platform/platform_pub.dart similarity index 100% rename from lib/view/page/setting/platform_pub.dart rename to lib/view/page/setting/platform/platform_pub.dart diff --git a/lib/view/page/setting/srv_detail_seq.dart b/lib/view/page/setting/seq/srv_detail_seq.dart similarity index 94% rename from lib/view/page/setting/srv_detail_seq.dart rename to lib/view/page/setting/seq/srv_detail_seq.dart index 1f214a16..5601ce6a 100644 --- a/lib/view/page/setting/srv_detail_seq.dart +++ b/lib/view/page/setting/seq/srv_detail_seq.dart @@ -5,9 +5,9 @@ import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/data/res/default.dart'; import 'package:toolbox/data/res/store.dart'; -import '../../../core/extension/order.dart'; -import '../../widget/appbar.dart'; -import '../../widget/cardx.dart'; +import '../../../../core/extension/order.dart'; +import '../../../widget/appbar.dart'; +import '../../../widget/cardx.dart'; class ServerDetailOrderPage extends StatefulWidget { const ServerDetailOrderPage({super.key}); diff --git a/lib/view/page/setting/seq/srv_func_seq.dart b/lib/view/page/setting/seq/srv_func_seq.dart new file mode 100644 index 00000000..0cf07c2c --- /dev/null +++ b/lib/view/page/setting/seq/srv_func_seq.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:toolbox/core/extension/context/locale.dart'; +import 'package:toolbox/core/extension/context/snackbar.dart'; +import 'package:toolbox/core/utils/platform/base.dart'; +import 'package:toolbox/data/model/app/menu/server_func.dart'; +import 'package:toolbox/data/res/store.dart'; + +import '../../../../core/extension/order.dart'; +import '../../../widget/appbar.dart'; +import '../../../widget/cardx.dart'; + +class ServerFuncBtnsOrderPage extends StatefulWidget { + const ServerFuncBtnsOrderPage({super.key}); + + @override + State createState() => _ServerDetailOrderPageState(); +} + +class _ServerDetailOrderPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: Text(l10n.serverDetailOrder), + ), + body: _buildBody(), + ); + } + + Widget _buildBody() { + final keys_ = Stores.setting.serverFuncBtns.fetch(); + final keys = []; + for (final key in keys_) { + keys.add(key); + } + final disabled = + ServerFuncBtn.values.where((e) => !keys.contains(e)).toList(); + final allKeys = [...keys, ...disabled]; + return ReorderableListView.builder( + padding: const EdgeInsets.all(7), + itemBuilder: (_, idx) { + final key = allKeys[idx]; + return CardX( + key: ValueKey(idx), + child: ListTile( + title: Text(key.toStr), + leading: _buildCheckBox(keys, key, idx, idx < keys.length), + trailing: isDesktop ? null : const Icon(Icons.drag_handle), + ), + ); + }, + itemCount: allKeys.length, + onReorder: (o, n) { + if (o >= keys.length || n >= keys.length) { + context.showSnackBar(l10n.disabled); + return; + } + keys.moveByItem(keys, o, n, property: Stores.setting.serverFuncBtns); + setState(() {}); + }, + ); + } + + Widget _buildCheckBox( + List keys, + ServerFuncBtn key, + int idx, + bool value, + ) { + return Checkbox( + value: value, + onChanged: (val) { + if (val == null) return; + if (val) { + if (idx >= keys.length) { + keys.add(key); + } else { + keys.insert(idx - 1, key); + } + } else { + keys.remove(key); + } + Stores.setting.serverFuncBtns.put(keys); + setState(() {}); + }, + ); + } +} diff --git a/lib/view/page/setting/srv_seq.dart b/lib/view/page/setting/seq/srv_seq.dart similarity index 98% rename from lib/view/page/setting/srv_seq.dart rename to lib/view/page/setting/seq/srv_seq.dart index 863288c5..2e46d251 100644 --- a/lib/view/page/setting/srv_seq.dart +++ b/lib/view/page/setting/seq/srv_seq.dart @@ -6,7 +6,7 @@ import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/data/res/ui.dart'; import 'package:toolbox/view/widget/cardx.dart'; -import '../../widget/appbar.dart'; +import '../../../widget/appbar.dart'; class ServerOrderPage extends StatefulWidget { const ServerOrderPage({super.key}); diff --git a/lib/view/page/setting/virt_key.dart b/lib/view/page/setting/seq/virt_key.dart similarity index 98% rename from lib/view/page/setting/virt_key.dart rename to lib/view/page/setting/seq/virt_key.dart index 7d46e9a0..b77ba22c 100644 --- a/lib/view/page/setting/virt_key.dart +++ b/lib/view/page/setting/seq/virt_key.dart @@ -8,7 +8,7 @@ import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/data/res/ui.dart'; import 'package:toolbox/view/widget/cardx.dart'; -import '../../widget/appbar.dart'; +import '../../../widget/appbar.dart'; class SSHVirtKeySettingPage extends StatefulWidget { const SSHVirtKeySettingPage({super.key}); diff --git a/lib/view/page/storage/local.dart b/lib/view/page/storage/local.dart index 44c31cf1..567295b7 100644 --- a/lib/view/page/storage/local.dart +++ b/lib/view/page/storage/local.dart @@ -180,9 +180,8 @@ class _LocalStoragePageState extends State { ? const Icon(Icons.folder_open) : const Icon(Icons.insert_drive_file), title: Text(fileName), - subtitle: isDir - ? null - : Text(stat.size.bytes2Str, style: UIs.textGrey), + subtitle: + isDir ? null : Text(stat.size.bytes2Str, style: UIs.textGrey), trailing: Text( stat.modified .toString() diff --git a/lib/view/widget/server_func_btns.dart b/lib/view/widget/server_func_btns.dart index 1539955c..01525fa4 100644 --- a/lib/view/widget/server_func_btns.dart +++ b/lib/view/widget/server_func_btns.dart @@ -16,6 +16,7 @@ import 'package:toolbox/data/model/server/dist.dart'; import 'package:toolbox/data/model/server/snippet.dart'; import 'package:toolbox/data/res/path.dart'; import 'package:toolbox/data/res/provider.dart'; +import 'package:toolbox/data/res/store.dart'; import '../../core/route.dart'; import '../../core/utils/server.dart'; @@ -33,9 +34,9 @@ class ServerFuncBtnsTopRight extends StatelessWidget { @override Widget build(BuildContext context) { - return PopupMenu( - items: ServerTabMenu.values - .map((e) => PopupMenuItem( + return PopupMenu( + items: ServerFuncBtn.values + .map((e) => PopupMenuItem( value: e, child: Row( children: [ @@ -94,7 +95,7 @@ class ServerFuncBtns extends StatelessWidget { // ); return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, - children: ServerTabMenu.values + children: Stores.setting.serverFuncBtns.fetch() .map( (e) => IconButton( onPressed: () => _onTapMoreBtns(e, spi, context), @@ -109,21 +110,21 @@ class ServerFuncBtns extends StatelessWidget { } void _onTapMoreBtns( - ServerTabMenu value, + ServerFuncBtn value, ServerPrivateInfo spi, BuildContext context, ) async { switch (value) { - case ServerTabMenu.pkg: + case ServerFuncBtn.pkg: _onPkg(context, spi); break; - case ServerTabMenu.sftp: + case ServerFuncBtn.sftp: AppRoute.sftp(spi: spi).checkGo( context: context, check: () => _checkClient(context, spi.id), ); break; - case ServerTabMenu.snippet: + case ServerFuncBtn.snippet: final snippet = await context.showPickSingleDialog( items: Pros.snippet.snippets, name: (e) => e.name, @@ -135,21 +136,27 @@ void _onTapMoreBtns( check: () => _checkClient(context, spi.id), ); break; - case ServerTabMenu.container: + case ServerFuncBtn.container: AppRoute.docker(spi: spi).checkGo( context: context, check: () => _checkClient(context, spi.id), ); break; - case ServerTabMenu.process: + case ServerFuncBtn.process: AppRoute.process(spi: spi).checkGo( context: context, check: () => _checkClient(context, spi.id), ); break; - case ServerTabMenu.terminal: + case ServerFuncBtn.terminal: _gotoSSH(spi, context); break; + case ServerFuncBtn.iperf: + AppRoute.iperf(spi: spi).checkGo( + context: context, + check: () => _checkClient(context, spi.id), + ); + break; } }