This commit is contained in:
lollipopkit
2023-09-23 10:54:42 +08:00
parent f2981c5b15
commit 5a9fd74470
22 changed files with 117 additions and 136 deletions

View File

@@ -586,7 +586,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -596,7 +596,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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";
@@ -720,7 +720,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -730,7 +730,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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";
@@ -748,7 +748,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -758,7 +758,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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";
@@ -779,7 +779,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -792,7 +792,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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;
@@ -818,7 +818,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -831,7 +831,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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)";
@@ -854,7 +854,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -867,7 +867,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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)";
@@ -890,7 +890,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -902,7 +902,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -931,7 +931,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -943,7 +943,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox; PRODUCT_NAME = ServerBox;
@@ -969,7 +969,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 = 569; CURRENT_PROJECT_VERSION = 570;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -981,7 +981,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox; PRODUCT_NAME = ServerBox;

View File

@@ -135,7 +135,7 @@ class AppRoute {
key: key, key: key,
spi: spi, spi: spi,
initPath: initPath, initPath: initPath,
selectPath: isSelect, isSelect: isSelect,
), ),
'sftp'); 'sftp');
} }

View File

@@ -1,15 +1,13 @@
import 'dart:io'; import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:plain_notification_token/plain_notification_token.dart'; import 'package:plain_notification_token/plain_notification_token.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:toolbox/core/extension/context/locale.dart'; import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/provider.dart';
Future<bool> shareFiles(BuildContext context, List<String> filePaths) async { Future<bool> shareFiles(List<String> filePaths) async {
for (final filePath in filePaths) { for (final filePath in filePaths) {
if (!await File(filePath).exists()) { if (!await File(filePath).exists()) {
return false; return false;
@@ -23,7 +21,7 @@ Future<bool> shareFiles(BuildContext context, List<String> filePaths) async {
} }
Providers.app.moveBg = false; Providers.app.moveBg = false;
// ignore: deprecated_member_use // ignore: deprecated_member_use
await Share.shareFiles(filePaths, subject: 'ServerBox -> $text'); await Share.shareFiles(filePaths, subject: text);
Providers.app.moveBg = true; Providers.app.moveBg = true;
return filePaths.isNotEmpty; return filePaths.isNotEmpty;
} }
@@ -59,23 +57,7 @@ String? getFileName(String? path) {
return path.split('/').last; return path.split('/').last;
} }
/// Return fmt: 2021-01-01 00:00:00
String getTime(int? unixMill) {
return DateTime.fromMillisecondsSinceEpoch((unixMill ?? 0) * 1000)
.toString()
.replaceFirst('.000', '');
}
/// Join two path with `/` /// Join two path with `/`
String pathJoin(String path1, String path2) { String pathJoin(String path1, String path2) {
return path1 + (path1.endsWith('/') ? '' : '/') + path2; return path1 + (path1.endsWith('/') ? '' : '/') + path2;
} }
Future<String?> getFileSha256(String path) async {
final file = File(path);
if (!(await file.exists())) {
return null;
}
final digest = await sha256.bind(file.openRead()).first;
return digest.toString();
}

View File

@@ -50,7 +50,7 @@ class DockerProvider extends ChangeNotifier {
Future<void> refresh() async { Future<void> refresh() async {
var raw = ''; var raw = '';
await client!.execWithPwd( await client!.execWithPwd(
AppShellFuncType.docker.exec, ShellFunc.docker.exec,
context: context, context: context,
onStdout: (data, _) => raw = '$raw$data', onStdout: (data, _) => raw = '$raw$data',
); );

View File

@@ -2,9 +2,9 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 569; static const int build = 570;
static const String engine = "3.13.4"; static const String engine = "3.13.5";
static const String buildAt = "2023-09-21 20:09:12"; static const String buildAt = "2023-09-21 10:20:10";
static const int modifications = 2; static const int modifications = 4;
static const int script = 16; static const int script = 17;
} }

View File

@@ -64,7 +64,7 @@ class BackupPage extends StatelessWidget {
Icons.save, Icons.save,
() async { () async {
await Backup.backup(); await Backup.backup();
await shareFiles(context, [await Paths.bak]); await shareFiles([await Paths.bak]);
}, },
) )
], ],

