import 'dart:developer'; import 'dart:io'; import 'package:async/async.dart'; import 'package:flutter/material.dart'; import 'package:nextcloud/nextcloud.dart'; import '../../../api/marianumcloud/webdav/webdavApi.dart'; class FileUploadDialog extends StatefulWidget { final String localPath; final List remotePath; final String fileName; final void Function() onUploadFinished; final bool doShowFinish; const FileUploadDialog({Key? key, required this.localPath, required this.remotePath, required this.fileName, required this.onUploadFinished, this.doShowFinish = true}) : super(key: key); @override State createState() => _FileUploadDialogState(); } class _FileUploadDialogState extends State { FileUploadState state = FileUploadState.naming; CancelableOperation? cancelableOperation; late String targetFileName; late String remoteFolderName; late String fullRemotePath = "${widget.remotePath.join("/")}/$targetFileName"; String? lastError; TextEditingController fileNameController = TextEditingController(); void upload({bool override = false}) async { setState(() { state = FileUploadState.upload; }); WebDavClient webdavClient = await WebdavApi.webdav; if(!override) { setState(() { state = FileUploadState.checkConflict; }); List result = (await webdavClient.propfind(widget.remotePath.join("/"))).responses; if(result.any((element) => element.href!.endsWith("/$targetFileName"))) { setState(() { state = FileUploadState.conflict; }); return; } else { setState(() { state = FileUploadState.upload; }); } } Future uploadTask = webdavClient.putFile(File(widget.localPath), FileStat.statSync(widget.localPath), fullRemotePath); // TODO use onProgress from putFile uploadTask.then((value) => Future.value(value)).catchError((e) { setState(() { state = FileUploadState.error; }); return null; }); cancelableOperation = CancelableOperation.fromFuture( uploadTask, onCancel: () => log("Upload cancelled"), ); cancelableOperation!.then((value) { setState(() { state = FileUploadState.done; }); }); } void cancel() { cancelableOperation?.cancel(); setState(() { state = FileUploadState.naming; }); } @override void initState() { super.initState(); targetFileName = widget.fileName; remoteFolderName = widget.remotePath.isNotEmpty ? widget.remotePath.last : "/"; fileNameController.text = widget.fileName; } @override Widget build(BuildContext context) { if(state == FileUploadState.naming) { return AlertDialog( title: const Text("Datei hochladen"), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: fileNameController, onChanged: (input) { targetFileName = input; }, autocorrect: false, decoration: const InputDecoration( labelText: "Dateiname", ), ), ], ), actions: [ TextButton(onPressed: () { Navigator.of(context).pop(); }, child: const Text("Abbrechen")), TextButton(onPressed: () async { upload(); }, child: const Text("Hochladen")), ], ); } if(state == FileUploadState.conflict) { return AlertDialog( icon: const Icon(Icons.error_outline), title: const Text("Datei konflikt"), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text("Es gibt bereits eine Datei mit dem Namen $targetFileName in dem ausgewählten Ordner '$remoteFolderName'", textAlign: TextAlign.center), ], ), actions: [ TextButton(onPressed: () { setState(() { state = FileUploadState.naming; }); }, child: const Text("Datei umbenennen")), TextButton(onPressed: () { upload(override: true); }, child: const Text("Datei überschreiben")), ], ); } if(state == FileUploadState.upload || state == FileUploadState.checkConflict) { return AlertDialog( icon: const Icon(Icons.upload), title: const Text("Hochladen"), content: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ Visibility( visible: state == FileUploadState.upload, replacement: const Text("Prüfe auf dateikonflikte..."), child: const Text("Upload läuft!\nDies kann je nach Dateigröße einige Zeit dauern...", textAlign: TextAlign.center), ), const SizedBox(height: 30), const CircularProgressIndicator() ], ), actions: const [ // TODO implement working upload cancelling // TextButton(onPressed: () { // cancel(); // }, child: const Text("Abbrechen")), ], ); } if(state == FileUploadState.done) { widget.onUploadFinished(); if(!widget.doShowFinish) { Navigator.of(context).pop(); return const SizedBox.shrink(); } return AlertDialog( icon: const Icon(Icons.done), title: const Text("Upload fertig"), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text("Die Datei wurde erfolgreich nach '$remoteFolderName' hochgeladen!", textAlign: TextAlign.center), ], ), actions: [ TextButton(onPressed: () { Navigator.of(context).pop(); }, child: const Text("Fertig")), ], ); } if(state == FileUploadState.error) { return AlertDialog( icon: const Icon(Icons.error_outline), title: const Text("Fehler"), content: const Column( mainAxisSize: MainAxisSize.min, children: [ Text("Es ist ein Fehler aufgetreten!", textAlign: TextAlign.center), ], ), actions: [ TextButton(onPressed: () { Navigator.of(context).pop(); }, child: const Text("Schlißen")), ], ); } throw UnimplementedError("Invalid state"); } } enum FileUploadState { naming, checkConflict, conflict, upload, done, error }