new & opt.

- new: support suspend and WOL #172
- opt.: `execWithPwd` when cancel
- opt.: extentions
This commit is contained in:
lollipopkit
2023-09-25 18:51:14 +08:00
parent df84aeb8b2
commit 4d06a52e99
45 changed files with 251 additions and 176 deletions

View File

@@ -1298,6 +1298,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 @suspendTip.
///
/// In en, this message translates to:
/// **'The suspend function requires root privileges and systemd support.'**
String get suspendTip;
/// No description provided for @syncTip. /// No description provided for @syncTip.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -640,6 +640,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 suspendTip => 'Die Suspend-Funktion erfordert Root-Rechte und systemd-Unterstützung.';
@override @override
String get syncTip => 'Nach der automatischen Synchronisierung kann es erforderlich sein, die App neu zu starten, damit bestimmte Änderungen wirksam werden.'; String get syncTip => 'Nach der automatischen Synchronisierung kann es erforderlich sein, die App neu zu starten, damit bestimmte Änderungen wirksam werden.';

View File

@@ -640,6 +640,9 @@ class SEn extends S {
return 'Are you sure to delete server [$server]?'; return 'Are you sure to delete server [$server]?';
} }
@override
String get suspendTip => 'The suspend function requires root privileges and systemd support.';
@override @override
String get syncTip => 'After auto sync, a restart may be required for some changes to take effect.'; String get syncTip => 'After auto sync, a restart may be required for some changes to take effect.';

View File

@@ -640,6 +640,9 @@ class SId extends S {
return 'Apakah Anda pasti akan menghapus server [$server]?'; return 'Apakah Anda pasti akan menghapus server [$server]?';
} }
@override
String get suspendTip => 'Fungsi penangguhan memerlukan hak akses root dan dukungan systemd.';
@override @override
String get syncTip => 'Setelah sinkronisasi otomatis, mungkin perlu memulai ulang aplikasi agar perubahan tertentu dapat diterapkan.'; String get syncTip => 'Setelah sinkronisasi otomatis, mungkin perlu memulai ulang aplikasi agar perubahan tertentu dapat diterapkan.';

View File

@@ -640,6 +640,9 @@ class SZh extends S {
return '你确定要删除服务器 [$server] 吗?'; return '你确定要删除服务器 [$server] 吗?';
} }
@override
String get suspendTip => 'suspend 功能需要 root 权限及 systemd 支持。';
@override @override
String get syncTip => '在自动同步后,可能需要重新启动,某些更改才能生效。'; String get syncTip => '在自动同步后,可能需要重新启动,某些更改才能生效。';
@@ -1387,6 +1390,9 @@ class SZhTw extends SZh {
return '你確定要刪除服務器 [$server] 嗎?'; return '你確定要刪除服務器 [$server] 嗎?';
} }
@override
String get suspendTip => 'suspend 功能需要 root 權限及 systemd 支持。';
@override @override
String get syncTip => '在自動同步後,可能需要重新啟動,某些更改才能生效。'; String get syncTip => '在自動同步後,可能需要重新啟動,某些更改才能生效。';

View File

