From b4defb9eda87c5358a85e520e1ce5ba147476216 Mon Sep 17 00:00:00 2001
From: Pupsi28 <larslukasneuhaus@gmx.de>
Date: Fri, 5 Apr 2024 18:16:12 +0200
Subject: [PATCH 1/6] added upload with multiple files

---
 lib/view/pages/files/files.dart               |  36 +-
 lib/view/pages/files/filesUploadDialog.dart   | 318 ++++++++++++++++++
 .../pages/talk/components/chatTextfield.dart  |  53 +--
 lib/widget/filePick.dart                      |   7 +-
 4 files changed, 369 insertions(+), 45 deletions(-)
 create mode 100644 lib/view/pages/files/filesUploadDialog.dart

diff --git a/lib/view/pages/files/files.dart b/lib/view/pages/files/files.dart
index b377cbf..f0c9add 100644
--- a/lib/view/pages/files/files.dart
+++ b/lib/view/pages/files/files.dart
@@ -1,9 +1,11 @@
 
+import 'dart:developer';
 import 'dart:io';
 
 import 'package:flutter/material.dart';
 import 'package:loader_overlay/loader_overlay.dart';
 import 'package:nextcloud/nextcloud.dart';
+import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
 import 'package:provider/provider.dart';
 
 import '../../../api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart';
@@ -15,8 +17,8 @@ import '../../../storage/base/settingsProvider.dart';
 import '../../../widget/loadingSpinner.dart';
 import '../../../widget/placeholderView.dart';
 import '../../../widget/filePick.dart';
-import 'fileUploadDialog.dart';
 import 'fileElement.dart';
