This commit is contained in:
lollipopkit
2023-08-09 23:58:38 +08:00
parent 58fbd62779
commit c512a6a274
36 changed files with 243 additions and 160 deletions

View File

@@ -266,6 +266,12 @@ abstract class S {
/// **'Connection'** /// **'Connection'**
String get conn; String get conn;
/// No description provided for @connected.
///
/// In en, this message translates to:
/// **'Connected'**
String get connected;
/// No description provided for @containerName. /// No description provided for @containerName.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -88,6 +88,9 @@ class SDe extends S {
@override @override
String get conn => 'Verbindung'; String get conn => 'Verbindung';
@override
String get connected => 'in Verbindung gebracht';
@override @override
String get containerName => 'Container Name'; String get containerName => 'Container Name';

View File

@@ -88,6 +88,9 @@ class SEn extends S {
@override @override
String get conn => 'Connection'; String get conn => 'Connection';
@override
String get connected => 'Connected';
@override @override
String get containerName => 'Container name'; String get containerName => 'Container name';

View File

@@ -88,6 +88,9 @@ class SId extends S {
@override @override
String get conn => 'Koneksi'; String get conn => 'Koneksi';
@override
String get connected => 'Terhubung';
@override @override
String get containerName => 'Nama kontainer'; String get containerName => 'Nama kontainer';

View File

@@ -88,6 +88,9 @@ class SZh extends S {
@override @override
String get conn => '连接'; String get conn => '连接';
@override
String get connected => '已连接';
@override @override
String get containerName => '容器名'; String get containerName => '容器名';
@@ -784,6 +787,9 @@ class SZhTw extends SZh {
@override @override
String get conn => '連接'; String get conn => '連接';
@override
String get connected => '已連接';
@override @override
String get containerName => '容器名稱'; String get containerName => '容器名稱';

View File

@@ -470,7 +470,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 = 454; CURRENT_PROJECT_VERSION = 457;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -478,7 +478,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
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";
@@ -602,7 +602,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 = 454; CURRENT_PROJECT_VERSION = 457;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -610,7 +610,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
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";
@@ -628,7 +628,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 = 454; CURRENT_PROJECT_VERSION = 457;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -636,7 +636,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
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";
@@ -657,7 +657,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 454; CURRENT_PROJECT_VERSION = 457;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -670,7 +670,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -696,7 +696,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 454; CURRENT_PROJECT_VERSION = 457;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -709,7 +709,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -732,7 +732,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 454; CURRENT_PROJECT_VERSION = 457;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -745,7 +745,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,31 +0,0 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
class ProviderBase with ChangeNotifier {
void setState(void Function() callback) {
callback();
notifyListeners();
}
}
class BusyProvider extends ProviderBase {
bool _isBusy = false;
bool get isBusy => _isBusy;
setBusyState([bool isBusy = true]) {
_isBusy = isBusy;
notifyListeners();
}
FutureOr<T> busyRun<T>(FutureOr<T> Function() func) async {
setBusyState(true);
try {
return await Future.sync(func);
} catch (e) {
rethrow;
} finally {
setBusyState(false);
}
}
}

View File

@@ -12,11 +12,22 @@ class Server {
} }
enum ServerState { enum ServerState {
failed,
disconnected, disconnected,
connecting, connecting,
connected,
failed;
bool get shouldConnect => /// Connected to server
this == ServerState.disconnected || this == ServerState.failed; connected,
/// Status parsing
loading,
/// Status parsing finished
finished;
bool get shouldConnect => this < ServerState.connecting;
bool get canViewDetails => this == ServerState.finished;
operator <(ServerState other) => index < other.index;
} }

View File

@@ -4,7 +4,7 @@ import '../../store/setting.dart';
class TryLimiter { class TryLimiter {
final Map<String, int> _triedTimes = {}; final Map<String, int> _triedTimes = {};
bool shouldTry(String id) { bool canTry(String id) {
final maxCount = locator<SettingStore>().maxRetryCount.fetch()!; final maxCount = locator<SettingStore>().maxRetryCount.fetch()!;
if (maxCount <= 0) { if (maxCount <= 0) {
return true; return true;
@@ -13,10 +13,13 @@ class TryLimiter {
if (times >= maxCount) { if (times >= maxCount) {
return false; return false;
} }
_triedTimes[id] = times + 1;
return true; return true;
} }
void inc(String sid) {
_triedTimes[sid] = (_triedTimes[sid] ?? 0) + 1;
}
void reset(String id) { void reset(String id) {
_triedTimes[id] = 0; _triedTimes[id] = 0;
} }

View File

@@ -1,6 +1,6 @@
import 'package:toolbox/core/provider_base.dart'; import 'package:flutter/material.dart';
class AppProvider extends BusyProvider { class AppProvider extends ChangeNotifier {
int? _newestBuild; int? _newestBuild;
int? get newestBuild => _newestBuild; int? get newestBuild => _newestBuild;

View File

@@ -2,16 +2,16 @@ import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:dartssh2/dartssh2.dart'; import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:toolbox/core/extension/ssh_client.dart'; import 'package:toolbox/core/extension/ssh_client.dart';
import 'package:toolbox/core/extension/stringx.dart'; import 'package:toolbox/core/extension/stringx.dart';
import 'package:toolbox/core/extension/uint8list.dart'; import 'package:toolbox/core/extension/uint8list.dart';
import 'package:toolbox/core/provider_base.dart';
import 'package:toolbox/data/model/pkg/manager.dart'; import 'package:toolbox/data/model/pkg/manager.dart';
import 'package:toolbox/data/model/pkg/upgrade_info.dart'; import 'package:toolbox/data/model/pkg/upgrade_info.dart';
import 'package:toolbox/data/model/server/dist.dart'; import 'package:toolbox/data/model/server/dist.dart';
class PkgProvider extends BusyProvider { class PkgProvider extends ChangeNotifier {
final logger = Logger('PKG'); final logger = Logger('PKG');
SSHClient? client; SSHClient? client;

View File

@@ -1,9 +1,9 @@
import 'package:toolbox/core/provider_base.dart'; import 'package:flutter/material.dart';
import 'package:toolbox/data/model/server/private_key_info.dart'; import 'package:toolbox/data/model/server/private_key_info.dart';
import 'package:toolbox/data/store/private_key.dart'; import 'package:toolbox/data/store/private_key.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
class PrivateKeyProvider extends BusyProvider { class PrivateKeyProvider extends ChangeNotifier {
List<PrivateKeyInfo> get pkis => _pkis; List<PrivateKeyInfo> get pkis => _pkis;
final _store = locator<PrivateKeyStore>(); final _store = locator<PrivateKeyStore>();
late List<PrivateKeyInfo> _pkis; late List<PrivateKeyInfo> _pkis;

View File

@@ -6,7 +6,6 @@ import 'package:toolbox/data/model/app/shell_func.dart';
import '../../core/extension/order.dart'; import '../../core/extension/order.dart';
import '../../core/extension/uint8list.dart'; import '../../core/extension/uint8list.dart';
import '../../core/provider_base.dart';
import '../../core/utils/server.dart'; import '../../core/utils/server.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../model/server/server.dart'; import '../model/server/server.dart';
@@ -21,7 +20,7 @@ import '../store/setting.dart';
typedef ServersMap = Map<String, Server>; typedef ServersMap = Map<String, Server>;
class ServerProvider extends BusyProvider { class ServerProvider extends ChangeNotifier {
final ServersMap _servers = {}; final ServersMap _servers = {};
ServersMap get servers => _servers; ServersMap get servers => _servers;
final Order<String> _serverOrder = []; final Order<String> _serverOrder = [];
@@ -39,7 +38,6 @@ class ServerProvider extends BusyProvider {
final _settingStore = locator<SettingStore>(); final _settingStore = locator<SettingStore>();
Future<void> loadLocalData() async { Future<void> loadLocalData() async {
setBusyState(true);
final spis = _serverStore.fetch(); final spis = _serverStore.fetch();
for (final spi in spis) { for (final spi in spis) {
_servers[spi.id] = genServer(spi); _servers[spi.id] = genServer(spi);
@@ -56,7 +54,6 @@ class ServerProvider extends BusyProvider {
} }
_settingStore.serverOrder.put(_serverOrder); _settingStore.serverOrder.put(_serverOrder);
_updateTags(); _updateTags();
setBusyState(false);
notifyListeners(); notifyListeners();
} }
@@ -204,77 +201,96 @@ class ServerProvider extends BusyProvider {
} }
} }
void _setServerState(Server s, ServerState ss) {
s.state = ss;
notifyListeners();
}
Future<void> _getData(ServerPrivateInfo spi) async { Future<void> _getData(ServerPrivateInfo spi) async {
final sid = spi.id; final sid = spi.id;
final s = _servers[sid]; final s = _servers[sid];
if (s == null) return; if (s == null) return;
var raw = ''; if (!_limiter.canTry(sid)) {
var segments = <String>[]; if (s.state != ServerState.failed) {
_setServerState(s, ServerState.failed);
try { }
final state = s.state;
if (state.shouldConnect) {
if (!_limiter.shouldTry(sid)) {
s.state = ServerState.failed;
notifyListeners();
return; return;
} }
s.state = ServerState.connecting;
notifyListeners();
// try to connect if (s.state.shouldConnect) {
_setServerState(s, ServerState.connecting);
final time1 = DateTime.now(); final time1 = DateTime.now();
try {
s.client = await genClient(spi); s.client = await genClient(spi);
} catch (e) {
_limiter.inc(sid);
s.status.failedInfo = e.toString();
_setServerState(s, ServerState.failed);
_logger.warning('Connect to $sid failed', e);
return;
}
final time2 = DateTime.now(); final time2 = DateTime.now();
final spentTime = time2.difference(time1).inMilliseconds; final spentTime = time2.difference(time1).inMilliseconds;
_logger.info('Connected to $sid in $spentTime ms.'); _logger.info('Connected to $sid in $spentTime ms.');
// after connected _setServerState(s, ServerState.connected);
s.state = ServerState.connected;
notifyListeners();
// write script to server
final writeResult = await s.client!.run(installShellCmd).string;
// if write failed try {
if (writeResult.isNotEmpty) { final writeResult = await s.client?.run(installShellCmd).string;
throw Exception(writeResult); if (writeResult == null || writeResult.isNotEmpty) {
} _limiter.inc(sid);
// reset try times if connected successfully s.status.failedInfo = writeResult;
_limiter.reset(sid); _setServerState(s, ServerState.failed);
}
if (s.client == null) return;
// run script to get server status
raw = await s.client!.run(AppShellFuncType.status.exec).string;
segments = raw.split(seperator).map((e) => e.trim()).toList();
if (raw.isEmpty || segments.length != StatusCmdType.values.length) {
s.state = ServerState.failed;
if (s.status.failedInfo?.isEmpty ?? true) {
s.status.failedInfo = 'Seperate segments failed, raw:\n$raw';
}
return; return;
} }
} catch (e) { } catch (e) {
s.state = ServerState.failed; _limiter.inc(sid);
s.status.failedInfo = e.toString(); s.status.failedInfo = e.toString();
rethrow; _setServerState(s, ServerState.failed);
} finally { _logger.warning('Write script to $sid failed', e);
notifyListeners(); return;
}
}
if (s.client == null) return;
if (s.state != ServerState.finished) {
_setServerState(s, ServerState.loading);
}
final raw = await s.client?.run(AppShellFuncType.status.exec).string;
final segments = raw?.split(seperator).map((e) => e.trim()).toList();
if (raw == null ||
raw.isEmpty ||
segments == null ||
segments.length != StatusCmdType.values.length) {
_limiter.inc(sid);
s.status.failedInfo = 'Seperate segments failed, raw:\n$raw';
_setServerState(s, ServerState.failed);
return;
} }
try { try {
final req = ServerStatusUpdateReq(s.status, segments); final req = ServerStatusUpdateReq(s.status, segments);
s.status = await compute(getStatus, req); s.status = await compute(getStatus, req);
// Comment for debug } catch (e, trace) {
// s.status = await getStatus(req); _limiter.inc(sid);
} catch (e) {
s.state = ServerState.failed;
s.status.failedInfo = 'Parse failed: $e\n\n$raw'; s.status.failedInfo = 'Parse failed: $e\n\n$raw';
rethrow; _setServerState(s, ServerState.failed);
} finally { _logger.warning('Parse failed', e, trace);
notifyListeners(); return;
} }
if (s.state != ServerState.finished) {
_setServerState(s, ServerState.finished);
}
// reset try times only after prepared successfully
_limiter.reset(sid);
} }
Future<String?> runSnippets(String id, List<Snippet> snippets) async { Future<String?> runSnippets(String id, List<Snippet> snippets) async {

View File

@@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:toolbox/core/provider_base.dart'; import 'package:flutter/material.dart';
import '../model/sftp/req.dart'; import '../model/sftp/req.dart';
class SftpProvider extends ProviderBase { class SftpProvider extends ChangeNotifier {
final List<SftpReqStatus> _status = []; final List<SftpReqStatus> _status = [];
List<SftpReqStatus> get status => _status; List<SftpReqStatus> get status => _status;

View File

@@ -1,6 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:toolbox/core/provider_base.dart'; import 'package:flutter/material.dart';
import 'package:toolbox/data/model/server/snippet.dart'; import 'package:toolbox/data/model/server/snippet.dart';
import 'package:toolbox/data/store/snippet.dart'; import 'package:toolbox/data/store/snippet.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
@@ -8,7 +8,7 @@ import 'package:toolbox/locator.dart';
import '../../core/extension/order.dart'; import '../../core/extension/order.dart';
import '../store/setting.dart'; import '../store/setting.dart';
class SnippetProvider extends BusyProvider { class SnippetProvider extends ChangeNotifier {
late Order<Snippet> _snippets; late Order<Snippet> _snippets;
Order<Snippet> get snippets => _snippets; Order<Snippet> get snippets => _snippets;

View File

@@ -2,8 +2,8 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 454; static const int build = 457;
static const String engine = "3.10.6"; static const String engine = "3.10.6";
static const String buildAt = "2023-08-08 17:50:11.695696"; static const String buildAt = "2023-08-09 23:52:33.375014";
static const int modifications = 3; static const int modifications = 30;
} }

View File

@@ -28,6 +28,7 @@
"close": "Schließen", "close": "Schließen",
"cmd": "Command", "cmd": "Command",
"conn": "Verbindung", "conn": "Verbindung",
"connected": "in Verbindung gebracht",
"containerName": "Container Name", "containerName": "Container Name",
"containerStatus": "Container Status", "containerStatus": "Container Status",
"convert": "Konvertieren", "convert": "Konvertieren",

View File

@@ -28,6 +28,7 @@
"close": "Close", "close": "Close",
"cmd": "Command", "cmd": "Command",
"conn": "Connection", "conn": "Connection",
"connected": "Connected",
"containerName": "Container name", "containerName": "Container name",
"containerStatus": "Container status", "containerStatus": "Container status",
"convert": "Convert", "convert": "Convert",

View File

@@ -28,6 +28,7 @@
"close": "Menutup", "close": "Menutup",
"cmd": "Memerintah", "cmd": "Memerintah",
"conn": "Koneksi", "conn": "Koneksi",
"connected": "Terhubung",
"containerName": "Nama kontainer", "containerName": "Nama kontainer",
"containerStatus": "Status wadah", "containerStatus": "Status wadah",
"convert": "Mengubah", "convert": "Mengubah",

View File

@@ -28,6 +28,7 @@
"close": "关闭", "close": "关闭",
"cmd": "命令", "cmd": "命令",
"conn": "连接", "conn": "连接",
"connected": "已连接",
"containerName": "容器名", "containerName": "容器名",
"containerStatus": "容器状态", "containerStatus": "容器状态",
"convert": "转换", "convert": "转换",

View File

@@ -28,6 +28,7 @@
"close": "關閉", "close": "關閉",
"cmd": "命令", "cmd": "命令",
"conn": "連接", "conn": "連接",
"connected": "已連接",
"containerName": "容器名稱", "containerName": "容器名稱",
"containerStatus": "容器狀態", "containerStatus": "容器狀態",
"convert": "轉換", "convert": "轉換",

View File

@@ -44,8 +44,17 @@ Future<void> initApp() async {
Logger.root.level = Level.ALL; Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) { Logger.root.onRecord.listen((record) {
var str = '[${record.loggerName}][${record.level.name}]: ${record.message}';
if (record.error != null) {
str += '\n${record.error}';
_debug.addMultiline(record.error.toString(), Colors.red);
}
if (record.stackTrace != null) {
str += '\n${record.stackTrace}';
_debug.addMultiline(record.stackTrace.toString(), Colors.white);
}
// ignore: avoid_print // ignore: avoid_print
print('[${record.loggerName}][${record.level.name}]: ${record.message}'); print(str);
}); });
} }
@@ -59,7 +68,7 @@ Future<void> initHive() async {
Hive.registerAdapter(NetViewTypeAdapter()); Hive.registerAdapter(NetViewTypeAdapter());
} }
void runInZone(dynamic Function() body) { void runInZone(void Function() body) {
final zoneSpec = ZoneSpecification( final zoneSpec = ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) { print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, line); parent.print(zone, line);
@@ -80,7 +89,7 @@ void runInZone(dynamic Function() body) {
} }
void onError(Object obj, StackTrace stack) { void onError(Object obj, StackTrace stack) {
Analysis.recordException(obj); Analysis.recordException(stack);
_debug.addMultiline(obj, Colors.red); _debug.addMultiline(obj, Colors.red);
_debug.addMultiline(stack, Colors.white); _debug.addMultiline(stack, Colors.white);
} }

View File

@@ -41,6 +41,13 @@ class _ConvertPageState extends State<ConvertPage>
_s = S.of(context)!; _s = S.of(context)!;
} }
@override
void dispose() {
super.dispose();
_textEditingController.dispose();
_textEditingControllerResult.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);

View File

@@ -41,6 +41,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
void dispose() { void dispose() {
super.dispose(); super.dispose();
_docker.clear(); _docker.clear();
_textController.dispose();
} }
@override @override
@@ -54,8 +55,6 @@ class _DockerManagePageState extends State<DockerManagePage> {
super.initState(); super.initState();
final client = locator<ServerProvider>().servers[widget.spi.id]?.client; final client = locator<ServerProvider>().servers[widget.spi.id]?.client;
if (client == null) { if (client == null) {
showSnackBar(context, Text(_s.noClient));
context.pop();
return; return;
} }
_docker.init(client, widget.spi.user, onPwdRequest, widget.spi.id); _docker.init(client, widget.spi.user, onPwdRequest, widget.spi.id);

View File

@@ -63,6 +63,7 @@ class _FullScreenPageState extends State<FullScreenPage> with AfterLayoutMixin {
void dispose() { void dispose() {
super.dispose(); super.dispose();
_timer.cancel(); _timer.cancel();
_pageController.dispose();
} }
@override @override

View File

@@ -75,6 +75,7 @@ class _HomePageState extends State<HomePage>
super.dispose(); super.dispose();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_serverProvider.closeServer(); _serverProvider.closeServer();
_pageController.dispose();
} }
@override @override

View File

@@ -48,6 +48,13 @@ class _PingPageState extends State<PingPage>
_s = S.of(context)!; _s = S.of(context)!;
} }
@override
void dispose() {
super.dispose();
_textEditingController.dispose();
_results.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);

View File

@@ -45,18 +45,17 @@ class _PkgManagePageState extends State<PkgManagePage>
void dispose() { void dispose() {
super.dispose(); super.dispose();
_pkgProvider.clear(); _pkgProvider.clear();
_textController.dispose();
_scrollController.dispose();
_scrollControllerUpdate.dispose();
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final si = locator<ServerProvider>().servers[widget.spi.id]; final si = locator<ServerProvider>().servers[widget.spi.id];
if (si == null || si.client == null) {
showSnackBar(context, Text(_s.waitConnection));
context.pop();
return;
}
if (si == null) return;
_pkgProvider.init( _pkgProvider.init(
si.client!, si.client!,
si.status.sysVer.dist, si.status.sysVer.dist,

View File

@@ -51,6 +51,17 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
_provider = locator<PrivateKeyProvider>(); _provider = locator<PrivateKeyProvider>();
} }
@override
void dispose() {
super.dispose();
_nameController.dispose();
_keyController.dispose();
_pwdController.dispose();
_nameNode.dispose();
_keyNode.dispose();
_pwdNode.dispose();
}
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();

View File

@@ -41,10 +41,6 @@ class _ProcessPageState extends State<ProcessPage> {
void initState() { void initState() {
super.initState(); super.initState();
_client = _serverProvider.servers[widget.spi.id]?.client; _client = _serverProvider.servers[widget.spi.id]?.client;
if (_client == null) {
showSnackBar(context, Text(_s.noClient));
return;
}
_timer = Timer.periodic(const Duration(seconds: 3), (_) => _refresh()); _timer = Timer.periodic(const Duration(seconds: 3), (_) => _refresh());
} }

View File

@@ -55,6 +55,22 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
_serverProvider = locator<ServerProvider>(); _serverProvider = locator<ServerProvider>();
} }
@override
void dispose() {
super.dispose();
_nameController.dispose();
_ipController.dispose();
_alterUrlController.dispose();
_portController.dispose();
_usernameController.dispose();
_passwordController.dispose();
_nameFocus.dispose();
_ipFocus.dispose();
_alterUrlFocus.dispose();
_portFocus.dispose();
_usernameFocus.dispose();
}
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();

View File

@@ -150,12 +150,12 @@ class _ServerPageState extends State<ServerPage>
return GestureDetector( return GestureDetector(
key: Key(si.spi.id + (_tag ?? '')), key: Key(si.spi.id + (_tag ?? '')),
onTap: () { onTap: () {
if (si.state == ServerState.connected) { if (si.state.canViewDetails) {
AppRoute( AppRoute(
ServerDetailPage(si.spi.id), ServerDetailPage(si.spi.id),
'server detail page', 'server detail page',
).go(context); ).go(context);
} else { } else if (si.status.failedInfo != null) {
_showFailReason(si.status); _showFailReason(si.status);
} }
}, },
@@ -176,7 +176,7 @@ class _ServerPageState extends State<ServerPage>
final rootDisk = findRootDisk(ss.disk); final rootDisk = findRootDisk(ss.disk);
late final List<Widget> children; late final List<Widget> children;
var height = 23.0; var height = 23.0;
if (cs != ServerState.connected) { if (cs != ServerState.finished) {
children = [ children = [
_buildServerCardTitle(ss, cs, spi), _buildServerCardTitle(ss, cs, spi),
]; ];
@@ -286,7 +286,7 @@ class _ServerPageState extends State<ServerPage>
context: context, context: context,
title: Text(_s.error), title: Text(_s.error),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Text(ss.failedInfo!), child: Text(ss.failedInfo ?? _s.unknownError),
), ),
actions: [ actions: [
TextButton( TextButton(
@@ -393,12 +393,16 @@ class _ServerPageState extends State<ServerPage>
switch (cs) { switch (cs) {
case ServerState.disconnected: case ServerState.disconnected:
return _s.disconnected; return _s.disconnected;
case ServerState.connected: case ServerState.finished:
final tempStr = temp == null ? '' : '${temp.toStringAsFixed(1)}°C'; final tempStr = temp == null ? '' : '${temp.toStringAsFixed(1)}°C';
final items = [tempStr, upTime]; final items = [tempStr, upTime];
final str = items.where((element) => element.isNotEmpty).join(' | '); final str = items.where((element) => element.isNotEmpty).join(' | ');
if (str.isEmpty) return _s.serverTabLoading; if (str.isEmpty) return _s.noResult;
return str; return str;
case ServerState.loading:
return _s.serverTabLoading;
case ServerState.connected:
return _s.connected;
case ServerState.connecting: case ServerState.connecting:
return _s.serverTabConnecting; return _s.serverTabConnecting;
case ServerState.failed: case ServerState.failed:
@@ -409,8 +413,6 @@ class _ServerPageState extends State<ServerPage>
return _s.serverTabPlzSave; return _s.serverTabPlzSave;
} }
return failedInfo; return failedInfo;
default:
return _s.serverTabUnkown;
} }
} }

View File

@@ -37,6 +37,14 @@ class _SnippetEditPageState extends State<SnippetEditPage>
_provider = locator<SnippetProvider>(); _provider = locator<SnippetProvider>();
} }
@override
void dispose() {
super.dispose();
_nameController.dispose();
_scriptController.dispose();
_scriptNode.dispose();
}
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();

View File

@@ -68,6 +68,18 @@ class _SSHPageState extends State<SSHPage> {
_initVirtKeys(); _initVirtKeys();
} }
@override
void dispose() {
super.dispose();
_virtKeyLongPressTimer?.cancel();
_terminalController.dispose();
_client?.close();
// ignore: unnecessary_null_comparison
if (_session != null) {
_session.close();
}
}
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
@@ -80,12 +92,6 @@ class _SSHPageState extends State<SSHPage> {
_virtKeysHeight = _media.size.height * 0.043 * _virtKeysList.length; _virtKeysHeight = _media.size.height * 0.043 * _virtKeysList.length;
} }
@override
void dispose() {
_client?.close();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget child = Scaffold( Widget child = Scaffold(

View File

@@ -49,7 +49,6 @@ class SftpPage extends StatefulWidget {
class _SftpPageState extends State<SftpPage> { class _SftpPageState extends State<SftpPage> {
final SftpBrowserStatus _status = SftpBrowserStatus(); final SftpBrowserStatus _status = SftpBrowserStatus();
final ScrollController _scrollController = ScrollController();
final _sftp = locator<SftpProvider>(); final _sftp = locator<SftpProvider>();
@@ -297,7 +296,6 @@ class _SftpPageState extends State<SftpPage> {
key: Key(widget.spi.name + _status.path!.path), key: Key(widget.spi.name + _status.path!.path),
child: ListView.builder( child: ListView.builder(
itemCount: _status.files!.length, itemCount: _status.files!.length,
controller: _scrollController,
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
itemBuilder: (_, index) => _buildItem(_status.files![index]), itemBuilder: (_, index) => _buildItem(_status.files![index]),
), ),
@@ -458,9 +456,7 @@ class _SftpPageState extends State<SftpPage> {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
context.pop(); context.pop();
showLoadingDialog( showLoadingDialog(context);
context
);
final remotePath = _getRemotePath(file); final remotePath = _getRemotePath(file);
try { try {
if (file.attr.isDirectory) { if (file.attr.isDirectory) {

View File

@@ -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 = 454; CURRENT_PROJECT_VERSION = 457;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
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 = 454; CURRENT_PROJECT_VERSION = 457;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
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 = 454; CURRENT_PROJECT_VERSION = 457;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.454; MARKETING_VERSION = 1.0.457;
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;