import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:loader_overlay/loader_overlay.dart'; import '../../../api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart'; import '../../../api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart'; import '../../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart'; import '../../../api/marianumcloud/webdav/webdavApi.dart'; import '../../../model/files/filesProps.dart'; import '../../../widget/errorView.dart'; import '../../../widget/filePick.dart'; import 'fileUploadDialog.dart'; import 'fileElement.dart'; class Files extends StatefulWidget { final List path; const Files(this.path, {Key? key}) : super(key: key); @override State createState() => _FilesState(); } class BetterSortOption { String displayName; int Function(CacheableFile, CacheableFile) compare; IconData icon; BetterSortOption({required this.displayName, required this.icon, required this.compare}); } enum SortOption { name, date, size } class SortOptions { static Map options = { SortOption.name: BetterSortOption( displayName: "Name", icon: Icons.sort_by_alpha_outlined, compare: (CacheableFile a, CacheableFile b) => a.name.compareTo(b.name) ), SortOption.date: BetterSortOption( displayName: "Datum", icon: Icons.history_outlined, compare: (CacheableFile a, CacheableFile b) => a.modifiedAt!.compareTo(b.modifiedAt!) ), SortOption.size: BetterSortOption( displayName: "Größe", icon: Icons.sd_card_outlined, compare: (CacheableFile a, CacheableFile b) { if(a.isDirectory || b.isDirectory) return a.isDirectory ? 1 : 0; if(a.size == null) return 0; if(b.size == null) return 1; return a.size!.compareTo(b.size!); } ) }; static BetterSortOption getOption(SortOption option) { return options[option]!; } } class _FilesState extends State { FilesProps props = FilesProps(); ListFilesResponse? data; SortOption currentSort = SortOption.name; bool currentSortDirection = true; @override void initState() { super.initState(); _query(); } void _query() { ListFilesCache( path: widget.path.isEmpty ? "/" : widget.path.join("/"), onUpdate: (ListFilesResponse d) { if(!context.mounted) return; // prevent setState when widget is possibly already disposed setState(() { data = d; }); } ); } @override Widget build(BuildContext context) { List files = (data?.files.toList() ?? List.empty())..sort(SortOptions.getOption(currentSort).compare); if(currentSortDirection) files = files.reversed.toList(); return Scaffold( appBar: AppBar( title: Text(widget.path.isNotEmpty ? widget.path.last : "Dateien"), actions: [ // IconButton( // icon: const Icon(Icons.search), // onPressed: () => { // // TODO implement search // }, // ), PopupMenuButton( icon: Icon(currentSortDirection ? Icons.text_rotate_up : Icons.text_rotation_down), itemBuilder: (context) { return [true, false].map((e) => PopupMenuItem( value: e, enabled: e != currentSortDirection, child: Row( children: [ Icon(e ? Icons.text_rotate_up : Icons.text_rotation_down, color: Colors.black), const SizedBox(width: 15), Text(e ? "Aufsteigend" : "Absteigend") ], ) )).toList(); }, onSelected: (e) { setState(() { currentSortDirection = e; }); }, ), PopupMenuButton( icon: const Icon(Icons.sort), itemBuilder: (context) { return SortOptions.options.keys.map((key) => PopupMenuItem( value: key, enabled: key != currentSort, child: Row( children: [ Icon(SortOptions.getOption(key).icon, color: Colors.black), const SizedBox(width: 15), Text(SortOptions.getOption(key).displayName) ], ) )).toList(); }, onSelected: (e) { setState(() { currentSort = e; }); }, ), ], ), floatingActionButton: FloatingActionButton( heroTag: "uploadFile", backgroundColor: Theme.of(context).primaryColor, onPressed: () { showDialog(context: context, builder: (context) { return SimpleDialog( children: [ ListTile( leading: const Icon(Icons.create_new_folder_outlined), title: const Text("Ordner erstellen"), onTap: () { 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.upload_file), title: const Text("Aus Dateien hochladen"), onTap: () { context.loaderOverlay.show(); FilePick.documentPick().then((value) { log(value ?? "?"); mediaUpload(value); }); Navigator.of(context).pop(); }, ), ListTile( leading: const Icon(Icons.add_a_photo_outlined), title: const Text("Aus Gallerie hochladen"), onTap: () { context.loaderOverlay.show(); FilePick.galleryPick().then((value) { log(value?.path ?? "?"); mediaUpload(value?.path); }); Navigator.of(context).pop(); }, ), ], ); }); }, 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( onRefresh: () { _query(); return Future.delayed(const Duration(seconds: 3)); }, child: ListView.builder( itemCount: files.length, itemBuilder: (context, index) { CacheableFile file = files.toList()[index]; return FileElement(file, widget.path, _query); }, ), ) ) ); } 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, triggerReload: () => _query()), barrierDismissible: false); } }