mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
optimized apt experience
This commit is contained in:
@@ -31,9 +31,9 @@ class AppUpdate {
|
|||||||
});
|
});
|
||||||
AppUpdate.fromJson(Map<String, dynamic> json) {
|
AppUpdate.fromJson(Map<String, dynamic> json) {
|
||||||
newest = json["newest"]?.toInt();
|
newest = json["newest"]?.toInt();
|
||||||
newest = json["macbuild"]?.toInt();
|
macbuild = json["macbuild"]?.toInt();
|
||||||
newest = json["iosbuild"]?.toInt();
|
iosbuild = json["iosbuild"]?.toInt();
|
||||||
newest = json["androidbuild"]?.toInt();
|
androidbuild = json["androidbuild"]?.toInt();
|
||||||
android = json["android"].toString();
|
android = json["android"].toString();
|
||||||
ios = json["ios"].toString();
|
ios = json["ios"].toString();
|
||||||
min = json["min"].toInt();
|
min = json["min"].toInt();
|
||||||
|
|||||||
@@ -1,25 +1,41 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
|
import 'package:logging/logging.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/core/provider_base.dart';
|
||||||
import 'package:toolbox/data/model/apt/upgrade_pkg_info.dart';
|
import 'package:toolbox/data/model/apt/upgrade_pkg_info.dart';
|
||||||
import 'package:toolbox/data/model/distribution.dart';
|
import 'package:toolbox/data/model/distribution.dart';
|
||||||
|
|
||||||
|
typedef PwdRequestFunc = Future<String> Function(String? userName);
|
||||||
|
final pwdRequestWithUserReg = RegExp(r'\[sudo\] password for (.+):');
|
||||||
|
|
||||||
class AptProvider extends BusyProvider {
|
class AptProvider extends BusyProvider {
|
||||||
|
final logger = Logger('AptProvider');
|
||||||
|
|
||||||
SSHClient? client;
|
SSHClient? client;
|
||||||
Distribution? dist;
|
Distribution? dist;
|
||||||
|
Function()? onUpgrade;
|
||||||
|
Function()? onUpdate;
|
||||||
|
PwdRequestFunc? onPasswordRequest;
|
||||||
|
|
||||||
String? whoami;
|
String? whoami;
|
||||||
List<UpgradePkgInfo>? upgradeable;
|
List<UpgradePkgInfo>? upgradeable;
|
||||||
String? error;
|
String? error;
|
||||||
|
String? upgradeLog;
|
||||||
String? updateLog;
|
String? updateLog;
|
||||||
Function()? onUpgrade;
|
String? savedPwd;
|
||||||
|
|
||||||
AptProvider();
|
AptProvider();
|
||||||
|
|
||||||
Future<void> init(
|
Future<void> init(SSHClient client, Distribution dist, Function() onUpgrade,
|
||||||
SSHClient client, Distribution dist, Function() onUpgrade) async {
|
Function() onUpdate, PwdRequestFunc onPasswordRequest) async {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.dist = dist;
|
this.dist = dist;
|
||||||
this.onUpgrade = onUpgrade;
|
this.onUpgrade = onUpgrade;
|
||||||
|
this.onPasswordRequest = onPasswordRequest;
|
||||||
whoami = (await client.run('whoami').string).trim();
|
whoami = (await client.run('whoami').string).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +46,12 @@ class AptProvider extends BusyProvider {
|
|||||||
dist = null;
|
dist = null;
|
||||||
upgradeable = null;
|
upgradeable = null;
|
||||||
error = null;
|
error = null;
|
||||||
updateLog = null;
|
upgradeLog = null;
|
||||||
whoami = null;
|
updateLog = whoami = null;
|
||||||
|
savedPwd = null;
|
||||||
|
onUpgrade = null;
|
||||||
|
onUpdate = null;
|
||||||
|
onPasswordRequest = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> refreshInstalled() async {
|
Future<void> refreshInstalled() async {
|
||||||
@@ -44,38 +64,43 @@ class AptProvider extends BusyProvider {
|
|||||||
try {
|
try {
|
||||||
getUpgradeableList(result);
|
getUpgradeableList(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e.toString();
|
error = '[Server Raw]:\n$result\n[App Error]:\n$e';
|
||||||
} finally {
|
} finally {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void getUpgradeableList(String raw) {
|
void getUpgradeableList(String? raw) {
|
||||||
|
if (raw == null) return;
|
||||||
|
var list = raw.split('\n');
|
||||||
switch (dist) {
|
switch (dist) {
|
||||||
case Distribution.rehl:
|
case Distribution.rehl:
|
||||||
var list = raw.split('\n').sublist(2);
|
list = list.sublist(2);
|
||||||
list.removeWhere((element) => element.isEmpty);
|
list.removeWhere((element) => element.isEmpty);
|
||||||
final endLine = list.lastIndexWhere(
|
final endLine = list.lastIndexWhere(
|
||||||
(element) => element.contains('Obsoleting Packages'));
|
(element) => element.contains('Obsoleting Packages'));
|
||||||
list = list.sublist(0, endLine);
|
list = list.sublist(0, endLine);
|
||||||
upgradeable = list.map((e) => UpgradePkgInfo(e, dist!)).toList();
|
|
||||||
break;
|
break;
|
||||||
case Distribution.debian:
|
|
||||||
case Distribution.unknown:
|
|
||||||
default:
|
default:
|
||||||
final list = raw.split('\n').sublist(4);
|
list = list.sublist(4);
|
||||||
list.removeWhere((element) => element.isEmpty);
|
list.removeWhere((element) => element.isEmpty);
|
||||||
upgradeable = list.map((e) => UpgradePkgInfo(e, dist!)).toList();
|
|
||||||
}
|
}
|
||||||
|
upgradeable = list.map((e) => UpgradePkgInfo(e, dist!)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> _update() async {
|
Future<String> _update() async {
|
||||||
switch (dist) {
|
switch (dist) {
|
||||||
case Distribution.rehl:
|
case Distribution.rehl:
|
||||||
return await client!.run('yum check-update').string;
|
return await client!.run(_wrap('yum check-update')).string;
|
||||||
case Distribution.debian:
|
|
||||||
default:
|
default:
|
||||||
await client!.run('apt update');
|
final session = await client!.execute(_wrap('apt update'));
|
||||||
|
session.stderr.listen((event) => _onPwd(event, session.stdin));
|
||||||
|
session.stdout.listen((event) {
|
||||||
|
updateLog = (updateLog ?? '') + event.string;
|
||||||
|
notifyListeners();
|
||||||
|
onUpdate!();
|
||||||
|
});
|
||||||
|
await session.done;
|
||||||
return await client!.run('apt list --upgradeable').string;
|
return await client!.run('apt list --upgradeable').string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,24 +110,50 @@ class AptProvider extends BusyProvider {
|
|||||||
error = 'No client';
|
error = 'No client';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateLog = null;
|
|
||||||
|
|
||||||
final session = await client!.execute(upgradeCmd);
|
final upgradeCmd = () {
|
||||||
session.stdout.listen((data) {
|
|
||||||
updateLog = (updateLog ?? '') + data.string;
|
|
||||||
notifyListeners();
|
|
||||||
onUpgrade!();
|
|
||||||
});
|
|
||||||
refreshInstalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
String get upgradeCmd {
|
|
||||||
switch (dist) {
|
switch (dist) {
|
||||||
case Distribution.rehl:
|
case Distribution.rehl:
|
||||||
return 'yum upgrade -y';
|
return 'yum upgrade -y';
|
||||||
case Distribution.debian:
|
|
||||||
default:
|
default:
|
||||||
return 'apt upgrade -y';
|
return 'apt upgrade -y';
|
||||||
}
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
final session = await client!.execute(_wrap(upgradeCmd));
|
||||||
|
session.stderr.listen((e) => _onPwd(e, session.stdin));
|
||||||
|
|
||||||
|
session.stdout.listen((data) async {
|
||||||
|
upgradeLog = (upgradeLog ?? '') + data.string;
|
||||||
|
notifyListeners();
|
||||||
|
onUpgrade!();
|
||||||
|
});
|
||||||
|
|
||||||
|
upgradeLog = null;
|
||||||
|
await session.done;
|
||||||
|
refreshInstalled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onPwd(Uint8List e, StreamSink<Uint8List> stdin) async {
|
||||||
|
final event = e.string;
|
||||||
|
if (event.contains('[sudo] password for ')) {
|
||||||
|
final user = pwdRequestWithUserReg.firstMatch(event)?.group(1);
|
||||||
|
logger.info('sudo password request for $user');
|
||||||
|
final pwd = await () async {
|
||||||
|
if (savedPwd != null) return savedPwd!;
|
||||||
|
final inputPwd = await (onPasswordRequest ?? (_) async => '')(user);
|
||||||
|
if (inputPwd.isNotEmpty) {
|
||||||
|
savedPwd = inputPwd;
|
||||||
|
}
|
||||||
|
return inputPwd;
|
||||||
|
}();
|
||||||
|
if (pwd.isEmpty) {
|
||||||
|
logger.info('sudo password request cancelled');
|
||||||
|
}
|
||||||
|
stdin.add(Uint8List.fromList(utf8.encode(pwd + '\n')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _wrap(String cmd) =>
|
||||||
|
'export LANG=en_US.utf-8 && ${isSU ? "" : "sudo -S "}$cmd';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,9 +327,7 @@ class ServerProvider extends BusyProvider {
|
|||||||
void _getMem(ServerPrivateInfo spi, String raw) {
|
void _getMem(ServerPrivateInfo spi, String raw) {
|
||||||
final info = _servers.firstWhere((e) => e.info == spi);
|
final info = _servers.firstWhere((e) => e.info == spi);
|
||||||
for (var item in raw.split('\n')) {
|
for (var item in raw.split('\n')) {
|
||||||
var found = false;
|
|
||||||
if (item.contains(memPrefix)) {
|
if (item.contains(memPrefix)) {
|
||||||
found = true;
|
|
||||||
final split = item.replaceFirst(memPrefix, '').split(' ');
|
final split = item.replaceFirst(memPrefix, '').split(' ');
|
||||||
split.removeWhere((e) => e == '');
|
split.removeWhere((e) => e == '');
|
||||||
final memList = split.map((e) => int.parse(e)).toList();
|
final memList = split.map((e) => int.parse(e)).toList();
|
||||||
@@ -342,7 +340,6 @@ class ServerProvider extends BusyProvider {
|
|||||||
avail: memList[5]);
|
avail: memList[5]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (found) break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,17 +38,19 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static String m7(myGithub) => "\nMade with ❤️ by ${myGithub}";
|
static String m7(myGithub) => "\nMade with ❤️ by ${myGithub}";
|
||||||
|
|
||||||
static String m8(time) => "Spent time: ${time}";
|
static String m8(user) => "Password for ${user}";
|
||||||
|
|
||||||
static String m9(name) => "Are you sure to delete [${name}]?";
|
static String m9(time) => "Spent time: ${time}";
|
||||||
|
|
||||||
static String m10(server) => "Are you sure to delete server [${server}]?";
|
static String m10(name) => "Are you sure to delete [${name}]?";
|
||||||
|
|
||||||
static String m11(build) => "Found: v1.0.${build}, click to update";
|
static String m11(server) => "Are you sure to delete server [${server}]?";
|
||||||
|
|
||||||
static String m12(build) => "Current: v1.0.${build}";
|
static String m12(build) => "Found: v1.0.${build}, click to update";
|
||||||
|
|
||||||
static String m13(build) => "Current: v1.0.${build}, is up to date";
|
static String m13(build) => "Current: v1.0.${build}";
|
||||||
|
|
||||||
|
static String m14(build) => "Current: v1.0.${build}, is up to date";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@@ -140,6 +142,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"pingAvg": MessageLookupByLibrary.simpleMessage("Avg:"),
|
"pingAvg": MessageLookupByLibrary.simpleMessage("Avg:"),
|
||||||
"pingInputIP": MessageLookupByLibrary.simpleMessage(
|
"pingInputIP": MessageLookupByLibrary.simpleMessage(
|
||||||
"Please input a target IP/domain."),
|
"Please input a target IP/domain."),
|
||||||
|
"platformNotSupportUpdate": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Current platform does not support in app update.\nPlease build from source and install it."),
|
||||||
"plzEnterHost":
|
"plzEnterHost":
|
||||||
MessageLookupByLibrary.simpleMessage("Please enter host."),
|
MessageLookupByLibrary.simpleMessage("Please enter host."),
|
||||||
"plzEnterPwd":
|
"plzEnterPwd":
|
||||||
@@ -149,6 +153,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"port": MessageLookupByLibrary.simpleMessage("Port"),
|
"port": MessageLookupByLibrary.simpleMessage("Port"),
|
||||||
"privateKey": MessageLookupByLibrary.simpleMessage("Private Key"),
|
"privateKey": MessageLookupByLibrary.simpleMessage("Private Key"),
|
||||||
"pwd": MessageLookupByLibrary.simpleMessage("Password"),
|
"pwd": MessageLookupByLibrary.simpleMessage("Password"),
|
||||||
|
"pwdForUser": m8,
|
||||||
"rename": MessageLookupByLibrary.simpleMessage("Rename"),
|
"rename": MessageLookupByLibrary.simpleMessage("Rename"),
|
||||||
"reportBugsOnGithubIssue": MessageLookupByLibrary.simpleMessage(
|
"reportBugsOnGithubIssue": MessageLookupByLibrary.simpleMessage(
|
||||||
"Please report bugs on https://github.com/LollipopKit/flutter_server_box/issues"),
|
"Please report bugs on https://github.com/LollipopKit/flutter_server_box/issues"),
|
||||||
@@ -175,11 +180,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"sftpSSHConnected":
|
"sftpSSHConnected":
|
||||||
MessageLookupByLibrary.simpleMessage("SFTP Connected"),
|
MessageLookupByLibrary.simpleMessage("SFTP Connected"),
|
||||||
"snippet": MessageLookupByLibrary.simpleMessage("Snippet"),
|
"snippet": MessageLookupByLibrary.simpleMessage("Snippet"),
|
||||||
"spentTime": m8,
|
"spentTime": m9,
|
||||||
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
||||||
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
|
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
|
||||||
"sureDelete": m9,
|
"sureDelete": m10,
|
||||||
"sureToDeleteServer": m10,
|
"sureToDeleteServer": m11,
|
||||||
"ttl": MessageLookupByLibrary.simpleMessage("TTL"),
|
"ttl": MessageLookupByLibrary.simpleMessage("TTL"),
|
||||||
"unknown": MessageLookupByLibrary.simpleMessage("unknown"),
|
"unknown": MessageLookupByLibrary.simpleMessage("unknown"),
|
||||||
"unknownError": MessageLookupByLibrary.simpleMessage("Unknown error"),
|
"unknownError": MessageLookupByLibrary.simpleMessage("Unknown error"),
|
||||||
@@ -193,9 +198,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"upsideDown": MessageLookupByLibrary.simpleMessage("Upside Down"),
|
"upsideDown": MessageLookupByLibrary.simpleMessage("Upside Down"),
|
||||||
"urlOrJson": MessageLookupByLibrary.simpleMessage("URL or JSON"),
|
"urlOrJson": MessageLookupByLibrary.simpleMessage("URL or JSON"),
|
||||||
"user": MessageLookupByLibrary.simpleMessage("User"),
|
"user": MessageLookupByLibrary.simpleMessage("User"),
|
||||||
"versionHaveUpdate": m11,
|
"versionHaveUpdate": m12,
|
||||||
"versionUnknownUpdate": m12,
|
"versionUnknownUpdate": m13,
|
||||||
"versionUpdated": m13,
|
"versionUpdated": m14,
|
||||||
"waitConnection": MessageLookupByLibrary.simpleMessage(
|
"waitConnection": MessageLookupByLibrary.simpleMessage(
|
||||||
"Please wait for the connection to be established."),
|
"Please wait for the connection to be established."),
|
||||||
"willTakEeffectImmediately":
|
"willTakEeffectImmediately":
|
||||||
|
|||||||
@@ -38,17 +38,19 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static String m7(myGithub) => "\n用❤️制作 by ${myGithub}";
|
static String m7(myGithub) => "\n用❤️制作 by ${myGithub}";
|
||||||
|
|
||||||
static String m8(time) => "耗时: ${time}";
|
static String m8(user) => "用户${user}的密码";
|
||||||
|
|
||||||
static String m9(name) => "确定删除[${name}]?";
|
static String m9(time) => "耗时: ${time}";
|
||||||
|
|
||||||
static String m10(server) => "你确定要删除服务器 [${server}] 吗?";
|
static String m10(name) => "确定删除[${name}]?";
|
||||||
|
|
||||||
static String m11(build) => "找到新版本:v1.0.${build}, 点击更新";
|
static String m11(server) => "你确定要删除服务器 [${server}] 吗?";
|
||||||
|
|
||||||
static String m12(build) => "当前:v1.0.${build}";
|
static String m12(build) => "找到新版本:v1.0.${build}, 点击更新";
|
||||||
|
|
||||||
static String m13(build) => "当前:v1.0.${build}, 已是最新版本";
|
static String m13(build) => "当前:v1.0.${build}";
|
||||||
|
|
||||||
|
static String m14(build) => "当前:v1.0.${build}, 已是最新版本";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@@ -122,12 +124,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"ping": MessageLookupByLibrary.simpleMessage("Ping"),
|
"ping": MessageLookupByLibrary.simpleMessage("Ping"),
|
||||||
"pingAvg": MessageLookupByLibrary.simpleMessage("平均:"),
|
"pingAvg": MessageLookupByLibrary.simpleMessage("平均:"),
|
||||||
"pingInputIP": MessageLookupByLibrary.simpleMessage("请输入目标IP或域名"),
|
"pingInputIP": MessageLookupByLibrary.simpleMessage("请输入目标IP或域名"),
|
||||||
|
"platformNotSupportUpdate":
|
||||||
|
MessageLookupByLibrary.simpleMessage("当前平台不支持更新,请编译最新源码后手动安装"),
|
||||||
"plzEnterHost": MessageLookupByLibrary.simpleMessage("请输入主机"),
|
"plzEnterHost": MessageLookupByLibrary.simpleMessage("请输入主机"),
|
||||||
"plzEnterPwd": MessageLookupByLibrary.simpleMessage("请输入密码"),
|
"plzEnterPwd": MessageLookupByLibrary.simpleMessage("请输入密码"),
|
||||||
"plzSelectKey": MessageLookupByLibrary.simpleMessage("请选择私钥"),
|
"plzSelectKey": MessageLookupByLibrary.simpleMessage("请选择私钥"),
|
||||||
"port": MessageLookupByLibrary.simpleMessage("端口"),
|
"port": MessageLookupByLibrary.simpleMessage("端口"),
|
||||||
"privateKey": MessageLookupByLibrary.simpleMessage("私钥"),
|
"privateKey": MessageLookupByLibrary.simpleMessage("私钥"),
|
||||||
"pwd": MessageLookupByLibrary.simpleMessage("密码"),
|
"pwd": MessageLookupByLibrary.simpleMessage("密码"),
|
||||||
|
"pwdForUser": m8,
|
||||||
"rename": MessageLookupByLibrary.simpleMessage("重命名"),
|
"rename": MessageLookupByLibrary.simpleMessage("重命名"),
|
||||||
"reportBugsOnGithubIssue": MessageLookupByLibrary.simpleMessage(
|
"reportBugsOnGithubIssue": MessageLookupByLibrary.simpleMessage(
|
||||||
"请到 https://github.com/LollipopKit/flutter_server_box/issues 提交问题"),
|
"请到 https://github.com/LollipopKit/flutter_server_box/issues 提交问题"),
|
||||||
@@ -149,11 +154,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"sftpSSHConnected":
|
"sftpSSHConnected":
|
||||||
MessageLookupByLibrary.simpleMessage("SFTP 已连接,即将开始下载..."),
|
MessageLookupByLibrary.simpleMessage("SFTP 已连接,即将开始下载..."),
|
||||||
"snippet": MessageLookupByLibrary.simpleMessage("代码片段"),
|
"snippet": MessageLookupByLibrary.simpleMessage("代码片段"),
|
||||||
"spentTime": m8,
|
"spentTime": m9,
|
||||||
"start": MessageLookupByLibrary.simpleMessage("开始"),
|
"start": MessageLookupByLibrary.simpleMessage("开始"),
|
||||||
"stop": MessageLookupByLibrary.simpleMessage("停止"),
|
"stop": MessageLookupByLibrary.simpleMessage("停止"),
|
||||||
"sureDelete": m9,
|
"sureDelete": m10,
|
||||||
"sureToDeleteServer": m10,
|
"sureToDeleteServer": m11,
|
||||||
"ttl": MessageLookupByLibrary.simpleMessage("缓存时间"),
|
"ttl": MessageLookupByLibrary.simpleMessage("缓存时间"),
|
||||||
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
||||||
"unknownError": MessageLookupByLibrary.simpleMessage("未知错误"),
|
"unknownError": MessageLookupByLibrary.simpleMessage("未知错误"),
|
||||||
@@ -166,9 +171,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"upsideDown": MessageLookupByLibrary.simpleMessage("上下交换"),
|
"upsideDown": MessageLookupByLibrary.simpleMessage("上下交换"),
|
||||||
"urlOrJson": MessageLookupByLibrary.simpleMessage("链接或JSON"),
|
"urlOrJson": MessageLookupByLibrary.simpleMessage("链接或JSON"),
|
||||||
"user": MessageLookupByLibrary.simpleMessage("用户"),
|
"user": MessageLookupByLibrary.simpleMessage("用户"),
|
||||||
"versionHaveUpdate": m11,
|
"versionHaveUpdate": m12,
|
||||||
"versionUnknownUpdate": m12,
|
"versionUnknownUpdate": m13,
|
||||||
"versionUpdated": m13,
|
"versionUpdated": m14,
|
||||||
"waitConnection": MessageLookupByLibrary.simpleMessage("请等待连接建立"),
|
"waitConnection": MessageLookupByLibrary.simpleMessage("请等待连接建立"),
|
||||||
"willTakEeffectImmediately":
|
"willTakEeffectImmediately":
|
||||||
MessageLookupByLibrary.simpleMessage("更改将会立即生效")
|
MessageLookupByLibrary.simpleMessage("更改将会立即生效")
|
||||||
|
|||||||
@@ -1180,6 +1180,26 @@ class S {
|
|||||||
args: [],
|
args: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Password for {user}`
|
||||||
|
String pwdForUser(Object user) {
|
||||||
|
return Intl.message(
|
||||||
|
'Password for $user',
|
||||||
|
name: 'pwdForUser',
|
||||||
|
desc: '',
|
||||||
|
args: [user],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Current platform does not support in app update.\nPlease build from source and install it.`
|
||||||
|
String get platformNotSupportUpdate {
|
||||||
|
return Intl.message(
|
||||||
|
'Current platform does not support in app update.\nPlease build from source and install it.',
|
||||||
|
name: 'platformNotSupportUpdate',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||||
|
|||||||
@@ -111,5 +111,7 @@
|
|||||||
"reportBugsOnGithubIssue": "Please report bugs on https://github.com/LollipopKit/flutter_server_box/issues",
|
"reportBugsOnGithubIssue": "Please report bugs on https://github.com/LollipopKit/flutter_server_box/issues",
|
||||||
"noUpdateAvailable": "No update available",
|
"noUpdateAvailable": "No update available",
|
||||||
"foundNUpdate": "Found {count} update",
|
"foundNUpdate": "Found {count} update",
|
||||||
"updateAll": "Update all"
|
"updateAll": "Update all",
|
||||||
|
"pwdForUser": "Password for {user}",
|
||||||
|
"platformNotSupportUpdate": "Current platform does not support in app update.\nPlease build from source and install it."
|
||||||
}
|
}
|
||||||
@@ -111,5 +111,7 @@
|
|||||||
"reportBugsOnGithubIssue": "请到 https://github.com/LollipopKit/flutter_server_box/issues 提交问题",
|
"reportBugsOnGithubIssue": "请到 https://github.com/LollipopKit/flutter_server_box/issues 提交问题",
|
||||||
"noUpdateAvailable": "没有可用更新",
|
"noUpdateAvailable": "没有可用更新",
|
||||||
"foundNUpdate": "找到 {count} 个更新",
|
"foundNUpdate": "找到 {count} 个更新",
|
||||||
"updateAll": "更新全部"
|
"updateAll": "更新全部",
|
||||||
|
"pwdForUser": "用户{user}的密码",
|
||||||
|
"platformNotSupportUpdate": "当前平台不支持更新,请编译最新源码后手动安装"
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,8 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
late MediaQueryData _media;
|
late MediaQueryData _media;
|
||||||
final greyStyle = const TextStyle(color: Colors.grey);
|
final greyStyle = const TextStyle(color: Colors.grey);
|
||||||
final scrollController = ScrollController();
|
final scrollController = ScrollController();
|
||||||
|
final scrollControllerUpdate = ScrollController();
|
||||||
|
final _aptProvider = locator<AptProvider>();
|
||||||
late S s;
|
late S s;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -34,6 +36,7 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
_media = MediaQuery.of(context);
|
_media = MediaQuery.of(context);
|
||||||
s = S.of(context);
|
s = S.of(context);
|
||||||
|
_aptProvider.refreshInstalled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -53,11 +56,52 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
locator<AptProvider>().init(
|
|
||||||
|
// ignore: prefer_function_declarations_over_variables
|
||||||
|
PwdRequestFunc onPwdRequest = (user) async {
|
||||||
|
final textController = TextEditingController();
|
||||||
|
await showRoundDialog(
|
||||||
|
context,
|
||||||
|
s.pwdForUser(user ?? s.unknown),
|
||||||
|
TextField(
|
||||||
|
controller: textController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: s.pwd,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.cancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (textController.text == '') {
|
||||||
|
showRoundDialog(
|
||||||
|
context, s.attention, Text(s.fieldMustNotEmpty), [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.ok)),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
s.ok,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
return textController.text.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
_aptProvider.init(
|
||||||
si.client!,
|
si.client!,
|
||||||
si.status.sysVer.dist,
|
si.status.sysVer.dist,
|
||||||
() =>
|
() =>
|
||||||
scrollController.jumpTo(scrollController.position.maxScrollExtent));
|
scrollController.jumpTo(scrollController.position.maxScrollExtent),
|
||||||
|
() => scrollControllerUpdate
|
||||||
|
.jumpTo(scrollControllerUpdate.position.maxScrollExtent),
|
||||||
|
onPwdRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -68,10 +112,40 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
title: TwoLineText(up: 'Apt', down: widget.spi.name),
|
title: TwoLineText(up: 'Apt', down: widget.spi.name),
|
||||||
),
|
),
|
||||||
body: Consumer<AptProvider>(builder: (_, apt, __) {
|
body: Consumer<AptProvider>(builder: (_, apt, __) {
|
||||||
if (apt.upgradeable == null) {
|
if (apt.error != null) {
|
||||||
apt.refreshInstalled();
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.redAccent,
|
||||||
|
size: 37,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 37,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
apt.error!,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (apt.updateLog == null && apt.upgradeable == null) {
|
||||||
return centerLoading;
|
return centerLoading;
|
||||||
}
|
}
|
||||||
|
if (apt.updateLog != null && apt.upgradeable == null) {
|
||||||
|
return SizedBox(
|
||||||
|
height: _media.size.height * 0.7,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints.expand(),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(18),
|
||||||
|
controller: scrollControllerUpdate,
|
||||||
|
child: Text(apt.updateLog!),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: const EdgeInsets.all(13),
|
padding: const EdgeInsets.all(13),
|
||||||
children: [
|
children: [
|
||||||
@@ -111,7 +185,7 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: greyStyle,
|
style: greyStyle,
|
||||||
),
|
),
|
||||||
children: apt.updateLog == null
|
children: apt.upgradeLog == null
|
||||||
? [
|
? [
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(s.updateAll),
|
child: Text(s.updateAll),
|
||||||
@@ -121,6 +195,7 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: _media.size.height * 0.73,
|
height: _media.size.height * 0.73,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
|
controller: scrollController,
|
||||||
children: apt.upgradeable!
|
children: apt.upgradeable!
|
||||||
.map((e) => _buildUpdateItem(e, apt))
|
.map((e) => _buildUpdateItem(e, apt))
|
||||||
.toList()),
|
.toList()),
|
||||||
@@ -129,10 +204,13 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
: [
|
: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: _media.size.height * 0.7,
|
height: _media.size.height * 0.7,
|
||||||
child: ListView(
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints.expand(),
|
||||||
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(18),
|
padding: const EdgeInsets.all(18),
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
children: [Text(apt.updateLog!)],
|
child: Text(apt.upgradeLog!),
|
||||||
|
),
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
widget.spi != null
|
widget.spi != null
|
||||||
? IconButton(
|
? IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showRoundDialog(context, 'Attention',
|
showRoundDialog(context, s.attention,
|
||||||
Text(s.sureToDeleteServer(widget.spi!.name)), [
|
Text(s.sureToDeleteServer(widget.spi!.name)), [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|||||||
@@ -90,8 +90,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
const Divider(),
|
const Divider(),
|
||||||
(_path?.path ?? s.loadingFiles).omitStartStr(
|
(_path?.path ?? s.loadingFiles).omitStartStr(
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color: color.isBrightColor ? Colors.black : Colors.white),
|
||||||
color.isBrightColor ? Colors.black : Colors.white),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -689,5 +689,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.15.1 <3.0.0"
|
dart: ">=2.16.0 <3.0.0"
|
||||||
flutter: ">=2.10.0"
|
flutter: ">=2.10.0"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.16.0 <3.0.0"
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
|||||||
Reference in New Issue
Block a user