View File

@@ -44,7 +44,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final client = widget.spi.findServer?.client; final client = widget.spi.server?.client;
if (client == null) { if (client == null) {
return; return;
} }

View File

@@ -136,10 +136,9 @@ class _FullScreenPageState extends State<FullScreenPage> with AfterLayoutMixin {
} }
return PageView.builder( return PageView.builder(
controller: _pageController, controller: _pageController,
itemCount: pro.servers.length, itemCount: pro.serverOrder.length,
itemBuilder: (_, idx) { itemBuilder: (_, idx) {
final id = pro.serverOrder[idx]; final s = pro.pick(id: pro.serverOrder[idx]);
final s = pro.servers[id];
if (s == null) { if (s == null) {
return Center(child: Text(l10n.noClient)); return Center(child: Text(l10n.noClient));
} }

View File

@@ -166,7 +166,7 @@ class _PingPageState extends State<PingPage>
return; return;
} }
if (Providers.server.servers.isEmpty) { if (Providers.server.serverOrder.isEmpty) {
context.showSnackBar(l10n.pingNoServer); context.showSnackBar(l10n.pingNoServer);
return; return;
} }
@@ -177,7 +177,7 @@ class _PingPageState extends State<PingPage>
return; return;
} }
await Future.wait(Providers.server.servers.values.map((e) async { await Future.wait(Providers.server.servers.map((e) async {
if (e.client == null) { if (e.client == null) {
return; return;
} }
@@ -197,7 +197,7 @@ class _PingPageState extends State<PingPage>
@override @override
Future<FutureOr<void>> afterFirstLayout(BuildContext context) async { Future<FutureOr<void>> afterFirstLayout(BuildContext context) async {
if (Providers.server.servers.isEmpty) { if (Providers.server.serverOrder.isEmpty) {
await Providers.server.loadLocalData(); await Providers.server.loadLocalData();
await Providers.server.refreshData(); await Providers.server.refreshData();
} }

View File

@@ -8,7 +8,6 @@ import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/context/snackbar.dart'; import 'package:toolbox/core/extension/context/snackbar.dart';
import 'package:toolbox/core/extension/uint8list.dart'; import 'package:toolbox/core/extension/uint8list.dart';
import 'package:toolbox/core/utils/misc.dart'; import 'package:toolbox/core/utils/misc.dart';
import 'package:toolbox/data/res/provider.dart';
import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/data/res/store.dart';
import '../../data/model/app/shell_func.dart'; import '../../data/model/app/shell_func.dart';
@@ -45,7 +44,7 @@ class _ProcessPageState extends State<ProcessPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_client = Providers.server.servers[widget.spi.id]?.client; _client = widget.spi.server?.client;
final duration = final duration =
Duration(seconds: Stores.setting.serverStatusUpdateInterval.fetch()); Duration(seconds: Stores.setting.serverStatusUpdateInterval.fetch());
_timer = Timer.periodic(duration, (_) => _refresh()); _timer = Timer.periodic(duration, (_) => _refresh());
@@ -59,7 +58,7 @@ class _ProcessPageState extends State<ProcessPage> {
Future<void> _refresh() async { Future<void> _refresh() async {
if (mounted) { if (mounted) {
final result = await _client?.run(AppShellFuncType.process.exec).string; final result = await _client?.run(ShellFunc.process.exec).string;
if (result == null || result.isEmpty) { if (result == null || result.isEmpty) {
context.showSnackBar(l10n.noResult); context.showSnackBar(l10n.noResult);
return; return;

View File

@@ -68,7 +68,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<ServerProvider>(builder: (_, provider, __) { return Consumer<ServerProvider>(builder: (_, provider, __) {
final s = provider.servers[widget.spi.id]; final s = widget.spi.server;
if (s == null) { if (s == null) {
return Scaffold( return Scaffold(
body: Center( body: Center(

View File

@@ -127,7 +127,7 @@ class _ServerPageState extends State<ServerPage>
if (index == count - 1) return UIs.height77; if (index == count - 1) return UIs.height77;
if (buildTags) index--; if (buildTags) index--;
return _buildEachServerCard(provider.servers[filtered[index]]); return _buildEachServerCard(provider.pick(id: filtered[index]));
}, },
); );
} }
@@ -246,14 +246,14 @@ class _ServerPageState extends State<ServerPage>
children: [ children: [
IconButton( IconButton(
onPressed: () => srv.client?.execWithPwd( onPressed: () => srv.client?.execWithPwd(
AppShellFuncType.shutdown.cmd, ShellFunc.shutdown.cmd,
context: context, context: context,
), ),
icon: const Icon(Icons.power_off), icon: const Icon(Icons.power_off),
), ),
IconButton( IconButton(
onPressed: () => srv.client?.execWithPwd( onPressed: () => srv.client?.execWithPwd(
AppShellFuncType.reboot.cmd, ShellFunc.reboot.cmd,
context: context, context: context,
), ),
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
@@ -456,16 +456,16 @@ 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();
if (Providers.server.servers.isEmpty) { if (Providers.server.serverOrder.isEmpty) {
await Providers.server.loadLocalData(); await Providers.server.loadLocalData();
} }
Providers.server.startAutoRefresh(); Providers.server.startAutoRefresh();
} }
List<String> _filterServers(ServerProvider pro) => pro.serverOrder List<String> _filterServers(ServerProvider pro) => pro.serverOrder
.where((e) => pro.servers.containsKey(e)) .where((e) => pro.serverOrder.contains(e))
.where((e) => .where((e) =>
_tag == null || (pro.servers[e]?.spi.tags?.contains(_tag) ?? false)) _tag == null || (pro.pick(id: e)?.spi.tags?.contains(_tag) ?? false))
.toList(); .toList();
String _getTopRightStr( String _getTopRightStr(

View File

@@ -310,12 +310,15 @@ class _SettingPageState extends State<SettingPage> {
Widget _buildAppColor() { Widget _buildAppColor() {
return ListTile( return ListTile(
trailing: ClipOval( trailing: ClipOval(
child: Container( child: ValueBuilder(
listenable: _selectedColorValue,
build: () => Container(
color: primaryColor, color: primaryColor,
height: 27, height: 27,
width: 27, width: 27,
), ),
), ),
),
title: Text(l10n.primaryColorSeed), title: Text(l10n.primaryColorSeed),
onTap: () async { onTap: () async {
final ctrl = TextEditingController(text: primaryColor.toHex); final ctrl = TextEditingController(text: primaryColor.toHex);
@@ -368,9 +371,11 @@ class _SettingPageState extends State<SettingPage> {
context.showSnackBar(l10n.failed); context.showSnackBar(l10n.failed);
return; return;
} }
// Change [primaryColor] first, then change [_selectedColorValue],
// So the [ValueBuilder] will be triggered with the new value
primaryColor = color;
_selectedColorValue.value = color.value; _selectedColorValue.value = color.value;
_setting.primaryColor.put(_selectedColorValue.value); _setting.primaryColor.put(_selectedColorValue.value);
primaryColor = color;
context.pop(); context.pop();
RebuildNodes.app.rebuild(); RebuildNodes.app.rebuild();
} }

View File

@@ -47,7 +47,7 @@ class _ServerOrderPageState extends State<ServerOrderPage> {
} }
Widget _buildItem(int index, String id) { Widget _buildItem(int index, String id) {
final spi = Providers.server.servers[id]?.spi; final spi = Providers.server.pick(id: id)?.spi;
if (spi == null) { if (spi == null) {
return const SizedBox(); return const SizedBox();
} }

View File

@@ -130,7 +130,7 @@ 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.values.toList(), items: Providers.server.servers.toList(),
tags: Providers.server.tags.toSet(), tags: Providers.server.tags.toSet(),
), ),
); );

View File

@@ -289,7 +289,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
], ],
); );
final id = ids[idx]; final id = ids[idx];
final spi = Providers.server.servers[id]?.spi; final spi = Providers.server.pick(id: id)?.spi;
if (spi == null) { if (spi == null) {
return; return;
} }
@@ -313,7 +313,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
leading: const Icon(Icons.open_in_new), leading: const Icon(Icons.open_in_new),
title: Text(l10n.open), title: Text(l10n.open),
onTap: () { onTap: () {
shareFiles(context, [file.absolute.path]); shareFiles([file.absolute.path]);
}, },
), ),
], ],

View File

@@ -33,12 +33,12 @@ import '../../widget/two_line_text.dart';
class SftpPage extends StatefulWidget { class SftpPage extends StatefulWidget {
final ServerPrivateInfo spi; final ServerPrivateInfo spi;
final String? initPath; final String? initPath;
final bool selectPath; final bool isSelect;
const SftpPage({ const SftpPage({
Key? key, Key? key,
required this.spi, required this.spi,
required this.selectPath, required this.isSelect,
this.initPath, this.initPath,
}) : super(key: key); }) : super(key: key);
@@ -47,20 +47,8 @@ class SftpPage extends StatefulWidget {
} }
class _SftpPageState extends State<SftpPage> with AfterLayoutMixin { class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
final SftpBrowserStatus _status = SftpBrowserStatus(); final _status = SftpBrowserStatus();
late final _client = widget.spi.server?.client;
SSHClient? _client;
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
@override
void initState() {
super.initState();
_client = Providers.server.servers[widget.spi.id]?.client;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -69,9 +57,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
leading: IconButton( leading: IconButton(
icon: const BackButtonIcon(), icon: const BackButtonIcon(),
onPressed: () { onPressed: () {
if (_status.path != null) { _status.path?.update('/');
_status.path!.update('/');
}
context.pop(); context.pop();
}, },
), ),
@@ -102,7 +88,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} }
Widget _buildBottom() { Widget _buildBottom() {
final children = widget.selectPath final children = widget.isSelect
? [ ? [
IconButton( IconButton(
onPressed: () => context.pop(_status.path?.path), onPressed: () => context.pop(_status.path?.path),
@@ -197,11 +183,13 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
ListTile( ListTile(
leading: const Icon(Icons.folder), leading: const Icon(Icons.folder),
title: Text(l10n.createFolder), title: Text(l10n.createFolder),
onTap: () => _mkdir(context)), onTap: _mkdir,
),
ListTile( ListTile(
leading: const Icon(Icons.insert_drive_file), leading: const Icon(Icons.insert_drive_file),
title: Text(l10n.createFile), title: Text(l10n.createFile),
onTap: () => _newFile(context)), onTap: _newFile,
),
], ],
), ),
)), )),
@@ -242,7 +230,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} }
_status.path?.update(p); _status.path?.update(p);
final suc = await _listDir(path: p); final suc = await _listDir();
if (suc && Stores.setting.recordHistory.fetch()) { if (suc && Stores.setting.recordHistory.fetch()) {
Stores.history.sftpPath.add(p); Stores.history.sftpPath.add(p);
} }
@@ -285,7 +273,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
Widget _buildItem(SftpName file) { Widget _buildItem(SftpName file) {
final isDir = file.attr.isDirectory; final isDir = file.attr.isDirectory;
final trailing = Text( final trailing = Text(
'${getTime(file.attr.modifyTime)}\n${file.attr.mode?.str ?? ''}', '${_getTime(file.attr.modifyTime)}\n${file.attr.mode?.str ?? ''}',
style: UIs.textGrey, style: UIs.textGrey,
textAlign: TextAlign.right, textAlign: TextAlign.right,
); );
@@ -302,26 +290,26 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
onTap: () { onTap: () {
if (isDir) { if (isDir) {
_status.path?.update(file.filename); _status.path?.update(file.filename);
_listDir(path: _status.path?.path); _listDir();
} else { } else {
_onItemPress(context, file, true); _onItemPress(file, true);
} }
}, },
onLongPress: () => _onItemPress(context, file, !isDir), onLongPress: () => _onItemPress(file, !isDir),
)); ));
} }
void _onItemPress(BuildContext context, SftpName file, bool notDir) { void _onItemPress(SftpName file, bool notDir) {
final children = [ final children = [
ListTile( ListTile(
leading: const Icon(Icons.delete), leading: const Icon(Icons.delete),
title: Text(l10n.delete), title: Text(l10n.delete),
onTap: () => _delete(context, file), onTap: () => _delete(file),
), ),
ListTile( ListTile(
leading: const Icon(Icons.abc), leading: const Icon(Icons.abc),
title: Text(l10n.rename), title: Text(l10n.rename),
onTap: () => _rename(context, file), onTap: () => _rename(file),
), ),
]; ];
if (notDir) { if (notDir) {
@@ -329,19 +317,19 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
ListTile( ListTile(
leading: const Icon(Icons.edit), leading: const Icon(Icons.edit),
title: Text(l10n.edit), title: Text(l10n.edit),
onTap: () => _edit(context, file), onTap: () => _edit(file),
), ),
ListTile( ListTile(
leading: const Icon(Icons.download), leading: const Icon(Icons.download),
title: Text(l10n.download), title: Text(l10n.download),
onTap: () => _download(context, file), onTap: () => _download(file),
), ),
// Only show decompress option when the file is a compressed file // Only show decompress option when the file is a compressed file
if (_canDecompress(file.filename)) if (_canDecompress(file.filename))
ListTile( ListTile(
leading: const Icon(Icons.folder_zip), leading: const Icon(Icons.folder_zip),
title: Text(l10n.decompress), title: Text(l10n.decompress),
onTap: () => _decompress(context, file), onTap: () => _decompress(file),
), ),
]); ]);
} }
@@ -353,7 +341,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
); );
} }
Future<void> _edit(BuildContext context, SftpName name) async { Future<void> _edit(SftpName name) async {
final size = name.attr.size; final size = name.attr.size;
if (size == null || size > Miscs.editorMaxSize) { if (size == null || size > Miscs.editorMaxSize) {
context.showSnackBar(l10n.fileTooLarge( context.showSnackBar(l10n.fileTooLarge(
@@ -387,7 +375,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} }
} }
void _download(BuildContext context, SftpName name) { void _download(SftpName name) {
context.showRoundDialog( context.showRoundDialog(
title: Text(l10n.attention), title: Text(l10n.attention),
child: Text('${l10n.dl2Local(name.filename)}\n${l10n.keepForeground}'), child: Text('${l10n.dl2Local(name.filename)}\n${l10n.keepForeground}'),
@@ -418,7 +406,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
); );
} }
void _delete(BuildContext context, SftpName file) { void _delete(SftpName file) {
context.pop(); context.pop();
final isDir = file.attr.isDirectory; final isDir = file.attr.isDirectory;
final useRmrf = Stores.setting.sftpRmrfDir.fetch(); final useRmrf = Stores.setting.sftpRmrfDir.fetch();
@@ -469,7 +457,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
); );
} }
void _mkdir(BuildContext context) { void _mkdir() {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
context.showRoundDialog( context.showRoundDialog(
@@ -510,7 +498,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
); );
} }
void _newFile(BuildContext context) { void _newFile() {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
context.showRoundDialog( context.showRoundDialog(
@@ -550,7 +538,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
); );
} }
void _rename(BuildContext context, SftpName file) { void _rename(SftpName file) {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
context.showRoundDialog( context.showRoundDialog(
@@ -588,7 +576,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
); );
} }
Future<void> _decompress(BuildContext context, SftpName name) async { Future<void> _decompress(SftpName name) async {
context.pop(); context.pop();
final absPath = _getRemotePath(name); final absPath = _getRemotePath(name);
final cmd = _getDecompressCmd(absPath); final cmd = _getDecompressCmd(absPath);
@@ -621,15 +609,18 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} }
/// Only return true if the path is changed /// Only return true if the path is changed
Future<bool> _listDir({String? path, SSHClient? client}) async { Future<bool> _listDir() async {
context.showLoadingDialog(); context.showLoadingDialog();
if (client != null) { if (_status.client == null) {
final sftpc = await client.sftp(); final sftpc = await _client?.sftp();
_status.client = sftpc; _status.client = sftpc;
} }
try { try {
final listPath = path ?? _status.path?.path ?? '/'; final listPath = _status.path?.path ?? '/';
final fs = await _status.client!.listdir(listPath); final fs = await _status.client?.listdir(listPath);
if (fs == null) {
return false;
}
fs.sort((a, b) => a.filename.compareTo(b.filename)); fs.sort((a, b) => a.filename.compareTo(b.filename));
/// Issue #97 /// Issue #97
@@ -676,16 +667,15 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} }
Future<void> _backward() async { Future<void> _backward() async {
if (_status.path!.undo()) { if (_status.path?.undo() ?? false) {
await _listDir(); await _listDir();
} }
} }
@override @override
FutureOr<void> afterFirstLayout(BuildContext context) { FutureOr<void> afterFirstLayout(BuildContext context) {
final p_ = widget.initPath ?? '/'; _status.path = AbsolutePath(widget.initPath ?? '/');
_status.path = AbsolutePath(p_); _listDir();
_listDir(path: p_, client: _client);
} }
} }
@@ -754,3 +744,10 @@ const _extCmdMap = {
'obscpio': 'cpio -idmvF FILE', 'obscpio': 'cpio -idmvF FILE',
'zpaq': 'zpaq x FILE', 'zpaq': 'zpaq x FILE',
}; };
/// Return fmt: 2021-01-01 00:00:00
String _getTime(int? unixMill) {
return DateTime.fromMillisecondsSinceEpoch((unixMill ?? 0) * 1000)
.toString()
.replaceFirst('.000', '');
}

View File

@@ -72,7 +72,7 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
}, },
icon: const Icon(Icons.file_open)), icon: const Icon(Icons.file_open)),
IconButton( IconButton(
onPressed: () => shareFiles(context, [status.req.localPath]), onPressed: () => shareFiles([status.req.localPath]),
icon: const Icon(Icons.open_in_new), icon: const Icon(Icons.open_in_new),
) )
], ],