@@ -56,7 +56,7 @@ extension DialogX on BuildContext {
void showSnippetDialog( void showSnippetDialog(
void Function(Snippet s) onSelected, void Function(Snippet s) onSelected,
) { ) {
if (Providers.snippet.snippets.isEmpty) { if (Pros.snippet.snippets.isEmpty) {
showRoundDialog( showRoundDialog(
child: Text(l10n.noSavedSnippet), child: Text(l10n.noSavedSnippet),
actions: [ actions: [
@@ -76,12 +76,12 @@ extension DialogX on BuildContext {
return; return;
} }
var snippet = Providers.snippet.snippets.first; var snippet = Pros.snippet.snippets.first;
showRoundDialog( showRoundDialog(
title: Text(l10n.choose), title: Text(l10n.choose),
child: Picker( child: Picker(
items: Providers.snippet.snippets.map((e) => Text(e.name)).toList(), items: Pros.snippet.snippets.map((e) => Text(e.name)).toList(),
onSelected: (idx) => snippet = Providers.snippet.snippets[idx], onSelected: (idx) => snippet = Pros.snippet.snippets[idx],
), ),
actions: [ actions: [
TextButton( TextButton(

View File

@@ -74,9 +74,11 @@ extension SSHClientX on SSHClient {
if (context == null) return; if (context == null) return;
final pwd = await context.showPwdDialog(user); final pwd = await context.showPwdDialog(user);
if (pwd == null || pwd.isEmpty) { if (pwd == null || pwd.isEmpty) {
return; // Add ctrl + c to exit.
sink.add('\x03'.uint8List);
} else {
sink.add('$pwd\n'.uint8List);
} }
sink.add('$pwd\n'.uint8List);
} }
}, },
onStdout: onStdout, onStdout: onStdout,

View File

@@ -14,52 +14,5 @@ extension StringX on String {
return Color(val); return Color(val);
} }
int get i => int.parse(this);
Uri get uri {
return Uri.parse(this);
}
Widget omitStartStr({
TextStyle? style,
TextOverflow? overflow,
int? maxLines,
}) {
return LayoutBuilder(builder: (context, size) {
bool exceeded = false;
int len = 0;
for (; !exceeded && len < length; len++) {
// Build the textspan
var span = TextSpan(
text: 'A' * 7 + substring(length - len),
style: style ?? Theme.of(context).textTheme.bodyMedium,
);
// Use a textpainter to determine if it will exceed max lines
var tp = TextPainter(
maxLines: maxLines ?? 1,
textDirection: TextDirection.ltr,
text: span,
);
// trigger it to layout
tp.layout(maxWidth: size.maxWidth);
// whether the text overflowed or not
exceeded = tp.didExceedMaxLines;
}
return Text(
(exceeded ? '...' : '') + substring(length - len),
overflow: overflow ?? TextOverflow.fade,
softWrap: false,
maxLines: maxLines ?? 1,
style: style,
);
});
}
String get withLangExport => 'export LANG=en_US.UTF-8 && $this';
Uint8List get uint8List => Uint8List.fromList(utf8.encode(this)); Uint8List get uint8List => Uint8List.fromList(utf8.encode(this));
} }

View File

@@ -39,7 +39,7 @@ Future<void> doUpdate(BuildContext context, {bool force = false}) async {
return; return;
} }
Providers.app.newestBuild = newest; Pros.app.newestBuild = newest;
if (!force && newest <= BuildData.build) { if (!force && newest <= BuildData.build) {
Loggers.app.info('Update ignored: ${BuildData.build} >= $newest'); Loggers.app.info('Update ignored: ${BuildData.build} >= $newest');

View File

@@ -19,10 +19,10 @@ Future<bool> shareFiles(List<String> filePaths) async {
} else { } else {
text = '${filePaths.length} ${l10n.files}'; text = '${filePaths.length} ${l10n.files}';
} }
Providers.app.moveBg = false; Pros.app.moveBg = false;
// ignore: deprecated_member_use // ignore: deprecated_member_use
await Share.shareFiles(filePaths, subject: text); await Share.shareFiles(filePaths, subject: text);
Providers.app.moveBg = true; Pros.app.moveBg = true;
return filePaths.isNotEmpty; return filePaths.isNotEmpty;
} }
@@ -31,9 +31,9 @@ void copy2Clipboard(String text) {
} }
Future<String?> pickOneFile() async { Future<String?> pickOneFile() async {
Providers.app.moveBg = false; Pros.app.moveBg = false;
final result = await FilePicker.platform.pickFiles(type: FileType.any); final result = await FilePicker.platform.pickFiles(type: FileType.any);
Providers.app.moveBg = true; Pros.app.moveBg = true;
return result?.files.single.path; return result?.files.single.path;
} }

View File

@@ -7,11 +7,10 @@ import 'package:toolbox/core/utils/platform/base.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'misc.dart'; import 'misc.dart';
import '../extension/stringx.dart';
import '../extension/uint8list.dart'; import '../extension/uint8list.dart';
Future<bool> openUrl(String url) async { Future<bool> openUrl(String url) async {
return await launchUrl(url.uri, mode: LaunchMode.externalApplication); return await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
} }
void setTransparentNavigationBar(BuildContext context) { void setTransparentNavigationBar(BuildContext context) {

View File

@@ -18,6 +18,7 @@ enum ShellFunc {
process, process,
shutdown, shutdown,
reboot, reboot,
suspend,
; ;
String get flag { String get flag {
@@ -32,6 +33,8 @@ enum ShellFunc {
return 'sd'; return 'sd';
case ShellFunc.reboot: case ShellFunc.reboot:
return 'r'; return 'r';
case ShellFunc.suspend:
return 'sp';
} }
} }
@@ -51,10 +54,12 @@ enum ShellFunc {
return 'ShutDown'; return 'ShutDown';
case ShellFunc.reboot: case ShellFunc.reboot:
return 'Reboot'; return 'Reboot';
case ShellFunc.suspend:
return 'Suspend';
} }
} }
String get cmd { String get _cmd {
switch (this) { switch (this) {
case ShellFunc.status: case ShellFunc.status:
return ''' return '''
@@ -96,6 +101,13 @@ if [ "\$userId" = "0" ]; then
\treboot \treboot
else else
\tsudo -S reboot \tsudo -S reboot
fi''';
case ShellFunc.suspend:
return '''
if [ "\$userId" = "0" ]; then
\tsystemctl suspend
else
\tsudo -S systemctl suspend
fi'''; fi''';
} }
} }
@@ -123,7 +135,7 @@ userId=\$(id -u)
for (final func in values) { for (final func in values) {
sb.write(''' sb.write('''
${func.name}() { ${func.name}() {
${func.cmd.split('\n').map((e) => '\t$e').join('\n')} ${func._cmd.split('\n').map((e) => '\t$e').join('\n')}
} }
'''); ''');

View File

@@ -1,4 +1,3 @@
import '../../../core/extension/stringx.dart';
import '../../res/misc.dart'; import '../../res/misc.dart';
class Conn { class Conn {
@@ -22,10 +21,10 @@ Conn? parseConn(String raw) {
if (idx != '') { if (idx != '') {
final vals = idx.split(Miscs.numReg); final vals = idx.split(Miscs.numReg);
return Conn( return Conn(
maxConn: vals[5].i, maxConn: int.tryParse(vals[5]) ?? 0,
active: vals[6].i, active: int.tryParse(vals[6]) ?? 0,
passive: vals[7].i, passive: int.tryParse(vals[7]) ?? 0,
fail: vals[8].i, fail: int.tryParse(vals[8]) ?? 0,
); );
} }
return null; return null;

View File

@@ -70,7 +70,7 @@ class ServerPrivateInfo {
return data; return data;
} }
Server? get server => Providers.server.pick(spi: this); Server? get server => Pros.server.pick(spi: this);
bool shouldReconnect(ServerPrivateInfo old) { bool shouldReconnect(ServerPrivateInfo old) {
return id != old.id || return id != old.id ||

View File

@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:dartssh2/dartssh2.dart'; import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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/data/model/app/shell_func.dart'; import 'package:toolbox/data/model/app/shell_func.dart';
import 'package:toolbox/data/model/docker/image.dart'; import 'package:toolbox/data/model/docker/image.dart';
import 'package:toolbox/data/model/docker/ps.dart'; import 'package:toolbox/data/model/docker/ps.dart';
@@ -175,9 +174,10 @@ class DockerProvider extends ChangeNotifier {
// judge whether to use DOCKER_HOST // judge whether to use DOCKER_HOST
String _wrap(String cmd) { String _wrap(String cmd) {
final dockerHost = Stores.docker.fetch(hostId!); final dockerHost = Stores.docker.fetch(hostId!);
cmd = 'export LANG=en_US.UTF-8 && $cmd';
if (dockerHost == null || dockerHost.isEmpty) { if (dockerHost == null || dockerHost.isEmpty) {
return cmd.withLangExport; return cmd;
} }
return 'export DOCKER_HOST=$dockerHost && $cmd'.withLangExport; return 'export DOCKER_HOST=$dockerHost && $cmd';
} }
} }

View File

@@ -291,7 +291,7 @@ class ServerProvider extends ChangeNotifier {
final localPath = joinPath(await Paths.doc, 'install.sh'); final localPath = joinPath(await Paths.doc, 'install.sh');
final file = File(localPath); final file = File(localPath);
file.writeAsString(ShellFunc.allScript); file.writeAsString(ShellFunc.allScript);
final sftp = Providers.sftp; final sftp = Pros.sftp;
final completer = Completer(); final completer = Completer();
sftp.add( sftp.add(
SftpReq(spi, installShellPath, localPath, SftpReqType.upload), SftpReq(spi, installShellPath, localPath, SftpReqType.upload),

View File

@@ -31,12 +31,13 @@ class SnippetProvider extends ChangeNotifier {
} }
void _addInternal() { void _addInternal() {
if (!Stores.setting.fTISBM.fetch() || _snippets.isNotEmpty) { if (!Stores.first.iSSBM.fetch() ||
_snippets.any((e) => e.name == installSBM.name)) {
return; return;
} }
_snippets.add(installSBM); _snippets.add(installSBM);
Stores.snippet.put(installSBM); Stores.snippet.put(installSBM);
Stores.setting.fTISBM.put(false); Stores.first.iSSBM.put(false);
} }
void _updateTags() { void _updateTags() {

View File

@@ -7,8 +7,8 @@ import 'package:toolbox/data/provider/sftp.dart';
import 'package:toolbox/data/provider/snippet.dart'; import 'package:toolbox/data/provider/snippet.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
class Providers { class Pros {
const Providers._(); const Pros._();
static final app = locator<AppProvider>(); static final app = locator<AppProvider>();
static final debug = locator<DebugProvider>(); static final debug = locator<DebugProvider>();

View File

@@ -1,4 +1,5 @@
import 'package:toolbox/data/store/docker.dart'; import 'package:toolbox/data/store/docker.dart';
import 'package:toolbox/data/store/first.dart';
import 'package:toolbox/data/store/history.dart'; import 'package:toolbox/data/store/history.dart';
import 'package:toolbox/data/store/private_key.dart'; import 'package:toolbox/data/store/private_key.dart';
import 'package:toolbox/data/store/server.dart'; import 'package:toolbox/data/store/server.dart';
@@ -15,4 +16,5 @@ class Stores {
static final history = locator<HistoryStore>(); static final history = locator<HistoryStore>();
static final key = locator<PrivateKeyStore>(); static final key = locator<PrivateKeyStore>();
static final snippet = locator<SnippetStore>(); static final snippet = locator<SnippetStore>();
static final first = locator<FirstStore>();
} }

10
lib/data/store/first.dart Normal file
View File

@@ -0,0 +1,10 @@
import 'package:toolbox/core/persistant_store.dart';
/// It stores whether is the first time of some.
class FirstStore extends PersistentStore<bool> {
/// Add Snippet `Install ServerBoxMonitor`
late final iSSBM = StoreProperty(box, 'installMonitorSnippet', true);
/// Show tip of suspend
late final showSuspendTip = StoreProperty(box, 'showSuspendTip', true);
}

View File

@@ -205,7 +205,7 @@ class SettingStore extends PersistentStore {
false, false,
); );
/// Only valid on iOS / Android /// Only valid on iOS / Android / Windows
late final useBioAuth = StoreProperty( late final useBioAuth = StoreProperty(
box, box,
'useBioAuth', 'useBioAuth',
@@ -213,15 +213,11 @@ class SettingStore extends PersistentStore {
); );
// Never show these settings for users // Never show these settings for users
// Guide for these settings:
// - key should start with `_` and be shorter as possible
// //
// ------BEGIN------ // ------BEGIN------
/// Version of store db /// Version of store db
late final storeVersion = StoreProperty(box, 'storeVersion', 0); late final storeVersion = StoreProperty(box, 'storeVersion', 0);
/// Whether is first time to add Snippet<Install ServerBoxMonitor>
late final fTISBM = StoreProperty(box, '_fTISBM', true);
// ------END------ // ------END------
} }

View File

@@ -200,6 +200,7 @@
"sureNoPwd": "Bist du sicher, dass du kein Passwort verwenden willst?", "sureNoPwd": "Bist du sicher, dass du kein Passwort verwenden willst?",
"sureStop": "Sind Sie sicher, dass Sie [{item}] stoppen möchten?", "sureStop": "Sind Sie sicher, dass Sie [{item}] stoppen möchten?",
"sureToDeleteServer": "Bist du sicher, dass du [{server}] löschen willst?", "sureToDeleteServer": "Bist du sicher, dass du [{server}] löschen willst?",
"suspendTip": "Die Suspend-Funktion erfordert Root-Rechte und systemd-Unterstützung.",
"syncTip": "Nach der automatischen Synchronisierung kann es erforderlich sein, die App neu zu starten, damit bestimmte Änderungen wirksam werden.", "syncTip": "Nach der automatischen Synchronisierung kann es erforderlich sein, die App neu zu starten, damit bestimmte Änderungen wirksam werden.",
"system": "Systeme", "system": "Systeme",
"tag": "Tags", "tag": "Tags",

View File

@@ -200,6 +200,7 @@
"sureNoPwd": "Are you sure to use no password?", "sureNoPwd": "Are you sure to use no password?",
"sureStop": "Sure to stop [{item}] ?", "sureStop": "Sure to stop [{item}] ?",
"sureToDeleteServer": "Are you sure to delete server [{server}]?", "sureToDeleteServer": "Are you sure to delete server [{server}]?",
"suspendTip": "The suspend function requires root privileges and systemd support.",
"syncTip": "After auto sync, a restart may be required for some changes to take effect.", "syncTip": "After auto sync, a restart may be required for some changes to take effect.",
"system": "System", "system": "System",
"tag": "Tags", "tag": "Tags",

View File

@@ -200,6 +200,7 @@
"sureNoPwd": "Apakah Anda pasti tidak menggunakan kata sandi?", "sureNoPwd": "Apakah Anda pasti tidak menggunakan kata sandi?",
"sureStop": "Anda yakin ingin menghentikan [{item}]?", "sureStop": "Anda yakin ingin menghentikan [{item}]?",
"sureToDeleteServer": "Apakah Anda pasti akan menghapus server [{server}]?", "sureToDeleteServer": "Apakah Anda pasti akan menghapus server [{server}]?",
"suspendTip": "Fungsi penangguhan memerlukan hak akses root dan dukungan systemd.",
"syncTip": "Setelah sinkronisasi otomatis, mungkin perlu memulai ulang aplikasi agar perubahan tertentu dapat diterapkan.", "syncTip": "Setelah sinkronisasi otomatis, mungkin perlu memulai ulang aplikasi agar perubahan tertentu dapat diterapkan.",
"system": "Sistem", "system": "Sistem",
"tag": "Tag", "tag": "Tag",

View File

@@ -200,6 +200,7 @@
"sureNoPwd": "确认使用无密码?", "sureNoPwd": "确认使用无密码?",
"sureStop": "确定要停止 [{item}] 吗?", "sureStop": "确定要停止 [{item}] 吗?",
"sureToDeleteServer": "你确定要删除服务器 [{server}] 吗?", "sureToDeleteServer": "你确定要删除服务器 [{server}] 吗?",
"suspendTip": "suspend 功能需要 root 权限及 systemd 支持。",
"syncTip": "在自动同步后,可能需要重新启动,某些更改才能生效。", "syncTip": "在自动同步后,可能需要重新启动,某些更改才能生效。",
"system": "系统", "system": "系统",
"tag": "标签", "tag": "标签",

View File

@@ -200,6 +200,7 @@
"sureNoPwd": "確認使用無密碼?", "sureNoPwd": "確認使用無密碼?",
"sureStop": "確定要停止 [{item}] 嗎?", "sureStop": "確定要停止 [{item}] 嗎?",
"sureToDeleteServer": "你確定要刪除服務器 [{server}] 嗎?", "sureToDeleteServer": "你確定要刪除服務器 [{server}] 嗎?",
"suspendTip": "suspend 功能需要 root 權限及 systemd 支持。",
"syncTip": "在自動同步後,可能需要重新啟動,某些更改才能生效。", "syncTip": "在自動同步後,可能需要重新啟動,某些更改才能生效。",
"system": "系統", "system": "系統",
"tag": "标签", "tag": "标签",

View File

@@ -1,4 +1,5 @@
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:toolbox/data/store/first.dart';
import 'data/provider/app.dart'; import 'data/provider/app.dart';
import 'data/provider/debug.dart'; import 'data/provider/debug.dart';
@@ -57,6 +58,10 @@ Future<void> _setupLocatorForStores() async {
final history = HistoryStore(); final history = HistoryStore();
await history.init(boxName: 'history'); await history.init(boxName: 'history');
locator.registerSingleton(history); locator.registerSingleton(history);
final first = FirstStore();
await first.init(boxName: 'first');
locator.registerSingleton(first);
} }
Future<void> setupLocator() async { Future<void> setupLocator() async {

View File

@@ -92,8 +92,8 @@ Future<void> initApp() async {
} }
void _setupProviders() { void _setupProviders() {
Providers.snippet.load(); Pros.snippet.load();
Providers.key.load(); Pros.key.load();
} }
Future<void> _initHive() async { Future<void> _initHive() async {
@@ -109,7 +109,7 @@ Future<void> _initHive() async {
void _setupLogger() { void _setupLogger() {
Logger.root.level = Level.ALL; Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) { Logger.root.onRecord.listen((record) {
Providers.debug.addLog(record); Pros.debug.addLog(record);
// ignore: avoid_print // ignore: avoid_print
print(record); print(record);
}); });

View File

@@ -164,7 +164,7 @@ class BackupPage extends StatelessWidget {
backup.restore(); backup.restore();
context.pop(); context.pop();
RebuildNodes.app.rebuild(); RebuildNodes.app.rebuild();
Providers.reload(); Pros.reload();
}, },
child: Text(l10n.ok), child: Text(l10n.ok),
), ),

View File

@@ -37,7 +37,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
Providers.docker.clear(); Pros.docker.clear();
_textController.dispose(); _textController.dispose();
} }
@@ -48,7 +48,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
if (client == null) { if (client == null) {
return; return;
} }
Providers.docker Pros.docker
..init( ..init(
client, client,
widget.spi.user, widget.spi.user,
@@ -70,7 +70,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
IconButton( IconButton(
onPressed: () async { onPressed: () async {
context.showLoadingDialog(); context.showLoadingDialog();
await Providers.docker.refresh(); await Pros.docker.refresh();
context.pop(); context.pop();
}, },
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
@@ -78,8 +78,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
], ],
), ),
body: _buildMain(), body: _buildMain(),
floatingActionButton: floatingActionButton: Pros.docker.error == null ? _buildFAB() : null,
Providers.docker.error == null ? _buildFAB() : null,
); );
}); });
} }
@@ -156,7 +155,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
onPressed: () async { onPressed: () async {
context.pop(); context.pop();
context.showLoadingDialog(); context.showLoadingDialog();
final result = await Providers.docker.run(cmd); final result = await Pros.docker.run(cmd);
context.pop(); context.pop();
if (result != null) { if (result != null) {
context.showSnackBar(result.message ?? l10n.unknownError); context.showSnackBar(result.message ?? l10n.unknownError);
@@ -182,7 +181,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
} }
Widget _buildMain() { Widget _buildMain() {
if (Providers.docker.error != null && Providers.docker.items == null) { if (Pros.docker.error != null && Pros.docker.items == null) {
return SizedBox.expand( return SizedBox.expand(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -193,17 +192,17 @@ class _DockerManagePageState extends State<DockerManagePage> {
size: 37, size: 37,
), ),
const SizedBox(height: 27), const SizedBox(height: 27),
Text(Providers.docker.error?.message ?? l10n.unknownError), Text(Pros.docker.error?.message ?? l10n.unknownError),
const SizedBox(height: 27), const SizedBox(height: 27),
Padding( Padding(
padding: const EdgeInsets.all(17), padding: const EdgeInsets.all(17),
child: _buildSolution(Providers.docker.error!), child: _buildSolution(Pros.docker.error!),
) )
], ],
), ),
); );
} }
if (Providers.docker.items == null || Providers.docker.images == null) { if (Pros.docker.items == null || Pros.docker.images == null) {
return UIs.centerLoading; return UIs.centerLoading;
} }
@@ -225,12 +224,12 @@ class _DockerManagePageState extends State<DockerManagePage> {
ListTile( ListTile(
title: Text(l10n.imagesList), title: Text(l10n.imagesList),
subtitle: Text( subtitle: Text(
l10n.dockerImagesFmt(Providers.docker.images!.length), l10n.dockerImagesFmt(Pros.docker.images!.length),
style: UIs.textGrey, style: UIs.textGrey,
), ),
), ),
]; ];
items.addAll(Providers.docker.images!.map(_buildImageItem)); items.addAll(Pros.docker.images!.map(_buildImageItem));
return Column(children: items); return Column(children: items);
} }
@@ -259,7 +258,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
context.pop(); context.pop();
final result = await Providers.docker.run( final result = await Pros.docker.run(
'docker rmi ${e.id} -f', 'docker rmi ${e.id} -f',
); );
if (result != null) { if (result != null) {
@@ -273,7 +272,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
} }
Widget _buildLoading() { Widget _buildLoading() {
if (Providers.docker.runLog == null) return UIs.placeholder; if (Pros.docker.runLog == null) return UIs.placeholder;
return Padding( return Padding(
padding: const EdgeInsets.all(17), padding: const EdgeInsets.all(17),
child: Column( child: Column(
@@ -282,7 +281,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
UIs.height13, UIs.height13,
Text(Providers.docker.runLog ?? '...'), Text(Pros.docker.runLog ?? '...'),
], ],
), ),
); );
@@ -323,8 +322,8 @@ class _DockerManagePageState extends State<DockerManagePage> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(Providers.docker.edition ?? l10n.unknown), Text(Pros.docker.edition ?? l10n.unknown),
Text(Providers.docker.version ?? l10n.unknown), Text(Pros.docker.version ?? l10n.unknown),
], ],
), ),
); );
@@ -335,12 +334,12 @@ class _DockerManagePageState extends State<DockerManagePage> {
ListTile( ListTile(
title: Text(l10n.containerStatus), title: Text(l10n.containerStatus),
subtitle: Text( subtitle: Text(
_buildPsCardSubtitle(Providers.docker.items!), _buildPsCardSubtitle(Pros.docker.items!),
style: UIs.textGrey, style: UIs.textGrey,
), ),
), ),
]; ];
items.addAll(Providers.docker.items!.map(_buildPsItem)); items.addAll(Pros.docker.items!.map(_buildPsItem));
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: items, children: items,
@@ -372,7 +371,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
onPressed: () async { onPressed: () async {
context.pop(); context.pop();
context.showLoadingDialog(); context.showLoadingDialog();
await Providers.docker.delete(dItem.containerId); await Pros.docker.delete(dItem.containerId);
context.pop(); context.pop();
}, },
child: Text(l10n.ok), child: Text(l10n.ok),
@@ -382,17 +381,17 @@ class _DockerManagePageState extends State<DockerManagePage> {
break; break;
case DockerMenuType.start: case DockerMenuType.start:
context.showLoadingDialog(); context.showLoadingDialog();
await Providers.docker.start(dItem.containerId); await Pros.docker.start(dItem.containerId);
context.pop(); context.pop();
break; break;
case DockerMenuType.stop: case DockerMenuType.stop:
context.showLoadingDialog(); context.showLoadingDialog();
await Providers.docker.stop(dItem.containerId); await Pros.docker.stop(dItem.containerId);
context.pop(); context.pop();
break; break;
case DockerMenuType.restart: case DockerMenuType.restart:
context.showLoadingDialog(); context.showLoadingDialog();
await Providers.docker.restart(dItem.containerId); await Pros.docker.restart(dItem.containerId);
context.pop(); context.pop();
break; break;
case DockerMenuType.logs: case DockerMenuType.logs:
@@ -441,7 +440,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
Widget _buildEditHost() { Widget _buildEditHost() {
final children = <Widget>[]; final children = <Widget>[];
if (Providers.docker.items!.isEmpty && Providers.docker.images!.isEmpty) { if (Pros.docker.items!.isEmpty && Pros.docker.images!.isEmpty) {
children.add(Padding( children.add(Padding(
padding: const EdgeInsets.fromLTRB(17, 17, 17, 0), padding: const EdgeInsets.fromLTRB(17, 17, 17, 0),
child: Text( child: Text(
@@ -484,6 +483,6 @@ class _DockerManagePageState extends State<DockerManagePage> {
void _onSaveDockerHost(String val) { void _onSaveDockerHost(String val) {
context.pop(); context.pop();
Stores.docker.put(widget.spi.id, val.trim()); Stores.docker.put(widget.spi.id, val.trim());
Providers.docker.refresh(); Pros.docker.refresh();
} }
} }

View File

@@ -368,8 +368,8 @@ class _FullScreenPageState extends State<FullScreenPage> with AfterLayoutMixin {
doUpdate(context); doUpdate(context);
} }
await GetIt.I.allReady(); await GetIt.I.allReady();
await Providers.server.load(); await Pros.server.load();
await Providers.server.refreshData(); await Pros.server.refreshData();
if (!Analysis.enabled) { if (!Analysis.enabled) {
await Analysis.init(); await Analysis.init();
} }

View File

@@ -72,7 +72,7 @@ class _HomePageState extends State<HomePage>
void dispose() { void dispose() {
super.dispose(); super.dispose();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
Providers.server.closeServer(); Pros.server.closeServer();
_pageController.dispose(); _pageController.dispose();
} }
@@ -84,20 +84,20 @@ class _HomePageState extends State<HomePage>
switch (state) { switch (state) {
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
_auth(); _auth();
if (!Providers.server.isAutoRefreshOn) { if (!Pros.server.isAutoRefreshOn) {
Providers.server.startAutoRefresh(); Pros.server.startAutoRefresh();
} }
updateHomeWidget(); updateHomeWidget();
break; break;
case AppLifecycleState.paused: case AppLifecycleState.paused:
// Keep running in background on Android device // Keep running in background on Android device
if (isAndroid && Stores.setting.bgRun.fetch()) { if (isAndroid && Stores.setting.bgRun.fetch()) {
if (Providers.app.moveBg) { if (Pros.app.moveBg) {
BgRunMC.moveToBg(); BgRunMC.moveToBg();
} }
} else { } else {
Providers.server.setDisconnected(); Pros.server.setDisconnected();
Providers.server.stopAutoRefresh(); Pros.server.stopAutoRefresh();
} }
break; break;
default: default:
@@ -148,7 +148,7 @@ class _HomePageState extends State<HomePage>
return IconButton( return IconButton(
icon: const Icon(Icons.refresh, size: 23), icon: const Icon(Icons.refresh, size: 23),
tooltip: 'Refresh', tooltip: 'Refresh',
onPressed: () => Providers.server.refreshData(onlyFailed: true), onPressed: () => Pros.server.refreshData(onlyFailed: true),
); );
}, },
), ),
@@ -343,8 +343,8 @@ class _HomePageState extends State<HomePage>
} }
updateHomeWidget(); updateHomeWidget();
await GetIt.I.allReady(); await GetIt.I.allReady();
await Providers.server.load(); await Pros.server.load();
await Providers.server.refreshData(); await Pros.server.refreshData();
if (!Analysis.enabled) { if (!Analysis.enabled) {
Analysis.init(); Analysis.init();
} }

View File

@@ -165,7 +165,7 @@ class _PingPageState extends State<PingPage>
return; return;
} }
if (Providers.server.serverOrder.isEmpty) { if (Pros.server.serverOrder.isEmpty) {
context.showSnackBar(l10n.pingNoServer); context.showSnackBar(l10n.pingNoServer);
return; return;
} }
@@ -176,7 +176,7 @@ class _PingPageState extends State<PingPage>
return; return;
} }
await Future.wait(Providers.server.servers.map((e) async { await Future.wait(Pros.server.servers.map((e) async {
if (e.client == null) { if (e.client == null) {
return; return;
} }

View File

@@ -95,7 +95,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
Providers.key.delete(widget.pki!); Pros.key.delete(widget.pki!);
context.pop(); context.pop();
context.pop(); context.pop();
}, },
@@ -135,9 +135,9 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
final decrypted = await compute(decyptPem, [key, pwd]); final decrypted = await compute(decyptPem, [key, pwd]);
final pki = PrivateKeyInfo(id: name, key: decrypted); final pki = PrivateKeyInfo(id: name, key: decrypted);
if (widget.pki != null) { if (widget.pki != null) {
Providers.key.update(widget.pki!, pki); Pros.key.update(widget.pki!, pki);
} else { } else {
Providers.key.add(pki); Pros.key.add(pki);
} }
} catch (e) { } catch (e) {
context.showSnackBar(e.toString()); context.showSnackBar(e.toString());

View File

@@ -58,7 +58,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
if (widget.spi?.pubKeyId == null) { if (widget.spi?.pubKeyId == null) {
_passwordController.text = widget.spi?.pwd ?? ''; _passwordController.text = widget.spi?.pwd ?? '';
} else { } else {
_keyIdx.value = Providers.key.pkis.indexWhere( _keyIdx.value = Pros.key.pkis.indexWhere(
(e) => e.id == widget.spi!.pubKeyId, (e) => e.id == widget.spi!.pubKeyId,
); );
} }
@@ -115,7 +115,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
Providers.server.delServer(widget.spi!.id); Pros.server.delServer(widget.spi!.id);
context.pop(); context.pop();
context.pop(true); context.pop(true);
}, },
@@ -183,8 +183,8 @@ class _ServerEditPageState extends State<ServerEditPage> {
TagEditor( TagEditor(
tags: _tags, tags: _tags,
onChanged: (p0) => _tags = p0, onChanged: (p0) => _tags = p0,
allTags: [...Providers.server.tags], allTags: [...Pros.server.tags],
onRenameTag: Providers.server.renameTag, onRenameTag: Pros.server.renameTag,
), ),
_buildAuth(), _buildAuth(),
ListTile( ListTile(
@@ -357,7 +357,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
user: _usernameController.text, user: _usernameController.text,
pwd: _passwordController.text.isEmpty ? null : _passwordController.text, pwd: _passwordController.text.isEmpty ? null : _passwordController.text,
pubKeyId: _keyIdx.value != null pubKeyId: _keyIdx.value != null
? Providers.key.pkis.elementAt(_keyIdx.value!).id ? Pros.key.pkis.elementAt(_keyIdx.value!).id
: null, : null,
tags: _tags, tags: _tags,
alterUrl: _altUrlController.text.isEmpty ? null : _altUrlController.text, alterUrl: _altUrlController.text.isEmpty ? null : _altUrlController.text,
@@ -365,9 +365,9 @@ class _ServerEditPageState extends State<ServerEditPage> {
); );
if (widget.spi == null) { if (widget.spi == null) {
Providers.server.addServer(spi); Pros.server.addServer(spi);
} else { } else {
Providers.server.updateServer(widget.spi!, spi); Pros.server.updateServer(widget.spi!, spi);
} }
context.pop(); context.pop();

View File

@@ -92,8 +92,7 @@ class _ServerPageState extends State<ServerPage>
return child; return child;
} }
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async => onRefresh: () async => await Pros.server.refreshData(onlyFailed: true),
await Providers.server.refreshData(onlyFailed: true),
child: child, child: child,
); );
} }
@@ -245,22 +244,42 @@ class _ServerPageState extends State<ServerPage>
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
IconButton( IconButton(
onPressed: () => srv.client?.execWithPwd( onPressed: () async {
ShellFunc.shutdown.cmd, if (Stores.first.showSuspendTip.fetch()) {
context: context, await context.showRoundDialog(
), title: Text(l10n.attention),
icon: const Icon(Icons.power_off), child: Text(l10n.suspendTip),
);
Stores.first.showSuspendTip.put(false);
}
srv.client?.execWithPwd(
ShellFunc.suspend.exec,
context: context,
);
},
icon: const Icon(Icons.stop),
tooltip: 'Suspend',
), ),
IconButton( IconButton(
onPressed: () => srv.client?.execWithPwd( onPressed: () => srv.client?.execWithPwd(
ShellFunc.reboot.cmd, ShellFunc.shutdown.exec,
context: context, context: context,
), ),
icon: const Icon(Icons.refresh), icon: const Icon(Icons.power_off),
tooltip: 'Shutdown',
),
IconButton(
onPressed: () => srv.client?.execWithPwd(
ShellFunc.reboot.exec,
context: context,
),
icon: const Icon(Icons.restart_alt),
tooltip: 'Reboot',
), ),
IconButton( IconButton(
onPressed: () => AppRoute.serverEdit(spi: srv.spi).go(context), onPressed: () => AppRoute.serverEdit(spi: srv.spi).go(context),
icon: const Icon(Icons.edit), icon: const Icon(Icons.edit),
tooltip: l10n.edit,
) )
], ],
) )
@@ -305,7 +324,7 @@ class _ServerPageState extends State<ServerPage>
Widget? rightCorner; Widget? rightCorner;
if (!(spi.autoConnect ?? true) && cs == ServerState.disconnected) { if (!(spi.autoConnect ?? true) && cs == ServerState.disconnected) {
rightCorner = InkWell( rightCorner = InkWell(
onTap: () => Providers.server.refreshData(spi: spi), onTap: () => Pros.server.refreshData(spi: spi),
child: const Padding( child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 7), padding: EdgeInsets.symmetric(horizontal: 7),
child: Icon( child: Icon(
@@ -456,8 +475,8 @@ class _ServerPageState extends State<ServerPage>
@override @override
Future<void> afterFirstLayout(BuildContext context) async { Future<void> afterFirstLayout(BuildContext context) async {
await GetIt.I.allReady(); await GetIt.I.allReady();
await Providers.server.load(); await Pros.server.load();
Providers.server.startAutoRefresh(); Pros.server.startAutoRefresh();
} }
List<String> _filterServers(ServerProvider pro) => pro.serverOrder List<String> _filterServers(ServerProvider pro) => pro.serverOrder

View File

@@ -298,7 +298,7 @@ class _SettingPageState extends State<SettingPage> {
onSelected: (int val) { onSelected: (int val) {
_updateInterval.value = val; _updateInterval.value = val;
_setting.serverStatusUpdateInterval.put(val); _setting.serverStatusUpdateInterval.put(val);
Providers.server.startAutoRefresh(); Pros.server.startAutoRefresh();
if (val == 0) { if (val == 0) {
context.showSnackBar(l10n.updateIntervalEqual0); context.showSnackBar(l10n.updateIntervalEqual0);
} }
@@ -883,7 +883,7 @@ class _SettingPageState extends State<SettingPage> {
child: Text(l10n.sureDelete(e)), child: Text(l10n.sureDelete(e)),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Providers.server.delServer(e), onPressed: () => Pros.server.delServer(e),
child: Text(l10n.ok), child: Text(l10n.ok),
) )
], ],

View File

@@ -26,13 +26,13 @@ class _ServerOrderPageState extends State<ServerOrderPage> {
} }
Widget _buildBody() { Widget _buildBody() {
if (Providers.server.serverOrder.isEmpty) { if (Pros.server.serverOrder.isEmpty) {
return Center(child: Text(l10n.noServerAvailable)); return Center(child: Text(l10n.noServerAvailable));
} }
return ReorderableListView.builder( return ReorderableListView.builder(
footer: const SizedBox(height: 77), footer: const SizedBox(height: 77),
onReorder: (oldIndex, newIndex) => setState(() { onReorder: (oldIndex, newIndex) => setState(() {
Providers.server.serverOrder.move( Pros.server.serverOrder.move(
oldIndex, oldIndex,
newIndex, newIndex,
property: Stores.setting.serverOrder, property: Stores.setting.serverOrder,
@@ -41,13 +41,13 @@ class _ServerOrderPageState extends State<ServerOrderPage> {
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
buildDefaultDragHandles: false, buildDefaultDragHandles: false,
itemBuilder: (_, index) => itemBuilder: (_, index) =>
_buildItem(index, Providers.server.serverOrder[index]), _buildItem(index, Pros.server.serverOrder[index]),
itemCount: Providers.server.serverOrder.length, itemCount: Pros.server.serverOrder.length,
); );
} }
Widget _buildItem(int index, String id) { Widget _buildItem(int index, String id) {
final spi = Providers.server.pick(id: id)?.spi; final spi = Pros.server.pick(id: id)?.spi;
if (spi == null) { if (spi == null) {
return const SizedBox(); return const SizedBox();
} }

View File

@@ -56,7 +56,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
return [ return [
IconButton( IconButton(
onPressed: () { onPressed: () {
Providers.snippet.del(widget.snippet!); Pros.snippet.del(widget.snippet!);
context.pop(); context.pop();
}, },
tooltip: l10n.delete, tooltip: l10n.delete,
@@ -84,9 +84,9 @@ class _SnippetEditPageState extends State<SnippetEditPage>
note: note.isEmpty ? null : note, note: note.isEmpty ? null : note,
); );
if (widget.snippet != null) { if (widget.snippet != null) {
Providers.snippet.update(widget.snippet!, snippet); Pros.snippet.update(widget.snippet!, snippet);
} else { } else {
Providers.snippet.add(snippet); Pros.snippet.add(snippet);
} }
context.pop(); context.pop();
}, },
@@ -118,9 +118,9 @@ class _SnippetEditPageState extends State<SnippetEditPage>
onChanged: (p0) => setState(() { onChanged: (p0) => setState(() {
_tags = p0; _tags = p0;
}), }),
allTags: [...Providers.server.tags], allTags: [...Pros.server.tags],
onRenameTag: (old, n) => setState(() { onRenameTag: (old, n) => setState(() {
Providers.server.renameTag(old, n); Pros.server.renameTag(old, n);
}), }),
), ),
Input( Input(

View File

@@ -130,15 +130,15 @@ class _SnippetListPageState extends State<SnippetListPage> {
final servers = await showDialog<List<Server>>( final servers = await showDialog<List<Server>>(
context: context, context: context,
builder: (_) => TagPicker<Server>( builder: (_) => TagPicker<Server>(
items: Providers.server.servers.toList(), items: Pros.server.servers.toList(),
tags: Providers.server.tags.toSet(), tags: Pros.server.tags.toSet(),
), ),
); );
if (servers == null) { if (servers == null) {
return; return;
} }
final ids = servers.map((e) => e.spi.id).toList(); final ids = servers.map((e) => e.spi.id).toList();
final results = await Providers.server.runSnippetsMulti(ids, [snippet]); final results = await Pros.server.runSnippetsMulti(ids, [snippet]);
if (results.isNotEmpty) { if (results.isNotEmpty) {
// SERVER_NAME: RESULT // SERVER_NAME: RESULT
final result = Map.fromIterables( final result = Map.fromIterables(

View File

@@ -9,11 +9,11 @@ import 'package:toolbox/data/model/sftp/req.dart';
import 'package:toolbox/data/res/misc.dart'; import 'package:toolbox/data/res/misc.dart';
import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/provider.dart';
import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/omit_start_text.dart';
import 'package:toolbox/view/widget/picker.dart'; import 'package:toolbox/view/widget/picker.dart';
import 'package:toolbox/view/widget/round_rect_card.dart'; import 'package:toolbox/view/widget/round_rect_card.dart';
import '../../../core/extension/numx.dart'; import '../../../core/extension/numx.dart';
import '../../../core/extension/stringx.dart';
import '../../../core/route.dart'; import '../../../core/route.dart';
import '../../../core/utils/misc.dart'; import '../../../core/utils/misc.dart';
import '../../../data/model/app/path_with_prefix.dart'; import '../../../data/model/app/path_with_prefix.dart';
@@ -89,7 +89,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
(_path?.path ?? l10n.loadingFiles).omitStartStr(), OmitStartText(_path?.path ?? l10n.loadingFiles),
_buildBtns(), _buildBtns(),
], ],
), ),
@@ -275,7 +275,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
title: Text(l10n.upload), title: Text(l10n.upload),
onTap: () async { onTap: () async {
context.pop(); context.pop();
final ids = Providers.server.serverOrder; final ids = Pros.server.serverOrder;
var idx = 0; var idx = 0;
await context.showRoundDialog( await context.showRoundDialog(
title: Text(l10n.server), title: Text(l10n.server),
@@ -289,7 +289,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
], ],
); );
final id = ids[idx]; final id = ids[idx];
final spi = Providers.server.pick(id: id)?.spi; final spi = Pros.server.pick(id: id)?.spi;
if (spi == null) { if (spi == null) {
return; return;
} }
@@ -300,7 +300,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
if (remotePath == null) { if (remotePath == null) {
return; return;
} }
Providers.sftp.add(SftpReq( Pros.sftp.add(SftpReq(
spi, spi,
'$remotePath/$fileName', '$remotePath/$fileName',
file.absolute.path, file.absolute.path,

View File

@@ -13,10 +13,10 @@ import 'package:toolbox/data/res/logger.dart';
import 'package:toolbox/data/res/misc.dart'; import 'package:toolbox/data/res/misc.dart';
import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/provider.dart';
import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/data/res/store.dart';
import 'package:toolbox/view/widget/omit_start_text.dart';
import 'package:toolbox/view/widget/round_rect_card.dart'; import 'package:toolbox/view/widget/round_rect_card.dart';
import '../../../core/extension/numx.dart'; import '../../../core/extension/numx.dart';
import '../../../core/extension/stringx.dart';
import '../../../core/route.dart'; import '../../../core/route.dart';
import '../../../core/utils/misc.dart'; import '../../../core/utils/misc.dart';
import '../../../data/model/server/server_private_info.dart'; import '../../../data/model/server/server_private_info.dart';
@@ -113,7 +113,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
(_status.path?.path ?? l10n.loadingFiles).omitStartStr(), OmitStartText(_status.path?.path ?? l10n.loadingFiles),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: children, children: children,
@@ -162,7 +162,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
context.showSnackBar('remote path is null'); context.showSnackBar('remote path is null');
return; return;
} }
Providers.sftp.add( Pros.sftp.add(
SftpReq( SftpReq(
widget.spi, widget.spi,
'$remotePath/${path.split('/').last}', '$remotePath/${path.split('/').last}',
@@ -362,14 +362,14 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
localPath, localPath,
SftpReqType.download, SftpReqType.download,
); );
Providers.sftp.add(req, completer: completer); Pros.sftp.add(req, completer: completer);
context.showLoadingDialog(); context.showLoadingDialog();
await completer.future; await completer.future;
context.pop(); context.pop();
final result = await AppRoute.editor(path: localPath).go<bool>(context); final result = await AppRoute.editor(path: localPath).go<bool>(context);
if (result != null && result) { if (result != null && result) {
Providers.sftp Pros.sftp
.add(SftpReq(req.spi, remotePath, localPath, SftpReqType.upload)); .add(SftpReq(req.spi, remotePath, localPath, SftpReqType.upload));
context.showSnackBar(l10n.added2List); context.showSnackBar(l10n.added2List);
} }
@@ -389,7 +389,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
context.pop(); context.pop();
final remotePath = _getRemotePath(name); final remotePath = _getRemotePath(name);
Providers.sftp.add( Pros.sftp.add(
SftpReq( SftpReq(
widget.spi, widget.spi,
remotePath, remotePath,

View File

@@ -141,7 +141,7 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
Providers.sftp.cancel(id); Pros.sftp.cancel(id);
context.pop(); context.pop();
}, },
child: Text(l10n.ok), child: Text(l10n.ok),

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
class OmitStartText extends StatelessWidget {
final String text;
final int? maxLines;
final TextStyle? style;
final TextOverflow? overflow;
const OmitStartText(
this.text, {
Key? key,
this.maxLines,
this.style,
this.overflow,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, size) {
bool exceeded = false;
int len = 0;
for (; !exceeded && len < text.length; len++) {
// Build the textspan
var span = TextSpan(
text: 'A' * 7 + text.substring(text.length - len),
style: style ?? Theme.of(context).textTheme.bodyMedium,
);
// Use a textpainter to determine if it will exceed max lines
var tp = TextPainter(
maxLines: maxLines ?? 1,
textDirection: TextDirection.ltr,
text: span,
);
// trigger it to layout
tp.layout(maxWidth: size.maxWidth);
// whether the text overflowed or not
exceeded = tp.didExceedMaxLines;
}
return Text(
(exceeded ? '...' : '') + text.substring(text.length - len),
overflow: overflow ?? TextOverflow.fade,
softWrap: false,
maxLines: maxLines ?? 1,
style: style,
);
});
}
}

View File

@@ -99,14 +99,14 @@ void _onTapMoreBtns(
final snippets = await showDialog<List<Snippet>>( final snippets = await showDialog<List<Snippet>>(
context: context, context: context,
builder: (_) => TagPicker<Snippet>( builder: (_) => TagPicker<Snippet>(
items: Providers.snippet.snippets, items: Pros.snippet.snippets,
tags: Providers.server.tags.toSet(), tags: Pros.server.tags.toSet(),
), ),
); );
if (snippets == null) { if (snippets == null) {
return; return;
} }
final result = await Providers.server.runSnippets(spi.id, snippets); final result = await Pros.server.runSnippets(spi.id, snippets);
if (result != null && result.isNotEmpty) { if (result != null && result.isNotEmpty) {
context.showRoundDialog( context.showRoundDialog(
title: Text(l10n.result), title: Text(l10n.result),
@@ -188,7 +188,7 @@ Future<void> _gotoSSH(
} }
bool _checkClient(BuildContext context, String id) { bool _checkClient(BuildContext context, String id) {
final server = Providers.server.pick(id: id); final server = Pros.server.pick(id: id);
if (server == null || server.client == null) { if (server == null || server.client == null) {
context.showSnackBar(l10n.waitConnection); context.showSnackBar(l10n.waitConnection);
return false; return false;