From 840748e5c598723d9059da0249a5454e129843db Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Fri, 3 Nov 2023 11:41:27 +0100 Subject: [PATCH] Fix directory export, closes #523 --- app/lib/api/file_system/file_system.dart | 21 ++++--- app/lib/api/file_system/file_system_dav.dart | 2 +- app/lib/api/file_system/file_system_html.dart | 56 ++++++------------- .../file_system/file_system_html_stub.dart | 2 +- app/lib/api/file_system/file_system_io.dart | 6 +- app/lib/settings/data.dart | 2 +- .../metadata/android/en-US/changelogs/78.txt | 1 + 7 files changed, 35 insertions(+), 55 deletions(-) diff --git a/app/lib/api/file_system/file_system.dart b/app/lib/api/file_system/file_system.dart index 10ea9694288e..9304a1868519 100644 --- a/app/lib/api/file_system/file_system.dart +++ b/app/lib/api/file_system/file_system.dart @@ -97,21 +97,23 @@ Future getAppDocumentFile( } abstract class DocumentFileSystem extends GeneralFileSystem { - Future getRootDirectory() { - return getAsset('').then((value) => value as AppDocumentDirectory); + Future getRootDirectory([bool recursive = false]) { + return getAsset('', recursive ? null : true) + .then((value) => value as AppDocumentDirectory); } @override FutureOr getDirectory(); - Stream fetchAsset(String path, [bool listFiles = false]); + /// If listFiles is null, it will fetch recursively + Stream fetchAsset(String path, [bool? listFiles = true]); Stream> fetchAssets(Stream paths, - [bool listFiles = false]) { + [bool? listFiles = true]) { final files = []; final streams = paths.asyncExpand((e) async* { int? index; - await for (final file in fetchAsset(e, false).whereNotNull()) { + await for (final file in fetchAsset(e, listFiles).whereNotNull()) { if (index == null) { index = files.length; files.add(file); @@ -125,12 +127,12 @@ abstract class DocumentFileSystem extends GeneralFileSystem { } Stream> fetchAssetsSync(Iterable paths, - [bool listFiles = false]) => + [bool? listFiles = true]) => fetchAssets(Stream.fromIterable(paths), listFiles); static Stream> fetchAssetsGlobal( Stream locations, ButterflySettings settings, - [bool listFiles = true]) { + [bool? listFiles = true]) { final files = []; final streams = locations.asyncExpand((e) async* { final fileSystem = @@ -152,10 +154,11 @@ abstract class DocumentFileSystem extends GeneralFileSystem { static Stream> fetchAssetsGlobalSync( Iterable locations, ButterflySettings settings, - [bool listFiles = true]) => + [bool? listFiles = true]) => fetchAssetsGlobal(Stream.fromIterable(locations), settings, listFiles); - Future getAsset(String path) => fetchAsset(path).last; + Future getAsset(String path, [bool? listFiles = true]) => + fetchAsset(path, listFiles).last; Future createDirectory(String path); diff --git a/app/lib/api/file_system/file_system_dav.dart b/app/lib/api/file_system/file_system_dav.dart index 97d146994d08..2600d1e07004 100644 --- a/app/lib/api/file_system/file_system_dav.dart +++ b/app/lib/api/file_system/file_system_dav.dart @@ -70,7 +70,7 @@ class DavRemoteDocumentFileSystem extends DocumentRemoteSystem { @override Stream fetchAsset(String path, - [bool listFiles = true, bool forceRemote = false]) async* { + [bool? listFiles = true, bool forceRemote = false]) async* { if (path.endsWith('/')) { path = path.substring(0, path.length - 1); } diff --git a/app/lib/api/file_system/file_system_html.dart b/app/lib/api/file_system/file_system_html.dart index 36d6c9674567..1524b741fa8a 100644 --- a/app/lib/api/file_system/file_system_html.dart +++ b/app/lib/api/file_system/file_system_html.dart @@ -7,7 +7,6 @@ import 'dart:js_util'; import 'package:butterfly/models/defaults.dart'; import 'package:butterfly_api/butterfly_api.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:idb_shim/idb.dart'; @@ -125,7 +124,7 @@ class WebDocumentFileSystem extends DocumentFileSystem { @override Stream fetchAsset(String path, - [bool listFiles = true]) async* { + [bool? listFiles = true]) async* { // Add leading slash if (!path.startsWith('/')) { path = '/$path'; @@ -134,6 +133,7 @@ class WebDocumentFileSystem extends DocumentFileSystem { path = '/'; } var db = await _getDatabase(); + final location = AssetLocation.local(path); var txn = db.transaction(['documents', 'documents-data'], 'readonly'); Future?> getData(String path) async { @@ -159,49 +159,25 @@ class WebDocumentFileSystem extends DocumentFileSystem { yield null; return; } - final file = getAppDocumentFile(AssetLocation.local(path), data); + final file = getAppDocumentFile(location, data); await txn.completed; yield await file; return; } else if (map['type'] == 'directory') { - if (!listFiles) { - await txn.completed; - yield AppDocumentDirectory(AssetLocation.local(path), const []); - return; - } - var cursor = store.openCursor(autoAdvance: true); - var assets = await Future.wait( - await cursor.map>((cursor) async { - // Add leading slash - var key = cursor.key.toString(); - if (!key.startsWith('/')) { - key = '/$key'; - } - // Is in current directory - if (key.startsWith(path) && - key != path && - !key.substring(path.length + 1).contains('/')) { - var data = cursor.value as Map; - if (data['type'] == 'file') { - final data = await getData(key); - if (data == null) return null; - return getAppDocumentFile(AssetLocation.local(key), data); - } else if (data['type'] == 'directory') { - return AppDocumentDirectory(AssetLocation.local(key), const []); - } - return null; + yield AppDocumentDirectory(location, []); + if (listFiles ?? true) { + var cursor = store.openKeyCursor(autoAdvance: true); + final streams = fetchAssetsSync( + await cursor.map((e) => e.key.toString()).where((e) { + return e.startsWith(path) && + e != path && + !e.substring(path.length + 1).contains('/'); + }).toList(), + listFiles == null ? null : false); + await for (var stream in streams) { + yield AppDocumentDirectory(location, stream); } - return null; - }).toList()) - .then((value) => value.whereNotNull().toList()); - // Sort assets, AppDocumentDirectory should be first, AppDocumentFile should be sorted by name - assets.sort((a, b) => a is AppDocumentDirectory - ? -1 - : (a as AppDocumentFile).fileName.compareTo(b is AppDocumentDirectory - ? '' - : (b as AppDocumentFile).fileName)); - await txn.completed; - yield AppDocumentDirectory(AssetLocation.local(path), assets.toList()); + } return; } yield null; diff --git a/app/lib/api/file_system/file_system_html_stub.dart b/app/lib/api/file_system/file_system_html_stub.dart index b4f6ebb26d92..522948e1706f 100644 --- a/app/lib/api/file_system/file_system_html_stub.dart +++ b/app/lib/api/file_system/file_system_html_stub.dart @@ -15,7 +15,7 @@ class WebDocumentFileSystem extends DocumentFileSystem { } @override - Stream fetchAsset(String path, [bool listFiles = true]) { + Stream fetchAsset(String path, [bool? listFiles = true]) { throw UnimplementedError(); } diff --git a/app/lib/api/file_system/file_system_io.dart b/app/lib/api/file_system/file_system_io.dart index 777f98faa5ad..87d354f77cb3 100644 --- a/app/lib/api/file_system/file_system_io.dart +++ b/app/lib/api/file_system/file_system_io.dart @@ -54,7 +54,7 @@ class IODocumentFileSystem extends DocumentFileSystem { @override Stream fetchAsset(String path, - [bool listFiles = true]) async* { + [bool? listFiles = true]) async* { // Add leading slash if (!path.startsWith('/')) { path = '/$path'; @@ -73,11 +73,11 @@ class IODocumentFileSystem extends DocumentFileSystem { } catch (_) {} } else if (await directory.exists()) { yield AppDocumentDirectory(location, []); - if (listFiles) { + if (listFiles ?? true) { final streams = fetchAssets( directory.list().map( (e) => '$path/${e.path.replaceAll('\\', '/').split('/').last}'), - false); + listFiles == null ? null : false); await for (final files in streams) { yield AppDocumentDirectory(location, files); } diff --git a/app/lib/settings/data.dart b/app/lib/settings/data.dart index 78958b8bb7bd..812b1762a059 100644 --- a/app/lib/settings/data.dart +++ b/app/lib/settings/data.dart @@ -115,7 +115,7 @@ class _DataSettingsPageState extends State { final fileSystem = DocumentFileSystem.fromPlatform(); final directory = - await fileSystem.getRootDirectory(); + await fileSystem.getRootDirectory(true); final archive = exportDirectory(directory); final encoder = ZipEncoder(); final bytes = encoder.encode(archive); diff --git a/fastlane/metadata/android/en-US/changelogs/78.txt b/fastlane/metadata/android/en-US/changelogs/78.txt index 5dfb71928472..5e2af68d76ad 100644 --- a/fastlane/metadata/android/en-US/changelogs/78.txt +++ b/fastlane/metadata/android/en-US/changelogs/78.txt @@ -13,6 +13,7 @@ * Fix locale spacing * Fix pack updating * Fix pdf operations on web +* Fix exporting whole directory ([#523](https://github.com/LinwoodDev/Butterfly/issues/523)) * Set full screen tool to action tool * Fix saving files in local external directory * Fixing missing saving of pdf quality and platform theme settings