diff --git a/lib/sftp_explorer.dart b/lib/sftp_explorer.dart index 7d8504d..f2de2d6 100644 --- a/lib/sftp_explorer.dart +++ b/lib/sftp_explorer.dart @@ -7,9 +7,10 @@ import 'package:flutter/material.dart'; import 'package:fluxcloud/sftp_worker.dart'; class SftpExplorer extends StatefulWidget { - const SftpExplorer({super.key, required this.sftpWorker}); + const SftpExplorer({super.key, required this.sftpWorker, this.path = '/'}); final SftpWorker sftpWorker; + final String path; @override State createState() => _SftpExplorerState(); @@ -17,8 +18,6 @@ class SftpExplorer extends StatefulWidget { class _SftpExplorerState extends State { - String path = '/'; - bool _isLoading = true; late List _dirContents; @@ -33,7 +32,7 @@ class _SftpExplorerState extends State { Future _listDir() async { setState(() => _isLoading = true); try { - _dirContents = await widget.sftpWorker.listdir(path); + _dirContents = await widget.sftpWorker.listdir(widget.path); } catch (e) { if (mounted) { @@ -52,20 +51,6 @@ class _SftpExplorerState extends State { title: Text('Explorer'), elevation: 2, actionsPadding: EdgeInsets.only(right: 20), - leading: IconButton( - onPressed: () { - if (path == '/') { - // TODO: figure this out - // Navigator.pop(context); - } - else { - path = path.substring(0, path.length - 1); - path = path.substring(0, path.lastIndexOf('/')+1); - _listDir(); - } - }, - icon: Icon(Icons.arrow_back) - ), actions: [ if (_progress != null) Stack( @@ -78,7 +63,7 @@ class _SftpExplorerState extends State { ), IconButton( onPressed: () { - + }, icon: Icon(Icons.upload) ), @@ -87,143 +72,127 @@ class _SftpExplorerState extends State { ], ), floatingActionButton: _buildFABs(context), - body: AnimatedSwitcher( - duration: Duration(milliseconds: 300), - transitionBuilder: (child, animation) { - final curved = CurvedAnimation( - parent: animation, - curve: Curves.fastOutSlowIn - ); - return FadeTransition( - opacity: curved, - child: ScaleTransition( - scale: Tween( - begin: 0.92, - end: 1 - ).animate(curved), - child: child, - ), - ); - }, - child: _isLoading ? Center(child: CircularProgressIndicator()) : ListView.builder( - key: ValueKey(path), - itemCount: _dirContents.length, - itemBuilder: (context, index) { - final dirEntry = _dirContents[index]; - return ListTile( - leading: Icon(dirEntry.attr.isDirectory ? Icons.folder : Icons.description), - title: Text(dirEntry.filename), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - onPressed: () { + body: _isLoading ? Center(child: CircularProgressIndicator()) : ListView.builder( + itemCount: _dirContents.length, + itemBuilder: (context, index) { + final dirEntry = _dirContents[index]; + return ListTile( + leading: Icon(dirEntry.attr.isDirectory ? Icons.folder : Icons.description), + title: Text(dirEntry.filename), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + onPressed: () { - }, - icon: Icon(Icons.drive_file_move) - ), - IconButton( - onPressed: () { + }, + icon: Icon(Icons.drive_file_move) + ), + IconButton( + onPressed: () { - }, - icon: Icon(Icons.copy) - ), - IconButton( - onPressed: () { - final newNameController = TextEditingController(text: dirEntry.filename); - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Rename'), - content: TextField( - controller: newNameController, - autofocus: true, - decoration: InputDecoration( - labelText: 'Enter new name' - ), + }, + icon: Icon(Icons.copy) + ), + IconButton( + onPressed: () { + final newNameController = TextEditingController(text: dirEntry.filename); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Rename'), + content: TextField( + controller: newNameController, + autofocus: true, + decoration: InputDecoration( + labelText: 'Enter new name' + ), + ), + actions: [ + TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancel')), + TextButton( + onPressed: () async { + // try { + // await widget.sftpWorker.rename('${widget.path}${dirEntry.filename}', '${widget.path}${newNameController.text}'); + // _listDir(); + // } + // on SftpStatusError catch (e) { + // if (context.mounted) { + // ScaffoldMessenger.of(context).showSnackBar(_buildErrorSnackBar(context, e.message)); + // } + // } + // if (context.mounted) { + // Navigator.pop(context); + // } + // + }, + child: Text('Rename') ), - actions: [ - TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancel')), - TextButton( - onPressed: () async { - // try { - // await widget.sftpWorker.rename('${path}${dirEntry.filename}', '${widget.path}${newNameController.text}'); - // _listDir(); - // } - // on SftpStatusError catch (e) { - // if (context.mounted) { - // ScaffoldMessenger.of(context).showSnackBar(_buildErrorSnackBar(context, e.message)); - // } - // } - // if (context.mounted) { - // Navigator.pop(context); - // } - // - }, - child: Text('Rename') - ), - ], - ) - ); - }, - icon: Icon(Icons.drive_file_rename_outline) - ), - IconButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Delete Permanently?'), - content: Text(dirEntry.attr.isDirectory ? 'The contents of this folder will be deleted as well\nThis action cannot be undone' : 'This action cannot be undone'), - actions: [ - TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancel')), - TextButton( - onPressed: () async { - // if (dirEntry.attr.isDirectory) { - // Future removeRecursively (String path) async { - // final dirContents = await widget.sftpWorker.listdir(path); - // for (SftpName entry in dirContents) { - // final fullPath = '$path${entry.filename}'; - // if (entry.attr.isDirectory) { - // await removeRecursively('$fullPath/'); - // await widget.sftpWorker.rmdir('$fullPath/'); - // } - // else { - // await widget.sftpWorker.remove(fullPath); - // } - // } - // await widget.sftpWorker.rmdir(path); - // } - // await removeRecursively('${path}${dirEntry.filename}/'); - // } - // else { - // await widget.sftpWorker.remove('${path}${dirEntry.filename}'); - // } - // _listDir(); - // if (context.mounted) { - // Navigator.pop(context); - // } - }, - child: Text('Yes') - ), - ], - ) - ); - }, - icon: Icon(Icons.delete) - ), - ], - ), - onTap: () { - if (dirEntry.attr.isDirectory) { - path = '$path${dirEntry.filename}/'; - _listDir(); - } - }, - ); - }, - ) + ], + ) + ); + }, + icon: Icon(Icons.drive_file_rename_outline) + ), + IconButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Delete Permanently?'), + content: Text(dirEntry.attr.isDirectory ? 'The contents of this folder will be deleted as well\nThis action cannot be undone' : 'This action cannot be undone'), + actions: [ + TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancel')), + TextButton( + onPressed: () async { + // if (dirEntry.attr.isDirectory) { + // Future removeRecursively (String path) async { + // final dirContents = await widget.sftpWorker.listdir(path); + // for (SftpName entry in dirContents) { + // final fullPath = '$path${entry.filename}'; + // if (entry.attr.isDirectory) { + // await removeRecursively('$fullPath/'); + // await widget.sftpWorker.rmdir('$fullPath/'); + // } + // else { + // await widget.sftpWorker.remove(fullPath); + // } + // } + // await widget.sftpWorker.rmdir(path); + // } + // await removeRecursively('${widget.path}${dirEntry.filename}/'); + // } + // else { + // await widget.sftpWorker.remove('${widget.path}${dirEntry.filename}'); + // } + // _listDir(); + // if (context.mounted) { + // Navigator.pop(context); + // } + }, + child: Text('Yes') + ), + ], + ) + ); + }, + icon: Icon(Icons.delete) + ), + ], + ), + onTap: () { + if (dirEntry.attr.isDirectory) { + Navigator.push(context, MaterialPageRoute( + builder: (context) => SftpExplorer( + sftpWorker: widget.sftpWorker, + path: '${widget.path}${dirEntry.filename}/', + ) + )); + } + }, + ); + }, ) ); } @@ -251,18 +220,23 @@ class _SftpExplorerState extends State { TextButton(onPressed: () => Navigator.pop(context), child: Text('Cancel')), TextButton( onPressed: () async { - try { - await widget.sftpWorker.mkdir('${path}${nameController.text}'); - _listDir(); - } - catch (e) { - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar(_buildErrorSnackBar(context, e.toString())); - } - } - if (context.mounted) { - Navigator.pop(context); - } + // try { + // await widget.sftpWorker.mkdir('${widget.path}${nameController.text}'); + // _listDir(); + // } + // on SftpStatusError catch (e) { + // if (context.mounted) { + // if (e.code == 4) { + // ScaffoldMessenger.of(context).showSnackBar(_buildErrorSnackBar(context, 'Folder Already Exists')); + // } + // else { + // ScaffoldMessenger.of(context).showSnackBar(_buildErrorSnackBar(context, 'Error: ${e.message}')); + // } + // } + // } + // if (context.mounted) { + // Navigator.pop(context); + // } }, child: Text('Ok') ), @@ -285,7 +259,8 @@ class _SftpExplorerState extends State { filePaths = files.map((file) => file.path).toList(); } try { - await for (final progress in widget.sftpWorker.uploadFiles(path, filePaths)) { + await for (final progress in widget.sftpWorker.uploadFiles(widget.path, filePaths)) { + print(progress); setState(() => _progress = progress); } } diff --git a/lib/sftp_worker.dart b/lib/sftp_worker.dart index 878c50b..61966e0 100644 --- a/lib/sftp_worker.dart +++ b/lib/sftp_worker.dart @@ -8,13 +8,11 @@ import 'package:path/path.dart'; import 'connection.dart'; sealed class SftpCommand {} - class ListDir extends SftpCommand { final String path; ListDir(this.path); } - class UploadFiles extends SftpCommand { final String path; final List fileNames; @@ -22,13 +20,6 @@ class UploadFiles extends SftpCommand { UploadFiles(this.path, this.fileNames); } -class MkDir extends SftpCommand { - final String path; - - MkDir(this.path); -} - - class SftpWorker { final ReceivePort _responses; @@ -88,7 +79,7 @@ class SftpWorker { static void _sftpCmdHandler(SendPort sendPort, ReceivePort receivePort, SftpClient sftpClient) { receivePort.listen((message) async { - final (int id, SftpCommand command) = message; + final (int id, dynamic command) = message; switch (command) { case ListDir(:final path): try { @@ -124,14 +115,6 @@ class SftpWorker { } sendPort.send((id, 1.0)); } - case MkDir(:final path): - try { - await sftpClient.mkdir(path); - sendPort.send((id, 0)); - } - on SftpStatusError catch (e) { - sendPort.send((id, RemoteError(e.message, ''))); - } } }); } @@ -166,7 +149,7 @@ class SftpWorker { Future> listdir(String path) async { - final completer = Completer.sync(); + final completer = Completer.sync(); final id = _idCounter++; _activeRequests[id] = completer; _commands.send((id, ListDir(path))); @@ -182,13 +165,5 @@ class SftpWorker { return controller.stream; } - Future mkdir(String path) async { - final completer = Completer.sync(); - final id = _idCounter++; - _activeRequests[id] = completer; - _commands.send((id, MkDir(path))); - await completer.future; - } - }