diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
index bca37eb..005afa6 100644
--- a/.idea/libraries/Dart_Packages.xml
+++ b/.idea/libraries/Dart_Packages.xml
@@ -278,7 +278,7 @@
-
+
@@ -1147,7 +1147,7 @@
-
+
diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/listFiles.dart b/lib/api/marianumcloud/webdav/queries/listFiles/listFiles.dart
index a9e6490..50618ab 100644
--- a/lib/api/marianumcloud/webdav/queries/listFiles/listFiles.dart
+++ b/lib/api/marianumcloud/webdav/queries/listFiles/listFiles.dart
@@ -12,8 +12,9 @@ class ListFiles extends WebdavApi {
@override
Future run() async {
- //Set files = (await (await WebdavApi.webdav).ls(params.path)).map((e) => CacheableFile.fromDavFile(e)).toSet();
- Set files = (await (await WebdavApi.webdav).ls(params.path)).toWebDavFiles((await WebdavApi.webdav)).map((e) => CacheableFile.fromDavFile(e)).toSet();
+ List davFiles = (await (await WebdavApi.webdav).ls(params.path)).toWebDavFiles((await WebdavApi.webdav));
+ davFiles.removeWhere((element) => element.path == "/${params.path}/" || element.path == "/"); // somehow the current working folder is also listed, it is filtered here.
+ Set files = davFiles.map((e) => CacheableFile.fromDavFile(e)).toSet();
return ListFilesResponse(files);
}
diff --git a/lib/screen/pages/files/fileUpload.dart b/lib/screen/pages/files/fileUpload.dart
deleted file mode 100644
index 84847ec..0000000
--- a/lib/screen/pages/files/fileUpload.dart
+++ /dev/null
@@ -1,72 +0,0 @@
-import 'dart:developer';
-import 'dart:io';
-
-import 'package:async/async.dart';
-import 'package:flutter/material.dart';
-import 'package:marianum_mobile/api/marianumcloud/webdav/webdavApi.dart';
-
-class FileUpload extends StatefulWidget {
- final String localPath;
- final String remotePath;
- const FileUpload({Key? key, required this.localPath, required this.remotePath}) : super(key: key);
-
- @override
- State createState() => _FileUploadState();
-}
-
-class _FileUploadState extends State {
- CancelableOperation? cancelableOperation;
- late File localFile;
-
- @override
- void initState() {
- super.initState();
- localFile = File(widget.localPath);
- }
-
- bool isRunning() {
- return cancelableOperation != null && !(cancelableOperation!.isCompleted);
- }
-
- bool isComplete() {
- return cancelableOperation?.isCompleted ?? false;
- }
-
- void start() async {
- cancelableOperation = CancelableOperation.fromFuture(
- (await WebdavApi.webdav).upload(localFile.readAsBytesSync(), widget.remotePath).then((value) {
- log("Upload done!");
- }),
- onCancel: () => log("Upload cancelled"),
- );
-
- cancelableOperation!.then((e) {
- setState(() {});
- });
- setState(() {});
- }
-
- void stop() {
- cancelableOperation?.cancel();
- setState(() {});
- Navigator.of(context).pop();
- }
-
-
- @override
- Widget build(BuildContext context) {
- List actions = List.empty(growable: true);
- if(!isRunning() && !isComplete()) actions.add(TextButton(onPressed: start, child: const Text("Upload starten")));
- if(isRunning()) actions.add(TextButton(onPressed: stop, child: const Text("Upload Abbrechen")));
- if(isComplete()) actions.add(TextButton(onPressed: Navigator.of(context).pop, child: const Text("Fertig")));
-
- return AlertDialog(
- title: const Text("Hochladen"),
- content: Center(
- child: isRunning() ? const CircularProgressIndicator() : const Text("Datei hochladen"),
- ),
- actions: actions,
- icon: const Icon(Icons.upload),
- );
- }
-}
diff --git a/lib/screen/pages/files/fileUploadDialog.dart b/lib/screen/pages/files/fileUploadDialog.dart
new file mode 100644
index 0000000..67a4000
--- /dev/null
+++ b/lib/screen/pages/files/fileUploadDialog.dart
@@ -0,0 +1,227 @@
+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() triggerReload;
+ const FileUploadDialog({Key? key, required this.localPath, required this.remotePath, required this.fileName, required this.triggerReload}) : 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.ls(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.upload(File(widget.localPath).readAsBytesSync(), fullRemotePath);
+ 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) {
+ 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.triggerReload();
+ return AlertDialog(
+ icon: const Icon(Icons.upload),
+ 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
diff --git a/lib/screen/pages/files/files.dart b/lib/screen/pages/files/files.dart
index b0e2a51..74a228a 100644
--- a/lib/screen/pages/files/files.dart
+++ b/lib/screen/pages/files/files.dart
@@ -5,20 +5,19 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:loader_overlay/loader_overlay.dart';
import 'package:marianum_mobile/api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart';
-import 'package:marianum_mobile/screen/pages/files/fileUpload.dart';
import 'package:marianum_mobile/widget/errorView.dart';
-import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart';
import '../../../api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart';
import '../../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart';
import '../../../api/marianumcloud/webdav/webdavApi.dart';
import '../../../data/files/filesProps.dart';
import '../../../widget/filePick.dart';
+import 'fileUploadDialog.dart';
import 'fileElement.dart';
class Files extends StatefulWidget {
- List path;
- Files(this.path, {Key? key}) : super(key: key);
+ final List path;
+ const Files(this.path, {Key? key}) : super(key: key);
@override
State createState() => _FilesState();
@@ -83,10 +82,11 @@ class _FilesState extends State {
void _query() {
ListFilesCache(
path: widget.path.isEmpty ? "/" : widget.path.join("/"),
- onUpdate: (ListFilesResponse d) => {
+ onUpdate: (ListFilesResponse d) {
+ if(!context.mounted) return; // prevent setState when widget is possibly already disposed
setState(() {
data = d;
- }),
+ });
}
);
}
@@ -100,12 +100,12 @@ class _FilesState extends State {
appBar: AppBar(
title: Text(widget.path.isNotEmpty ? widget.path.last : "Dateien"),
actions: [
- IconButton(
- icon: const Icon(Icons.search),
- onPressed: () => {
- // TODO implement search
- },
- ),
+ // IconButton(
+ // icon: const Icon(Icons.search),
+ // onPressed: () => {
+ // // TODO implement search
+ // },
+ // ),
PopupMenuButton(
icon: Icon(currentSortDirection ? Icons.text_rotate_up : Icons.text_rotation_down),
itemBuilder: (context) {
@@ -151,22 +151,44 @@ class _FilesState extends State {
],
),
floatingActionButton: FloatingActionButton(
+ backgroundColor: Theme.of(context).primaryColor,
onPressed: () {
showDialog(context: context, builder: (context) {
return SimpleDialog(
children: [
ListTile(
leading: const Icon(Icons.folder),
- title: const Text("Neuer Ordner"),
+ title: const Text("Ordner erstellen"),
onTap: () {
- WebdavApi.webdav.then((webdav) {
- webdav.mkdirs("/MarianumMobileTest");
+ Navigator.of(context).pop();
+ showDialog(context: context, builder: (context) {
+ var inputController = TextEditingController();
+ return AlertDialog(
+ title: const Text("Neuer Ordner"),
+ content: TextField(
+ controller: inputController,
+ decoration: const InputDecoration(
+ labelText: "Name",
+ ),
+ ),
+ actions: [
+ TextButton(onPressed: () {
+ Navigator.of(context).pop();
+ }, child: const Text("Abbrechen")),
+ TextButton(onPressed: () {
+ WebdavApi.webdav.then((webdav) {
+ webdav.mkdirs("${widget.path.join("/")}/${inputController.text}").then((value) => _query());
+ });
+ Navigator.of(context).pop();
+ }, child: const Text("Ordner erstellen")),
+ ],
+ );
});
},
),
ListTile(
leading: const Icon(Icons.file_open),
- title: const Text("Aus Dateien auswählen"),
+ title: const Text("Aus Dateien hochladen"),
onTap: () {
context.loaderOverlay.show();
FilePick.documentPick().then((value) {
@@ -178,7 +200,7 @@ class _FilesState extends State {
),
ListTile(
leading: const Icon(Icons.image),
- title: const Text("Aus Gallerie auswählen"),
+ title: const Text("Aus Gallerie hochladen"),
onTap: () {
context.loaderOverlay.show();
FilePick.galleryPick().then((value) {
@@ -192,7 +214,7 @@ class _FilesState extends State {
);
});
},
- child: const Icon(Icons.upload),
+ child: const Icon(Icons.add),
),
body: data == null ? const Center(child: CircularProgressIndicator()) : data!.files.isEmpty ? const ErrorView(icon: Icons.folder_off_rounded, text: "Der Ordner ist leer") : LoaderOverlay(
child: RefreshIndicator(
@@ -203,7 +225,7 @@ class _FilesState extends State {
child: ListView.builder(
itemCount: files.length,
itemBuilder: (context, index) {
- CacheableFile file = files.toList().skip(index).first;
+ CacheableFile file = files.toList()[index];
return FileElement(file, widget.path);
},
),
@@ -213,15 +235,13 @@ class _FilesState extends State {
}
void mediaUpload(String? path) async {
+ context.loaderOverlay.hide();
+
if(path == null) {
- context.loaderOverlay.hide();
return;
}
- context.loaderOverlay.show();
- File file = File(path);
- var remotePath = "${widget.path.join("/")}/${file.path.split(Platform.pathSeparator).last}";
- PersistentNavBarNavigator.pushNewScreen(context, screen: FileUpload(localPath: path, remotePath: remotePath), withNavBar: false);
-
+ var fileName = path.split(Platform.pathSeparator).last;
+ showDialog(context: context, builder: (context) => FileUploadDialog(localPath: path, remotePath: widget.path, fileName: fileName, triggerReload: () => _query()), barrierDismissible: false);
}
}
diff --git a/lib/theming/darkAppTheme.dart b/lib/theming/darkAppTheme.dart
index 0191f20..be3bf3e 100644
--- a/lib/theming/darkAppTheme.dart
+++ b/lib/theming/darkAppTheme.dart
@@ -13,7 +13,7 @@ class DarkAppTheme {
surface: Colors.black,
onSurface: Colors.white,
- primary: Colors.black,
+ primary: Colors.white,
onPrimary: Colors.white,
secondary: Colors.grey,
@@ -34,6 +34,12 @@ class DarkAppTheme {
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: marianumRed,
),
+ iconButtonTheme: IconButtonThemeData(
+ style: ButtonStyle(
+ textStyle: MaterialStateProperty.all(const TextStyle(color: Colors.white)),
+ backgroundColor: MaterialStateProperty.all(Colors.white),
+ )
+ ),
);
}
\ No newline at end of file