mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
opt.: cancel sftp mission
This commit is contained in:
5
lib/core/extension/datetime.dart
Normal file
5
lib/core/extension/datetime.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
extension DateTimeX on DateTime {
|
||||||
|
String get hourMinute {
|
||||||
|
return '${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ class PathWithPrefix {
|
|||||||
_path = pathJoin(_path, newPath);
|
_path = pathJoin(_path, newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get canBack => path != '$_prefixPath/';
|
||||||
|
|
||||||
bool undo() {
|
bool undo() {
|
||||||
if (_prePath == null || _path == _prePath) {
|
if (_prePath == null || _path == _prePath) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ Future<void> _download(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Read 10m each time
|
// Read 10m each time
|
||||||
const defaultChunkSize = 1024 * 1024 * 10;
|
const defaultChunkSize = 1024 * 1024;
|
||||||
final chunkSize = size > defaultChunkSize ? defaultChunkSize : size;
|
final chunkSize = size > defaultChunkSize ? defaultChunkSize : size;
|
||||||
mainSendPort.send(size);
|
mainSendPort.send(size);
|
||||||
mainSendPort.send(SftpWorkerStatus.downloading);
|
mainSendPort.send(SftpWorkerStatus.downloading);
|
||||||
|
|||||||
@@ -8,21 +8,8 @@ class SftpProvider extends ProviderBase {
|
|||||||
final List<SftpReqStatus> _status = [];
|
final List<SftpReqStatus> _status = [];
|
||||||
List<SftpReqStatus> get status => _status;
|
List<SftpReqStatus> get status => _status;
|
||||||
|
|
||||||
Iterable<SftpReqStatus> gets({int? id, String? fileName}) {
|
SftpReqStatus? get(int id) {
|
||||||
Iterable<SftpReqStatus> found = [];
|
return _status.singleWhere((element) => element.id == id);
|
||||||
if (id != null) {
|
|
||||||
found = _status.where((e) => e.id == id);
|
|
||||||
}
|
|
||||||
if (fileName != null) {
|
|
||||||
found = found.where((e) => e.req.localPath.split('/').last == fileName);
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
SftpReqStatus? get({int? id, String? name}) {
|
|
||||||
final found = gets(id: id, fileName: name);
|
|
||||||
if (found.isEmpty) return null;
|
|
||||||
return found.first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(SftpReq req, {Completer? completer}) {
|
void add(SftpReq req, {Completer? completer}) {
|
||||||
@@ -32,4 +19,19 @@ class SftpProvider extends ProviderBase {
|
|||||||
req: req,
|
req: req,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
for (final item in _status) {
|
||||||
|
item.worker.dispose();
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel(int id) {
|
||||||
|
final idx = _status.indexWhere((element) => element.id == id);
|
||||||
|
_status[idx].worker.dispose();
|
||||||
|
_status.removeAt(idx);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import 'convert.dart';
|
|||||||
import 'debug.dart';
|
import 'debug.dart';
|
||||||
import 'private_key/list.dart';
|
import 'private_key/list.dart';
|
||||||
import 'setting.dart';
|
import 'setting.dart';
|
||||||
import 'sftp/local.dart';
|
import 'storage/local.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
const HomePage({Key? key}) : super(key: key);
|
const HomePage({Key? key}) : super(key: key);
|
||||||
@@ -219,7 +219,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
leading: const Icon(Icons.download),
|
leading: const Icon(Icons.download),
|
||||||
title: Text(_s.download),
|
title: Text(_s.download),
|
||||||
onTap: () => AppRoute(
|
onTap: () => AppRoute(
|
||||||
const SFTPDownloadedPage(),
|
const LocalStoragePage(),
|
||||||
'sftp local page',
|
'sftp local page',
|
||||||
).go(context),
|
).go(context),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -291,9 +291,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
pwd: authorization,
|
pwd: authorization,
|
||||||
pubKeyId: usePublicKey ? _keyInfo!.id : null,
|
pubKeyId: usePublicKey ? _keyInfo!.id : null,
|
||||||
tags: _tags,
|
tags: _tags,
|
||||||
alterUrl: _alterUrlController.text == ''
|
alterUrl:
|
||||||
? null
|
_alterUrlController.text == '' ? null : _alterUrlController.text,
|
||||||
: _alterUrlController.text,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.spi == null) {
|
if (widget.spi == null) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import '../../widget/popup_menu.dart';
|
|||||||
import '../../widget/round_rect_card.dart';
|
import '../../widget/round_rect_card.dart';
|
||||||
import '../docker.dart';
|
import '../docker.dart';
|
||||||
import '../pkg.dart';
|
import '../pkg.dart';
|
||||||
import '../sftp/remote.dart';
|
import '../storage/sftp.dart';
|
||||||
import '../ssh/term.dart';
|
import '../ssh/term.dart';
|
||||||
import 'detail.dart';
|
import 'detail.dart';
|
||||||
import 'edit.dart';
|
import 'edit.dart';
|
||||||
@@ -286,7 +286,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
AppRoute(PkgManagePage(spi), 'pkg manage').go(context);
|
AppRoute(PkgManagePage(spi), 'pkg manage').go(context);
|
||||||
break;
|
break;
|
||||||
case ServerTabMenuType.sftp:
|
case ServerTabMenuType.sftp:
|
||||||
AppRoute(SFTPPage(spi), 'SFTP').go(context);
|
AppRoute(SftpPage(spi), 'SFTP').go(context);
|
||||||
break;
|
break;
|
||||||
case ServerTabMenuType.snippet:
|
case ServerTabMenuType.snippet:
|
||||||
final provider = locator<SnippetProvider>();
|
final provider = locator<SnippetProvider>();
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
import '../../../core/extension/numx.dart';
|
|
||||||
import '../../../core/utils/misc.dart';
|
|
||||||
import '../../../core/utils/ui.dart';
|
|
||||||
import '../../../data/model/sftp/req.dart';
|
|
||||||
import '../../../data/provider/sftp.dart';
|
|
||||||
import '../../../data/res/ui.dart';
|
|
||||||
import '../../widget/round_rect_card.dart';
|
|
||||||
|
|
||||||
class SFTPDownloadingPage extends StatefulWidget {
|
|
||||||
const SFTPDownloadingPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_SFTPDownloadingPageState createState() => _SFTPDownloadingPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|
||||||
late S _s;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
_s = S.of(context)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(
|
|
||||||
_s.mission,
|
|
||||||
style: textSize18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: _buildBody(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBody() {
|
|
||||||
return Consumer<SftpProvider>(builder: (__, pro, _) {
|
|
||||||
if (pro.status.isEmpty) {
|
|
||||||
return Center(
|
|
||||||
child: Text(_s.sftpNoDownloadTask),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return ListView.builder(
|
|
||||||
padding: const EdgeInsets.all(11),
|
|
||||||
itemCount: pro.status.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final status = pro.status[index];
|
|
||||||
return _buildItem(status);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _wrapInCard(SftpReqStatus status, String? subtitle,
|
|
||||||
{Widget? trailing}) {
|
|
||||||
return RoundRectCard(
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
status.fileName,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
subtitle: subtitle == null
|
|
||||||
? null
|
|
||||||
: Text(
|
|
||||||
subtitle,
|
|
||||||
style: grey,
|
|
||||||
),
|
|
||||||
trailing: trailing,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildItem(SftpReqStatus status) {
|
|
||||||
if (status.error != null) {
|
|
||||||
final err = status.error.toString();
|
|
||||||
Future.delayed(
|
|
||||||
const Duration(milliseconds: 377),
|
|
||||||
() => showSnackBar(context, Text(err)),
|
|
||||||
);
|
|
||||||
status.error = null;
|
|
||||||
}
|
|
||||||
switch (status.status) {
|
|
||||||
case SftpWorkerStatus.finished:
|
|
||||||
final time = status.spentTime.toString();
|
|
||||||
final str = '${_s.finished} ${_s.spentTime(
|
|
||||||
time == 'null' ? _s.unknown : (time.substring(0, time.length - 7)),
|
|
||||||
)}';
|
|
||||||
return _wrapInCard(
|
|
||||||
status,
|
|
||||||
str,
|
|
||||||
trailing: IconButton(
|
|
||||||
onPressed: () => shareFiles(context, [status.req.localPath]),
|
|
||||||
icon: const Icon(Icons.open_in_new),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
case SftpWorkerStatus.downloading:
|
|
||||||
final percentStr = (status.progress ?? 0.0).toStringAsFixed(2);
|
|
||||||
final percent = (status.progress ?? 0) / 100;
|
|
||||||
final size = (status.size ?? 0).convertBytes;
|
|
||||||
return _wrapInCard(
|
|
||||||
status,
|
|
||||||
_s.downloadStatus(percentStr, size),
|
|
||||||
trailing: SizedBox(
|
|
||||||
height: 27,
|
|
||||||
width: 27,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
value: percent,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
case SftpWorkerStatus.preparing:
|
|
||||||
return _wrapInCard(
|
|
||||||
status,
|
|
||||||
_s.sftpDlPrepare,
|
|
||||||
trailing: _loading,
|
|
||||||
);
|
|
||||||
case SftpWorkerStatus.sshConnectted:
|
|
||||||
return _wrapInCard(
|
|
||||||
status,
|
|
||||||
_s.sftpSSHConnected,
|
|
||||||
trailing: _loading,
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return _wrapInCard(
|
|
||||||
status,
|
|
||||||
_s.unknown,
|
|
||||||
trailing: const Icon(Icons.error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const _loading =
|
|
||||||
SizedBox(height: 27, width: 27, child: CircularProgressIndicator());
|
|
||||||
@@ -23,7 +23,7 @@ import '../../../data/res/color.dart';
|
|||||||
import '../../../data/res/terminal.dart';
|
import '../../../data/res/terminal.dart';
|
||||||
import '../../../data/store/setting.dart';
|
import '../../../data/store/setting.dart';
|
||||||
import '../../../locator.dart';
|
import '../../../locator.dart';
|
||||||
import '../sftp/remote.dart';
|
import '../storage/sftp.dart';
|
||||||
|
|
||||||
class SSHPage extends StatefulWidget {
|
class SSHPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final ServerPrivateInfo spi;
|
||||||
@@ -260,7 +260,7 @@ class _SSHPageState extends State<SSHPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AppRoute(
|
AppRoute(
|
||||||
SFTPPage(
|
SftpPage(
|
||||||
widget.spi,
|
widget.spi,
|
||||||
initPath: initPath,
|
initPath: initPath,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'package:toolbox/data/provider/sftp.dart';
|
|||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:toolbox/data/res/misc.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import 'package:toolbox/locator.dart';
|
||||||
import 'package:toolbox/view/page/editor.dart';
|
import 'package:toolbox/view/page/editor.dart';
|
||||||
import 'package:toolbox/view/page/sftp/remote.dart';
|
import 'package:toolbox/view/page/storage/sftp.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import 'package:toolbox/view/widget/input_field.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';
|
||||||
@@ -23,20 +23,20 @@ import '../../../data/model/app/path_with_prefix.dart';
|
|||||||
import '../../../data/res/path.dart';
|
import '../../../data/res/path.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../widget/fade_in.dart';
|
import '../../widget/fade_in.dart';
|
||||||
import 'mission.dart';
|
import 'sftp_mission.dart';
|
||||||
|
|
||||||
class SFTPDownloadedPage extends StatefulWidget {
|
class LocalStoragePage extends StatefulWidget {
|
||||||
final bool isPickFile;
|
final bool isPickFile;
|
||||||
const SFTPDownloadedPage({Key? key, this.isPickFile = false})
|
final String? initDir;
|
||||||
|
const LocalStoragePage({Key? key, this.isPickFile = false, this.initDir})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SFTPDownloadedPage> createState() => _SFTPDownloadedPageState();
|
State<LocalStoragePage> createState() => _LocalStoragePageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
class _LocalStoragePageState extends State<LocalStoragePage> {
|
||||||
PathWithPrefix? _path;
|
PathWithPrefix? _path;
|
||||||
String? _prefixPath;
|
|
||||||
late S _s;
|
late S _s;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -44,7 +44,9 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
sftpDir.then((dir) {
|
sftpDir.then((dir) {
|
||||||
_path = PathWithPrefix(dir.path);
|
_path = PathWithPrefix(dir.path);
|
||||||
_prefixPath = '${dir.path}/';
|
if (widget.initDir != null) {
|
||||||
|
_path!.update(widget.initDir!.replaceFirst('${dir.path}/', ''));
|
||||||
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -64,7 +66,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.downloading),
|
icon: const Icon(Icons.downloading),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
AppRoute(const SFTPDownloadingPage(), 'sftp downloading')
|
AppRoute(const SftpMissionPage(), 'sftp downloading')
|
||||||
.go(context),
|
.go(context),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -100,7 +102,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
}
|
}
|
||||||
final dir = Directory(_path!.path);
|
final dir = Directory(_path!.path);
|
||||||
final files = dir.listSync();
|
final files = dir.listSync();
|
||||||
final canGoBack = _path!.path != _prefixPath;
|
final canGoBack = _path!.canBack;
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: canGoBack ? files.length + 1 : files.length,
|
itemCount: canGoBack ? files.length + 1 : files.length,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 7),
|
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 7),
|
||||||
@@ -126,7 +128,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
? const Icon(Icons.folder)
|
? const Icon(Icons.folder)
|
||||||
: const Icon(Icons.insert_drive_file),
|
: const Icon(Icons.insert_drive_file),
|
||||||
title: Text(fileName),
|
title: Text(fileName),
|
||||||
subtitle: isDir ? null : Text(stat.size.convertBytes),
|
subtitle: isDir ? null : Text(stat.size.convertBytes, style: grey),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
stat.modified
|
stat.modified
|
||||||
.toString()
|
.toString()
|
||||||
@@ -266,18 +268,16 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
final id = ids[idx];
|
final id = ids[idx];
|
||||||
final spi = serverProvider.servers[id]?.spi;
|
final spi = serverProvider.servers[id]?.spi;
|
||||||
if (spi == null) {
|
if (spi == null) {
|
||||||
showSnackBar(context, Text(_s.noResult));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final remotePath = await AppRoute(
|
final remotePath = await AppRoute(
|
||||||
SFTPPage(
|
SftpPage(
|
||||||
spi,
|
spi,
|
||||||
selectPath: true,
|
selectPath: true,
|
||||||
),
|
),
|
||||||
'SFTP page (select)',
|
'SFTP page (select)',
|
||||||
).go<String>(context);
|
).go<String>(context);
|
||||||
if (remotePath == null) {
|
if (remotePath == null) {
|
||||||
showSnackBar(context, Text(_s.fieldMustNotEmpty));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
locator<SftpProvider>().add(SftpReq(
|
locator<SftpProvider>().add(SftpReq(
|
||||||
@@ -8,7 +8,7 @@ import 'package:toolbox/core/extension/navigator.dart';
|
|||||||
import 'package:toolbox/core/extension/sftpfile.dart';
|
import 'package:toolbox/core/extension/sftpfile.dart';
|
||||||
import 'package:toolbox/data/res/misc.dart';
|
import 'package:toolbox/data/res/misc.dart';
|
||||||
import 'package:toolbox/view/page/editor.dart';
|
import 'package:toolbox/view/page/editor.dart';
|
||||||
import 'package:toolbox/view/page/sftp/local.dart';
|
import 'package:toolbox/view/page/storage/local.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';
|
||||||
@@ -29,14 +29,14 @@ import '../../../locator.dart';
|
|||||||
import '../../widget/fade_in.dart';
|
import '../../widget/fade_in.dart';
|
||||||
import '../../widget/input_field.dart';
|
import '../../widget/input_field.dart';
|
||||||
import '../../widget/two_line_text.dart';
|
import '../../widget/two_line_text.dart';
|
||||||
import 'mission.dart';
|
import 'sftp_mission.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 selectPath;
|
||||||
|
|
||||||
const SFTPPage(
|
const SftpPage(
|
||||||
this.spi, {
|
this.spi, {
|
||||||
Key? key,
|
Key? key,
|
||||||
this.initPath,
|
this.initPath,
|
||||||
@@ -44,10 +44,10 @@ class SFTPPage extends StatefulWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_SFTPPageState createState() => _SFTPPageState();
|
_SftpPageState createState() => _SftpPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SFTPPageState extends State<SFTPPage> {
|
class _SftpPageState extends State<SftpPage> {
|
||||||
final SftpBrowserStatus _status = SftpBrowserStatus();
|
final SftpBrowserStatus _status = SftpBrowserStatus();
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.downloading),
|
icon: const Icon(Icons.downloading),
|
||||||
onPressed: () => AppRoute(
|
onPressed: () => AppRoute(
|
||||||
const SFTPDownloadingPage(),
|
const SftpMissionPage(),
|
||||||
'sftp downloading',
|
'sftp downloading',
|
||||||
).go(context),
|
).go(context),
|
||||||
),
|
),
|
||||||
@@ -154,7 +154,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
switch (idx) {
|
switch (idx) {
|
||||||
case 0:
|
case 0:
|
||||||
return await AppRoute(
|
return await AppRoute(
|
||||||
const SFTPDownloadedPage(
|
const LocalStoragePage(
|
||||||
isPickFile: true,
|
isPickFile: true,
|
||||||
),
|
),
|
||||||
'sftp dled pick')
|
'sftp dled pick')
|
||||||
@@ -262,7 +262,8 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
_status.path = AbsolutePath(p_);
|
_status.path = AbsolutePath(p_);
|
||||||
_listDir(path: p_, client: _client);
|
_listDir(path: p_, client: _client);
|
||||||
return centerLoading;
|
return centerLoading;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
child: FadeIn(
|
child: FadeIn(
|
||||||
key: Key(widget.spi.name + _status.path!.path),
|
key: Key(widget.spi.name + _status.path!.path),
|
||||||
@@ -270,19 +271,30 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
itemCount: _status.files!.length,
|
itemCount: _status.files!.length,
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (_, index) => _buildItem(_status.files![index]),
|
||||||
final file = _status.files![index];
|
),
|
||||||
|
),
|
||||||
|
onRefresh: () => _listDir(path: _status.path?.path),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(SftpName file) {
|
||||||
final isDir = file.attr.isDirectory;
|
final isDir = file.attr.isDirectory;
|
||||||
|
final trailing = Text(
|
||||||
|
'${getTime(file.attr.modifyTime)}\n${file.attr.mode?.str ?? ''}',
|
||||||
|
style: grey,
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
);
|
||||||
return RoundRectCard(ListTile(
|
return RoundRectCard(ListTile(
|
||||||
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
||||||
title: Text(file.filename),
|
title: Text(file.filename),
|
||||||
trailing: Text(
|
trailing: trailing,
|
||||||
'${getTime(file.attr.modifyTime)}\n${file.attr.mode?.str ?? ''}',
|
subtitle: isDir
|
||||||
style: const TextStyle(color: Colors.grey),
|
? null
|
||||||
textAlign: TextAlign.right,
|
: Text(
|
||||||
|
(file.attr.size ?? 0).convertBytes,
|
||||||
|
style: grey,
|
||||||
),
|
),
|
||||||
subtitle:
|
|
||||||
isDir ? null : Text((file.attr.size ?? 0).convertBytes),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (isDir) {
|
if (isDir) {
|
||||||
_status.path?.update(file.filename);
|
_status.path?.update(file.filename);
|
||||||
@@ -293,12 +305,6 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
},
|
},
|
||||||
onLongPress: () => _onItemPress(context, file, !isDir),
|
onLongPress: () => _onItemPress(context, file, !isDir),
|
||||||
));
|
));
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onRefresh: () => _listDir(path: _status.path?.path),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onItemPress(BuildContext context, SftpName file, bool notDir) {
|
void _onItemPress(BuildContext context, SftpName file, bool notDir) {
|
||||||
@@ -479,7 +485,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
child: Text(_s.cancel),
|
child: Text(_s.cancel),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
if (textController.text == '') {
|
if (textController.text == '') {
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -493,8 +499,8 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_status.client!
|
final dir = '${_status.path!.path}/${textController.text}';
|
||||||
.mkdir('${_status.path!.path}/${textController.text}');
|
await _status.client!.mkdir(dir);
|
||||||
context.pop();
|
context.pop();
|
||||||
_listDir();
|
_listDir();
|
||||||
},
|
},
|
||||||
@@ -538,9 +544,9 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(await _status.client!
|
final path = '${_status.path!.path}/${textController.text}';
|
||||||
.open('${_status.path!.path}/${textController.text}'))
|
final file = await _status.client!.open(path);
|
||||||
.writeBytes(Uint8List(0));
|
await file.writeBytes(Uint8List(0));
|
||||||
context.pop();
|
context.pop();
|
||||||
_listDir();
|
_listDir();
|
||||||
},
|
},
|
||||||
174
lib/view/page/storage/sftp_mission.dart
Normal file
174
lib/view/page/storage/sftp_mission.dart
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:toolbox/core/extension/datetime.dart';
|
||||||
|
import 'package:toolbox/core/extension/navigator.dart';
|
||||||
|
import 'package:toolbox/core/route.dart';
|
||||||
|
import 'package:toolbox/locator.dart';
|
||||||
|
import 'package:toolbox/view/page/storage/local.dart';
|
||||||
|
|
||||||
|
import '../../../core/extension/numx.dart';
|
||||||
|
import '../../../core/utils/misc.dart';
|
||||||
|
import '../../../core/utils/ui.dart';
|
||||||
|
import '../../../data/model/sftp/req.dart';
|
||||||
|
import '../../../data/provider/sftp.dart';
|
||||||
|
import '../../../data/res/ui.dart';
|
||||||
|
import '../../widget/round_rect_card.dart';
|
||||||
|
|
||||||
|
class SftpMissionPage extends StatefulWidget {
|
||||||
|
const SftpMissionPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SftpMissionPageState createState() => _SftpMissionPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SftpMissionPageState extends State<SftpMissionPage> {
|
||||||
|
late S _s;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
_s = S.of(context)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(
|
||||||
|
_s.mission,
|
||||||
|
style: textSize18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: _buildBody(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody() {
|
||||||
|
return Consumer<SftpProvider>(builder: (__, pro, _) {
|
||||||
|
if (pro.status.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Text(_s.sftpNoDownloadTask),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(11),
|
||||||
|
itemCount: pro.status.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final status = pro.status[index];
|
||||||
|
return _buildItem(status);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(SftpReqStatus status) {
|
||||||
|
switch (status.status) {
|
||||||
|
case SftpWorkerStatus.finished:
|
||||||
|
final time = status.spentTime.toString();
|
||||||
|
final str = '${_s.finished} ${_s.spentTime(
|
||||||
|
time == 'null' ? _s.unknown : (time.substring(0, time.length - 7)),
|
||||||
|
)}';
|
||||||
|
return _wrapInCard(
|
||||||
|
status: status,
|
||||||
|
subtitle: str,
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
final idx = status.req.localPath.lastIndexOf('/');
|
||||||
|
final dir = status.req.localPath.substring(0, idx);
|
||||||
|
AppRoute(
|
||||||
|
LocalStoragePage(initDir: dir),
|
||||||
|
'sftp local',
|
||||||
|
).go(context);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.file_open)),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => shareFiles(context, [status.req.localPath]),
|
||||||
|
icon: const Icon(Icons.open_in_new),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case SftpWorkerStatus.downloading:
|
||||||
|
final percentStr = (status.progress ?? 0.0).toStringAsFixed(2);
|
||||||
|
final size = (status.size ?? 0).convertBytes;
|
||||||
|
return _wrapInCard(
|
||||||
|
status: status,
|
||||||
|
subtitle: _s.downloadStatus(percentStr, size),
|
||||||
|
trailing: _buildDelete(status.fileName, status.id),
|
||||||
|
);
|
||||||
|
case SftpWorkerStatus.preparing:
|
||||||
|
return _wrapInCard(
|
||||||
|
status: status,
|
||||||
|
subtitle: _s.sftpDlPrepare,
|
||||||
|
trailing: _buildDelete(status.fileName, status.id),
|
||||||
|
);
|
||||||
|
case SftpWorkerStatus.sshConnectted:
|
||||||
|
return _wrapInCard(
|
||||||
|
status: status,
|
||||||
|
subtitle: _s.sftpSSHConnected,
|
||||||
|
trailing: _buildDelete(status.fileName, status.id),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return _wrapInCard(
|
||||||
|
status: status,
|
||||||
|
subtitle: _s.unknown,
|
||||||
|
trailing: IconButton(
|
||||||
|
onPressed: () => showRoundDialog(
|
||||||
|
context: context,
|
||||||
|
title: Text(_s.error),
|
||||||
|
child: Text((status.error ?? _s.unknown).toString()),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.error),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _wrapInCard({
|
||||||
|
required SftpReqStatus status,
|
||||||
|
String? subtitle,
|
||||||
|
Widget? trailing,
|
||||||
|
}) {
|
||||||
|
final time = DateTime.fromMicrosecondsSinceEpoch(status.id);
|
||||||
|
return RoundRectCard(
|
||||||
|
ListTile(
|
||||||
|
leading: Text(time.hourMinute),
|
||||||
|
title: Text(
|
||||||
|
status.fileName,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
subtitle: subtitle == null
|
||||||
|
? null
|
||||||
|
: Text(
|
||||||
|
subtitle,
|
||||||
|
style: grey,
|
||||||
|
),
|
||||||
|
trailing: trailing,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDelete(String name, int id) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: () => showRoundDialog(
|
||||||
|
context: context,
|
||||||
|
title: Text(_s.attention),
|
||||||
|
child: Text(_s.sureDelete(name)),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
locator<SftpProvider>().cancel(id);
|
||||||
|
context.pop();
|
||||||
|
},
|
||||||
|
child: Text(_s.ok),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user