import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/data/model/sftp/req.dart'; import 'package:toolbox/data/provider/server.dart'; 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/widget/input_field.dart'; import 'package:toolbox/view/widget/picker.dart'; import 'package:toolbox/view/widget/round_rect_card.dart'; import '../../../core/extension/numx.dart'; import '../../../core/extension/stringx.dart'; import '../../../core/route.dart'; import '../../../core/utils/misc.dart'; import '../../../core/utils/ui.dart'; 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'; class SFTPDownloadedPage extends StatefulWidget { final bool isPickFile; const SFTPDownloadedPage({Key? key, this.isPickFile = false}) : super(key: key); @override State createState() => _SFTPDownloadedPageState(); } class _SFTPDownloadedPageState extends State { PathWithPrefix? _path; String? _prefixPath; late S _s; @override void initState() { super.initState(); sftpDir.then((dir) { _path = PathWithPrefix(dir.path); _prefixPath = '${dir.path}/'; setState(() {}); }); } @override void didChangeDependencies() { super.didChangeDependencies(); _s = S.of(context)!; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(_s.download), actions: [ IconButton( icon: const Icon(Icons.downloading), onPressed: () => AppRoute(const SFTPDownloadingPage(), 'sftp downloading') .go(context), ) ], ), body: FadeIn( key: UniqueKey(), child: _buildBody(), ), bottomNavigationBar: SafeArea( child: _buildPath(), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: FloatingActionButton( onPressed: (() { if (_path!.path == _prefixPath) { showSnackBar(context, Text(_s.alreadyLastDir)); return; } _path!.update('..'); setState(() {}); }), child: const Icon(Icons.keyboard_arrow_left), ), ); } Widget _buildPath() { return Container( padding: const EdgeInsets.fromLTRB(11, 7, 11, 11), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Divider(), (_path?.path ?? _s.loadingFiles).omitStartStr(), ], ), ); } Widget _buildBody() { if (_path == null) { return const Center( child: CircularProgressIndicator(), ); } final dir = Directory(_path!.path); final files = dir.listSync(); return ListView.builder( itemCount: files.length, padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 7), itemBuilder: (context, index) { var file = files[index]; var fileName = file.path.split('/').last; var stat = file.statSync(); var isDir = stat.type == FileSystemEntityType.directory; return RoundRectCard(ListTile( leading: isDir ? const Icon(Icons.folder) : const Icon(Icons.insert_drive_file), title: Text(fileName), subtitle: isDir ? null : Text(stat.size.convertBytes), trailing: Text( stat.modified .toString() .substring(0, stat.modified.toString().length - 4), style: grey, ), onTap: () async { if (!isDir) { await showFileActionDialog(file); return; } _path!.update(fileName); setState(() {}); }, )); }, ); } Future showFileActionDialog(FileSystemEntity file) async { final fileName = file.path.split('/').last; if (widget.isPickFile) { await showRoundDialog( context: context, title: Text(_s.pickFile), child: Text(fileName), actions: [ TextButton( onPressed: () { context.pop(); context.pop(file.path); }, child: Text(_s.ok), ), ]); return; } showRoundDialog( context: context, child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.edit), title: Text(_s.edit), onTap: () async { context.pop(); final stat = await file.stat(); if (stat.size > editorMaxSize) { showRoundDialog( context: context, child: Text(_s.fileTooLarge(fileName, stat.size, '1m')), ); return; } final f = File(file.absolute.path); final result = await AppRoute( EditorPage( path: file.absolute.path, ), 'sftp dled editor', ).go(context); if (result != null) { f.writeAsString(result); showSnackBar(context, Text(_s.saved)); setState(() {}); } }, ), ListTile( leading: const Icon(Icons.abc), title: Text(_s.rename), onTap: () { context.pop(); showRoundDialog( context: context, title: Text(_s.rename), child: Input( controller: TextEditingController(text: fileName), onSubmitted: (p0) { context.pop(); final newPath = '${file.parent.path}/$p0'; file.renameSync(newPath); setState(() {}); }, ), ); }, ), ListTile( leading: const Icon(Icons.delete), title: Text(_s.delete), onTap: () { context.pop(); showRoundDialog( context: context, child: Text(_s.sureDelete(fileName)), actions: [ TextButton( onPressed: () => context.pop(), child: Text(_s.cancel), ), TextButton( onPressed: () { file.deleteSync(); setState(() {}); context.pop(); }, child: Text(_s.ok), ), ], ); }, ), ListTile( leading: const Icon(Icons.upload), title: Text(_s.upload), onTap: () async { context.pop(); final serverProvider = locator(); final ids = serverProvider.serverOrder; var idx = 0; await showRoundDialog( context: context, title: Text(_s.server), child: Picker( items: ids.map((e) => Text(e)).toList(), onSelected: (idx_) => idx = idx_, ), actions: [ TextButton( onPressed: () => context.pop(), child: Text(_s.ok)), ], ); final id = ids[idx]; final spi = serverProvider.servers[id]?.spi; if (spi == null) { showSnackBar(context, Text(_s.noResult)); return; } final remotePath = await AppRoute( SFTPPage( spi, selectPath: true, ), 'SFTP page (select)', ).go(context); if (remotePath == null) { showSnackBar(context, Text(_s.fieldMustNotEmpty)); return; } locator().add( SftpReqItem(spi, remotePath, file.absolute.path), SftpReqType.upload, ); showSnackBar(context, Text(_s.added2List)); }, ), ListTile( leading: const Icon(Icons.open_in_new), title: Text(_s.open), onTap: () { shareFiles(context, [file.absolute.path]); }, ), ], ), ); } }