mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
#33 new: upload file to sftp from file picker
This commit is contained in:
@@ -360,12 +360,6 @@ abstract class S {
|
|||||||
/// **'Download'**
|
/// **'Download'**
|
||||||
String get download;
|
String get download;
|
||||||
|
|
||||||
/// No description provided for @downloadFinished.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Download finished'**
|
|
||||||
String get downloadFinished;
|
|
||||||
|
|
||||||
/// No description provided for @downloadStatus.
|
/// No description provided for @downloadStatus.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -462,6 +456,12 @@ abstract class S {
|
|||||||
/// **'Files'**
|
/// **'Files'**
|
||||||
String get files;
|
String get files;
|
||||||
|
|
||||||
|
/// No description provided for @finished.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Finished'**
|
||||||
|
String get finished;
|
||||||
|
|
||||||
/// No description provided for @font.
|
/// No description provided for @font.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -528,6 +528,12 @@ abstract class S {
|
|||||||
/// **'Import'**
|
/// **'Import'**
|
||||||
String get import;
|
String get import;
|
||||||
|
|
||||||
|
/// No description provided for @inner.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Inner'**
|
||||||
|
String get inner;
|
||||||
|
|
||||||
/// No description provided for @inputDomainHere.
|
/// No description provided for @inputDomainHere.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -666,6 +672,12 @@ abstract class S {
|
|||||||
/// **'min'**
|
/// **'min'**
|
||||||
String get min;
|
String get min;
|
||||||
|
|
||||||
|
/// No description provided for @mission.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Mission'**
|
||||||
|
String get mission;
|
||||||
|
|
||||||
/// No description provided for @ms.
|
/// No description provided for @ms.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -1062,6 +1074,12 @@ abstract class S {
|
|||||||
/// **'Are you sure to delete server [{server}]?'**
|
/// **'Are you sure to delete server [{server}]?'**
|
||||||
String sureToDeleteServer(Object server);
|
String sureToDeleteServer(Object server);
|
||||||
|
|
||||||
|
/// No description provided for @system.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'System'**
|
||||||
|
String get system;
|
||||||
|
|
||||||
/// No description provided for @tag.
|
/// No description provided for @tag.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -144,9 +144,6 @@ class SDe extends S {
|
|||||||
@override
|
@override
|
||||||
String get download => 'Download';
|
String get download => 'Download';
|
||||||
|
|
||||||
@override
|
|
||||||
String get downloadFinished => 'Download abgeschlossen';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String downloadStatus(Object percent, Object size) {
|
String downloadStatus(Object percent, Object size) {
|
||||||
return '$percent% von $size';
|
return '$percent% von $size';
|
||||||
@@ -201,6 +198,9 @@ class SDe extends S {
|
|||||||
@override
|
@override
|
||||||
String get files => 'Dateien';
|
String get files => 'Dateien';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get finished => 'fertiggestellt';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get font => 'Schriftarten';
|
String get font => 'Schriftarten';
|
||||||
|
|
||||||
@@ -238,6 +238,9 @@ class SDe extends S {
|
|||||||
@override
|
@override
|
||||||
String get import => 'Importieren';
|
String get import => 'Importieren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get inner => 'Eingebaut';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get inputDomainHere => 'Domain eingeben';
|
String get inputDomainHere => 'Domain eingeben';
|
||||||
|
|
||||||
@@ -311,6 +314,9 @@ class SDe extends S {
|
|||||||
@override
|
@override
|
||||||
String get min => 'min';
|
String get min => 'min';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mission => 'Mission';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ms => 'ms';
|
String get ms => 'ms';
|
||||||
|
|
||||||
@@ -521,6 +527,9 @@ class SDe extends S {
|
|||||||
return 'Bist du sicher, dass du [$server] löschen willst?';
|
return 'Bist du sicher, dass du [$server] löschen willst?';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get system => 'Systeme';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get tag => 'Tags';
|
String get tag => 'Tags';
|
||||||
|
|
||||||
|
|||||||
@@ -144,9 +144,6 @@ class SEn extends S {
|
|||||||
@override
|
@override
|
||||||
String get download => 'Download';
|
String get download => 'Download';
|
||||||
|
|
||||||
@override
|
|
||||||
String get downloadFinished => 'Download finished';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String downloadStatus(Object percent, Object size) {
|
String downloadStatus(Object percent, Object size) {
|
||||||
return '$percent% of $size';
|
return '$percent% of $size';
|
||||||
@@ -201,6 +198,9 @@ class SEn extends S {
|
|||||||
@override
|
@override
|
||||||
String get files => 'Files';
|
String get files => 'Files';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get finished => 'Finished';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get font => 'Font';
|
String get font => 'Font';
|
||||||
|
|
||||||
@@ -238,6 +238,9 @@ class SEn extends S {
|
|||||||
@override
|
@override
|
||||||
String get import => 'Import';
|
String get import => 'Import';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get inner => 'Inner';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get inputDomainHere => 'Input Domain here';
|
String get inputDomainHere => 'Input Domain here';
|
||||||
|
|
||||||
@@ -311,6 +314,9 @@ class SEn extends S {
|
|||||||
@override
|
@override
|
||||||
String get min => 'min';
|
String get min => 'min';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mission => 'Mission';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ms => 'ms';
|
String get ms => 'ms';
|
||||||
|
|
||||||
@@ -521,6 +527,9 @@ class SEn extends S {
|
|||||||
return 'Are you sure to delete server [$server]?';
|
return 'Are you sure to delete server [$server]?';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get system => 'System';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get tag => 'Tags';
|
String get tag => 'Tags';
|
||||||
|
|
||||||
|
|||||||
@@ -144,9 +144,6 @@ class SZh extends S {
|
|||||||
@override
|
@override
|
||||||
String get download => '下载';
|
String get download => '下载';
|
||||||
|
|
||||||
@override
|
|
||||||
String get downloadFinished => '下载完成';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String downloadStatus(Object percent, Object size) {
|
String downloadStatus(Object percent, Object size) {
|
||||||
return '$size 的 $percent%';
|
return '$size 的 $percent%';
|
||||||
@@ -201,6 +198,9 @@ class SZh extends S {
|
|||||||
@override
|
@override
|
||||||
String get files => '文件';
|
String get files => '文件';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get finished => '已完成';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get font => '字体';
|
String get font => '字体';
|
||||||
|
|
||||||
@@ -238,6 +238,9 @@ class SZh extends S {
|
|||||||
@override
|
@override
|
||||||
String get import => '导入';
|
String get import => '导入';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get inner => '内置';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get inputDomainHere => '在这里输入域名';
|
String get inputDomainHere => '在这里输入域名';
|
||||||
|
|
||||||
@@ -311,6 +314,9 @@ class SZh extends S {
|
|||||||
@override
|
@override
|
||||||
String get min => '最小';
|
String get min => '最小';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mission => '任务';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ms => '毫秒';
|
String get ms => '毫秒';
|
||||||
|
|
||||||
@@ -521,6 +527,9 @@ class SZh extends S {
|
|||||||
return '你确定要删除服务器 [$server] 吗?';
|
return '你确定要删除服务器 [$server] 吗?';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get system => '系统';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get tag => '标签';
|
String get tag => '标签';
|
||||||
|
|
||||||
@@ -751,9 +760,6 @@ class SZhTw extends SZh {
|
|||||||
@override
|
@override
|
||||||
String get download => '下載';
|
String get download => '下載';
|
||||||
|
|
||||||
@override
|
|
||||||
String get downloadFinished => '下載完成';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String downloadStatus(Object percent, Object size) {
|
String downloadStatus(Object percent, Object size) {
|
||||||
return '$size 的 $percent%';
|
return '$size 的 $percent%';
|
||||||
@@ -808,6 +814,9 @@ class SZhTw extends SZh {
|
|||||||
@override
|
@override
|
||||||
String get files => '文件';
|
String get files => '文件';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get finished => '已完成';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get font => '字體';
|
String get font => '字體';
|
||||||
|
|
||||||
@@ -845,6 +854,9 @@ class SZhTw extends SZh {
|
|||||||
@override
|
@override
|
||||||
String get import => '導入';
|
String get import => '導入';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get inner => '內置';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get inputDomainHere => '在這裡輸入域名';
|
String get inputDomainHere => '在這裡輸入域名';
|
||||||
|
|
||||||
@@ -918,6 +930,9 @@ class SZhTw extends SZh {
|
|||||||
@override
|
@override
|
||||||
String get min => '最小';
|
String get min => '最小';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mission => '任務';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ms => '毫秒';
|
String get ms => '毫秒';
|
||||||
|
|
||||||
@@ -1128,6 +1143,9 @@ class SZhTw extends SZh {
|
|||||||
return '你確定要刪除服務器 [$server] 嗎?';
|
return '你確定要刪除服務器 [$server] 嗎?';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get system => '系統';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get tag => '标签';
|
String get tag => '标签';
|
||||||
|
|
||||||
|
|||||||
@@ -360,7 +360,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 341;
|
CURRENT_PROJECT_VERSION = 343;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -368,7 +368,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.341;
|
MARKETING_VERSION = 1.0.343;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -491,7 +491,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 341;
|
CURRENT_PROJECT_VERSION = 343;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -499,7 +499,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.341;
|
MARKETING_VERSION = 1.0.343;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -516,7 +516,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 341;
|
CURRENT_PROJECT_VERSION = 343;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||||
@@ -524,7 +524,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.341;
|
MARKETING_VERSION = 1.0.343;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import '../server/server_private_info.dart';
|
import '../server/server_private_info.dart';
|
||||||
import 'worker.dart';
|
import 'worker.dart';
|
||||||
|
|
||||||
@@ -24,6 +26,7 @@ class SftpReqStatus {
|
|||||||
final SftpReqItem item;
|
final SftpReqItem item;
|
||||||
final void Function() notifyListeners;
|
final void Function() notifyListeners;
|
||||||
late SftpWorker worker;
|
late SftpWorker worker;
|
||||||
|
final Completer? completer;
|
||||||
|
|
||||||
String get fileName => item.localPath.split('/').last;
|
String get fileName => item.localPath.split('/').last;
|
||||||
|
|
||||||
@@ -38,12 +41,11 @@ class SftpReqStatus {
|
|||||||
required this.item,
|
required this.item,
|
||||||
required this.notifyListeners,
|
required this.notifyListeners,
|
||||||
required SftpReqType type,
|
required SftpReqType type,
|
||||||
String? key,
|
this.completer,
|
||||||
}) : id = DateTime.now().microsecondsSinceEpoch {
|
}) : id = DateTime.now().microsecondsSinceEpoch {
|
||||||
worker = SftpWorker(
|
worker = SftpWorker(
|
||||||
onNotify: onNotify,
|
onNotify: onNotify,
|
||||||
item: item,
|
item: item,
|
||||||
privateKey: key,
|
|
||||||
type: type,
|
type: type,
|
||||||
);
|
);
|
||||||
worker.init();
|
worker.init();
|
||||||
@@ -61,6 +63,7 @@ class SftpReqStatus {
|
|||||||
status = event;
|
status = event;
|
||||||
if (status == SftpWorkerStatus.finished) {
|
if (status == SftpWorkerStatus.finished) {
|
||||||
worker.dispose();
|
worker.dispose();
|
||||||
|
completer?.complete();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case double:
|
case double:
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import 'req.dart';
|
|||||||
class SftpWorker {
|
class SftpWorker {
|
||||||
final Function(Object event) onNotify;
|
final Function(Object event) onNotify;
|
||||||
final SftpReqItem item;
|
final SftpReqItem item;
|
||||||
final String? privateKey;
|
|
||||||
final SftpReqType type;
|
final SftpReqType type;
|
||||||
|
|
||||||
final worker = Worker();
|
final worker = Worker();
|
||||||
@@ -21,7 +20,6 @@ class SftpWorker {
|
|||||||
required this.onNotify,
|
required this.onNotify,
|
||||||
required this.item,
|
required this.item,
|
||||||
required this.type,
|
required this.type,
|
||||||
this.privateKey,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -37,7 +35,7 @@ class SftpWorker {
|
|||||||
isolateMessageHandler,
|
isolateMessageHandler,
|
||||||
errorHandler: print,
|
errorHandler: print,
|
||||||
);
|
);
|
||||||
worker.sendMessage(SftpReq(item: item, privateKey: privateKey, type: type));
|
worker.sendMessage(SftpReq(item: item, type: type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle the messages coming from the isolate
|
/// Handle the messages coming from the isolate
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:toolbox/core/provider_base.dart';
|
import 'package:toolbox/core/provider_base.dart';
|
||||||
|
|
||||||
import '../model/sftp/req.dart';
|
import '../model/sftp/req.dart';
|
||||||
@@ -25,12 +27,12 @@ class SftpProvider extends ProviderBase {
|
|||||||
return found.first;
|
return found.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(SftpReqItem item, SftpReqType type, {String? key}) {
|
void add(SftpReqItem item, SftpReqType type, {Completer? completer}) {
|
||||||
_status.add(SftpReqStatus(
|
_status.add(SftpReqStatus(
|
||||||
item: item,
|
item: item,
|
||||||
notifyListeners: notifyListeners,
|
notifyListeners: notifyListeners,
|
||||||
key: key,
|
|
||||||
type: type,
|
type: type,
|
||||||
|
completer: completer,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 341;
|
static const int build = 343;
|
||||||
static const String engine = "3.10.2";
|
static const String engine = "3.10.2";
|
||||||
static const String buildAt = "2023-05-31 19:23:57.263324";
|
static const String buildAt = "2023-06-01 16:27:31.059809";
|
||||||
static const int modifications = 6;
|
static const int modifications = 4;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"dockerStatusRunningAndStoppedFmt": "{runningCount} aktiv, {stoppedCount} container gestoppt.",
|
"dockerStatusRunningAndStoppedFmt": "{runningCount} aktiv, {stoppedCount} container gestoppt.",
|
||||||
"dockerStatusRunningFmt": "{count} Container aktiv",
|
"dockerStatusRunningFmt": "{count} Container aktiv",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"downloadFinished": "Download abgeschlossen",
|
|
||||||
"downloadStatus": "{percent}% von {size}",
|
"downloadStatus": "{percent}% von {size}",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"editor": "Redakteure",
|
"editor": "Redakteure",
|
||||||
@@ -61,6 +60,7 @@
|
|||||||
"fileNotExist": "{file} existiert nicht",
|
"fileNotExist": "{file} existiert nicht",
|
||||||
"fileTooLarge": "Datei '{file}' ist zu groß {size}, max {sizeMax}",
|
"fileTooLarge": "Datei '{file}' ist zu groß {size}, max {sizeMax}",
|
||||||
"files": "Dateien",
|
"files": "Dateien",
|
||||||
|
"finished": "fertiggestellt",
|
||||||
"font": "Schriftarten",
|
"font": "Schriftarten",
|
||||||
"fontSize": "Schriftgröße",
|
"fontSize": "Schriftgröße",
|
||||||
"foundNUpdate": "Update {count} gefunden",
|
"foundNUpdate": "Update {count} gefunden",
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
"image": "Image",
|
"image": "Image",
|
||||||
"imagesList": "Images",
|
"imagesList": "Images",
|
||||||
"import": "Importieren",
|
"import": "Importieren",
|
||||||
|
"inner": "Eingebaut",
|
||||||
"inputDomainHere": "Domain eingeben",
|
"inputDomainHere": "Domain eingeben",
|
||||||
"install": "install",
|
"install": "install",
|
||||||
"installDockerWithUrl": "Bitte installiere docker zuerst. https://docs.docker.com/engine/install",
|
"installDockerWithUrl": "Bitte installiere docker zuerst. https://docs.docker.com/engine/install",
|
||||||
@@ -95,6 +96,7 @@
|
|||||||
"maxRetryCount": "Anzahl an Verbindungsversuchen",
|
"maxRetryCount": "Anzahl an Verbindungsversuchen",
|
||||||
"maxRetryCountEqual0": "Unbegrenzte Verbindungsversuche zum Server",
|
"maxRetryCountEqual0": "Unbegrenzte Verbindungsversuche zum Server",
|
||||||
"min": "min",
|
"min": "min",
|
||||||
|
"mission": "Mission",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"needRestart": "App muss neugestartet werden",
|
"needRestart": "App muss neugestartet werden",
|
||||||
@@ -161,6 +163,7 @@
|
|||||||
"sureDirEmpty": "Stelle sicher, dass der Ordner leer ist.",
|
"sureDirEmpty": "Stelle sicher, dass der Ordner leer ist.",
|
||||||
"sureNoPwd": "Bist du sicher, dass du kein Passwort verwenden willst?",
|
"sureNoPwd": "Bist du sicher, dass du kein Passwort verwenden willst?",
|
||||||
"sureToDeleteServer": "Bist du sicher, dass du [{server}] löschen willst?",
|
"sureToDeleteServer": "Bist du sicher, dass du [{server}] löschen willst?",
|
||||||
|
"system": "Systeme",
|
||||||
"tag": "Tags",
|
"tag": "Tags",
|
||||||
"terminal": "Terminal",
|
"terminal": "Terminal",
|
||||||
"theme": "Themen",
|
"theme": "Themen",
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"dockerStatusRunningAndStoppedFmt": "{runningCount} running, {stoppedCount} container stopped.",
|
"dockerStatusRunningAndStoppedFmt": "{runningCount} running, {stoppedCount} container stopped.",
|
||||||
"dockerStatusRunningFmt": "{count} container running.",
|
"dockerStatusRunningFmt": "{count} container running.",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"downloadFinished": "Download finished",
|
|
||||||
"downloadStatus": "{percent}% of {size}",
|
"downloadStatus": "{percent}% of {size}",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
@@ -61,6 +60,7 @@
|
|||||||
"fileNotExist": "{file} not exist",
|
"fileNotExist": "{file} not exist",
|
||||||
"fileTooLarge": "File '{file}' too large {size}, max {sizeMax}",
|
"fileTooLarge": "File '{file}' too large {size}, max {sizeMax}",
|
||||||
"files": "Files",
|
"files": "Files",
|
||||||
|
"finished": "Finished",
|
||||||
"font": "Font",
|
"font": "Font",
|
||||||
"fontSize": "Font size",
|
"fontSize": "Font size",
|
||||||
"foundNUpdate": "Found {count} update",
|
"foundNUpdate": "Found {count} update",
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
"image": "Image",
|
"image": "Image",
|
||||||
"imagesList": "Images list",
|
"imagesList": "Images list",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
|
"inner": "Inner",
|
||||||
"inputDomainHere": "Input Domain here",
|
"inputDomainHere": "Input Domain here",
|
||||||
"install": "install",
|
"install": "install",
|
||||||
"installDockerWithUrl": "Please https://docs.docker.com/engine/install docker first.",
|
"installDockerWithUrl": "Please https://docs.docker.com/engine/install docker first.",
|
||||||
@@ -95,6 +96,7 @@
|
|||||||
"maxRetryCount": "Number of server reconnection",
|
"maxRetryCount": "Number of server reconnection",
|
||||||
"maxRetryCountEqual0": "Will retry again and again.",
|
"maxRetryCountEqual0": "Will retry again and again.",
|
||||||
"min": "min",
|
"min": "min",
|
||||||
|
"mission": "Mission",
|
||||||
"ms": "ms",
|
"ms": "ms",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"needRestart": "Need to restart app",
|
"needRestart": "Need to restart app",
|
||||||
@@ -161,6 +163,7 @@
|
|||||||
"sureDirEmpty": "Make sure dir is empty.",
|
"sureDirEmpty": "Make sure dir is empty.",
|
||||||
"sureNoPwd": "Are you sure to use no password?",
|
"sureNoPwd": "Are you sure to use no password?",
|
||||||
"sureToDeleteServer": "Are you sure to delete server [{server}]?",
|
"sureToDeleteServer": "Are you sure to delete server [{server}]?",
|
||||||
|
"system": "System",
|
||||||
"tag": "Tags",
|
"tag": "Tags",
|
||||||
"terminal": "Terminal",
|
"terminal": "Terminal",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"dockerStatusRunningAndStoppedFmt": "{runningCount}个正在运行, {stoppedCount}个已停止",
|
"dockerStatusRunningAndStoppedFmt": "{runningCount}个正在运行, {stoppedCount}个已停止",
|
||||||
"dockerStatusRunningFmt": "{count}个容器正在运行",
|
"dockerStatusRunningFmt": "{count}个容器正在运行",
|
||||||
"download": "下载",
|
"download": "下载",
|
||||||
"downloadFinished": "下载完成",
|
|
||||||
"downloadStatus": "{size} 的 {percent}%",
|
"downloadStatus": "{size} 的 {percent}%",
|
||||||
"edit": "编辑",
|
"edit": "编辑",
|
||||||
"editor": "编辑器",
|
"editor": "编辑器",
|
||||||
@@ -61,6 +60,7 @@
|
|||||||
"fileNotExist": "{file} 不存在",
|
"fileNotExist": "{file} 不存在",
|
||||||
"fileTooLarge": "文件 '{file}' 过大 '{size}',超过了 {sizeMax}",
|
"fileTooLarge": "文件 '{file}' 过大 '{size}',超过了 {sizeMax}",
|
||||||
"files": "文件",
|
"files": "文件",
|
||||||
|
"finished": "已完成",
|
||||||
"font": "字体",
|
"font": "字体",
|
||||||
"fontSize": "字体大小",
|
"fontSize": "字体大小",
|
||||||
"foundNUpdate": "找到 {count} 个更新",
|
"foundNUpdate": "找到 {count} 个更新",
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
"image": "镜像",
|
"image": "镜像",
|
||||||
"imagesList": "镜像列表",
|
"imagesList": "镜像列表",
|
||||||
"import": "导入",
|
"import": "导入",
|
||||||
|
"inner": "内置",
|
||||||
"inputDomainHere": "在这里输入域名",
|
"inputDomainHere": "在这里输入域名",
|
||||||
"install": "安装",
|
"install": "安装",
|
||||||
"installDockerWithUrl": "请先 https://docs.docker.com/engine/install docker",
|
"installDockerWithUrl": "请先 https://docs.docker.com/engine/install docker",
|
||||||
@@ -95,6 +96,7 @@
|
|||||||
"maxRetryCount": "服务器尝试重连次数",
|
"maxRetryCount": "服务器尝试重连次数",
|
||||||
"maxRetryCountEqual0": "会无限重试",
|
"maxRetryCountEqual0": "会无限重试",
|
||||||
"min": "最小",
|
"min": "最小",
|
||||||
|
"mission": "任务",
|
||||||
"ms": "毫秒",
|
"ms": "毫秒",
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
"needRestart": "需要重启 App",
|
"needRestart": "需要重启 App",
|
||||||
@@ -161,6 +163,7 @@
|
|||||||
"sureDirEmpty": "请确保文件夹为空",
|
"sureDirEmpty": "请确保文件夹为空",
|
||||||
"sureNoPwd": "确认使用无密码?",
|
"sureNoPwd": "确认使用无密码?",
|
||||||
"sureToDeleteServer": "你确定要删除服务器 [{server}] 吗?",
|
"sureToDeleteServer": "你确定要删除服务器 [{server}] 吗?",
|
||||||
|
"system": "系统",
|
||||||
"tag": "标签",
|
"tag": "标签",
|
||||||
"terminal": "终端",
|
"terminal": "终端",
|
||||||
"theme": "主题",
|
"theme": "主题",
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"dockerStatusRunningAndStoppedFmt": "{runningCount}個正在運行, {stoppedCount}個已停止",
|
"dockerStatusRunningAndStoppedFmt": "{runningCount}個正在運行, {stoppedCount}個已停止",
|
||||||
"dockerStatusRunningFmt": "{count}個容器正在運行",
|
"dockerStatusRunningFmt": "{count}個容器正在運行",
|
||||||
"download": "下載",
|
"download": "下載",
|
||||||
"downloadFinished": "下載完成",
|
|
||||||
"downloadStatus": "{size} 的 {percent}%",
|
"downloadStatus": "{size} 的 {percent}%",
|
||||||
"edit": "編輯",
|
"edit": "編輯",
|
||||||
"editor": "編輯器",
|
"editor": "編輯器",
|
||||||
@@ -61,6 +60,7 @@
|
|||||||
"fileNotExist": "{file} 不存在",
|
"fileNotExist": "{file} 不存在",
|
||||||
"fileTooLarge": "文件 '{file}' 過大 '{size}',超過了 {sizeMax}",
|
"fileTooLarge": "文件 '{file}' 過大 '{size}',超過了 {sizeMax}",
|
||||||
"files": "文件",
|
"files": "文件",
|
||||||
|
"finished": "已完成",
|
||||||
"font": "字體",
|
"font": "字體",
|
||||||
"fontSize": "字體大小",
|
"fontSize": "字體大小",
|
||||||
"foundNUpdate": "找到 {count} 個更新",
|
"foundNUpdate": "找到 {count} 個更新",
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
"image": "鏡像",
|
"image": "鏡像",
|
||||||
"imagesList": "鏡像列表",
|
"imagesList": "鏡像列表",
|
||||||
"import": "導入",
|
"import": "導入",
|
||||||
|
"inner": "內置",
|
||||||
"inputDomainHere": "在這裡輸入域名",
|
"inputDomainHere": "在這裡輸入域名",
|
||||||
"install": "安裝",
|
"install": "安裝",
|
||||||
"installDockerWithUrl": "請先 https://docs.docker.com/engine/install docker",
|
"installDockerWithUrl": "請先 https://docs.docker.com/engine/install docker",
|
||||||
@@ -95,6 +96,7 @@
|
|||||||
"maxRetryCount": "服務器嘗試重連次數",
|
"maxRetryCount": "服務器嘗試重連次數",
|
||||||
"maxRetryCountEqual0": "會無限重試",
|
"maxRetryCountEqual0": "會無限重試",
|
||||||
"min": "最小",
|
"min": "最小",
|
||||||
|
"mission": "任務",
|
||||||
"ms": "毫秒",
|
"ms": "毫秒",
|
||||||
"name": "名稱",
|
"name": "名稱",
|
||||||
"needRestart": "需要重啓 App",
|
"needRestart": "需要重啓 App",
|
||||||
@@ -161,6 +163,7 @@
|
|||||||
"sureDirEmpty": "請確保文件夾為空",
|
"sureDirEmpty": "請確保文件夾為空",
|
||||||
"sureNoPwd": "確認使用無密碼?",
|
"sureNoPwd": "確認使用無密碼?",
|
||||||
"sureToDeleteServer": "你確定要刪除服務器 [{server}] 嗎?",
|
"sureToDeleteServer": "你確定要刪除服務器 [{server}] 嗎?",
|
||||||
|
"system": "系統",
|
||||||
"tag": "标签",
|
"tag": "标签",
|
||||||
"terminal": "终端機",
|
"terminal": "终端機",
|
||||||
"theme": "主題",
|
"theme": "主題",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import 'ping.dart';
|
|||||||
import 'private_key/list.dart';
|
import 'private_key/list.dart';
|
||||||
import 'server/tab.dart';
|
import 'server/tab.dart';
|
||||||
import 'setting.dart';
|
import 'setting.dart';
|
||||||
import 'sftp/downloaded.dart';
|
import 'sftp/local.dart';
|
||||||
import 'snippet/list.dart';
|
import 'snippet/list.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:after_layout/after_layout.dart';
|
|||||||
import 'package:circle_chart/circle_chart.dart';
|
import 'package:circle_chart/circle_chart.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/extension/navigator.dart';
|
import 'package:toolbox/core/extension/navigator.dart';
|
||||||
@@ -26,7 +25,7 @@ import '../../widget/round_rect_card.dart';
|
|||||||
import '../../widget/url_text.dart';
|
import '../../widget/url_text.dart';
|
||||||
import '../docker.dart';
|
import '../docker.dart';
|
||||||
import '../pkg.dart';
|
import '../pkg.dart';
|
||||||
import '../sftp/view.dart';
|
import '../sftp/remote.dart';
|
||||||
import '../ssh.dart';
|
import '../ssh.dart';
|
||||||
import 'detail.dart';
|
import 'detail.dart';
|
||||||
import 'edit.dart';
|
import 'edit.dart';
|
||||||
@@ -80,9 +79,53 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTagsSwitcher(ServerProvider pro) {
|
Widget _buildBody() {
|
||||||
if (pro.tags.isEmpty) return placeholder;
|
return RefreshIndicator(
|
||||||
final items = <String?>[null, ...pro.tags];
|
onRefresh: () async =>
|
||||||
|
await _serverProvider.refreshData(onlyFailed: true),
|
||||||
|
child: Consumer<ServerProvider>(
|
||||||
|
builder: (_, pro, __) {
|
||||||
|
if (!pro.tags.contains(_tag)) {
|
||||||
|
_tag = null;
|
||||||
|
}
|
||||||
|
if (pro.serverOrder.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
_s.serverTabEmpty,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final filtered = pro.serverOrder
|
||||||
|
.where((e) => pro.servers.containsKey(e))
|
||||||
|
.where((e) =>
|
||||||
|
_tag == null ||
|
||||||
|
(pro.servers[e]?.spi.tags?.contains(_tag) ?? false))
|
||||||
|
.toList();
|
||||||
|
return ReorderableListView.builder(
|
||||||
|
header: _buildTagsSwitcher(pro.tags),
|
||||||
|
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
|
||||||
|
onReorder: (oldIndex, newIndex) => setState(() {
|
||||||
|
pro.serverOrder.moveById(
|
||||||
|
filtered[oldIndex],
|
||||||
|
filtered[newIndex],
|
||||||
|
_settingStore.serverOrder,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
itemBuilder: (_, index) => _buildEachServerCard(
|
||||||
|
pro.servers[filtered[index]],
|
||||||
|
index,
|
||||||
|
),
|
||||||
|
itemCount: filtered.length,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTagsSwitcher(List<String> tags) {
|
||||||
|
if (tags.isEmpty) return placeholder;
|
||||||
|
final items = <String?>[null, ...tags];
|
||||||
return Container(
|
return Container(
|
||||||
height: 37,
|
height: 37,
|
||||||
width: _media.size.width,
|
width: _media.size.width,
|
||||||
@@ -125,51 +168,6 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody() {
|
|
||||||
return RefreshIndicator(
|
|
||||||
onRefresh: () async =>
|
|
||||||
await _serverProvider.refreshData(onlyFailed: true),
|
|
||||||
child: Consumer<ServerProvider>(
|
|
||||||
builder: (_, pro, __) {
|
|
||||||
if (!pro.tags.contains(_tag)) {
|
|
||||||
_tag = null;
|
|
||||||
}
|
|
||||||
if (pro.serverOrder.isEmpty) {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
_s.serverTabEmpty,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final filtered = pro.serverOrder
|
|
||||||
.where((e) => pro.servers.containsKey(e))
|
|
||||||
.where((e) =>
|
|
||||||
_tag == null ||
|
|
||||||
(pro.servers[e]?.spi.tags?.contains(_tag) ?? false))
|
|
||||||
.toList();
|
|
||||||
return AnimationLimiter(
|
|
||||||
key: ValueKey(_tag),
|
|
||||||
child: ReorderableListView.builder(
|
|
||||||
header: _buildTagsSwitcher(pro),
|
|
||||||
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
onReorder: (oldIndex, newIndex) => setState(() {
|
|
||||||
pro.serverOrder.moveById(
|
|
||||||
filtered[oldIndex],
|
|
||||||
filtered[newIndex],
|
|
||||||
_settingStore.serverOrder,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
itemBuilder: (context, index) =>
|
|
||||||
_buildEachServerCard(pro.servers[filtered[index]], index),
|
|
||||||
itemCount: filtered.length,
|
|
||||||
));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildEachServerCard(Server? si, int index) {
|
Widget _buildEachServerCard(Server? si, int index) {
|
||||||
if (si == null) {
|
if (si == null) {
|
||||||
return placeholder;
|
return placeholder;
|
||||||
@@ -180,18 +178,12 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
ServerDetailPage(si.spi.id),
|
ServerDetailPage(si.spi.id),
|
||||||
'server detail page',
|
'server detail page',
|
||||||
).go(context),
|
).go(context),
|
||||||
child: AnimationConfiguration.staggeredList(
|
child: RoundRectCard(
|
||||||
position: index,
|
Padding(
|
||||||
duration: const Duration(milliseconds: 375),
|
padding: const EdgeInsets.all(13),
|
||||||
child: SlideAnimation(
|
child: _buildRealServerCard(si.status, si.state, si.spi),
|
||||||
verticalOffset: 50.0,
|
),
|
||||||
child: FadeInAnimation(
|
),
|
||||||
child: RoundRectCard(
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(13),
|
|
||||||
child: _buildRealServerCard(si.status, si.state, si.spi),
|
|
||||||
),
|
|
||||||
)))),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import '../../../data/model/app/path_with_prefix.dart';
|
|||||||
import '../../../data/res/path.dart';
|
import '../../../data/res/path.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../widget/fade_in.dart';
|
import '../../widget/fade_in.dart';
|
||||||
import 'downloading.dart';
|
import 'mission.dart';
|
||||||
|
|
||||||
class SFTPDownloadedPage extends StatefulWidget {
|
class SFTPDownloadedPage extends StatefulWidget {
|
||||||
final bool isPickFile;
|
final bool isPickFile;
|
||||||
@@ -31,7 +31,7 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
_s.download,
|
_s.mission,
|
||||||
style: textSize18,
|
style: textSize18,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -61,7 +61,11 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|||||||
{Widget? trailing}) {
|
{Widget? trailing}) {
|
||||||
return RoundRectCard(
|
return RoundRectCard(
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(status.fileName),
|
title: Text(
|
||||||
|
status.fileName,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
subtitle: subtitle == null
|
subtitle: subtitle == null
|
||||||
? null
|
? null
|
||||||
: Text(
|
: Text(
|
||||||
@@ -81,21 +85,26 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|||||||
switch (status.status) {
|
switch (status.status) {
|
||||||
case SftpWorkerStatus.finished:
|
case SftpWorkerStatus.finished:
|
||||||
final time = status.spentTime.toString();
|
final time = status.spentTime.toString();
|
||||||
|
final str = '${_s.finished} ${_s.spentTime(
|
||||||
|
time == 'null' ? _s.unknown : (time.substring(0, time.length - 7)),
|
||||||
|
)}';
|
||||||
return _wrapInCard(
|
return _wrapInCard(
|
||||||
status,
|
status,
|
||||||
'${_s.downloadFinished} ${_s.spentTime(time == 'null' ? _s.unknown : (time.substring(0, time.length - 7)))}',
|
str,
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
onPressed: () => shareFiles(context, [status.item.localPath]),
|
onPressed: () => shareFiles(context, [status.item.localPath]),
|
||||||
icon: const Icon(Icons.open_in_new),
|
icon: const Icon(Icons.open_in_new),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case SftpWorkerStatus.downloading:
|
case SftpWorkerStatus.downloading:
|
||||||
|
final percentStr = (status.progress ?? 0.0).toStringAsFixed(2);
|
||||||
|
final percent = (status.progress ?? 0) / 100;
|
||||||
|
final size = (status.size ?? 0).convertBytes;
|
||||||
return _wrapInCard(
|
return _wrapInCard(
|
||||||
status,
|
status,
|
||||||
_s.downloadStatus((status.progress ?? 0.0).toStringAsFixed(2),
|
_s.downloadStatus(percentStr, size),
|
||||||
(status.size ?? 0).convertBytes),
|
trailing: CircularProgressIndicator(value: percent),
|
||||||
trailing:
|
);
|
||||||
CircularProgressIndicator(value: (status.progress ?? 0) / 100));
|
|
||||||
case SftpWorkerStatus.preparing:
|
case SftpWorkerStatus.preparing:
|
||||||
return _wrapInCard(status, _s.sftpDlPrepare, trailing: loadingIcon);
|
return _wrapInCard(status, _s.sftpDlPrepare, trailing: loadingIcon);
|
||||||
case SftpWorkerStatus.sshConnectted:
|
case SftpWorkerStatus.sshConnectted:
|
||||||
@@ -104,10 +113,7 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|||||||
return _wrapInCard(
|
return _wrapInCard(
|
||||||
status,
|
status,
|
||||||
_s.unknown,
|
_s.unknown,
|
||||||
trailing: const Icon(
|
trailing: const Icon(Icons.error, size: 40),
|
||||||
Icons.error,
|
|
||||||
size: 40,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'dart:io';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
@@ -8,7 +8,7 @@ import 'package:toolbox/core/extension/navigator.dart';
|
|||||||
import 'package:toolbox/core/extension/sftpfile.dart';
|
import 'package:toolbox/core/extension/sftpfile.dart';
|
||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:toolbox/data/res/misc.dart';
|
||||||
import 'package:toolbox/view/page/editor.dart';
|
import 'package:toolbox/view/page/editor.dart';
|
||||||
import 'package:toolbox/view/page/sftp/downloaded.dart';
|
import 'package:toolbox/view/page/sftp/local.dart';
|
||||||
|
|
||||||
import '../../../core/extension/numx.dart';
|
import '../../../core/extension/numx.dart';
|
||||||
import '../../../core/extension/stringx.dart';
|
import '../../../core/extension/stringx.dart';
|
||||||
@@ -24,12 +24,11 @@ import '../../../data/provider/server.dart';
|
|||||||
import '../../../data/provider/sftp.dart';
|
import '../../../data/provider/sftp.dart';
|
||||||
import '../../../data/res/path.dart';
|
import '../../../data/res/path.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../../data/store/private_key.dart';
|
|
||||||
import '../../../locator.dart';
|
import '../../../locator.dart';
|
||||||
import '../../widget/fade_in.dart';
|
import '../../widget/fade_in.dart';
|
||||||
import '../../widget/input_field.dart';
|
import '../../widget/input_field.dart';
|
||||||
import '../../widget/two_line_text.dart';
|
import '../../widget/two_line_text.dart';
|
||||||
import 'downloading.dart';
|
import 'mission.dart';
|
||||||
|
|
||||||
class SFTPPage extends StatefulWidget {
|
class SFTPPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final ServerPrivateInfo spi;
|
||||||
@@ -44,6 +43,8 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
final SftpBrowserStatus _status = SftpBrowserStatus();
|
final SftpBrowserStatus _status = SftpBrowserStatus();
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
final _sftp = locator<SftpProvider>();
|
||||||
|
|
||||||
late S _s;
|
late S _s;
|
||||||
|
|
||||||
ServerState? _state;
|
ServerState? _state;
|
||||||
@@ -117,12 +118,38 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
Widget _buildUploadBtn() {
|
Widget _buildUploadBtn() {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final path = await AppRoute(
|
final idx = await showRoundDialog(
|
||||||
const SFTPDownloadedPage(
|
context: context,
|
||||||
isPickFile: true,
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.open_in_new),
|
||||||
|
title: Text(_s.system),
|
||||||
|
onTap: () => context.pop(1),
|
||||||
),
|
),
|
||||||
'sftp dled pick')
|
ListTile(
|
||||||
.go<String>(context);
|
leading: const Icon(Icons.folder),
|
||||||
|
title: Text(_s.inner),
|
||||||
|
onTap: () => context.pop(0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
final path = await () async {
|
||||||
|
switch (idx) {
|
||||||
|
case 0:
|
||||||
|
return await AppRoute(
|
||||||
|
const SFTPDownloadedPage(
|
||||||
|
isPickFile: true,
|
||||||
|
),
|
||||||
|
'sftp dled pick')
|
||||||
|
.go<String>(context);
|
||||||
|
case 1:
|
||||||
|
return await pickOneFile();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}();
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -131,7 +158,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
showSnackBar(context, const Text('remote path is null'));
|
showSnackBar(context, const Text('remote path is null'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
locator<SftpProvider>().add(
|
_sftp.add(
|
||||||
SftpReqItem(widget.spi, remotePath, path),
|
SftpReqItem(widget.spi, remotePath, path),
|
||||||
SftpReqType.upload,
|
SftpReqType.upload,
|
||||||
);
|
);
|
||||||
@@ -302,39 +329,20 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final file = await _status.client!.open(
|
final remotePath = _getRemotePath(name);
|
||||||
_getRemotePath(name),
|
final localPath = await _getLocalPath(remotePath);
|
||||||
mode: SftpFileOpenMode.read | SftpFileOpenMode.write,
|
final completer = Completer();
|
||||||
);
|
final req = SftpReqItem(widget.spi, remotePath, localPath);
|
||||||
final localPath = '${(await sftpDir).path}${_getRemotePath(name)}';
|
_sftp.add(req, SftpReqType.download, completer: completer);
|
||||||
await Directory(localPath.substring(0, localPath.lastIndexOf('/')))
|
await completer.future;
|
||||||
.create(recursive: true);
|
|
||||||
final local = File(localPath);
|
|
||||||
if (await local.exists()) {
|
|
||||||
await local.delete();
|
|
||||||
}
|
|
||||||
final localFile = local.openWrite(mode: FileMode.append);
|
|
||||||
const defaultChunkSize = 1024 * 1024;
|
|
||||||
final chunkSize = size > defaultChunkSize ? defaultChunkSize : size;
|
|
||||||
for (var i = 0; i < size; i += chunkSize) {
|
|
||||||
final fileData = file.read(length: chunkSize);
|
|
||||||
await for (var form in fileData) {
|
|
||||||
localFile.add(form);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await localFile.close();
|
|
||||||
context.pop();
|
|
||||||
|
|
||||||
final result = await AppRoute(
|
final result = await AppRoute(
|
||||||
EditorPage(path: localPath),
|
EditorPage(path: localPath),
|
||||||
'SFTP edit',
|
'SFTP edit',
|
||||||
).go<String>(context);
|
).go<String>(context);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
await local.writeAsString(result);
|
_sftp.add(req, SftpReqType.upload);
|
||||||
await file.writeBytes(result.uint8List);
|
|
||||||
showSnackBar(context, Text(_s.saved));
|
|
||||||
}
|
}
|
||||||
await file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _download(BuildContext context, SftpName name) {
|
void _download(BuildContext context, SftpName name) {
|
||||||
@@ -351,18 +359,14 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
context.pop();
|
context.pop();
|
||||||
final remotePath = _getRemotePath(name);
|
final remotePath = _getRemotePath(name);
|
||||||
final local = '${(await sftpDir).path}$remotePath';
|
|
||||||
final pubKeyId = widget.spi.pubKeyId;
|
|
||||||
final key = locator<PrivateKeyStore>().get(pubKeyId)?.privateKey;
|
|
||||||
|
|
||||||
locator<SftpProvider>().add(
|
_sftp.add(
|
||||||
SftpReqItem(
|
SftpReqItem(
|
||||||
widget.spi,
|
widget.spi,
|
||||||
remotePath,
|
remotePath,
|
||||||
local,
|
await _getLocalPath(remotePath),
|
||||||
),
|
),
|
||||||
SftpReqType.download,
|
SftpReqType.download,
|
||||||
key: key,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
context.pop();
|
context.pop();
|
||||||
@@ -564,6 +568,10 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
return pathJoin(prePath, name.filename);
|
return pathJoin(prePath, name.filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> _getLocalPath(String remotePath) async {
|
||||||
|
return '${(await sftpDir).path}$remotePath';
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _listDir({String? path, SSHClient? client}) async {
|
Future<void> _listDir({String? path, SSHClient? client}) async {
|
||||||
if (_status.isBusy) {
|
if (_status.isBusy) {
|
||||||
return;
|
return;
|
||||||
@@ -23,7 +23,7 @@ import '../../data/res/terminal.dart';
|
|||||||
import '../../data/res/virtual_key.dart';
|
import '../../data/res/virtual_key.dart';
|
||||||
import '../../data/store/setting.dart';
|
import '../../data/store/setting.dart';
|
||||||
import '../../locator.dart';
|
import '../../locator.dart';
|
||||||
import 'sftp/view.dart';
|
import 'sftp/remote.dart';
|
||||||
|
|
||||||
class SSHPage extends StatefulWidget {
|
class SSHPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final ServerPrivateInfo spi;
|
||||||
|
|||||||
@@ -3,8 +3,13 @@ import 'package:flutter/material.dart';
|
|||||||
/// 渐隐渐显实现
|
/// 渐隐渐显实现
|
||||||
class FadeIn extends StatefulWidget {
|
class FadeIn extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
final Duration duration;
|
||||||
|
|
||||||
const FadeIn({Key? key, required this.child}) : super(key: key);
|
const FadeIn({
|
||||||
|
Key? key,
|
||||||
|
required this.child,
|
||||||
|
this.duration = const Duration(milliseconds: 377),
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MyFadeInState createState() => _MyFadeInState();
|
_MyFadeInState createState() => _MyFadeInState();
|
||||||
@@ -19,7 +24,7 @@ class _MyFadeInState extends State<FadeIn> with SingleTickerProviderStateMixin {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(milliseconds: 377),
|
duration: widget.duration,
|
||||||
);
|
);
|
||||||
_animation = Tween(
|
_animation = Tween(
|
||||||
begin: 0.0,
|
begin: 0.0,
|
||||||
|
|||||||
@@ -475,9 +475,9 @@
|
|||||||
baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */;
|
baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 341;
|
CURRENT_PROJECT_VERSION = 343;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0.341;
|
MARKETING_VERSION = 1.0.343;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -490,9 +490,9 @@
|
|||||||
baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */;
|
baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 341;
|
CURRENT_PROJECT_VERSION = 343;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0.341;
|
MARKETING_VERSION = 1.0.343;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -505,9 +505,9 @@
|
|||||||
baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */;
|
baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 341;
|
CURRENT_PROJECT_VERSION = 343;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0.341;
|
MARKETING_VERSION = 1.0.343;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|||||||
13
make.dart
13
make.dart
@@ -142,6 +142,19 @@ Future<void> flutterBuildMacOS() async {
|
|||||||
Future<void> flutterBuildAndroid() async {
|
Future<void> flutterBuildAndroid() async {
|
||||||
await flutterBuild('apk');
|
await flutterBuild('apk');
|
||||||
await killJava();
|
await killJava();
|
||||||
|
await scp2CDN();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> scp2CDN() async {
|
||||||
|
final result = await Process.run('scp', [
|
||||||
|
apkPath,
|
||||||
|
'custcdn:/usr/share/caddy/uploads/${appName}_${build}_Arm64.apk'
|
||||||
|
]);
|
||||||
|
print(result.stdout);
|
||||||
|
if (result.exitCode != 0) {
|
||||||
|
print(result.stderr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeAppleVersion() async {
|
Future<void> changeAppleVersion() async {
|
||||||
|
|||||||
@@ -372,14 +372,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.14"
|
version: "2.0.14"
|
||||||
flutter_staggered_animations:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_staggered_animations
|
|
||||||
sha256: "81d3c816c9bb0dca9e8a5d5454610e21ffb068aedb2bde49d2f8d04f75538351"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.1"
|
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ dependencies:
|
|||||||
highlight: ^0.7.0
|
highlight: ^0.7.0
|
||||||
flutter_highlight: ^0.7.0
|
flutter_highlight: ^0.7.0
|
||||||
code_text_field: ^1.1.0
|
code_text_field: ^1.1.0
|
||||||
flutter_staggered_animations: ^1.1.1
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_native_splash: ^2.1.6
|
flutter_native_splash: ^2.1.6
|
||||||
|
|||||||
Reference in New Issue
Block a user