From d70d4684fe548e5694f390fd138db2db76ed67a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?lollipopkit=F0=9F=8F=B3=EF=B8=8F=E2=80=8D=E2=9A=A7?= =?UTF-8?q?=EF=B8=8F?= <10864310+lollipopkit@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:42:37 +0800 Subject: [PATCH 1/2] opt.: sftp home & back Fixes #531 --- analysis_options.yaml | 9 +- lib/core/extension/ssh_client.dart | 22 +- lib/core/route.dart | 24 +- lib/core/utils/server.dart | 2 +- lib/core/utils/sync/icloud.dart | 2 +- lib/data/model/app/shell_func.dart | 8 +- lib/data/model/container/image.dart | 48 +- lib/data/model/container/ps.dart | 32 +- lib/data/model/pkg/upgrade_info.dart | 6 +- lib/data/model/server/conn.dart | 2 +- lib/data/model/server/custom.dart | 20 +- lib/data/model/server/disk.dart | 2 +- lib/data/model/server/net_speed.dart | 2 +- lib/data/model/server/private_key_info.dart | 8 +- lib/data/model/server/proc.dart | 6 +- lib/data/model/server/server.dart | 2 +- .../model/server/server_private_info.dart | 66 +-- .../server/server_status_update_req.dart | 12 +- lib/data/model/server/snippet.dart | 2 +- lib/data/model/sftp/absolute_path.dart | 14 +- lib/data/model/sftp/browser_status.dart | 8 +- lib/data/provider/container.dart | 2 +- lib/data/provider/pve.dart | 2 +- lib/data/provider/server.dart | 12 +- lib/data/res/build_data.dart | 2 +- lib/data/res/status.dart | 12 +- lib/data/store/private_key.dart | 2 +- lib/data/store/server.dart | 2 +- lib/data/store/setting.dart | 4 +- lib/data/store/snippet.dart | 2 +- lib/l10n/app_de.arb | 1 - lib/l10n/app_en.arb | 1 - lib/l10n/app_es.arb | 1 - lib/l10n/app_fr.arb | 1 - lib/l10n/app_id.arb | 1 - lib/l10n/app_ja.arb | 1 - lib/l10n/app_nl.arb | 1 - lib/l10n/app_pt.arb | 1 - lib/l10n/app_ru.arb | 1 - lib/l10n/app_tr.arb | 1 - lib/l10n/app_zh.arb | 1 - lib/l10n/app_zh_tw.arb | 1 - lib/view/page/backup.dart | 2 +- lib/view/page/container.dart | 8 +- lib/view/page/editor.dart | 2 +- lib/view/page/ping.dart | 2 +- lib/view/page/private_key/edit.dart | 6 +- lib/view/page/private_key/list.dart | 6 +- lib/view/page/process.dart | 8 +- lib/view/page/server/detail/view.dart | 6 +- lib/view/page/server/edit.dart | 6 +- lib/view/page/server/tab.dart | 12 +- lib/view/page/setting/entry.dart | 7 +- lib/view/page/snippet/list.dart | 6 +- lib/view/page/ssh/page.dart | 8 +- lib/view/page/storage/local.dart | 4 +- lib/view/page/storage/sftp.dart | 487 +++++++++--------- lib/view/widget/server_func_btns.dart | 14 +- lib/view/widget/unix_perm.dart | 2 +- 59 files changed, 463 insertions(+), 472 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 91b9e0f1d..72d290fc8 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -30,14 +30,19 @@ linter: # `// ignore_for_file: name_of_lint` syntax on the line or in the file # producing the lint. rules: - library_private_types_in_public_api: false + library_private_types_in_public_api: true use_build_context_synchronously: false depend_on_referenced_packages: false prefer_final_locals: true unnecessary_parenthesis: true implicit_call_tearoffs: true + always_declare_return_types: true + always_use_package_imports: true + annotate_overrides: true + avoid_empty_else: true # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + avoid_return_types_on_setters: true # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/lib/core/extension/ssh_client.dart b/lib/core/extension/ssh_client.dart index 459936aa2..eef9c48e0 100644 --- a/lib/core/extension/ssh_client.dart +++ b/lib/core/extension/ssh_client.dart @@ -5,10 +5,10 @@ import 'package:dartssh2/dartssh2.dart'; import 'package:fl_lib/fl_lib.dart'; import 'package:flutter/widgets.dart'; -import '../../data/res/misc.dart'; +import 'package:server_box/data/res/misc.dart'; -typedef _OnStdout = void Function(String data, SSHSession session); -typedef _OnStdin = void Function(SSHSession session); +typedef OnStdout = void Function(String data, SSHSession session); +typedef OnStdin = void Function(SSHSession session); typedef PwdRequestFunc = Future Function(String? user); @@ -16,15 +16,15 @@ extension SSHClientX on SSHClient { /// TODO: delete [exec] Future exec( String cmd, { - _OnStdout? onStderr, - _OnStdout? onStdout, - _OnStdin? stdin, + OnStdout? onStderr, + OnStdout? onStdout, + OnStdin? stdin, bool redirectToBash = false, // not working yet. do not use }) async { - final session = await execute(redirectToBash ? "head -1 | bash" : cmd); + final session = await execute(redirectToBash ? 'head -1 | bash' : cmd); if (redirectToBash) { - session.stdin.add("$cmd\n".uint8List); + session.stdin.add('$cmd\n'.uint8List); } final stdoutDone = Completer(); @@ -62,9 +62,9 @@ extension SSHClientX on SSHClient { Future execWithPwd( String cmd, { BuildContext? context, - _OnStdout? onStdout, - _OnStdout? onStderr, - _OnStdin? stdin, + OnStdout? onStdout, + OnStdout? onStderr, + OnStdin? stdin, bool redirectToBash = false, // not working yet. do not use required String id, }) async { diff --git a/lib/core/route.dart b/lib/core/route.dart index 9a92b0aca..4fb04d0fb 100644 --- a/lib/core/route.dart +++ b/lib/core/route.dart @@ -20,18 +20,18 @@ import 'package:server_box/view/page/ssh/page.dart'; import 'package:server_box/view/page/setting/seq/virt_key.dart'; import 'package:server_box/view/page/storage/local.dart'; -import '../data/model/server/snippet.dart'; -import '../view/page/editor.dart'; -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/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'; -import '../view/page/storage/sftp_mission.dart'; +import 'package:server_box/data/model/server/snippet.dart'; +import 'package:server_box/view/page/editor.dart'; +import 'package:server_box/view/page/process.dart'; +import 'package:server_box/view/page/server/edit.dart'; +import 'package:server_box/view/page/server/tab.dart'; +import 'package:server_box/view/page/setting/entry.dart'; +import 'package:server_box/view/page/setting/seq/srv_detail_seq.dart'; +import 'package:server_box/view/page/setting/seq/srv_seq.dart'; +import 'package:server_box/view/page/snippet/edit.dart'; +import 'package:server_box/view/page/snippet/list.dart'; +import 'package:server_box/view/page/storage/sftp.dart'; +import 'package:server_box/view/page/storage/sftp_mission.dart'; class AppRoutes { final Widget page; diff --git a/lib/core/utils/server.dart b/lib/core/utils/server.dart index b080a422e..99062a0e6 100644 --- a/lib/core/utils/server.dart +++ b/lib/core/utils/server.dart @@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:server_box/data/model/app/error.dart'; import 'package:server_box/data/res/store.dart'; -import '../../data/model/server/server_private_info.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; /// Must put this func out of any Class. /// diff --git a/lib/core/utils/sync/icloud.dart b/lib/core/utils/sync/icloud.dart index 9065b3f62..ba878e3b2 100644 --- a/lib/core/utils/sync/icloud.dart +++ b/lib/core/utils/sync/icloud.dart @@ -9,7 +9,7 @@ import 'package:server_box/data/model/app/backup.dart'; import 'package:server_box/data/model/app/sync.dart'; import 'package:server_box/data/res/misc.dart'; -import '../../../data/model/app/error.dart'; +import 'package:server_box/data/model/app/error.dart'; abstract final class ICloud { static const _containerId = 'iCloud.tech.lolli.serverbox'; diff --git a/lib/data/model/app/shell_func.dart b/lib/data/model/app/shell_func.dart index a9a9ca649..4592fe72f 100644 --- a/lib/data/model/app/shell_func.dart +++ b/lib/data/model/app/shell_func.dart @@ -1,7 +1,7 @@ import 'package:server_box/core/extension/context/locale.dart'; -import '../../res/build_data.dart'; -import '../server/system.dart'; +import 'package:server_box/data/res/build_data.dart'; +import 'package:server_box/data/model/server/system.dart'; enum ShellFunc { status, @@ -47,11 +47,11 @@ enum ShellFunc { static String getInstallShellCmd(String id) { final scriptDir = getScriptDir(id); final scriptPath = '$scriptDir/$scriptFile'; - return """ + return ''' mkdir -p $scriptDir cat > $scriptPath chmod 744 $scriptPath -"""; +'''; } String get flag => switch (this) { diff --git a/lib/data/model/container/image.dart b/lib/data/model/container/image.dart index 398f605c7..1df001c98 100644 --- a/lib/data/model/container/image.dart +++ b/lib/data/model/container/image.dart @@ -45,21 +45,21 @@ final class PodmanImg implements ContainerImg { String toRawJson() => json.encode(toJson()); factory PodmanImg.fromJson(Map json) => PodmanImg( - repository: json["repository"], - tag: json["tag"], - id: json["Id"], - created: json["Created"], - size: json["Size"], - containers: json["Containers"], + repository: json['repository'], + tag: json['tag'], + id: json['Id'], + created: json['Created'], + size: json['Size'], + containers: json['Containers'], ); Map toJson() => { - "repository": repository, - "tag": tag, - "Id": id, - "Created": created, - "Size": size, - "Containers": containers, + 'repository': repository, + 'tag': tag, + 'Id': id, + 'Created': created, + 'Size': size, + 'Containers': containers, }; } @@ -96,36 +96,36 @@ final class DockerImg implements ContainerImg { String toRawJson() => json.encode(toJson()); factory DockerImg.fromJson(Map json) { - final containers = switch (json["Containers"]) { + final containers = switch (json['Containers']) { final String a => a, final Object? a => a.toString(), }; - final repo = switch (json["Repository"] ?? json["Names"]) { + final repo = switch (json['Repository'] ?? json['Names']) { final String a => a, final List a => a.firstOrNull.toString(), final Object? a => a.toString(), }; - final size = switch (json["Size"]) { + final size = switch (json['Size']) { final String a => a, final int a => a.bytes2Str, final Object? a => a.toString(), }; return DockerImg( containers: containers, - createdAt: json["CreatedAt"], - id: json["ID"] ?? json["Id"] ?? '', + createdAt: json['CreatedAt'], + id: json['ID'] ?? json['Id'] ?? '', repository: repo, size: size, - tag: json["Tag"], + tag: json['Tag'], ); } Map toJson() => { - "Containers": containers, - "CreatedAt": createdAt, - "ID": id, - "Repository": repository, - "Size": size, - "Tag": tag, + 'Containers': containers, + 'CreatedAt': createdAt, + 'ID': id, + 'Repository': repository, + 'Size': size, + 'Tag': tag, }; } diff --git a/lib/data/model/container/ps.dart b/lib/data/model/container/ps.dart index 016380d57..9d5865a9c 100644 --- a/lib/data/model/container/ps.dart +++ b/lib/data/model/container/ps.dart @@ -84,29 +84,29 @@ final class PodmanPs implements ContainerPs { String toRawJson() => json.encode(toJson()); factory PodmanPs.fromJson(Map json) => PodmanPs( - command: json["Command"] == null + command: json['Command'] == null ? [] - : List.from(json["Command"]!.map((x) => x)), + : List.from(json['Command']!.map((x) => x)), created: - json["Created"] == null ? null : DateTime.parse(json["Created"]), - exited: json["Exited"], - id: json["Id"], - image: json["Image"], - names: json["Names"] == null + json['Created'] == null ? null : DateTime.parse(json['Created']), + exited: json['Exited'], + id: json['Id'], + image: json['Image'], + names: json['Names'] == null ? [] - : List.from(json["Names"]!.map((x) => x)), - startedAt: json["StartedAt"], + : List.from(json['Names']!.map((x) => x)), + startedAt: json['StartedAt'], ); Map toJson() => { - "Command": + 'Command': command == null ? [] : List.from(command!.map((x) => x)), - "Created": created?.toIso8601String(), - "Exited": exited, - "Id": id, - "Image": image, - "Names": names == null ? [] : List.from(names!.map((x) => x)), - "StartedAt": startedAt, + 'Created': created?.toIso8601String(), + 'Exited': exited, + 'Id': id, + 'Image': image, + 'Names': names == null ? [] : List.from(names!.map((x) => x)), + 'StartedAt': startedAt, }; } diff --git a/lib/data/model/pkg/upgrade_info.dart b/lib/data/model/pkg/upgrade_info.dart index 47a67fece..1dc03dfcf 100644 --- a/lib/data/model/pkg/upgrade_info.dart +++ b/lib/data/model/pkg/upgrade_info.dart @@ -34,9 +34,9 @@ class UpgradePkgInfo { } void _parseApt(String raw) { - final split1 = raw.split("/"); + final split1 = raw.split('/'); package = split1[0]; - final split2 = split1[1].split(" "); + final split2 = split1[1].split(' '); newVersion = split2[1]; arch = split2[2]; nowVersion = split2[5].replaceFirst(']', ''); @@ -53,7 +53,7 @@ class UpgradePkgInfo { } void _parseZypper(String raw) { - final cols = raw.split("|"); + final cols = raw.split('|'); package = cols[2].trim(); nowVersion = cols[3].trim(); newVersion = cols[4].trim(); diff --git a/lib/data/model/server/conn.dart b/lib/data/model/server/conn.dart index b150a7386..320e4fc6b 100644 --- a/lib/data/model/server/conn.dart +++ b/lib/data/model/server/conn.dart @@ -1,4 +1,4 @@ -import '../../res/misc.dart'; +import 'package:server_box/data/res/misc.dart'; class Conn { final int maxConn; diff --git a/lib/data/model/server/custom.dart b/lib/data/model/server/custom.dart index 50b34044b..0720378ed 100644 --- a/lib/data/model/server/custom.dart +++ b/lib/data/model/server/custom.dart @@ -30,11 +30,11 @@ final class ServerCustom { static ServerCustom fromJson(Map json) { //final temperature = json["temperature"] as String?; - final pveAddr = json["pveAddr"] as String?; - final pveIgnoreCert = json["pveIgnoreCert"] as bool; - final cmds = json["cmds"] as Map?; - final preferTempDev = json["preferTempDev"] as String?; - final logoUrl = json["logoUrl"] as String?; + final pveAddr = json['pveAddr'] as String?; + final pveIgnoreCert = json['pveIgnoreCert'] as bool; + final cmds = json['cmds'] as Map?; + final preferTempDev = json['preferTempDev'] as String?; + final logoUrl = json['logoUrl'] as String?; return ServerCustom( //temperature: temperature, pveAddr: pveAddr, @@ -51,18 +51,18 @@ final class ServerCustom { // json["temperature"] = temperature; // } if (pveAddr != null) { - json["pveAddr"] = pveAddr; + json['pveAddr'] = pveAddr; } - json["pveIgnoreCert"] = pveIgnoreCert; + json['pveIgnoreCert'] = pveIgnoreCert; if (cmds != null) { - json["cmds"] = cmds; + json['cmds'] = cmds; } if (preferTempDev != null) { - json["preferTempDev"] = preferTempDev; + json['preferTempDev'] = preferTempDev; } if (logoUrl != null) { - json["logoUrl"] = logoUrl; + json['logoUrl'] = logoUrl; } return json; } diff --git a/lib/data/model/server/disk.dart b/lib/data/model/server/disk.dart index 47b3c74c2..77b31f547 100644 --- a/lib/data/model/server/disk.dart +++ b/lib/data/model/server/disk.dart @@ -1,7 +1,7 @@ import 'package:fl_lib/fl_lib.dart'; import 'package:server_box/data/model/server/time_seq.dart'; -import '../../res/misc.dart'; +import 'package:server_box/data/res/misc.dart'; class Disk { final String fs; diff --git a/lib/data/model/server/net_speed.dart b/lib/data/model/server/net_speed.dart index cedfd2259..8bc33a194 100644 --- a/lib/data/model/server/net_speed.dart +++ b/lib/data/model/server/net_speed.dart @@ -1,6 +1,6 @@ import 'package:fl_lib/fl_lib.dart'; -import 'time_seq.dart'; +import 'package:server_box/data/model/server/time_seq.dart'; class NetSpeedPart extends TimeSeqIface { final String device; diff --git a/lib/data/model/server/private_key_info.dart b/lib/data/model/server/private_key_info.dart index d86a1c744..bb63d74d7 100644 --- a/lib/data/model/server/private_key_info.dart +++ b/lib/data/model/server/private_key_info.dart @@ -28,13 +28,13 @@ class PrivateKeyInfo { } PrivateKeyInfo.fromJson(Map json) - : id = json["id"].toString(), - key = json["private_key"].toString(); + : id = json['id'].toString(), + key = json['private_key'].toString(); Map toJson() { final data = {}; - data["id"] = id; - data["private_key"] = key; + data['id'] = id; + data['private_key'] = key; return data; } } diff --git a/lib/data/model/server/proc.dart b/lib/data/model/server/proc.dart index dad0d2f5f..6ce777383 100644 --- a/lib/data/model/server/proc.dart +++ b/lib/data/model/server/proc.dart @@ -1,6 +1,6 @@ import 'package:fl_lib/fl_lib.dart'; -import '../../../data/res/misc.dart'; +import 'package:server_box/data/res/misc.dart'; class _ProcValIdxMap { final int pid; @@ -58,7 +58,7 @@ class Proc { required this.command, }); - factory Proc.parse(String raw, _ProcValIdxMap map) { + factory Proc._parse(String raw, _ProcValIdxMap map) { final parts = raw.split(RegExp(r'\s+')); return Proc( user: map.user == null ? null : parts[map.user!], @@ -139,7 +139,7 @@ class PsResult { final line = lines[i]; if (line.isEmpty) continue; try { - procs.add(Proc.parse(line, map)); + procs.add(Proc._parse(line, map)); } catch (e, trace) { errs.add('$line: $e'); Loggers.app.warning('Process failed', e, trace); diff --git a/lib/data/model/server/server.dart b/lib/data/model/server/server.dart index ee2e476e3..74e6cbaad 100644 --- a/lib/data/model/server/server.dart +++ b/lib/data/model/server/server.dart @@ -90,5 +90,5 @@ enum ServerConn { /// Status parsing finished finished; - operator <(ServerConn other) => index < other.index; + bool operator <(ServerConn other) => index < other.index; } diff --git a/lib/data/model/server/server_private_info.dart b/lib/data/model/server/server_private_info.dart index ea8bc8597..b42bf0e12 100644 --- a/lib/data/model/server/server_private_info.dart +++ b/lib/data/model/server/server_private_info.dart @@ -6,7 +6,7 @@ import 'package:server_box/data/model/server/server.dart'; import 'package:server_box/data/model/server/wol_cfg.dart'; import 'package:server_box/data/res/provider.dart'; -import '../app/error.dart'; +import 'package:server_box/data/model/app/error.dart'; part 'server_private_info.g.dart'; @@ -67,23 +67,23 @@ class ServerPrivateInfo { }) : id = '$user@$ip:$port'; static ServerPrivateInfo fromJson(Map json) { - final ip = json["ip"] as String? ?? ''; - final port = json["port"] as int? ?? 22; - final user = json["user"] as String? ?? 'root'; - final name = json["name"] as String? ?? ''; - final pwd = json["pwd"] as String? ?? json["authorization"] as String?; - final keyId = json["pubKeyId"] as String?; - final tags = (json["tags"] as List?)?.cast(); - final alterUrl = json["alterUrl"] as String?; - final autoConnect = json["autoConnect"] as bool?; - final jumpId = json["jumpId"] as String?; - final custom = json["customCmd"] == null + final ip = json['ip'] as String? ?? ''; + final port = json['port'] as int? ?? 22; + final user = json['user'] as String? ?? 'root'; + final name = json['name'] as String? ?? ''; + final pwd = json['pwd'] as String? ?? json['authorization'] as String?; + final keyId = json['pubKeyId'] as String?; + final tags = (json['tags'] as List?)?.cast(); + final alterUrl = json['alterUrl'] as String?; + final autoConnect = json['autoConnect'] as bool?; + final jumpId = json['jumpId'] as String?; + final custom = json['customCmd'] == null ? null - : ServerCustom.fromJson(json["custom"].cast()); - final wolCfg = json["wolCfg"] == null + : ServerCustom.fromJson(json['custom'].cast()); + final wolCfg = json['wolCfg'] == null ? null - : WakeOnLanCfg.fromJson(json["wolCfg"].cast()); - final envs_ = json["envs"] as Map?; + : WakeOnLanCfg.fromJson(json['wolCfg'].cast()); + final envs_ = json['envs'] as Map?; final envs = {}; if (envs_ != null) { envs_.forEach((key, value) { @@ -112,36 +112,36 @@ class ServerPrivateInfo { Map toJson() { final Map data = {}; - data["name"] = name; - data["ip"] = ip; - data["port"] = port; - data["user"] = user; + data['name'] = name; + data['ip'] = ip; + data['port'] = port; + data['user'] = user; if (pwd != null) { - data["pwd"] = pwd; + data['pwd'] = pwd; } if (keyId != null) { - data["pubKeyId"] = keyId; + data['pubKeyId'] = keyId; } if (tags != null) { - data["tags"] = tags; + data['tags'] = tags; } if (alterUrl != null) { - data["alterUrl"] = alterUrl; + data['alterUrl'] = alterUrl; } if (autoConnect != null) { - data["autoConnect"] = autoConnect; + data['autoConnect'] = autoConnect; } if (jumpId != null) { - data["jumpId"] = jumpId; + data['jumpId'] = jumpId; } if (custom != null) { - data["custom"] = custom?.toJson(); + data['custom'] = custom?.toJson(); } if (wolCfg != null) { - data["wolCfg"] = wolCfg?.toJson(); + data['wolCfg'] = wolCfg?.toJson(); } if (envs != null) { - data["envs"] = envs; + data['envs'] = envs; } return data; } @@ -160,7 +160,7 @@ class ServerPrivateInfo { custom?.cmds != old.custom?.cmds; } - _IpPort fromStringUrl() { + IpPort fromStringUrl() { if (alterUrl == null) { throw SSHErr(type: SSHErrType.connect, message: 'alterUrl is null'); } @@ -177,7 +177,7 @@ class ServerPrivateInfo { if (port <= 0 || port > 65535) { throw SSHErr(type: SSHErrType.connect, message: 'alterUrl port error'); } - return _IpPort(ip_, port_); + return IpPort(ip_, port_); } @override @@ -208,9 +208,9 @@ class ServerPrivateInfo { ); } -class _IpPort { +class IpPort { final String ip; final int port; - _IpPort(this.ip, this.port); + IpPort(this.ip, this.port); } diff --git a/lib/data/model/server/server_status_update_req.dart b/lib/data/model/server/server_status_update_req.dart index 3b9e5a3f1..a717d1bd3 100644 --- a/lib/data/model/server/server_status_update_req.dart +++ b/lib/data/model/server/server_status_update_req.dart @@ -5,12 +5,12 @@ import 'package:server_box/data/model/server/sensors.dart'; import 'package:server_box/data/model/server/server.dart'; import 'package:server_box/data/model/server/system.dart'; -import '../app/shell_func.dart'; -import 'cpu.dart'; -import 'disk.dart'; -import 'memory.dart'; -import 'net_speed.dart'; -import 'conn.dart'; +import 'package:server_box/data/model/app/shell_func.dart'; +import 'package:server_box/data/model/server/cpu.dart'; +import 'package:server_box/data/model/server/disk.dart'; +import 'package:server_box/data/model/server/memory.dart'; +import 'package:server_box/data/model/server/net_speed.dart'; +import 'package:server_box/data/model/server/conn.dart'; class ServerStatusUpdateReq { final ServerStatus ss; diff --git a/lib/data/model/server/snippet.dart b/lib/data/model/server/snippet.dart index bead2f27c..667d2f60f 100644 --- a/lib/data/model/server/snippet.dart +++ b/lib/data/model/server/snippet.dart @@ -5,7 +5,7 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:xterm/core.dart'; -import '../app/tag_pickable.dart'; +import 'package:server_box/data/model/app/tag_pickable.dart'; part 'snippet.g.dart'; diff --git a/lib/data/model/sftp/absolute_path.dart b/lib/data/model/sftp/absolute_path.dart index 360c65224..9e824588b 100644 --- a/lib/data/model/sftp/absolute_path.dart +++ b/lib/data/model/sftp/absolute_path.dart @@ -2,12 +2,14 @@ import 'package:fl_lib/fl_lib.dart'; class AbsolutePath { String _path; - String get path => _path; - final List _prePath; + final _prePath = []; + + AbsolutePath(this._path); - AbsolutePath(this._path) : _prePath = ['/']; + String get path => _path; - void update(String newPath) { + /// Update path, not set path + set path(String newPath) { _prePath.add(_path); if (newPath == '..') { _path = _path.substring(0, _path.lastIndexOf('/')); @@ -16,10 +18,6 @@ class AbsolutePath { } return; } - if (newPath == '/') { - _path = '/'; - return; - } if (newPath.startsWith('/')) { _path = newPath; return; diff --git a/lib/data/model/sftp/browser_status.dart b/lib/data/model/sftp/browser_status.dart index 6a199f659..c09b9e6d4 100644 --- a/lib/data/model/sftp/browser_status.dart +++ b/lib/data/model/sftp/browser_status.dart @@ -2,9 +2,11 @@ import 'package:dartssh2/dartssh2.dart'; import 'package:server_box/data/model/sftp/absolute_path.dart'; class SftpBrowserStatus { - List? files; - AbsolutePath? path; + final List files = []; + final AbsolutePath path = AbsolutePath('/'); SftpClient? client; - SftpBrowserStatus(); + SftpBrowserStatus(SSHClient client) { + client.sftp().then((value) => this.client = value); + } } diff --git a/lib/data/provider/container.dart b/lib/data/provider/container.dart index 2e9ec1257..f8a9b6b4c 100644 --- a/lib/data/provider/container.dart +++ b/lib/data/provider/container.dart @@ -70,7 +70,7 @@ class ContainerProvider extends ChangeNotifier { } final res = await client?.run(_wrap(ContainerCmdType.images.exec(type))); - if (res?.string.toLowerCase().contains("permission denied") ?? false) { + if (res?.string.toLowerCase().contains('permission denied') ?? false) { return sudoCompleter.complete(true); } return sudoCompleter.complete(false); diff --git a/lib/data/provider/pve.dart b/lib/data/provider/pve.dart index 831f5ffa6..b0d482d36 100644 --- a/lib/data/provider/pve.dart +++ b/lib/data/provider/pve.dart @@ -104,7 +104,7 @@ final class PveProvider extends ChangeNotifier { socket.cast>().pipe(forward.sink); });*/ - if (url.isScheme("https")) { + if (url.isScheme('https')) { return SecureSocket.startConnect('localhost', _localPort, onBadCertificate: (_) => true); } else { diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index 5d410229d..4be71c2d6 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -14,12 +14,12 @@ import 'package:server_box/data/model/server/system.dart'; // import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; -import '../../core/utils/server.dart'; -import '../model/server/server.dart'; -import '../model/server/server_private_info.dart'; -import '../model/server/server_status_update_req.dart'; -import '../model/server/try_limiter.dart'; -import '../res/status.dart'; +import 'package:server_box/core/utils/server.dart'; +import 'package:server_box/data/model/server/server.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; +import 'package:server_box/data/model/server/server_status_update_req.dart'; +import 'package:server_box/data/model/server/try_limiter.dart'; +import 'package:server_box/data/res/status.dart'; class ServerProvider extends ChangeNotifier { final Map _servers = {}; diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 6553d8f2b..36c2d3fba 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -1,7 +1,7 @@ // This file is generated by fl_build. Do not edit. class BuildData { - static const String name = "ServerBox"; + static const String name = 'ServerBox'; static const int build = 1058; static const int script = 56; } diff --git a/lib/data/res/status.dart b/lib/data/res/status.dart index 7e909db2a..89aadb2fa 100644 --- a/lib/data/res/status.dart +++ b/lib/data/res/status.dart @@ -1,12 +1,12 @@ import 'package:server_box/data/model/server/server.dart'; import 'package:server_box/data/model/server/temp.dart'; -import '../model/server/cpu.dart'; -import '../model/server/disk.dart'; -import '../model/server/memory.dart'; -import '../model/server/net_speed.dart'; -import '../model/server/conn.dart'; -import '../model/server/system.dart'; +import 'package:server_box/data/model/server/cpu.dart'; +import 'package:server_box/data/model/server/disk.dart'; +import 'package:server_box/data/model/server/memory.dart'; +import 'package:server_box/data/model/server/net_speed.dart'; +import 'package:server_box/data/model/server/conn.dart'; +import 'package:server_box/data/model/server/system.dart'; abstract final class InitStatus { static SingleCpuCore get _initOneTimeCpuStatus => SingleCpuCore( diff --git a/lib/data/store/private_key.dart b/lib/data/store/private_key.dart index 63bc2251a..e70badfd8 100644 --- a/lib/data/store/private_key.dart +++ b/lib/data/store/private_key.dart @@ -1,6 +1,6 @@ import 'package:fl_lib/fl_lib.dart'; -import '../model/server/private_key_info.dart'; +import 'package:server_box/data/model/server/private_key_info.dart'; class PrivateKeyStore extends PersistentStore { PrivateKeyStore() : super('key'); diff --git a/lib/data/store/server.dart b/lib/data/store/server.dart index aebcd9b9b..2f773d35d 100644 --- a/lib/data/store/server.dart +++ b/lib/data/store/server.dart @@ -1,6 +1,6 @@ import 'package:fl_lib/fl_lib.dart'; -import '../model/server/server_private_info.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; class ServerStore extends PersistentStore { ServerStore() : super('server'); diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index a57e66ba0..53f1359f8 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -3,8 +3,8 @@ import 'package:server_box/data/model/app/menu/server_func.dart'; import 'package:server_box/data/model/app/server_detail_card.dart'; import 'package:server_box/data/model/ssh/virtual_key.dart'; -import '../model/app/net_view.dart'; -import '../res/default.dart'; +import 'package:server_box/data/model/app/net_view.dart'; +import 'package:server_box/data/res/default.dart'; class SettingStore extends PersistentStore { SettingStore() : super('setting'); diff --git a/lib/data/store/snippet.dart b/lib/data/store/snippet.dart index 53c84cad5..004b81cab 100644 --- a/lib/data/store/snippet.dart +++ b/lib/data/store/snippet.dart @@ -1,6 +1,6 @@ import 'package:fl_lib/fl_lib.dart'; -import '../model/server/snippet.dart'; +import 'package:server_box/data/model/server/snippet.dart'; class SnippetStore extends PersistentStore { SnippetStore() : super('snippet'); diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 9ded01893..929aa143e 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -65,7 +65,6 @@ "goBackQ": "Zurückkommen?", "goto": "Pfad öffnen", "hideTitleBar": "Titelleiste ausblenden", - "hideTitleBarTip": "Nach dem Einschalten halten Sie bitte die drei Tasten in der oberen rechten Ecke gedrückt, um sie zu ziehen.", "highlight": "Code highlight", "homeWidgetUrlConfig": "Home-Widget-Link konfigurieren", "host": "Host", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f6b985a45..b6da932ca 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -65,7 +65,6 @@ "goBackQ": "Go back?", "goto": "Go to", "hideTitleBar": "Hide title bar", - "hideTitleBarTip": "After turning it on, please hold down the three buttons in the top right corner to drag.", "highlight": "Code highlighting", "homeWidgetUrlConfig": "Config home widget url", "host": "Host", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index e34b55b9a..1bb7db65f 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -65,7 +65,6 @@ "goBackQ": "¿Regresar?", "goto": "Ir a", "hideTitleBar": "Ocultar barra de título", - "hideTitleBarTip": "Después de encenderlo, mantenga presionados los tres botones en la esquina superior derecha para arrastrar.", "highlight": "Resaltar código", "homeWidgetUrlConfig": "Configuración de URL del widget de inicio", "host": "Anfitrión", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 2bb25f1db..bc145bad5 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -65,7 +65,6 @@ "goBackQ": "Revenir en arrière ?", "goto": "Aller à", "hideTitleBar": "Masquer la barre de titre", - "hideTitleBarTip": "Après l'avoir allumé, veuillez maintenir les trois boutons dans le coin supérieur droit pour les faire glisser.", "highlight": "Mise en surbrillance du code", "homeWidgetUrlConfig": "Configurer l'URL du widget d'accueil", "host": "Hôte", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 58bda9bb5..4d384e488 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -65,7 +65,6 @@ "goBackQ": "Datang kembali?", "goto": "Pergi ke", "hideTitleBar": "Sembunyikan bilah judul", - "hideTitleBarTip": "Setelah dinyalakan, tekan dan tahan tiga tombol di sudut kanan atas untuk menyeret.", "highlight": "Sorotan kode", "homeWidgetUrlConfig": "Konfigurasi URL Widget Rumah", "host": "Host", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index b6186c846..0a644cbb8 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -65,7 +65,6 @@ "goBackQ": "戻りますか?", "goto": "移動", "hideTitleBar": "タイトルバーを非表示にする", - "hideTitleBarTip": "電源を入れた後、右上隅の3つのボタンを押し続けてドラッグしてください。", "highlight": "コードハイライト", "homeWidgetUrlConfig": "ホームウィジェットURL設定", "host": "ホスト", diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 6feab1fa5..1a547dbfa 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -65,7 +65,6 @@ "goBackQ": "Terug gaan?", "goto": "Ga naar", "hideTitleBar": "Titelbalk verbergen", - "hideTitleBarTip": "Houd na het inschakelen de drie knoppen in de rechterbovenhoek ingedrukt om te slepen.", "highlight": "Code-highlight", "homeWidgetUrlConfig": "Home-widget-url configureren", "host": "Host", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 06d686644..4ee0424dc 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -65,7 +65,6 @@ "goBackQ": "Voltar?", "goto": "Ir para", "hideTitleBar": "Ocultar barra de título", - "hideTitleBarTip": "Após ligar, segure os três botões no canto superior direito para arrastar.", "highlight": "Destaque de código", "homeWidgetUrlConfig": "Configuração de URL do widget da tela inicial", "host": "Host", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index b570c8eab..d0c4d7ce8 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -65,7 +65,6 @@ "goBackQ": "Вернуться?", "goto": "перейти к", "hideTitleBar": "Скрыть заголовок", - "hideTitleBarTip": "После включения удерживайте три кнопки в правом верхнем углу, чтобы перетаскивать.", "highlight": "подсветка кода", "homeWidgetUrlConfig": "конфигурация URL виджета домашнего экрана", "host": "хост", diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb index 55743ef70..d7bf66b87 100644 --- a/lib/l10n/app_tr.arb +++ b/lib/l10n/app_tr.arb @@ -65,7 +65,6 @@ "goBackQ": "Geri dön?", "goto": "Git", "hideTitleBar": "Başlık çubuğunu gizle", - "hideTitleBarTip": "Açtıktan sonra, sağ üst köşedeki üç düğmeyi basılı tutarak sürükleyin.", "highlight": "Kod vurgulama", "homeWidgetUrlConfig": "Ana sayfa widget URL'sini yapılandır", "host": "Sunucu", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 2697c3382..7d8d2fa09 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -65,7 +65,6 @@ "goBackQ": "返回?", "goto": "前往", "hideTitleBar": "隐藏标题栏", - "hideTitleBarTip": "开启后请按住右上角三个按钮来拖动", "highlight": "代码高亮", "homeWidgetUrlConfig": "桌面部件链接配置", "host": "主机", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index 5d515b27b..cf7aec593 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -65,7 +65,6 @@ "goBackQ": "返回?", "goto": "前往", "hideTitleBar": "隱藏標題欄", - "hideTitleBarTip": "開啟後請按住右上角三個按鈕來拖動", "highlight": "代碼高亮", "homeWidgetUrlConfig": "桌面部件鏈接配置", "host": "主機", diff --git a/lib/view/page/backup.dart b/lib/view/page/backup.dart index 7c776839f..eee3d78a4 100644 --- a/lib/view/page/backup.dart +++ b/lib/view/page/backup.dart @@ -339,7 +339,7 @@ class BackupPage extends StatelessWidget { Future _onTapWebdavUp(BuildContext context) async { webdavLoading.value = true; - final date = DateTime.now().ymdhms(ymdSep: "-", hmsSep: "-", sep: "-"); + final date = DateTime.now().ymdhms(ymdSep: '-', hmsSep: '-', sep: '-'); final bakName = '$date-${Miscs.bakFileName}'; try { await Backup.backup(bakName); diff --git a/lib/view/page/container.dart b/lib/view/page/container.dart index 75eec3b8a..1a90b753c 100644 --- a/lib/view/page/container.dart +++ b/lib/view/page/container.dart @@ -11,10 +11,10 @@ import 'package:server_box/data/model/container/image.dart'; import 'package:server_box/data/model/container/type.dart'; import 'package:server_box/data/res/store.dart'; -import '../../data/model/container/ps.dart'; -import '../../data/model/server/server_private_info.dart'; -import '../../data/provider/container.dart'; -import '../widget/two_line_text.dart'; +import 'package:server_box/data/model/container/ps.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; +import 'package:server_box/data/provider/container.dart'; +import 'package:server_box/view/widget/two_line_text.dart'; class ContainerPage extends StatefulWidget { final ServerPrivateInfo spi; diff --git a/lib/view/page/editor.dart b/lib/view/page/editor.dart index 97d09ccd3..04f5629ce 100644 --- a/lib/view/page/editor.dart +++ b/lib/view/page/editor.dart @@ -12,7 +12,7 @@ import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/res/highlight.dart'; import 'package:server_box/data/res/store.dart'; -import '../widget/two_line_text.dart'; +import 'package:server_box/view/widget/two_line_text.dart'; class EditorPage extends StatefulWidget { /// If path is not null, then it's a file editor diff --git a/lib/view/page/ping.dart b/lib/view/page/ping.dart index 129a7e393..af36194d2 100644 --- a/lib/view/page/ping.dart +++ b/lib/view/page/ping.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/res/provider.dart'; -import '../../data/model/server/ping_result.dart'; +import 'package:server_box/data/model/server/ping_result.dart'; /// Only permit ipv4 / ipv6 / domain chars final targetReg = RegExp(r'[a-zA-Z0-9\.-_:]+'); diff --git a/lib/view/page/private_key/edit.dart b/lib/view/page/private_key/edit.dart index 1f70cd536..335853240 100644 --- a/lib/view/page/private_key/edit.dart +++ b/lib/view/page/private_key/edit.dart @@ -8,8 +8,8 @@ import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/res/misc.dart'; import 'package:server_box/data/res/provider.dart'; -import '../../../core/utils/server.dart'; -import '../../../data/model/server/private_key_info.dart'; +import 'package:server_box/core/utils/server.dart'; +import 'package:server_box/data/model/server/private_key_info.dart'; const _format = 'text/plain'; @@ -107,7 +107,7 @@ class _PrivateKeyEditPageState extends State { } String _standardizeLineSeparators(String value) { - return value.replaceAll("\r\n", "\n").replaceAll("\r", "\n"); + return value.replaceAll('\r\n', '\n').replaceAll('\r', '\n'); } Widget _buildFAB() { diff --git a/lib/view/page/private_key/list.dart b/lib/view/page/private_key/list.dart index afc708ac2..448a17488 100644 --- a/lib/view/page/private_key/list.dart +++ b/lib/view/page/private_key/list.dart @@ -7,9 +7,9 @@ import 'package:provider/provider.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/res/store.dart'; -import '../../../core/route.dart'; -import '../../../data/model/server/private_key_info.dart'; -import '../../../data/provider/private_key.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/server/private_key_info.dart'; +import 'package:server_box/data/provider/private_key.dart'; class PrivateKeysListPage extends StatefulWidget { const PrivateKeysListPage({super.key}); diff --git a/lib/view/page/process.dart b/lib/view/page/process.dart index 988675a3e..fec929bb0 100644 --- a/lib/view/page/process.dart +++ b/lib/view/page/process.dart @@ -6,10 +6,10 @@ import 'package:flutter/material.dart'; import 'package:server_box/core/extension/context/locale.dart'; import 'package:server_box/data/res/store.dart'; -import '../../data/model/app/shell_func.dart'; -import '../../data/model/server/proc.dart'; -import '../../data/model/server/server_private_info.dart'; -import '../widget/two_line_text.dart'; +import 'package:server_box/data/model/app/shell_func.dart'; +import 'package:server_box/data/model/server/proc.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; +import 'package:server_box/view/widget/two_line_text.dart'; class ProcessPage extends StatefulWidget { final ServerPrivateInfo spi; diff --git a/lib/view/page/server/detail/view.dart b/lib/view/page/server/detail/view.dart index 116067227..c561fa2c8 100644 --- a/lib/view/page/server/detail/view.dart +++ b/lib/view/page/server/detail/view.dart @@ -20,9 +20,9 @@ import 'package:server_box/data/model/server/system.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/view/widget/server_func_btns.dart'; -import '../../../../core/route.dart'; -import '../../../../data/model/server/server.dart'; -import '../../../../data/provider/server.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/server/server.dart'; +import 'package:server_box/data/provider/server.dart'; part 'misc.dart'; diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 0cad3cd99..20d29818c 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -9,9 +9,9 @@ import 'package:server_box/data/model/server/custom.dart'; import 'package:server_box/data/model/server/wol_cfg.dart'; import 'package:server_box/data/res/provider.dart'; -import '../../../core/route.dart'; -import '../../../data/model/server/server_private_info.dart'; -import '../../../data/provider/private_key.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; +import 'package:server_box/data/provider/private_key.dart'; class ServerEditPage extends StatefulWidget { const ServerEditPage({super.key, this.spi}); diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index 51f07bdab..4deab0188 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -13,12 +13,12 @@ import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/view/widget/percent_circle.dart'; -import '../../../core/route.dart'; -import '../../../data/model/app/net_view.dart'; -import '../../../data/model/server/server.dart'; -import '../../../data/model/server/server_private_info.dart'; -import '../../../data/provider/server.dart'; -import '../../widget/server_func_btns.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/app/net_view.dart'; +import 'package:server_box/data/model/server/server.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; +import 'package:server_box/data/provider/server.dart'; +import 'package:server_box/view/widget/server_func_btns.dart'; class ServerPage extends StatefulWidget { const ServerPage({super.key}); diff --git a/lib/view/page/setting/entry.dart b/lib/view/page/setting/entry.dart index 0d4a83449..dbd260e81 100644 --- a/lib/view/page/setting/entry.dart +++ b/lib/view/page/setting/entry.dart @@ -10,9 +10,9 @@ import 'package:server_box/data/res/rebuild.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/data/res/url.dart'; -import '../../../core/route.dart'; -import '../../../data/model/app/net_view.dart'; -import '../../../data/res/build_data.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/app/net_view.dart'; +import 'package:server_box/data/res/build_data.dart'; const _kIconSize = 23.0; @@ -1032,7 +1032,6 @@ class _SettingPageState extends State { Widget _buildHideTitleBar() { return ListTile( title: Text(l10n.hideTitleBar), - subtitle: Text(l10n.hideTitleBarTip, style: UIs.textGrey), trailing: StoreSwitch(prop: _setting.hideTitleBar), ); } diff --git a/lib/view/page/snippet/list.dart b/lib/view/page/snippet/list.dart index 224f897a0..94fcbae0a 100644 --- a/lib/view/page/snippet/list.dart +++ b/lib/view/page/snippet/list.dart @@ -3,9 +3,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:server_box/data/res/store.dart'; -import '../../../data/model/server/snippet.dart'; -import '/core/route.dart'; -import '/data/provider/snippet.dart'; +import 'package:server_box/data/model/server/snippet.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/provider/snippet.dart'; class SnippetListPage extends StatefulWidget { const SnippetListPage({super.key}); diff --git a/lib/view/page/ssh/page.dart b/lib/view/page/ssh/page.dart index b8ebbcee4..2ec755ebe 100644 --- a/lib/view/page/ssh/page.dart +++ b/lib/view/page/ssh/page.dart @@ -18,10 +18,10 @@ import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:xterm/core.dart'; import 'package:xterm/ui.dart' hide TerminalThemes; -import '../../../core/route.dart'; -import '../../../data/model/server/server_private_info.dart'; -import '../../../data/model/ssh/virtual_key.dart'; -import '../../../data/res/terminal.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; +import 'package:server_box/data/model/ssh/virtual_key.dart'; +import 'package:server_box/data/res/terminal.dart'; const _echoPWD = 'echo \$PWD'; diff --git a/lib/view/page/storage/local.dart b/lib/view/page/storage/local.dart index 8398f40e0..74d6a256d 100644 --- a/lib/view/page/storage/local.dart +++ b/lib/view/page/storage/local.dart @@ -9,8 +9,8 @@ import 'package:server_box/data/res/misc.dart'; import 'package:server_box/data/res/provider.dart'; import 'package:server_box/view/widget/omit_start_text.dart'; -import '../../../core/route.dart'; -import '../../../data/model/app/path_with_prefix.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/data/model/app/path_with_prefix.dart'; class LocalStoragePage extends StatefulWidget { final bool isPickFile; diff --git a/lib/view/page/storage/sftp.dart b/lib/view/page/storage/sftp.dart index be42f5382..ff528d712 100644 --- a/lib/view/page/storage/sftp.dart +++ b/lib/view/page/storage/sftp.dart @@ -9,7 +9,6 @@ import 'package:server_box/core/extension/sftpfile.dart'; import 'package:server_box/core/route.dart'; import 'package:server_box/core/utils/comparator.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; -import 'package:server_box/data/model/sftp/absolute_path.dart'; import 'package:server_box/data/model/sftp/browser_status.dart'; import 'package:server_box/data/model/sftp/worker.dart'; import 'package:server_box/data/res/misc.dart'; @@ -38,111 +37,99 @@ class SftpPage extends StatefulWidget { } class _SftpPageState extends State with AfterLayoutMixin { - final _status = SftpBrowserStatus(); - late final _client = widget.spi.server?.client; - - final _sortOption = - ValueNotifier(_SortOption(sortBy: _SortType.name, reversed: false)); + late final _status = SftpBrowserStatus(_client); + late final _client = widget.spi.server!.client!; + final _sortOption = _SortOption().vn; @override Widget build(BuildContext context) { + final children = [ + Btn.icon( + icon: const Icon(Icons.downloading), + onTap: () => AppRoutes.sftpMission().go(context), + ), + _buildSortMenu(), + _buildSearchBtn(), + ]; + if (isDesktop) children.add(_buildRefreshBtn()); + return Scaffold( appBar: CustomAppBar( - leading: IconButton( - icon: const BackButtonIcon(), - onPressed: () { - _status.path?.update('/'); - context.pop(); - }, - ), title: TwoLineText(up: 'SFTP', down: widget.spi.name), - actions: [ - IconButton( - icon: const Icon(Icons.downloading), - onPressed: () => AppRoutes.sftpMission().go(context), - ), - ValBuilder( - listenable: _sortOption, - builder: (value) { - return PopupMenuButton<_SortType>( - icon: const Icon(Icons.sort), - itemBuilder: (context) { - final currentSelectedOption = _sortOption.value; - final options = [ - (_SortType.name, libL10n.name), - (_SortType.size, l10n.size), - (_SortType.time, l10n.time), - ]; - return options.map((r) { - final (type, name) = r; - return PopupMenuItem( - value: type, - child: Text( - type == currentSelectedOption.sortBy - ? "$name (${currentSelectedOption.reversed ? '-' : '+'})" - : name, - style: TextStyle( - color: type == currentSelectedOption.sortBy - ? UIs.primaryColor - : null, - fontWeight: type == currentSelectedOption.sortBy - ? FontWeight.bold - : null, - ), - ), - ); - }).toList(); - }, - onSelected: (sortBy) { - final oldValue = _sortOption.value; - if (oldValue.sortBy == sortBy) { - _sortOption.value = _SortOption( - sortBy: sortBy, reversed: !oldValue.reversed); - } else { - _sortOption.value = - _SortOption(sortBy: sortBy, reversed: false); - } - }, - ); - }, - ), - ], + actions: children, ), body: _buildFileView(), bottomNavigationBar: _buildBottom(), ); } + Widget _buildSortMenu() { + return ValBuilder( + listenable: _sortOption, + builder: (value) { + return PopupMenuButton<_SortType>( + icon: const Icon(Icons.sort), + itemBuilder: (context) { + final currentSelectedOption = _sortOption.value; + final options = [ + (_SortType.name, libL10n.name), + (_SortType.size, l10n.size), + (_SortType.time, l10n.time), + ]; + return options.map((r) { + final (type, name) = r; + final selected = type == currentSelectedOption.sortBy; + final title = selected + ? "$name (${currentSelectedOption.reversed ? '-' : '+'})" + : name; + return PopupMenuItem( + value: type, + child: Text( + title, + style: TextStyle( + color: selected ? UIs.primaryColor : null, + fontWeight: selected ? FontWeight.bold : null, + ), + ), + ); + }).toList(); + }, + onSelected: (sortBy) { + final old = _sortOption.value; + if (old.sortBy == sortBy) { + _sortOption.value = old.copyWith(reversed: !old.reversed); + } else { + _sortOption.value = old.copyWith(sortBy: sortBy); + } + }, + ); + }, + ); + } + Widget _buildBottom() { final children = widget.isSelect ? [ IconButton( - onPressed: () => context.pop(_status.path?.path), + onPressed: () => context.pop(_status.path.path), icon: const Icon(Icons.done), ), _buildSearchBtn(), ] : [ - IconButton( - padding: const EdgeInsets.all(0), - onPressed: () async { - await _backward(); - }, - icon: const Icon(Icons.arrow_back), - ), + _buildBackBtn(), + _buildHomeBtn(), _buildAddBtn(), _buildGotoBtn(), _buildUploadBtn(), - _buildSearchBtn(), ]; - if (isDesktop) children.add(_buildRefreshBtn()); return SafeArea( child: Container( padding: const EdgeInsets.fromLTRB(11, 7, 11, 11), child: Column( mainAxisSize: MainAxisSize.min, children: [ - OmitStartText(_status.path?.path ?? '...'), + OmitStartText(_status.path.path), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: children, @@ -153,169 +140,18 @@ class _SftpPageState extends State with AfterLayoutMixin { ); } - Widget _buildSearchBtn() { - return IconButton( - onPressed: () async { - Stream find(String query) async* { - final fs = _status.files; - if (fs == null) return; - for (final f in fs) { - if (f.filename.contains(query)) yield f; - } - } - - final search = SearchPage( - padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), - future: (q) => find(q).toList(), - builder: (ctx, e) => _buildItem(e, beforeTap: () => ctx.pop()), - ); - await showSearch(context: context, delegate: search); - }, - icon: const Icon(Icons.search), - ); - } - - Widget _buildUploadBtn() { - return IconButton( - onPressed: () async { - final idx = await context.showRoundDialog( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - leading: const Icon(Icons.open_in_new), - title: Text(l10n.system), - onTap: () => context.pop(1), - ), - ListTile( - leading: const Icon(Icons.folder), - title: Text(l10n.inner), - onTap: () => context.pop(0), - ), - ], - )); - final path = await () async { - switch (idx) { - case 0: - return await AppRoutes.localStorage(isPickFile: true) - .go(context); - case 1: - return await Pfs.pickFilePath(); - default: - return null; - } - }(); - if (path == null) { - return; - } - final remoteDir = _status.path?.path; - if (remoteDir == null) { - context.showSnackBar('remote path is null'); - return; - } - final fileName = path.split(Platform.pathSeparator).lastOrNull; - final remotePath = '$remoteDir/$fileName'; - Loggers.app.info('SFTP upload local: $path, remote: $remotePath'); - Pros.sftp.add( - SftpReq(widget.spi, remotePath, path, SftpReqType.upload), - ); - }, - icon: const Icon(Icons.upload_file), - ); - } - - Widget _buildAddBtn() { - return IconButton( - onPressed: () => context.showRoundDialog( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - leading: const Icon(Icons.folder), - title: Text(libL10n.folder), - onTap: _mkdir, - ), - ListTile( - leading: const Icon(Icons.insert_drive_file), - title: Text(libL10n.file), - onTap: _newFile, - ), - ], - ), - ), - icon: const Icon(Icons.add), - ); - } - - Widget _buildGotoBtn() { - return IconButton( - padding: const EdgeInsets.all(0), - onPressed: () async { - final p = await context.showRoundDialog( - title: l10n.goto, - child: Autocomplete( - optionsBuilder: (val) { - if (!Stores.setting.recordHistory.fetch()) { - return []; - } - return Stores.history.sftpGoPath.all.cast().where( - (element) => element.contains(val.text), - ); - }, - fieldViewBuilder: (_, controller, node, __) { - return Input( - autoFocus: true, - icon: Icons.abc, - label: libL10n.path, - node: node, - controller: controller, - suggestion: true, - onSubmitted: (value) => context.pop(value), - ); - }, - ), - ); - - if (p == null || p.isEmpty) { - return; - } - - _status.path?.update(p); - final suc = await _listDir() ?? false; - if (suc && Stores.setting.recordHistory.fetch()) { - Stores.history.sftpGoPath.add(p); - } - }, - icon: const Icon(Icons.gps_fixed), - ); - } - - Widget _buildRefreshBtn() { - return IconButton( - onPressed: () => _listDir(), - icon: const Icon(Icons.refresh), - ); - } - Widget _buildFileView() { - if (_status.files == null) { - return UIs.centerLoading; - } - - if (_status.files!.isEmpty) { - return const Center( - child: Text('~'), - ); - } + if (_status.files.isEmpty) return Center(child: Text(libL10n.empty)); return RefreshIndicator( + onRefresh: _listDir, child: FadeIn( - key: Key(widget.spi.name + _status.path!.path), + key: Key(widget.spi.name + _status.path.path), child: ValBuilder( listenable: _sortOption, builder: (sortOption) { final files = sortOption.sortBy.sort( - _status.files!, + _status.files, reversed: sortOption.reversed, ); return ListView.builder( @@ -326,7 +162,6 @@ class _SftpPageState extends State with AfterLayoutMixin { }, ), ), - onRefresh: () => _listDir(), ); } @@ -351,7 +186,7 @@ class _SftpPageState extends State with AfterLayoutMixin { onTap: () { beforeTap?.call(); if (isDir) { - _status.path?.update(file.filename); + _status.path.path = file.filename; _listDir(); } else { _onItemPress(file, true); @@ -402,7 +237,7 @@ class _SftpPageState extends State with AfterLayoutMixin { final permStr = newPerm.perm; if (ok == true && permStr != perm.perm) { await context.showLoadingDialog(fn: () async { - await _client!.run('chmod $permStr "${_getRemotePath(file)}"'); + await _client.run('chmod $permStr "${_getRemotePath(file)}"'); await _listDir(); }); } @@ -570,7 +405,7 @@ class _SftpPageState extends State with AfterLayoutMixin { fn: () async { final remotePath = _getRemotePath(file); if (useRmr) { - await _client!.run('rm -r "$remotePath"'); + await _client.run('rm -r "$remotePath"'); } else if (file.attr.isDirectory) { await _status.client!.rmdir(remotePath); } else { @@ -606,7 +441,7 @@ class _SftpPageState extends State with AfterLayoutMixin { final (suc, err) = await context.showLoadingDialog( fn: () async { - final dir = '${_status.path!.path}/$text'; + final dir = '${_status.path.path}/$text'; await _status.client!.mkdir(dir); return true; }, @@ -651,8 +486,8 @@ class _SftpPageState extends State with AfterLayoutMixin { final (suc, err) = await context.showLoadingDialog( fn: () async { - final path = '${_status.path!.path}/$text'; - await _client!.run('touch "$path"'); + final path = '${_status.path.path}/$text'; + await _client.run('touch "$path"'); return true; }, ); @@ -748,7 +583,7 @@ class _SftpPageState extends State with AfterLayoutMixin { } String _getRemotePath(SftpName name) { - final prePath = _status.path!.path; + final prePath = _status.path.path; // Only support Linux as remote now, so the seperator is '/' return prePath.joinPath(name.filename, seperator: '/'); } @@ -761,8 +596,8 @@ class _SftpPageState extends State with AfterLayoutMixin { Future _listDir() async { final (ret, err) = await context.showLoadingDialog( fn: () async { - _status.client ??= await _client?.sftp(); - final listPath = _status.path?.path ?? '/'; + _status.client ??= await _client.sftp(); + final listPath = _status.path.path; final fs = await _status.client?.listdir(listPath); if (fs == null) { return false; @@ -785,7 +620,9 @@ class _SftpPageState extends State with AfterLayoutMixin { } if (mounted) { setState(() { - _status.files = fs; + _status.files + ..clear() + ..addAll(fs); }); // Only update history when success @@ -803,11 +640,162 @@ class _SftpPageState extends State with AfterLayoutMixin { } Future _backward() async { - if (_status.path?.undo() ?? false) { + if (_status.path.undo()) { await _listDir(); } } + Widget _buildBackBtn() { + return Btn.icon( + onTap: _backward, + icon: const Icon(Icons.arrow_back), + ); + } + + Widget _buildSearchBtn() { + return Btn.icon( + onTap: () { + Stream find(String query) async* { + final fs = _status.files; + for (final f in fs) { + if (f.filename.contains(query)) yield f; + } + } + + showSearch( + context: context, + delegate: SearchPage( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), + future: (q) => find(q).toList(), + builder: (ctx, e) => _buildItem(e, beforeTap: () => ctx.pop()), + )); + }, + icon: const Icon(Icons.search), + ); + } + + Widget _buildUploadBtn() { + return Btn.icon( + onTap: () async { + final idx = await context.showRoundDialog( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Btn.tile( + icon: const Icon(Icons.open_in_new), + text: l10n.system, + onTap: () => context.pop(1), + ), + Btn.tile( + icon: const Icon(Icons.folder), + text: l10n.inner, + onTap: () => context.pop(0), + ), + ], + )); + final path = switch (idx) { + 0 => + await AppRoutes.localStorage(isPickFile: true).go(context), + 1 => await Pfs.pickFilePath(), + _ => null, + }; + if (path == null) return; + + final remoteDir = _status.path.path; + final fileName = path.split(Platform.pathSeparator).lastOrNull; + final remotePath = '$remoteDir/$fileName'; + Loggers.app.info('SFTP upload local: $path, remote: $remotePath'); + Pros.sftp.add( + SftpReq(widget.spi, remotePath, path, SftpReqType.upload), + ); + }, + icon: const Icon(Icons.upload_file), + ); + } + + Widget _buildAddBtn() { + return Btn.icon( + onTap: () => context.showRoundDialog( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Btn.tile( + icon: const Icon(Icons.folder), + text: libL10n.folder, + onTap: _mkdir, + ), + Btn.tile( + icon: const Icon(Icons.insert_drive_file), + text: libL10n.file, + onTap: _newFile, + ), + ], + ), + ), + icon: const Icon(Icons.add), + ); + } + + Widget _buildGotoBtn() { + return Btn.icon( + onTap: () async { + final p = await context.showRoundDialog( + title: l10n.goto, + child: Autocomplete( + optionsBuilder: (val) { + if (!Stores.setting.recordHistory.fetch()) { + return []; + } + return Stores.history.sftpGoPath.all.cast().where( + (element) => element.contains(val.text), + ); + }, + fieldViewBuilder: (_, controller, node, __) { + return Input( + autoFocus: true, + icon: Icons.abc, + label: libL10n.path, + node: node, + controller: controller, + suggestion: true, + onSubmitted: (value) => context.pop(value), + ); + }, + ), + ); + + if (p == null || p.isEmpty) { + return; + } + + _status.path.path = p; + final suc = await _listDir() ?? false; + if (suc && Stores.setting.recordHistory.fetch()) { + Stores.history.sftpGoPath.add(p); + } + }, + icon: const Icon(Icons.gps_fixed), + ); + } + + Widget _buildRefreshBtn() { + return Btn.icon( + onTap: _listDir, + icon: const Icon(Icons.refresh), + ); + } + + Widget _buildHomeBtn() { + return IconButton( + onPressed: () { + final user = widget.spi.user; + _status.path.path = user != 'root' ? '/home/$user' : '/root'; + _listDir(); + }, + icon: const Icon(Icons.home), + ); + } + @override FutureOr afterFirstLayout(BuildContext context) { var initPath = '/'; @@ -817,7 +805,8 @@ class _SftpPageState extends State with AfterLayoutMixin { initPath = history; } } - _status.path = AbsolutePath(widget.initPath ?? initPath); + + _status.path.path = widget.initPath ?? initPath; _listDir(); } } @@ -947,5 +936,15 @@ class _SortOption { final _SortType sortBy; final bool reversed; - _SortOption({required this.sortBy, required this.reversed}); + _SortOption({this.sortBy = _SortType.name, this.reversed = false}); + + _SortOption copyWith({ + _SortType? sortBy, + bool? reversed, + }) { + return _SortOption( + sortBy: sortBy ?? this.sortBy, + reversed: reversed ?? this.reversed, + ); + } } diff --git a/lib/view/widget/server_func_btns.dart b/lib/view/widget/server_func_btns.dart index 3835c9242..b4238ea54 100644 --- a/lib/view/widget/server_func_btns.dart +++ b/lib/view/widget/server_func_btns.dart @@ -10,9 +10,9 @@ import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/store.dart'; import 'package:server_box/view/page/systemd.dart'; -import '../../core/route.dart'; -import '../../core/utils/server.dart'; -import '../../data/model/server/server_private_info.dart'; +import 'package:server_box/core/route.dart'; +import 'package:server_box/core/utils/server.dart'; +import 'package:server_box/data/model/server/server_private_info.dart'; class ServerFuncBtnsTopRight extends StatelessWidget { final ServerPrivateInfo spi; @@ -196,17 +196,17 @@ void _gotoSSH(ServerPrivateInfo spi, BuildContext context) async { await file.delete(); } await file.writeAsString(getPrivateKey(spi.keyId!)); - extraArgs.addAll(["-i", path]); + extraArgs.addAll(['-i', path]); } - final sshCommand = ["ssh", "${spi.user}@${spi.ip}"] + extraArgs; + final sshCommand = ['ssh', '${spi.user}@${spi.ip}'] + extraArgs; final system = Pfs.type; switch (system) { case Pfs.windows: - await Process.start("cmd", ["/c", "start"] + sshCommand); + await Process.start('cmd', ['/c', 'start'] + sshCommand); break; case Pfs.linux: - await Process.start("x-terminal-emulator", ["-e"] + sshCommand); + await Process.start('x-terminal-emulator', ['-e'] + sshCommand); break; default: context.showSnackBar('Mismatch system: $system'); diff --git a/lib/view/widget/unix_perm.dart b/lib/view/widget/unix_perm.dart index b7c2e2a53..ad8d053e3 100644 --- a/lib/view/widget/unix_perm.dart +++ b/lib/view/widget/unix_perm.dart @@ -59,7 +59,7 @@ final class UnixPermEditor extends StatefulWidget { {super.key, required this.perm, required this.onChanged}); @override - _UnixPermEditorState createState() => _UnixPermEditorState(); + State createState() => _UnixPermEditorState(); } final class _UnixPermEditorState extends State { From cba32e6e9942aeb61939ce621291b6fdb19c9330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?lollipopkit=F0=9F=8F=B3=EF=B8=8F=E2=80=8D=E2=9A=A7?= =?UTF-8?q?=EF=B8=8F?= <10864310+lollipopkit@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:54:08 +0800 Subject: [PATCH 2/2] opt. --- lib/view/page/storage/sftp.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/view/page/storage/sftp.dart b/lib/view/page/storage/sftp.dart index ff528d712..3684d08b1 100644 --- a/lib/view/page/storage/sftp.dart +++ b/lib/view/page/storage/sftp.dart @@ -611,11 +611,9 @@ class _SftpPageState extends State with AfterLayoutMixin { fs.removeAt(0); } - /// Issue #96 - /// Due to [WillPopScope] added in this page - /// There is no need to keep '..' folder in listdir - /// So remove it - if (fs.isNotEmpty && fs.firstOrNull?.filename == '..') { + if (fs.isNotEmpty && + fs.firstOrNull?.filename == '..' && + _status.path.path == '/') { fs.removeAt(0); } if (mounted) {