diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 9e17dcc2..8f8fa3fd 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -690,7 +690,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -700,7 +700,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -826,7 +826,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -836,7 +836,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -854,7 +854,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -864,7 +864,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -885,7 +885,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -898,7 +898,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; @@ -924,7 +924,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -937,7 +937,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -960,7 +960,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -973,7 +973,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -996,7 +996,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -1008,7 +1008,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; @@ -1037,7 +1037,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -1049,7 +1049,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; @@ -1075,7 +1075,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -1087,7 +1087,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; diff --git a/lib/data/model/server/server_private_info.dart b/lib/data/model/server/server_private_info.dart index 14f8df94..91d11849 100644 --- a/lib/data/model/server/server_private_info.dart +++ b/lib/data/model/server/server_private_info.dart @@ -1,6 +1,7 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:toolbox/data/model/server/custom.dart'; import 'package:toolbox/data/model/server/server.dart'; +import 'package:toolbox/data/model/server/wol_cfg.dart'; import 'package:toolbox/data/res/provider.dart'; import '../app/error.dart'; @@ -38,6 +39,9 @@ class ServerPrivateInfo { @HiveField(10) final ServerCustom? custom; + @HiveField(11) + final WakeOnLanCfg? wolCfg; + final String id; const ServerPrivateInfo({ @@ -52,6 +56,7 @@ class ServerPrivateInfo { this.autoConnect, this.jumpId, this.custom, + this.wolCfg, }) : id = '$user@$ip:$port'; static ServerPrivateInfo fromJson(Map json) { @@ -68,6 +73,9 @@ class ServerPrivateInfo { final custom = json["customCmd"] == null ? null : ServerCustom.fromJson(json["custom"].cast()); + final wolCfg = json["wolCfg"] == null + ? null + : WakeOnLanCfg.fromJson(json["wolCfg"].cast()); return ServerPrivateInfo( name: name, @@ -81,6 +89,7 @@ class ServerPrivateInfo { autoConnect: autoConnect, jumpId: jumpId, custom: custom, + wolCfg: wolCfg, ); } @@ -111,6 +120,9 @@ class ServerPrivateInfo { if (custom != null) { data["custom"] = custom?.toJson(); } + if (wolCfg != null) { + data["wolCfg"] = wolCfg?.toJson(); + } return data; } diff --git a/lib/data/model/server/server_private_info.g.dart b/lib/data/model/server/server_private_info.g.dart index 2ab99e6d..c91bdd5a 100644 --- a/lib/data/model/server/server_private_info.g.dart +++ b/lib/data/model/server/server_private_info.g.dart @@ -28,13 +28,14 @@ class ServerPrivateInfoAdapter extends TypeAdapter { autoConnect: fields[8] as bool?, jumpId: fields[9] as String?, custom: fields[10] as ServerCustom?, + wolCfg: fields[11] as WakeOnLanCfg?, ); } @override void write(BinaryWriter writer, ServerPrivateInfo obj) { writer - ..writeByte(11) + ..writeByte(12) ..writeByte(0) ..write(obj.name) ..writeByte(1) @@ -56,7 +57,9 @@ class ServerPrivateInfoAdapter extends TypeAdapter { ..writeByte(9) ..write(obj.jumpId) ..writeByte(10) - ..write(obj.custom); + ..write(obj.custom) + ..writeByte(11) + ..write(obj.wolCfg); } @override diff --git a/lib/data/model/server/wol_cfg.dart b/lib/data/model/server/wol_cfg.dart new file mode 100644 index 00000000..78098190 --- /dev/null +++ b/lib/data/model/server/wol_cfg.dart @@ -0,0 +1,71 @@ +import 'dart:io'; + +import 'package:hive_flutter/hive_flutter.dart'; +import 'package:wake_on_lan/wake_on_lan.dart'; + +@HiveType(typeId: 8) +final class WakeOnLanCfg { + @HiveField(0) + final String mac; + @HiveField(1) + final String ip; + @HiveField(2) + final String? pwd; + + const WakeOnLanCfg({ + required this.mac, + required this.ip, + this.pwd, + }); + + (Object?, bool) validate() { + final macValidation = MACAddress.validate(mac); + final ipValidation = IPAddress.validate( + ip, + type: ip.contains(':') + ? InternetAddressType.IPv6 + : InternetAddressType.IPv4, + ); + final pwdValidation = SecureONPassword.validate(pwd); + + final valid = + macValidation.state && ipValidation.state && pwdValidation.state; + final err = + macValidation.error ?? ipValidation.error ?? pwdValidation.error; + return (err, valid); + } + + Future wake() async { + if (!validate().$2) { + throw Exception('Invalid WakeOnLanCfg'); + } + + final ip_ = IPAddress(ip); + final mac_ = MACAddress(mac); + final pwd_ = pwd != null ? SecureONPassword(pwd!) : null; + final obj = WakeOnLAN(ip_, mac_, password: pwd_); + await obj.wake( + repeat: 3, + repeatDelay: const Duration(milliseconds: 500), + ); + } + + static WakeOnLanCfg fromJson(Map json) { + return WakeOnLanCfg( + mac: json['mac'] as String, + ip: json['ip'] as String, + pwd: json['pwd'] as String?, + ); + } + + Map toJson() { + final map = { + 'mac': mac, + 'ip': ip, + }; + if (pwd != null) { + map['pwd'] = pwd; + } + return map; + } +} diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index 81d0bfd8..e9301e83 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -141,8 +141,7 @@ class ServerProvider extends ChangeNotifier { TryLimiter.reset(s.spi.id); } - if (!(s.spi.autoConnect ?? true) && - s.conn == ServerConn.disconnected || + if (!(s.spi.autoConnect ?? true) && s.conn == ServerConn.disconnected || _manualDisconnectedIds.contains(s.spi.id)) { return; } @@ -277,6 +276,23 @@ class ServerProvider extends ChangeNotifier { if (s.needGenClient || (s.client?.isClosed ?? true)) { _setServerState(s, ServerConn.connecting); + final wol = spi.wolCfg; + if (wol != null) { + /// TODO: test it + try { + await wol.wake(); + } catch (e) { + // TryLimiter.inc(sid); + // s.status.err = SSHErr( + // type: SSHErrType.connect, + // message: 'Wake on lan failed: $e', + // ); + // _setServerState(s, ServerConn.failed); + // Loggers.app.warning('Wake on lan failed', e); + // return; + } + } + try { final time1 = DateTime.now(); s.client = await genClient( diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index c3e879ce..aabdc719 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 = 884; + static const int build = 887; static const String engine = "3.19.6"; - static const String buildAt = "2024-05-09 17:08:49"; - static const int modifications = 4; + static const String buildAt = "2024-05-09 23:08:15"; + static const int modifications = 22; static const int script = 46; } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index ad81d7fc..857db550 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "Webdav-Einstellungen sind leer", "whenOpenApp": "Beim Öffnen der App", "willTakEeffectImmediately": "Wird sofort angewendet", + "wolTip": "Nach der Konfiguration von WOL (Wake-on-LAN) wird jedes Mal, wenn der Server verbunden wird, eine WOL-Anfrage gesendet.", "write": "Schreiben", "writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht." } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 48c1eba1..364934b0 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "Webdav setting is empty", "whenOpenApp": "When opening the app", "willTakEeffectImmediately": "Will take effect immediately", + "wolTip": "After configuring WOL (Wake-on-LAN), a WOL request is sent each time the server is connected.", "write": "Write", "writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist." } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index d732883a..1ce283d8 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "La configuración de Webdav está vacía", "whenOpenApp": "Al abrir la App", "willTakEeffectImmediately": "Los cambios tendrán efecto inmediatamente", + "wolTip": "Después de configurar WOL (Wake-on-LAN), se envía una solicitud de WOL cada vez que se conecta el servidor.", "write": "Escribir", "writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe." } \ No newline at end of file diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index f6027ea1..433859b6 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "La configuration Webdav est vide", "whenOpenApp": "À l'ouverture de l'application", "willTakEeffectImmediately": "Prendra effet immédiatement", + "wolTip": "Après avoir configuré WOL (Wake-on-LAN), une requête WOL est envoyée à chaque connexion au serveur.", "write": "Écrire", "writeScriptFailTip": "L'écriture du script a échoué, peut-être en raison d'un manque de permissions ou parce que le répertoire n'existe pas." } \ No newline at end of file diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 4101f13f..618d8af7 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "Pengaturan webdav kosong", "whenOpenApp": "Saat membuka aplikasi", "willTakEeffectImmediately": "Akan segera berlaku", + "wolTip": "Setelah mengonfigurasi WOL (Wake-on-LAN), permintaan WOL dikirim setiap kali server terhubung.", "write": "Tulis", "writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada." } \ No newline at end of file diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 42c2f6ed..cb73db96 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "Webdavの設定が空です", "whenOpenApp": "アプリを開くとき", "willTakEeffectImmediately": "変更は即座に有効になります", + "wolTip": "WOL(Wake-on-LAN)を設定した後、サーバーに接続するたびにWOLリクエストが送信されます。", "write": "書き込み", "writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。" } \ No newline at end of file diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 4aabdd9d..63eaaede 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -327,6 +327,7 @@ "webdavSettingEmpty": "Webdav-instelling is leeg", "whenOpenApp": "Bij het openen van de app", "willTakEeffectImmediately": "Zal onmiddellijk van kracht worden", + "wolTip": "Na het configureren van WOL (Wake-on-LAN), wordt elke keer dat de server wordt verbonden een WOL-verzoek verzonden.", "write": "Schrijven", "writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat." } \ No newline at end of file diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index d54d6764..c44314bb 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "Configurações de Webdav estão vazias", "whenOpenApp": "Ao abrir o app", "willTakEeffectImmediately": "As alterações serão aplicadas imediatamente", + "wolTip": "Após configurar o WOL (Wake-on-LAN), um pedido de WOL é enviado cada vez que o servidor é conectado.", "write": "Escrita", "writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe." } \ No newline at end of file diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 68a3289e..add106eb 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "Настройки Webdav пусты", "whenOpenApp": "при открытии приложения", "willTakEeffectImmediately": "Изменения вступят в силу немедленно", + "wolTip": "После настройки WOL (Wake-on-LAN) при каждом подключении к серверу отправляется запрос WOL.", "write": "запись", "writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует." } \ No newline at end of file diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index f7813f18..7efed18e 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -323,11 +323,12 @@ "virtKeyHelpIME": "打开/关闭键盘", "virtKeyHelpSFTP": "在 SFTP 中打开当前路径。", "waitConnection": "请等待连接建立", - "wakeLock": "保持喚醒", + "wakeLock": "保持唤醒", "watchNotPaired": "没有已配对的 Apple Watch", "webdavSettingEmpty": "Webdav 设置项为空", "whenOpenApp": "当打开 App 时", "willTakEeffectImmediately": "更改将会立即生效", + "wolTip": "在配置 WOL 后,每次连接服务器都会先发送一次 WOl 请求", "write": "写", "writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等" } \ No newline at end of file diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index 5c5bd4ec..802b63e3 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -328,6 +328,7 @@ "webdavSettingEmpty": "Webdav 設置項爲空", "whenOpenApp": "當打開 App 時", "willTakEeffectImmediately": "更改將會立即生效", + "wolTip": "在配置WOL(網絡喚醒)後,每次連接伺服器都會先發送一次WOL請求。", "write": "写", "writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。" } \ No newline at end of file diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 2ee3d262..79276ef6 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -12,6 +12,7 @@ import 'package:toolbox/core/extension/widget.dart'; import 'package:toolbox/core/utils/ui.dart'; import 'package:toolbox/data/model/app/shell_func.dart'; import 'package:toolbox/data/model/server/custom.dart'; +import 'package:toolbox/data/model/server/wol_cfg.dart'; import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/view/widget/expand_tile.dart'; @@ -44,6 +45,9 @@ class _ServerEditPageState extends State { final _customCmdCtrl = TextEditingController(); final _preferTempDevCtrl = TextEditingController(); final _logoUrlCtrl = TextEditingController(); + final _wolMacCtrl = TextEditingController(); + final _wolIpCtrl = TextEditingController(); + final _wolPwdCtrl = TextEditingController(); final _nameFocus = FocusNode(); final _ipFocus = FocusNode(); @@ -99,6 +103,13 @@ class _ServerEditPageState extends State { _preferTempDevCtrl.text = custom.preferTempDev ?? ''; _logoUrlCtrl.text = custom.logoUrl ?? ''; } + + final wol = spi.wolCfg; + if (wol != null) { + _wolMacCtrl.text = wol.mac; + _wolIpCtrl.text = wol.ip; + _wolPwdCtrl.text = wol.pwd ?? ''; + } } } @@ -120,6 +131,9 @@ class _ServerEditPageState extends State { _customCmdCtrl.dispose(); _preferTempDevCtrl.dispose(); _logoUrlCtrl.dispose(); + _wolMacCtrl.dispose(); + _wolIpCtrl.dispose(); + _wolPwdCtrl.dispose(); } @override @@ -380,52 +394,9 @@ class _ServerEditPageState extends State { hint: 'https://example.com/logo.png', ), UIs.height7, - const Text('PVE', style: UIs.text13Grey), + ..._buildPVEs(), UIs.height7, - Input( - controller: _pveAddrCtrl, - type: TextInputType.url, - icon: MingCute.web_line, - label: l10n.addr, - hint: 'https://example.com:8006', - ), - ListTile( - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(MingCute.certificate_line), - ), - title: Text('PVE ${l10n.ignoreCert}'), - subtitle: Text(l10n.pveIgnoreCertTip, style: UIs.text12Grey), - trailing: ListenableBuilder( - listenable: _pveIgnoreCert, - builder: (_, __) => Switch( - value: _pveIgnoreCert.value, - onChanged: (val) { - _pveIgnoreCert.value = val; - }, - ), - ), - ).card, - UIs.height7, - Text(l10n.customCmd, style: UIs.text13Grey), - UIs.height7, - Input( - controller: _customCmdCtrl, - type: TextInputType.text, - maxLines: 3, - label: 'JSON', - icon: Icons.code, - hint: '{${l10n.customCmdHint}}', - ), - ListTile( - leading: const Padding( - padding: EdgeInsets.only(left: 10), - child: Icon(MingCute.doc_line), - ), - title: Text(l10n.doc), - trailing: const Icon(Icons.open_in_new, size: 17), - onTap: () => openUrl(l10n.customCmdDocUrl), - ).card, + ..._buildCustomCmds(), UIs.height7, Text(l10n.temperature, style: UIs.text13Grey), UIs.height7, @@ -436,10 +407,101 @@ class _ServerEditPageState extends State { icon: MingCute.low_temperature_line, hint: 'nvme-pci-0400', ), + UIs.height7, + ..._buildWOLs(), ], ); } + List _buildPVEs() { + return [ + const Text('PVE', style: UIs.text13Grey), + UIs.height7, + Input( + controller: _pveAddrCtrl, + type: TextInputType.url, + icon: MingCute.web_line, + label: l10n.addr, + hint: 'https://example.com:8006', + ), + ListTile( + leading: const Padding( + padding: EdgeInsets.only(left: 10), + child: Icon(MingCute.certificate_line), + ), + title: Text('PVE ${l10n.ignoreCert}'), + subtitle: Text(l10n.pveIgnoreCertTip, style: UIs.text12Grey), + trailing: ListenableBuilder( + listenable: _pveIgnoreCert, + builder: (_, __) => Switch( + value: _pveIgnoreCert.value, + onChanged: (val) { + _pveIgnoreCert.value = val; + }, + ), + ), + ).card, + ]; + } + + List _buildCustomCmds() { + return [ + Text(l10n.customCmd, style: UIs.text13Grey), + UIs.height7, + Input( + controller: _customCmdCtrl, + type: TextInputType.text, + maxLines: 3, + label: 'JSON', + icon: Icons.code, + hint: '{${l10n.customCmdHint}}', + ), + ListTile( + leading: const Padding( + padding: EdgeInsets.only(left: 10), + child: Icon(MingCute.doc_line), + ), + title: Text(l10n.doc), + trailing: const Icon(Icons.open_in_new, size: 17), + onTap: () => openUrl(l10n.customCmdDocUrl), + ).card, + ]; + } + + List _buildWOLs() { + return [ + const Text('Wake On LAN', style: UIs.text13Grey), + UIs.height7, + ListTile( + leading: const Icon(BoxIcons.bxs_help_circle), + title: Text(l10n.about), + subtitle: Text(l10n.wolTip, style: UIs.text12Grey), + ).card, + Input( + controller: _wolMacCtrl, + type: TextInputType.text, + label: 'Mac ${l10n.addr}', + icon: Icons.computer, + hint: '00:11:22:33:44:55', + ), + Input( + controller: _wolIpCtrl, + type: TextInputType.text, + label: 'IP ${l10n.addr}', + icon: Icons.network_cell, + hint: '192.168.1.x', + ), + Input( + controller: _wolPwdCtrl, + type: TextInputType.text, + obscureText: true, + label: l10n.pwd, + icon: Icons.password, + hint: l10n.pwd, + ), + ]; + } + Widget _buildFAB() { return FloatingActionButton( onPressed: _onSave, @@ -544,6 +606,17 @@ class _ServerEditPageState extends State { logoUrl: _logoUrlCtrl.text.selfIfNotNullEmpty, ); + final wol = WakeOnLanCfg( + mac: _wolMacCtrl.text, + ip: _wolIpCtrl.text, + pwd: _wolPwdCtrl.text.selfIfNotNullEmpty, + ); + final wolValidation = wol.validate(); + if (!wolValidation.$2) { + context.showSnackBar('${l10n.failed}: ${wolValidation.$1}'); + return; + } + final spi = ServerPrivateInfo( name: _nameController.text.isEmpty ? _ipController.text @@ -560,6 +633,7 @@ class _ServerEditPageState extends State { autoConnect: _autoConnect.value, jumpId: _jumpServer.value, custom: custom, + wolCfg: wol, ); if (widget.spi == null) { diff --git a/lib/view/page/setting/entry.dart b/lib/view/page/setting/entry.dart index 85532c7d..4b117516 100644 --- a/lib/view/page/setting/entry.dart +++ b/lib/view/page/setting/entry.dart @@ -1135,7 +1135,6 @@ class _SettingPageState extends State { Widget _buildWakeLock() { return ListTile( - leading: const Icon(MingCute.lock_fill), title: Text(l10n.wakeLock), trailing: StoreSwitch(prop: _setting.generalWakeLock), ); diff --git a/lib/view/widget/icon_text_btn.dart b/lib/view/widget/icon_text_btn.dart index 0963a4da..79d42b5c 100644 --- a/lib/view/widget/icon_text_btn.dart +++ b/lib/view/widget/icon_text_btn.dart @@ -18,21 +18,22 @@ final class IconTextBtn extends StatelessWidget { @override Widget build(BuildContext context) { return IconButton( - onPressed: onPressed, - tooltip: text, - icon: orientation == Orientation.landscape ? Row( - children: [ - Icon(icon), - UIs.width7, - Text(text, style: UIs.text13Grey), - ], - ) : Column( - children: [ - Icon(icon), - UIs.height7, - Text(text, style: UIs.text13Grey), - ], - ) - ); + onPressed: onPressed, + tooltip: text, + icon: orientation == Orientation.landscape + ? Row( + children: [ + Icon(icon), + UIs.width7, + Text(text, style: UIs.text13Grey), + ], + ) + : Column( + children: [ + Icon(icon), + UIs.height7, + Text(text, style: UIs.text13Grey), + ], + )); } } diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index dbe945c1..cbb82802 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -471,7 +471,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -481,7 +481,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -608,7 +608,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -618,7 +618,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -638,7 +638,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 884; + CURRENT_PROJECT_VERSION = 887; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; @@ -649,7 +649,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.884; + MARKETING_VERSION = 1.0.887; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/pubspec.lock b/pubspec.lock index fb8b383c..33941be1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1333,6 +1333,14 @@ packages: url: "https://pub.dev" source: hosted version: "13.0.0" + wake_on_lan: + dependency: "direct main" + description: + name: wake_on_lan + sha256: a0db43df0cd05181f476f38ec63345a763b7d3b9d8ab25cabbff45881780cb8e + url: "https://pub.dev" + source: hosted + version: "4.1.1+3" wakelock_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4f237514..ff500bb3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -78,6 +78,7 @@ dependencies: fl_chart: ^0.67.0 wakelock_plus: ^1.2.4 extended_image: ^8.2.0 + wake_on_lan: ^4.1.1+3 dev_dependencies: flutter_native_splash: ^2.1.6