+import 'filesUploadDialog.dart';
 
 class Files extends StatefulWidget {
   final List<String> path;
@@ -90,7 +92,8 @@ class _FilesState extends State<Files> {
     ListFilesCache(
         path: widget.path.isEmpty ? '/' : widget.path.join('/'),
         onUpdate: (ListFilesResponse d) {
-          if(!context.mounted) return; // prevent setState when widget is possibly already disposed
+          log('_query');
+          if(!context.mounted) return;  // prevent setState when widget is possibly already disposed
           d.files.removeWhere((element) => element.name.isEmpty || element.name == widget.path.lastOrNull());
           setState(() {
             data = d;
@@ -99,6 +102,18 @@ class _FilesState extends State<Files> {
     );
   }
 
+  void mediaUpload(List<String>? paths) async {
+    if(paths == null) return;
+
+    pushScreen(
+      context,
+      withNavBar: false,
+      screen: FilesUploadDialog(filePaths: paths, remotePath: widget.path.join('/'), onUploadFinished: (uploadedFilePaths) => _query),
+    );
+
+    return;
+  }
+
   @override
   Widget build(BuildContext context) {
     List<CacheableFile> files = data?.sortBy(
@@ -149,7 +164,7 @@ class _FilesState extends State<Files> {
                     children: [
                       Icon(SortOptions.getOption(key).icon, color: Theme.of(context).colorScheme.onSurface),
                       const SizedBox(width: 15),
-                      Text(SortOptions.getOption(key).displayName)
+                      Text(SortOptions.getOption(key).displayName),
                     ],
                   )
               )).toList();
@@ -204,7 +219,6 @@ class _FilesState extends State<Files> {
                   leading: const Icon(Icons.upload_file),
                   title: const Text('Aus Dateien hochladen'),
                   onTap: () {
-                    context.loaderOverlay.show();
                     FilePick.documentPick().then(mediaUpload);
                     Navigator.of(context).pop();
                   },
@@ -215,9 +229,8 @@ class _FilesState extends State<Files> {
                     leading: const Icon(Icons.add_a_photo_outlined),
                     title: const Text('Aus Gallerie hochladen'),
                     onTap: () {
-                      context.loaderOverlay.show();
                       FilePick.galleryPick().then((value) {
-                        mediaUpload(value?.path);
+                        if(value != null) mediaUpload([value.path]);
                       });
                       Navigator.of(context).pop();
                     },
@@ -247,15 +260,4 @@ class _FilesState extends State<Files> {
       )
     );
   }
-
-  void mediaUpload(String? path) async {
-    context.loaderOverlay.hide();
-
-    if(path == null) {
-      return;
-    }
-
-    var fileName = path.split(Platform.pathSeparator).last;
-    showDialog(context: context, builder: (context) => FileUploadDialog(localPath: path, remotePath: widget.path, fileName: fileName, onUploadFinished: _query), barrierDismissible: false);
-  }
 }
diff --git a/lib/view/pages/files/filesUploadDialog.dart b/lib/view/pages/files/filesUploadDialog.dart
new file mode 100644
index 0000000..9f9ce02
--- /dev/null
+++ b/lib/view/pages/files/filesUploadDialog.dart
@@ -0,0 +1,318 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:loader_overlay/loader_overlay.dart';
+import 'package:nextcloud/nextcloud.dart';
+import 'package:uuid/uuid.dart';
+
+import '../../../api/marianumcloud/webdav/webdavApi.dart';
+import '../../../widget/focusBehaviour.dart';
+
+class FilesUploadDialog extends StatefulWidget {
+  final List<String> filePaths;
+  final String remotePath;
+  final void Function(List<String> uploadedFilePaths) onUploadFinished;
+  final bool uniqueNames;
+
+  const FilesUploadDialog({super.key, required this.filePaths, required this.remotePath, required this.onUploadFinished, this.uniqueNames = false});
+
+  @override
+  State<FilesUploadDialog> createState() => _FilesUploadDialogState();
+}
+
+class UploadableFile {
+  TextEditingController fileNameController = TextEditingController();
+  String filePath;
+  String fileName;
+  double? _uploadProgress;
+  bool isConflicting = false;
+
+  UploadableFile(this.filePath, this.fileName);
+}
+
+
+class _FilesUploadDialogState extends State<FilesUploadDialog> {
+  final List<UploadableFile> _uploadableFiles = [];
+  bool _isUploading = false;
+  double _progressValue = 0.0;
+  String _infoText = '';
+
+  @override
+  void initState() {
+    super.initState();
+
+    _uploadableFiles.addAll(widget.filePaths.map((filePath) {
+      String fileName = filePath.split(Platform.pathSeparator).last;
+      return UploadableFile(filePath, fileName);
+    }));
+  }
+
+  void showErrorMessage(int errorCode){
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: const Text('Ein Fehler ist aufgetreten'),
+          contentPadding: const EdgeInsets.all(10),
+          content: Text('Error code: $errorCode'),
+          actions: [
+            TextButton(
+              onPressed: () => Navigator.of(context).pop(),
+              child: const Text('Schließen', textAlign: TextAlign.center),
+            ),
+          ],
+        );
+      }
+    );
+  }
+
+  void uploadSelectedFiles({bool override = false}) async {
+    setState(() {
+      _isUploading = true;
+      _infoText = 'Vorbereiten';
+    });
+
+    for (var element in _uploadableFiles) {
+      setState(() {
+        element.isConflicting = false;
+      });
+    }
+
+    WebDavClient webdavClient = await WebdavApi.webdav;
+
+    if (!override) {
+      List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath))).responses;
+      List<UploadableFile> conflictingFiles = [];
+
+      for (var file in _uploadableFiles) {
+        String fileName = file.fileName;
+        if (result.any((element) => Uri.decodeComponent(element.href!).endsWith('/$fileName'))) {
+          // konflikt
+          conflictingFiles.add(file);
+        }
+      }
+
+      if(conflictingFiles.isNotEmpty) {
+        bool replaceFiles = await showDialog(
+          context: context,
+          barrierDismissible: false,
+          builder: (context) {
+            return AlertDialog(
+              contentPadding: const EdgeInsets.all(10),
+              title: const Text('Konflikt', textAlign: TextAlign.center),
+              content: conflictingFiles.length == 1 ?
+              Text(
+                'Eine Datei mit dem Namen "${conflictingFiles.map((e) => e.fileName).first}" existiert bereits.\n'
+                    '(Datei ${_uploadableFiles.indexOf(conflictingFiles.first)+1})',
+                textAlign: TextAlign.left,
+              ) :
+              Text(
+                '${conflictingFiles.length} Dateien mit den Namen ${conflictingFiles.map((e) => e.fileName).toList()} existieren bereits.\n'
+                    '(Dateien ${conflictingFiles.map((e) => _uploadableFiles.indexOf(e)+1).toList()})',
+                textAlign: TextAlign.left,
+              ),
+              actions: [
+                TextButton(
+                  onPressed: () {
+                    Navigator.pop(context, false);
+                  },
+                  child: const Text('Bearbeiten', textAlign: TextAlign.center),
+                ),
+                TextButton(
+                  onPressed: () {
+                    Navigator.pop(context, true);
+                  },
+                  child: const Text('Ersetzen', textAlign: TextAlign.center),
+                ),
+              ],
+            );
+          }
+        );
+
+        if(!replaceFiles) {
+          for (var element in conflictingFiles) {
+            element.isConflicting = true;
+          }
+          setState(() {
+            _isUploading = false;
+            _progressValue = 0.0;
+            _infoText = '';
+          });
+          return;
+        }
+      }
+    }
+
+    List<String> uploadetFilePaths = [];
+    for (var file in _uploadableFiles) {
+      String fileName = file.fileName;
+      String filePath = file.filePath;
+
+      if(widget.uniqueNames) fileName = '${fileName.split('.').first}-${const Uuid().v4()}.${fileName.split('.').last}';
+
+      String fullRemotePath = '${widget.remotePath}/$fileName';
+
+      setState(() {
+        _infoText = '${_uploadableFiles.indexOf(file) + 1}/${_uploadableFiles.length}';
+      });
+
+      HttpClientResponse uploadTask = await webdavClient.putFile(
+        File(filePath),
+        FileStat.statSync(filePath),
+        PathUri.parse(fullRemotePath),
+        onProgress: (progress) {
+          setState(() {
+            file._uploadProgress = progress;
+            _progressValue = ((progress + _uploadableFiles.indexOf(file)) / _uploadableFiles.length).toDouble();
+          });
+        },
+      );
+
+      if(uploadTask.statusCode < 200 || uploadTask.statusCode > 299) {
+        // error code
+        setState(() {
+          _isUploading = false;
+          _progressValue = 0.0;
+          _infoText = '';
+        });
+        Navigator.of(context).pop();
+        showErrorMessage(uploadTask.statusCode);
+      } else {
+        uploadetFilePaths.add(fullRemotePath);
+      }
+    }
+
+    setState(() {
+      _isUploading = false;
+      _progressValue = 0.0;
+      _infoText = '';
+    });
+    Navigator.of(context).pop();
+    widget.onUploadFinished(uploadetFilePaths);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Dateien hochladen'),
+        automaticallyImplyLeading: false,
+      ),
+      body: LoaderOverlay(
+        overlayWholeScreen: true,
+        child: Column(
+          children: [
+            Expanded(
+              child: ListView.builder(
+                itemCount: _uploadableFiles.length,
+                itemBuilder: (context, index) {
+                  final currentFile = _uploadableFiles[index];
+                  currentFile.fileNameController.text = currentFile.fileName;
+                  return ListTile(
+                    title: TextField(
+                      readOnly: _isUploading,
+                      controller: currentFile.fileNameController,
+                      decoration: InputDecoration(
+                        border: const UnderlineInputBorder(),
+                        label: Text('Datei ${index+1}'),
+                        errorText: currentFile.isConflicting ? 'existiert bereits' : null,
+                        errorStyle: const TextStyle(color: Colors.red),
+                      ),
+                      onChanged: (input) {
+                        currentFile.fileName = input;
+                      },
+                      onTapOutside: (PointerDownEvent event) {
+                        FocusBehaviour.textFieldTapOutside(context);
+                        if(currentFile.isConflicting){
+                          setState(() {
+                            currentFile.isConflicting = false;
+                          });
+                        }
+                      },
+                      onEditingComplete: () {
+                        if(currentFile.isConflicting){
+                          setState(() {
+                            currentFile.isConflicting = false;
+                          });
+                        }
+                      },
+                    ),
+                    subtitle: _isUploading && (currentFile._uploadProgress ?? 0) < 1 ? LinearProgressIndicator(
+                      value: currentFile._uploadProgress,
+                      borderRadius: const BorderRadius.all(Radius.circular(2)),
+                    ) : null,
+                    leading: Container(
+                      width: 24,
+                      height: 24,
+                      padding: EdgeInsets.zero,
+                      child: IconButton(
+                        tooltip: 'Datei entfernen',
+                        padding: EdgeInsets.zero,
+                        onPressed: () {
+                          if(!_isUploading) {
+                            if(_uploadableFiles.length-1 <= 0) Navigator.of(context).pop();
+                            setState(() {
+                              _uploadableFiles.removeAt(index);
+                            });
+                          }
+                        },
+                        icon: const Icon(Icons.close_outlined),
+                      ),
+                    ),
+                    trailing: Container(
+                      width: 24,
+                      height: 24,
+                      padding: EdgeInsets.zero,
+                      child: IconButton(
+                        tooltip: 'Namen löschen',
+                        padding: EdgeInsets.zero,
+                        onPressed: () {
+                          if(!_isUploading) {
+                            setState(() {
+                              currentFile.fileName = '';
+                            });
+                          }
+                        },
+                        icon: const Icon(Icons.delete_outlined),
+                      ),
+                    ),
+                  );
+                },
+              ),
+            ),
+            Padding(
+              padding: const EdgeInsets.only(left: 15, right: 15, bottom: 15, top: 5),
+              child: Row(
+                mainAxisAlignment: MainAxisAlignment.end,
+                children: [
+                  Visibility(
+                    visible: !_isUploading,
+                    child: TextButton(
+                      onPressed: () => Navigator.of(context).pop(),
+                      child: const Text('Abbrechen'),
+                    ),
+                  ),
+                  const Expanded(child: SizedBox.shrink()),
+                  Visibility(
+                    visible: _isUploading,
+                    replacement: TextButton(
+                      onPressed: () => uploadSelectedFiles(override: widget.uniqueNames),
+                      child: const Text('Hochladen'),
+                    ),
+                    child: Stack(
+                      alignment: Alignment.center,
+                      children: [
+                        CircularProgressIndicator(value: _progressValue),
+                        Center(child: Text(_infoText)),
+                      ],
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
\ No newline at end of file
diff --git a/lib/view/pages/talk/components/chatTextfield.dart b/lib/view/pages/talk/components/chatTextfield.dart
index 5513d8a..09c81d7 100644
--- a/lib/view/pages/talk/components/chatTextfield.dart
+++ b/lib/view/pages/talk/components/chatTextfield.dart
@@ -1,10 +1,9 @@
 
 import 'dart:io';
 import 'package:flutter/material.dart';
-import 'package:loader_overlay/loader_overlay.dart';
 import 'package:nextcloud/nextcloud.dart';
+import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
 import 'package:provider/provider.dart';
-import 'package:uuid/uuid.dart';
 
 import '../../../../api/marianumcloud/files-sharing/fileSharingApi.dart';
 import '../../../../api/marianumcloud/files-sharing/fileSharingApiParams.dart';
@@ -15,7 +14,7 @@ import '../../../../model/chatList/chatProps.dart';
 import '../../../../storage/base/settingsProvider.dart';
 import '../../../../widget/filePick.dart';
 import '../../../../widget/focusBehaviour.dart';
-import '../../files/fileUploadDialog.dart';
+import '../../files/filesUploadDialog.dart';
 
 class ChatTextfield extends StatefulWidget {
   final String sendToToken;
@@ -34,34 +33,40 @@ class _ChatTextfieldState extends State<ChatTextfield> {
     Provider.of<ChatProps>(context, listen: false).run();
   }
 
-  void mediaUpload(String? path) async {
-    context.loaderOverlay.hide();
-
-    if(path == null) {
-      return;
+  void share(String shareFolder, List<String> filePaths) {
+    for (var element in filePaths) {
+      String fileName = element.split(Platform.pathSeparator).last;
+      FileSharingApi().share(FileSharingApiParams(
+          shareType: 10,
+          shareWith: widget.sendToToken,
+          path: '$shareFolder/$fileName',
+      )).then((value) => _query());
     }
+  }
+
+  void mediaUpload(List<String>? paths) async {
+    if (paths == null) return;
 
-    String filename = "${path.split("/").last.split(".").first}-${const Uuid().v4()}.${path.split(".").last}";
     String shareFolder = 'MarianumMobile';
     WebdavApi.webdav.then((webdav) {
       webdav.mkcol(PathUri.parse('/$shareFolder'));
     });
 
-    showDialog(context: context, builder: (context) => FileUploadDialog(
-      doShowFinish: false,
-      fileName: filename,
-      localPath: path,
-      remotePath: [shareFolder],
-      onUploadFinished: () {
-        FileSharingApi().share(FileSharingApiParams(
-          shareType: 10,
-          shareWith: widget.sendToToken,
-          path: '$shareFolder/$filename',
-        )).then((value) => _query());
-      },
-    ), barrierDismissible: false);
+    pushScreen(
+      context,
+      withNavBar: false,
+      screen: FilesUploadDialog(
+        filePaths: paths,
+        remotePath: shareFolder,
+        onUploadFinished: (uploadedFilePaths) {
+          share(shareFolder, uploadedFilePaths);
+        },
+        uniqueNames: true,
+      ),
+    );
   }
 
+
   void setDraft(String text) {
     if(text.isNotEmpty) {
       settings.val(write: true).talkSettings.drafts[widget.sendToToken] = text;
@@ -98,7 +103,6 @@ class _ChatTextfieldState extends State<ChatTextfield> {
                             leading: const Icon(Icons.file_open),
                             title: const Text('Aus Dateien auswählen'),
                             onTap: () {
-                              context.loaderOverlay.show();
                               FilePick.documentPick().then(mediaUpload);
                               Navigator.of(context).pop();
                             },
@@ -109,9 +113,8 @@ class _ChatTextfieldState extends State<ChatTextfield> {
                               leading: const Icon(Icons.image),
                               title: const Text('Aus Gallerie auswählen'),
                               onTap: () {
-                                context.loaderOverlay.show();
                                 FilePick.galleryPick().then((value) {
-                                  mediaUpload(value?.path);
+                                  if(value != null) mediaUpload([value.path]);
                                 });
                                 Navigator.of(context).pop();
                               },
diff --git a/lib/widget/filePick.dart b/lib/widget/filePick.dart
index b37fef4..0d7c0e5 100644
--- a/lib/widget/filePick.dart
+++ b/lib/widget/filePick.dart
@@ -13,8 +13,9 @@ class FilePick {
     return null;
   }
 
-  static Future<String?> documentPick() async {
-    FilePickerResult? result = await FilePicker.platform.pickFiles();
-    return result?.files.single.path;
+  static Future<List<String>?> documentPick() async {
+    FilePickerResult? result = await FilePicker.platform.pickFiles(allowMultiple: true);
+    List<String?>? paths = result?.files.nonNulls.map((e) => e.path).toList();
+    return paths?.nonNulls.toList();
   }
 }
\ No newline at end of file
-- 
2.30.2


From 277b3366f9d0ea308fbde23e8519c8fbbfdac514 Mon Sep 17 00:00:00 2001
From: Pupsi28 <larslukasneuhaus@gmx.de>
Date: Fri, 5 Apr 2024 18:19:49 +0200
Subject: [PATCH 2/6] added upload with multiple files

---
 lib/view/pages/files/fileUploadDialog.dart | 233 ---------------------
 1 file changed, 233 deletions(-)
 delete mode 100644 lib/view/pages/files/fileUploadDialog.dart

diff --git a/lib/view/pages/files/fileUploadDialog.dart b/lib/view/pages/files/fileUploadDialog.dart
deleted file mode 100644
index 6322ee5..0000000
--- a/lib/view/pages/files/fileUploadDialog.dart
+++ /dev/null
@@ -1,233 +0,0 @@
-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({super.key, required this.localPath, required this.remotePath, required this.fileName, required this.onUploadFinished, this.doShowFinish = true});
-
-  @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;
-      });
-      List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(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.putFile(File(widget.localPath), FileStat.statSync(widget.localPath), PathUri.parse(fullRemotePath)); // TODO use onProgress from putFile
-    uploadTask.then(Future<HttpClientResponse?>.value).catchError((e) {
-      setState(() {
-        state = FileUploadState.error;
-      });
-      return null;
-    });
-
-
-    cancelableOperation = CancelableOperation<HttpClientResponse>.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
-}
\ No newline at end of file
-- 
2.30.2


From e901f139d60902f0134d9d7eb58eb39e4c21adbf Mon Sep 17 00:00:00 2001
From: Pupsi28 <larslukasneuhaus@gmx.de>
Date: Sat, 6 Apr 2024 13:34:52 +0200
Subject: [PATCH 3/6] solved some pr comments

---
 lib/view/pages/files/files.dart             |  3 -
 lib/view/pages/files/filesUploadDialog.dart | 70 +++++++++++++--------
 2 files changed, 43 insertions(+), 30 deletions(-)

diff --git a/lib/view/pages/files/files.dart b/lib/view/pages/files/files.dart
index f0c9add..3402bba 100644
--- a/lib/view/pages/files/files.dart
+++ b/lib/view/pages/files/files.dart
@@ -1,5 +1,4 @@
 
-import 'dart:developer';
 import 'dart:io';
 
 import 'package:flutter/material.dart';
@@ -92,8 +91,6 @@ class _FilesState extends State<Files> {
     ListFilesCache(
         path: widget.path.isEmpty ? '/' : widget.path.join('/'),
         onUpdate: (ListFilesResponse d) {
-          log('_query');
-          if(!context.mounted) return;  // prevent setState when widget is possibly already disposed
           d.files.removeWhere((element) => element.name.isEmpty || element.name == widget.path.lastOrNull());
           setState(() {
             data = d;
diff --git a/lib/view/pages/files/filesUploadDialog.dart b/lib/view/pages/files/filesUploadDialog.dart
index 9f9ce02..8e19d8a 100644
--- a/lib/view/pages/files/filesUploadDialog.dart
+++ b/lib/view/pages/files/filesUploadDialog.dart
@@ -6,6 +6,7 @@ import 'package:nextcloud/nextcloud.dart';
 import 'package:uuid/uuid.dart';
 
 import '../../../api/marianumcloud/webdav/webdavApi.dart';
+import '../../../widget/confirmDialog.dart';
 import '../../../widget/focusBehaviour.dart';
 
 class FilesUploadDialog extends StatefulWidget {
@@ -32,29 +33,34 @@ class UploadableFile {
 
 
 class _FilesUploadDialogState extends State<FilesUploadDialog> {
-  final List<UploadableFile> _uploadableFiles = [];
+  late List<UploadableFile> _uploadableFiles;
   bool _isUploading = false;
-  double _progressValue = 0.0;
+  double _overallProgressValue = 0.0;
   String _infoText = '';
 
   @override
   void initState() {
     super.initState();
 
-    _uploadableFiles.addAll(widget.filePaths.map((filePath) {
+    _uploadableFiles = widget.filePaths.map((filePath) {
       String fileName = filePath.split(Platform.pathSeparator).last;
       return UploadableFile(filePath, fileName);
-    }));
+    }).toList();
+
+    /*_uploadableFiles.addAll(widget.filePaths.map((filePath) {
+      String fileName = filePath.split(Platform.pathSeparator).last;
+      return UploadableFile(filePath, fileName);
+    }));*/
   }
 
-  void showErrorMessage(int errorCode){
+  void showHttpErrorCode(int httpErrorCode){
     showDialog(
       context: context,
       builder: (BuildContext context) {
         return AlertDialog(
           title: const Text('Ein Fehler ist aufgetreten'),
           contentPadding: const EdgeInsets.all(10),
-          content: Text('Error code: $errorCode'),
+          content: Text('Error code: $httpErrorCode'),
           actions: [
             TextButton(
               onPressed: () => Navigator.of(context).pop(),
@@ -66,31 +72,27 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
     );
   }
 
-  void uploadSelectedFiles({bool override = false}) async {
+  void uploadFiles({bool override = false}) async {
     setState(() {
       _isUploading = true;
       _infoText = 'Vorbereiten';
+      for (var file in _uploadableFiles) {
+        file.isConflicting = false;
+      }
     });
 
-    for (var element in _uploadableFiles) {
-      setState(() {
-        element.isConflicting = false;
-      });
-    }
-
     WebDavClient webdavClient = await WebdavApi.webdav;
 
     if (!override) {
       List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath))).responses;
-      List<UploadableFile> conflictingFiles = [];
-
-      for (var file in _uploadableFiles) {
+      List<UploadableFile> conflictingFiles = _uploadableFiles.where((file) {
         String fileName = file.fileName;
         if (result.any((element) => Uri.decodeComponent(element.href!).endsWith('/$fileName'))) {
           // konflikt
-          conflictingFiles.add(file);
+          return true;
         }
-      }
+        return false;
+      }).toList();
 
       if(conflictingFiles.isNotEmpty) {
         bool replaceFiles = await showDialog(
@@ -120,9 +122,23 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                 ),
                 TextButton(
                   onPressed: () {
-                    Navigator.pop(context, true);
+                    showDialog(
+                      context: context,
+                      builder: (context) {
+                        return ConfirmDialog(
+                          title: 'Bist du sicher?',
+                          content: '',
+                          onConfirm: () {
+                            Navigator.pop(context, true);
+                          },
+                          confirmButton: 'Ja',
+                          cancelButton: 'Nein',
+                        );
+                      }
+                    );
+
                   },
-                  child: const Text('Ersetzen', textAlign: TextAlign.center),
+                  child: const Text('Überschreiben', textAlign: TextAlign.center),
                 ),
               ],
             );
@@ -135,7 +151,7 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
           }
           setState(() {
             _isUploading = false;
-            _progressValue = 0.0;
+            _overallProgressValue = 0.0;
             _infoText = '';
           });
           return;
@@ -163,7 +179,7 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
         onProgress: (progress) {
           setState(() {
             file._uploadProgress = progress;
-            _progressValue = ((progress + _uploadableFiles.indexOf(file)) / _uploadableFiles.length).toDouble();
+            _overallProgressValue = ((progress + _uploadableFiles.indexOf(file)) / _uploadableFiles.length).toDouble();
           });
         },
       );
@@ -172,11 +188,11 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
         // error code
         setState(() {
           _isUploading = false;
-          _progressValue = 0.0;
+          _overallProgressValue = 0.0;
           _infoText = '';
         });
         Navigator.of(context).pop();
-        showErrorMessage(uploadTask.statusCode);
+        showHttpErrorCode(uploadTask.statusCode);
       } else {
         uploadetFilePaths.add(fullRemotePath);
       }
@@ -184,7 +200,7 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
 
     setState(() {
       _isUploading = false;
-      _progressValue = 0.0;
+      _overallProgressValue = 0.0;
       _infoText = '';
     });
     Navigator.of(context).pop();
@@ -296,13 +312,13 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                   Visibility(
                     visible: _isUploading,
                     replacement: TextButton(
-                      onPressed: () => uploadSelectedFiles(override: widget.uniqueNames),
+                      onPressed: () => uploadFiles(override: widget.uniqueNames),
                       child: const Text('Hochladen'),
                     ),
                     child: Stack(
                       alignment: Alignment.center,
                       children: [
-                        CircularProgressIndicator(value: _progressValue),
+                        CircularProgressIndicator(value: _overallProgressValue),
                         Center(child: Text(_infoText)),
                       ],
                     ),
-- 
2.30.2


From 8131ccae1e77dfaf11455004880cf6bc353f8dff Mon Sep 17 00:00:00 2001
From: Pupsi28 <larslukasneuhaus@gmx.de>
Date: Sun, 7 Apr 2024 15:49:43 +0200
Subject: [PATCH 4/6] solved some pr comments

---
 lib/view/pages/files/files.dart             |  2 +-
 lib/view/pages/files/filesUploadDialog.dart | 15 +++++++++------
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/lib/view/pages/files/files.dart b/lib/view/pages/files/files.dart
index 3402bba..00b6719 100644
--- a/lib/view/pages/files/files.dart
+++ b/lib/view/pages/files/files.dart
@@ -105,7 +105,7 @@ class _FilesState extends State<Files> {
     pushScreen(
       context,
       withNavBar: false,
-      screen: FilesUploadDialog(filePaths: paths, remotePath: widget.path.join('/'), onUploadFinished: (uploadedFilePaths) => _query),
+      screen: FilesUploadDialog(filePaths: paths, remotePath: widget.path.join('/'), onUploadFinished: (uploadedFilePaths) => _query()),
     );
 
     return;
diff --git a/lib/view/pages/files/filesUploadDialog.dart b/lib/view/pages/files/filesUploadDialog.dart
index 8e19d8a..c90b5b0 100644
--- a/lib/view/pages/files/filesUploadDialog.dart
+++ b/lib/view/pages/files/filesUploadDialog.dart
@@ -108,11 +108,14 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                     '(Datei ${_uploadableFiles.indexOf(conflictingFiles.first)+1})',
                 textAlign: TextAlign.left,
               ) :
-              Text(
-                '${conflictingFiles.length} Dateien mit den Namen ${conflictingFiles.map((e) => e.fileName).toList()} existieren bereits.\n'
-                    '(Dateien ${conflictingFiles.map((e) => _uploadableFiles.indexOf(e)+1).toList()})',
-                textAlign: TextAlign.left,
+              SingleChildScrollView(
+                child: Text(
+                  '${conflictingFiles.length} Dateien mit den Namen: ${conflictingFiles.map((e) => '\n${e.fileName}').join(', ')}\n existieren bereits.\n'
+                      '(Dateien ${conflictingFiles.map((e) => _uploadableFiles.indexOf(e)+1).join(', ')})',
+                  textAlign: TextAlign.left,
+                ),
               ),
+
               actions: [
                 TextButton(
                   onPressed: () {
@@ -126,8 +129,8 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                       context: context,
                       builder: (context) {
                         return ConfirmDialog(
-                          title: 'Bist du sicher?',
-                          content: '',
+                          title: 'Bestätigen',
+                          content: 'Bist du sicher, dass du ${conflictingFiles.map((e) => e.fileName).toList()} überschreiben möchtest?',
                           onConfirm: () {
                             Navigator.pop(context, true);
                           },
-- 
2.30.2


From d8c72a5d2865249e15c9613d28e7300ebab27f07 Mon Sep 17 00:00:00 2001
From: Pupsi28 <larslukasneuhaus@gmx.de>
Date: Sun, 7 Apr 2024 16:48:38 +0200
Subject: [PATCH 5/6] solved most pr comments and a bug

---
 lib/view/pages/files/files.dart               |   6 +-
 lib/view/pages/files/filesUploadDialog.dart   | 108 ++++++++----------
 .../pages/talk/components/chatTextfield.dart  |   6 +-
 3 files changed, 55 insertions(+), 65 deletions(-)

diff --git a/lib/view/pages/files/files.dart b/lib/view/pages/files/files.dart
index 2b1a6f9..a39bdbe 100644
--- a/lib/view/pages/files/files.dart
+++ b/lib/view/pages/files/files.dart
@@ -65,9 +65,7 @@ class SortOptions {
     )
   };
 
-  static BetterSortOption getOption(SortOption option) {
-    return options[option]!;
-  }
+  static BetterSortOption getOption(SortOption option) => options[option]!;
 }
 
 class _FilesState extends State<Files> {
@@ -99,7 +97,7 @@ class _FilesState extends State<Files> {
     );
   }
 
-  void mediaUpload(List<String>? paths) async {
+  Future<void> mediaUpload(List<String>? paths) async {
     if(paths == null) return;
 
     pushScreen(
diff --git a/lib/view/pages/files/filesUploadDialog.dart b/lib/view/pages/files/filesUploadDialog.dart
index c90b5b0..794d9a7 100644
--- a/lib/view/pages/files/filesUploadDialog.dart
+++ b/lib/view/pages/files/filesUploadDialog.dart
@@ -43,21 +43,15 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
     super.initState();
 
     _uploadableFiles = widget.filePaths.map((filePath) {
-      String fileName = filePath.split(Platform.pathSeparator).last;
+      var fileName = filePath.split(Platform.pathSeparator).last;
       return UploadableFile(filePath, fileName);
     }).toList();
-
-    /*_uploadableFiles.addAll(widget.filePaths.map((filePath) {
-      String fileName = filePath.split(Platform.pathSeparator).last;
-      return UploadableFile(filePath, fileName);
-    }));*/
   }
 
   void showHttpErrorCode(int httpErrorCode){
     showDialog(
       context: context,
-      builder: (BuildContext context) {
-        return AlertDialog(
+      builder: (BuildContext context) => AlertDialog(
           title: const Text('Ein Fehler ist aufgetreten'),
           contentPadding: const EdgeInsets.all(10),
           content: Text('Error code: $httpErrorCode'),
@@ -67,12 +61,11 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
               child: const Text('Schließen', textAlign: TextAlign.center),
             ),
           ],
-        );
-      }
+        )
     );
   }
 
-  void uploadFiles({bool override = false}) async {
+  Future<void> uploadFiles({bool override = false}) async {
     setState(() {
       _isUploading = true;
       _infoText = 'Vorbereiten';
@@ -81,41 +74,33 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
       }
     });
 
-    WebDavClient webdavClient = await WebdavApi.webdav;
+    var webdavClient = await WebdavApi.webdav;
 
     if (!override) {
-      List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath))).responses;
-      List<UploadableFile> conflictingFiles = _uploadableFiles.where((file) {
-        String fileName = file.fileName;
-        if (result.any((element) => Uri.decodeComponent(element.href!).endsWith('/$fileName'))) {
-          // konflikt
-          return true;
-        }
-        return false;
+      var result = (await webdavClient.propfind(PathUri.parse(widget.remotePath))).responses;
+      var conflictingFiles = _uploadableFiles.where((file) {
+        var fileName = file.fileName;
+        return result.any((element) => Uri.decodeComponent(element.href!).endsWith('/$fileName'));
       }).toList();
 
       if(conflictingFiles.isNotEmpty) {
         bool replaceFiles = await showDialog(
           context: context,
           barrierDismissible: false,
-          builder: (context) {
-            return AlertDialog(
+          builder: (context) => AlertDialog(
               contentPadding: const EdgeInsets.all(10),
               title: const Text('Konflikt', textAlign: TextAlign.center),
               content: conflictingFiles.length == 1 ?
               Text(
-                'Eine Datei mit dem Namen "${conflictingFiles.map((e) => e.fileName).first}" existiert bereits.\n'
-                    '(Datei ${_uploadableFiles.indexOf(conflictingFiles.first)+1})',
+                'Eine Datei mit dem Namen "${conflictingFiles.map((e) => e.fileName).first}" existiert bereits.',
                 textAlign: TextAlign.left,
               ) :
               SingleChildScrollView(
                 child: Text(
-                  '${conflictingFiles.length} Dateien mit den Namen: ${conflictingFiles.map((e) => '\n${e.fileName}').join(', ')}\n existieren bereits.\n'
-                      '(Dateien ${conflictingFiles.map((e) => _uploadableFiles.indexOf(e)+1).join(', ')})',
+                  '${conflictingFiles.length} Dateien mit folgenden Namen existieren bereits: \n${conflictingFiles.map((e) => '\n -  ${e.fileName}').join('')}',
                   textAlign: TextAlign.left,
                 ),
               ),
-
               actions: [
                 TextButton(
                   onPressed: () {
@@ -127,55 +112,52 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                   onPressed: () {
                     showDialog(
                       context: context,
-                      builder: (context) {
-                        return ConfirmDialog(
-                          title: 'Bestätigen',
-                          content: 'Bist du sicher, dass du ${conflictingFiles.map((e) => e.fileName).toList()} überschreiben möchtest?',
-                          onConfirm: () {
-                            Navigator.pop(context, true);
-                          },
-                          confirmButton: 'Ja',
-                          cancelButton: 'Nein',
-                        );
-                      }
+                      builder: (context) => ConfirmDialog(
+                        title: 'Bestätigen?',
+                        content: 'Bist du sicher, dass du ${conflictingFiles.length} Dateien überschreiben möchtest?',
+                        onConfirm: () {
+                          Navigator.pop(context, true);
+                        },
+                        confirmButton: 'Ja',
+                        cancelButton: 'Nein',
+                        ),
                     );
 
                   },
                   child: const Text('Überschreiben', textAlign: TextAlign.center),
                 ),
               ],
-            );
-          }
+            )
         );
 
         if(!replaceFiles) {
-          for (var element in conflictingFiles) {
-            element.isConflicting = true;
-          }
           setState(() {
             _isUploading = false;
             _overallProgressValue = 0.0;
             _infoText = '';
+            for (var element in conflictingFiles) {
+              element.isConflicting = true;
+            }
           });
           return;
         }
       }
     }
 
-    List<String> uploadetFilePaths = [];
+    var uploadetFilePaths = <String>[];
     for (var file in _uploadableFiles) {
-      String fileName = file.fileName;
-      String filePath = file.filePath;
+      var fileName = file.fileName;
+      var filePath = file.filePath;
 
       if(widget.uniqueNames) fileName = '${fileName.split('.').first}-${const Uuid().v4()}.${fileName.split('.').last}';
 
-      String fullRemotePath = '${widget.remotePath}/$fileName';
+      var fullRemotePath = '${widget.remotePath}/$fileName';
 
       setState(() {
         _infoText = '${_uploadableFiles.indexOf(file) + 1}/${_uploadableFiles.length}';
       });
 
-      HttpClientResponse uploadTask = await webdavClient.putFile(
+      var uploadTask = await webdavClient.putFile(
         File(filePath),
         FileStat.statSync(filePath),
         PathUri.parse(fullRemotePath),
@@ -211,8 +193,7 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
   }
 
   @override
-  Widget build(BuildContext context) {
-    return Scaffold(
+  Widget build(BuildContext context) => Scaffold(
       appBar: AppBar(
         title: const Text('Dateien hochladen'),
         automaticallyImplyLeading: false,
@@ -318,13 +299,25 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                       onPressed: () => uploadFiles(override: widget.uniqueNames),
                       child: const Text('Hochladen'),
                     ),
-                    child: Stack(
-                      alignment: Alignment.center,
-                      children: [
-                        CircularProgressIndicator(value: _overallProgressValue),
-                        Center(child: Text(_infoText)),
-                      ],
+                    child: Visibility(
+                      visible: _infoText.length < 5,
+                      replacement: Row(
+                        children: [
+                          Text(_infoText),
+                          const SizedBox(width: 15),
+                          CircularProgressIndicator(value: _overallProgressValue),
+                        ],
+                      ),
+                      child: Stack(
+                        alignment: Alignment.center,
+                        children: [
+                          CircularProgressIndicator(value: _overallProgressValue),
+                          Center(child: Text(_infoText)),
+                        ],
+                      ),
                     ),
+
+
                   ),
                 ],
               ),
@@ -333,5 +326,4 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
         ),
       ),
     );
-  }
-}
\ No newline at end of file
+}
diff --git a/lib/view/pages/talk/components/chatTextfield.dart b/lib/view/pages/talk/components/chatTextfield.dart
index 791db03..a39be26 100644
--- a/lib/view/pages/talk/components/chatTextfield.dart
+++ b/lib/view/pages/talk/components/chatTextfield.dart
@@ -35,7 +35,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
 
   void share(String shareFolder, List<String> filePaths) {
     for (var element in filePaths) {
-      String fileName = element.split(Platform.pathSeparator).last;
+      var fileName = element.split(Platform.pathSeparator).last;
       FileSharingApi().share(FileSharingApiParams(
           shareType: 10,
           shareWith: widget.sendToToken,
@@ -44,10 +44,10 @@ class _ChatTextfieldState extends State<ChatTextfield> {
     }
   }
 
-  void mediaUpload(List<String>? paths) async {
+  Future<void> mediaUpload(List<String>? paths) async {
     if (paths == null) return;
 
-    String shareFolder = 'MarianumMobile';
+    var shareFolder = 'MarianumMobile';
     WebdavApi.webdav.then((webdav) {
       webdav.mkcol(PathUri.parse('/$shareFolder'));
     });
-- 
2.30.2


From cf4dea566e125656d31f279daa38c056c37f2d11 Mon Sep 17 00:00:00 2001
From: Pupsi28 <larslukasneuhaus@gmx.de>
Date: Mon, 8 Apr 2024 22:39:28 +0200
Subject: [PATCH 6/6] solved pr comments; picking multiple Images from Gallery
 is now possible

---
 lib/view/pages/files/files.dart               |  4 ++--
 lib/view/pages/files/filesUploadDialog.dart   | 20 +------------------
 .../pages/talk/components/chatTextfield.dart  |  4 ++--
 lib/widget/filePick.dart                      |  8 ++++++++
 4 files changed, 13 insertions(+), 23 deletions(-)

diff --git a/lib/view/pages/files/files.dart b/lib/view/pages/files/files.dart
index a39bdbe..8068408 100644
--- a/lib/view/pages/files/files.dart
+++ b/lib/view/pages/files/files.dart
@@ -219,8 +219,8 @@ class _FilesState extends State<Files> {
                     leading: const Icon(Icons.add_a_photo_outlined),
                     title: const Text('Aus Gallerie hochladen'),
                     onTap: () {
-                      FilePick.galleryPick().then((value) {
-                        if(value != null) mediaUpload([value.path]);
+                      FilePick.multipleGalleryPick().then((value) {
+                        if(value != null) mediaUpload(value.map((e) => e.path).toList());
                       });
                       Navigator.of(context).pop();
                     },
diff --git a/lib/view/pages/files/filesUploadDialog.dart b/lib/view/pages/files/filesUploadDialog.dart
index 794d9a7..72803c2 100644
--- a/lib/view/pages/files/filesUploadDialog.dart
+++ b/lib/view/pages/files/filesUploadDialog.dart
@@ -170,7 +170,6 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
       );
 
       if(uploadTask.statusCode < 200 || uploadTask.statusCode > 299) {
-        // error code
         setState(() {
           _isUploading = false;
           _overallProgressValue = 0.0;
@@ -241,7 +240,7 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                       value: currentFile._uploadProgress,
                       borderRadius: const BorderRadius.all(Radius.circular(2)),
                     ) : null,
-                    leading: Container(
+                    trailing: Container(
                       width: 24,
                       height: 24,
                       padding: EdgeInsets.zero,
@@ -256,23 +255,6 @@ class _FilesUploadDialogState extends State<FilesUploadDialog> {
                             });
                           }
                         },
-                        icon: const Icon(Icons.close_outlined),
-                      ),
-                    ),
-                    trailing: Container(
-                      width: 24,
-                      height: 24,
-                      padding: EdgeInsets.zero,
-                      child: IconButton(
-                        tooltip: 'Namen löschen',
-                        padding: EdgeInsets.zero,
-                        onPressed: () {
-                          if(!_isUploading) {
-                            setState(() {
-                              currentFile.fileName = '';
-                            });
-                          }
-                        },
                         icon: const Icon(Icons.delete_outlined),
                       ),
                     ),
diff --git a/lib/view/pages/talk/components/chatTextfield.dart b/lib/view/pages/talk/components/chatTextfield.dart
index a39be26..3628006 100644
--- a/lib/view/pages/talk/components/chatTextfield.dart
+++ b/lib/view/pages/talk/components/chatTextfield.dart
@@ -111,8 +111,8 @@ class _ChatTextfieldState extends State<ChatTextfield> {
                               leading: const Icon(Icons.image),
                               title: const Text('Aus Gallerie auswählen'),
                               onTap: () {
-                                FilePick.galleryPick().then((value) {
-                                  if(value != null) mediaUpload([value.path]);
+                                FilePick.multipleGalleryPick().then((value) {
+                                  if(value != null) mediaUpload(value.map((e) => e.path).toList());
                                 });
                                 Navigator.of(context).pop();
                               },
diff --git a/lib/widget/filePick.dart b/lib/widget/filePick.dart
index a4d490d..b9d7dbb 100644
--- a/lib/widget/filePick.dart
+++ b/lib/widget/filePick.dart
@@ -13,6 +13,14 @@ class FilePick {
     return null;
   }
 
+  static Future<List<XFile>?> multipleGalleryPick() async {
+    final pickedImages = await _picker.pickMultiImage();
+    if(pickedImages.isNotEmpty) {
+      return pickedImages;
+    }
+    return null;
+  }
+
   static Future<List<String>?> documentPick() async {
     var result = await FilePicker.platform.pickFiles(allowMultiple: true);
     var paths = result?.files.nonNulls.map((e) => e.path).toList();
-- 
2.30.2