View File

@@ -188,7 +188,7 @@ Future<void> _gotoSSH(
} }
bool _checkClient(BuildContext context, String id) { bool _checkClient(BuildContext context, String id) {
final server = Providers.server.servers[id]; final server = Providers.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;
@@ -197,7 +197,7 @@ bool _checkClient(BuildContext context, String id) {
} }
Future<void> _onPkg(BuildContext context, ServerPrivateInfo spi) async { Future<void> _onPkg(BuildContext context, ServerPrivateInfo spi) async {
final server = spi.findServer; final server = spi.server;
if (server == null) { if (server == null) {
context.showSnackBar(l10n.noClient); context.showSnackBar(l10n.noClient);
return; return;

View File

@@ -476,9 +476,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 = 569; CURRENT_PROJECT_VERSION = 570;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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;
@@ -491,9 +491,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 = 569; CURRENT_PROJECT_VERSION = 570;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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;
@@ -506,9 +506,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 = 569; CURRENT_PROJECT_VERSION = 570;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.569; MARKETING_VERSION = 1.0.570;
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;

View File

@@ -211,7 +211,7 @@ packages:
source: hosted source: hosted
version: "0.3.3+5" version: "0.3.3+5"
crypto: crypto:
dependency: "direct main" dependency: transitive
description: description:
name: crypto name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab

View File

@@ -45,7 +45,6 @@ dependencies:
flutter_highlight: ^0.7.0 flutter_highlight: ^0.7.0
code_text_field: ^1.1.0 code_text_field: ^1.1.0
shared_preferences: ^2.1.1 shared_preferences: ^2.1.1
crypto: ^3.0.3
macos_window_utils: ^1.2.0 macos_window_utils: ^1.2.0
dynamic_color: ^1.6.6 dynamic_color: ^1.6.6
icloud_storage: ^2.2.0 icloud_storage: ^2.2.0