235 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| 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<String> 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<FileUploadDialog> createState() => _FileUploadDialogState();
 | |
| }
 | |
| 
 | |
| class _FileUploadDialogState extends State<FileUploadDialog> {
 | |
|   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;
 | |
|       });
 | |
|       await (await WebdavApi.webdav).mkdirs(widget.remotePath.join("/"));
 | |
|       List<WebDavResponse> result = (await webdavClient.ls(widget.remotePath.join("/"))).responses;
 | |
|       if(result.any((element) => element.href!.endsWith("/$targetFileName"))) {
 | |
|         setState(() {
 | |
|           state = FileUploadState.conflict;
 | |
|         });
 | |
|         return;
 | |
|       } else {
 | |
|         setState(() {
 | |
|           state = FileUploadState.upload;
 | |
|         });
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Future<HttpClientResponse> uploadTask = webdavClient.upload(File(widget.localPath).readAsBytesSync(), fullRemotePath);
 | |
|     uploadTask.then((value) => Future<HttpClientResponse?>.value(value)).catchError((e) {
 | |
|       setState(() {
 | |
|         state = FileUploadState.error;
 | |
|       });
 | |
|       return null;
 | |
|     });
 | |
| 
 | |
| 
 | |
|     cancelableOperation = CancelableOperation<HttpClientResponse>.fromFuture(
 | |
|       uploadTask,
 | |
|       onCancel: () => log("Upload cancelled"),
 | |
|     );
 | |
| 
 | |
|     cancelableOperation!.then((value) {
 | |
|       log("Upload done!");
 | |
|       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
 | |
| } |