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);
|
||||
}
|
||||
|
||||
bool get canBack => path != '$_prefixPath/';
|
||||
|
||||
bool undo() {
|
||||
if (_prePath == null || _path == _prePath) {
|
||||
return false;
|
||||
|
||||
@@ -94,7 +94,7 @@ Future<void> _download(
|
||||
return;
|
||||
}
|
||||
// Read 10m each time
|
||||
const defaultChunkSize = 1024 * 1024 * 10;
|
||||
const defaultChunkSize = 1024 * 1024;
|
||||
final chunkSize = size > defaultChunkSize ? defaultChunkSize : size;
|
||||
mainSendPort.send(size);
|
||||
mainSendPort.send(SftpWorkerStatus.downloading);
|
||||
|
||||
@@ -8,21 +8,8 @@ class SftpProvider extends ProviderBase {
|
||||
final List<SftpReqStatus> _status = [];
|
||||
List<SftpReqStatus> get status => _status;
|
||||
|
||||
Iterable<SftpReqStatus> gets({int? id, String? fileName}) {
|
||||
Iterable<SftpReqStatus> found = [];
|
||||
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;
|
||||
SftpReqStatus? get(int id) {
|
||||
return _status.singleWhere((element) => element.id == id);
|
||||
}
|
||||
|
||||
void add(SftpReq req, {Completer? completer}) {
|
||||
@@ -32,4 +19,19 @@ class SftpProvider extends ProviderBase {
|
||||
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 'private_key/list.dart';
|
||||
import 'setting.dart';
|
||||
import 'sftp/local.dart';
|
||||
import 'storage/local.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
@@ -219,7 +219,7 @@ class _HomePageState extends State<HomePage>
|
||||
leading: const Icon(Icons.download),
|
||||
title: Text(_s.download),
|
||||
onTap: () => AppRoute(
|
||||
const SFTPDownloadedPage(),
|
||||
const LocalStoragePage(),
|
||||
'sftp local page',
|
||||
).go(context),
|
||||
),
|
||||
|
||||
@@ -291,9 +291,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
pwd: authorization,
|
||||
pubKeyId: usePublicKey ? _keyInfo!.id : null,
|
||||
tags: _tags,
|
||||
alterUrl: _alterUrlController.text == ''
|
||||
? null
|
||||
: _alterUrlController.text,
|
||||
alterUrl:
|
||||
_alterUrlController.text == '' ? null : _alterUrlController.text,
|
||||
);
|
||||
|
||||
if (widget.spi == null) {
|
||||
|
||||
@@ -29,7 +29,7 @@ import '../../widget/popup_menu.dart';
|
||||
import '../../widget/round_rect_card.dart';
|
||||
import '../docker.dart';
|
||||
import '../pkg.dart';
|
||||
import '../sftp/remote.dart';
|
||||
import '../storage/sftp.dart';
|
||||
import '../ssh/term.dart';
|
||||
import 'detail.dart';
|
||||
import 'edit.dart';
|
||||
@@ -286,7 +286,7 @@ class _ServerPageState extends State<ServerPage>
|
||||
AppRoute(PkgManagePage(spi), 'pkg manage').go(context);
|
||||
break;
|
||||
case ServerTabMenuType.sftp:
|
||||
AppRoute(SFTPPage(spi), 'SFTP').go(context);
|
||||
AppRoute(SftpPage(spi), 'SFTP').go(context);
|
||||
break;
|
||||
case ServerTabMenuType.snippet:
|
||||
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/store/setting.dart';
|
||||
import '../../../locator.dart';
|
||||
import '../sftp/remote.dart';
|
||||
import '../storage/sftp.dart';
|
||||
|
||||
class SSHPage extends StatefulWidget {
|
||||
final ServerPrivateInfo spi;
|
||||
@@ -260,7 +260,7 @@ class _SSHPageState extends State<SSHPage> {
|
||||
return;
|
||||
}
|
||||
AppRoute(
|
||||
SFTPPage(
|
||||
SftpPage(
|
||||
widget.spi,
|
||||
initPath: initPath,
|
||||
),
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:toolbox/data/provider/sftp.dart';
|
||||
import 'package:toolbox/data/res/misc.dart';
|
||||
import 'package:toolbox/locator.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/picker.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/ui.dart';
|
||||
import '../../widget/fade_in.dart';
|
||||
import 'mission.dart';
|
||||
import 'sftp_mission.dart';
|
||||
|
||||
class SFTPDownloadedPage extends StatefulWidget {
|
||||
class LocalStoragePage extends StatefulWidget {
|
||||
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);
|
||||
|
||||
@override
|
||||
State<SFTPDownloadedPage> createState() => _SFTPDownloadedPageState();
|
||||
State<LocalStoragePage> createState() => _LocalStoragePageState();
|
||||
}
|
||||
|
||||
class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||
class _LocalStoragePageState extends State<LocalStoragePage> {
|
||||
PathWithPrefix? _path;
|
||||
String? _prefixPath;
|
||||
late S _s;
|
||||
|
||||
@override
|
||||
@@ -44,7 +44,9 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||
super.initState();
|
||||
sftpDir.then((dir) {
|
||||
_path = PathWithPrefix(dir.path);
|
||||
_prefixPath = '${dir.path}/';
|
||||
if (widget.initDir != null) {
|
||||
_path!.update(widget.initDir!.replaceFirst('${dir.path}/', ''));
|
||||
}
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
@@ -64,7 +66,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||
IconButton(
|
||||
icon: const Icon(Icons.downloading),
|
||||
onPressed: () =>
|
||||
AppRoute(const SFTPDownloadingPage(), 'sftp downloading')
|
||||
AppRoute(const SftpMissionPage(), 'sftp downloading')
|
||||
.go(context),
|
||||
)
|
||||
],
|
||||
@@ -100,7 +102,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||
}
|
||||
final dir = Directory(_path!.path);
|
||||
final files = dir.listSync();
|
||||
final canGoBack = _path!.path != _prefixPath;
|
||||
final canGoBack = _path!.canBack;
|
||||
return ListView.builder(
|
||||
itemCount: canGoBack ? files.length + 1 : files.length,
|
||||
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 7),
|
||||
@@ -126,7 +128,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||
? const Icon(Icons.folder)
|
||||
: const Icon(Icons.insert_drive_file),
|
||||
title: Text(fileName),
|
||||
subtitle: isDir ? null : Text(stat.size.convertBytes),
|
||||
subtitle: isDir ? null : Text(stat.size.convertBytes, style: grey),
|
||||
trailing: Text(
|
||||
stat.modified
|
||||
.toString()
|
||||
@@ -266,18 +268,16 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||
final id = ids[idx];
|
||||
final spi = serverProvider.servers[id]?.spi;
|
||||
if (spi == null) {
|
||||
showSnackBar(context, Text(_s.noResult));
|
||||
return;
|
||||
}
|
||||
final remotePath = await AppRoute(
|
||||
SFTPPage(
|
||||
SftpPage(
|
||||
spi,
|
||||
selectPath: true,
|
||||
),
|
||||
'SFTP page (select)',
|
||||
).go<String>(context);
|
||||
if (remotePath == null) {
|
||||
showSnackBar(context, Text(_s.fieldMustNotEmpty));
|
||||
return;
|
||||
}
|
||||
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/data/res/misc.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 '../../../core/extension/numx.dart';
|
||||
@@ -29,14 +29,14 @@ import '../../../locator.dart';
|
||||
import '../../widget/fade_in.dart';
|
||||
import '../../widget/input_field.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 String? initPath;
|
||||
final bool selectPath;
|
||||
|
||||
const SFTPPage(
|
||||
const SftpPage(
|
||||
this.spi, {
|
||||
Key? key,
|
||||
this.initPath,
|
||||
@@ -44,10 +44,10 @@ class SFTPPage extends StatefulWidget {
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SFTPPageState createState() => _SFTPPageState();
|
||||
_SftpPageState createState() => _SftpPageState();
|
||||
}
|
||||
|
||||
class _SFTPPageState extends State<SFTPPage> {
|
||||
class _SftpPageState extends State<SftpPage> {
|
||||
final SftpBrowserStatus _status = SftpBrowserStatus();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@@ -82,7 +82,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
IconButton(
|
||||
icon: const Icon(Icons.downloading),
|
||||
onPressed: () => AppRoute(
|
||||
const SFTPDownloadingPage(),
|
||||
const SftpMissionPage(),
|
||||
'sftp downloading',
|
||||
).go(context),
|
||||
),
|
||||
@@ -154,7 +154,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
return await AppRoute(
|
||||
const SFTPDownloadedPage(
|
||||
const LocalStoragePage(
|
||||
isPickFile: true,
|
||||
),
|
||||
'sftp dled pick')
|
||||
@@ -262,43 +262,49 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
_status.path = AbsolutePath(p_);
|
||||
_listDir(path: p_, client: _client);
|
||||
return centerLoading;
|
||||
} else {
|
||||
return RefreshIndicator(
|
||||
child: FadeIn(
|
||||
key: Key(widget.spi.name + _status.path!.path),
|
||||
child: ListView.builder(
|
||||
itemCount: _status.files!.length,
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||
itemBuilder: (context, index) {
|
||||
final file = _status.files![index];
|
||||
final isDir = file.attr.isDirectory;
|
||||
return RoundRectCard(ListTile(
|
||||
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
||||
title: Text(file.filename),
|
||||
trailing: Text(
|
||||
'${getTime(file.attr.modifyTime)}\n${file.attr.mode?.str ?? ''}',
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
subtitle:
|
||||
isDir ? null : Text((file.attr.size ?? 0).convertBytes),
|
||||
onTap: () {
|
||||
if (isDir) {
|
||||
_status.path?.update(file.filename);
|
||||
_listDir(path: _status.path?.path);
|
||||
} else {
|
||||
_onItemPress(context, file, true);
|
||||
}
|
||||
},
|
||||
onLongPress: () => _onItemPress(context, file, !isDir),
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
onRefresh: () => _listDir(path: _status.path?.path),
|
||||
);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
child: FadeIn(
|
||||
key: Key(widget.spi.name + _status.path!.path),
|
||||
child: ListView.builder(
|
||||
itemCount: _status.files!.length,
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||
itemBuilder: (_, index) => _buildItem(_status.files![index]),
|
||||
),
|
||||
),
|
||||
onRefresh: () => _listDir(path: _status.path?.path),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem(SftpName file) {
|
||||
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(
|
||||
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
||||
title: Text(file.filename),
|
||||
trailing: trailing,
|
||||
subtitle: isDir
|
||||
? null
|
||||
: Text(
|
||||
(file.attr.size ?? 0).convertBytes,
|
||||
style: grey,
|
||||
),
|
||||
onTap: () {
|
||||
if (isDir) {
|
||||
_status.path?.update(file.filename);
|
||||
_listDir(path: _status.path?.path);
|
||||
} else {
|
||||
_onItemPress(context, file, true);
|
||||
}
|
||||
},
|
||||
onLongPress: () => _onItemPress(context, file, !isDir),
|
||||
));
|
||||
}
|
||||
|
||||
void _onItemPress(BuildContext context, SftpName file, bool notDir) {
|
||||
@@ -479,7 +485,7 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
child: Text(_s.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
if (textController.text == '') {
|
||||
showRoundDialog(
|
||||
context: context,
|
||||
@@ -493,8 +499,8 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
);
|
||||
return;
|
||||
}
|
||||
_status.client!
|
||||
.mkdir('${_status.path!.path}/${textController.text}');
|
||||
final dir = '${_status.path!.path}/${textController.text}';
|
||||
await _status.client!.mkdir(dir);
|
||||
context.pop();
|
||||
_listDir();
|
||||
},
|
||||
@@ -538,9 +544,9 @@ class _SFTPPageState extends State<SFTPPage> {
|
||||
);
|
||||
return;
|
||||
}
|
||||
(await _status.client!
|
||||
.open('${_status.path!.path}/${textController.text}'))
|
||||
.writeBytes(Uint8List(0));
|
||||
final path = '${_status.path!.path}/${textController.text}';
|
||||
final file = await _status.client!.open(path);
|
||||
await file.writeBytes(Uint8List(0));
|
||||
context.pop();
|
||||
_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