diff --git a/CHANGELOG.md b/CHANGELOG.md index 2716adc25..73e9bf0c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ All notable changes to this project will be documented in this file. ### Changed - improved subsampling and filter quality strategy +- ignore moving an item to its current directory +- keep selection when action on several items is interrupted before processing - upgraded Flutter to stable v3.27.3 ### Fixed diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index d3c94fad5..0822a4d5b 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -292,26 +292,29 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware final details = vaults.getVault(entry.directory); return details?.useBin ?? settings.enableBin; }); - await Future.forEach( - byBinUsage.entries, - (kv) => doDelete( - context: context, - entries: kv.value.toSet(), - enableBin: kv.key, - )); + var completed = true; + await Future.forEach(byBinUsage.entries, (kv) async { + completed &= await doDelete( + context: context, + entries: kv.value.toSet(), + enableBin: kv.key, + ); + }); - _browse(context); + if (completed) { + _browse(context); + } } - Future doDelete({ + // returns whether it completed the action (with or without failures) + Future doDelete({ required BuildContext context, required Set entries, required bool enableBin, }) async { final pureTrash = entries.every((entry) => entry.trashed); if (enableBin && !pureTrash) { - await doMove(context, moveType: MoveType.toBin, entries: entries); - return; + return await doMove(context, moveType: MoveType.toBin, entries: entries); } final l10n = context.l10n; @@ -325,10 +328,10 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware message: l10n.deleteEntriesConfirmationDialogMessage(todoCount), confirmationButtonLabel: l10n.deleteButtonLabel, )) { - return; + return false; } - if (!await checkStoragePermissionForAlbums(context, storageDirs, entries: entries)) return; + if (!await checkStoragePermissionForAlbums(context, storageDirs, entries: entries)) return false; source.pauseMonitoring(); final opId = mediaEditService.newOpId; @@ -354,13 +357,16 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware await storageService.deleteEmptyRegularDirectories(storageDirs); }, ); + return true; } Future _move(BuildContext context, {required MoveType moveType}) async { final entries = _getTargetItems(context); - await doMove(context, moveType: moveType, entries: entries); + final completed = await doMove(context, moveType: moveType, entries: entries); - _browse(context); + if (completed) { + _browse(context); + } } Future _rename(BuildContext context) async { @@ -381,9 +387,11 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware return MapEntry(entry, '$newName${entry.extension}'); }); final entriesToNewName = Map.fromEntries(await Future.wait(namingFutures)).whereNotNullValue(); - await rename(context, entriesToNewName: entriesToNewName, persist: true); + final completed = await rename(context, entriesToNewName: entriesToNewName, persist: true); - _browse(context); + if (completed) { + _browse(context); + } } Future _convert(BuildContext context) async { @@ -398,13 +406,14 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware switch (options.action) { case EntryConvertAction.convert: - await doExport(context, entries, options); + final completed = await doExport(context, entries, options); + if (completed) { + _browse(context); + } case EntryConvertAction.convertMotionPhotoToStillImage: final todoItems = entries.where((entry) => entry.isMotionPhoto).toSet(); await _edit(context, todoItems, (entry) => entry.removeTrailerVideo()); } - - _browse(context); } Future _toggleFavourite(BuildContext context) async { diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index e8dc67f26..bd060720e 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -37,14 +37,15 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { - Future doExport(BuildContext context, Set targetEntries, EntryConvertOptions options) async { + // returns whether it completed the action (with or without failures) + Future doExport(BuildContext context, Set targetEntries, EntryConvertOptions options) async { final destinationAlbumFilter = await pickAlbum(context: context, moveType: MoveType.export, storedAlbumsOnly: true); - if (destinationAlbumFilter == null || destinationAlbumFilter is! StoredAlbumFilter) return; + if (destinationAlbumFilter == null || destinationAlbumFilter is! StoredAlbumFilter) return false; final destinationAlbum = destinationAlbumFilter.album; - if (!await checkStoragePermissionForAlbums(context, {destinationAlbum})) return; + if (!await checkStoragePermissionForAlbums(context, {destinationAlbum})) return false; - if (!await checkFreeSpaceForMove(context, targetEntries, destinationAlbum, MoveType.export)) return; + if (!await checkFreeSpaceForMove(context, targetEntries, destinationAlbum, MoveType.export)) return false; final transientMultiPageInfo = {}; final selection = {}; @@ -89,7 +90,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { ), routeSettings: const RouteSettings(name: AvesSingleSelectionDialog.routeName), ); - if (value == null) return; + if (value == null) return false; nameConflictStrategy = value; } @@ -157,9 +158,11 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { }, ); transientMultiPageInfo.forEach((v) => v.dispose()); + return true; } - Future doQuickMove( + // returns whether it completed the action (with or without failures) + Future doQuickMove( BuildContext context, { required MoveType moveType, required Map> entriesByDestination, @@ -176,23 +179,23 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { final entries = entriesByDestination.values.expand((v) => v).toSet(); final todoCount = entries.length; - if (todoCount == 0) return; + if (todoCount == 0) return true; final toBin = moveType == MoveType.toBin; final copy = moveType == MoveType.copy; // permission for modification at destinations final destinationAlbums = entriesByDestination.keys.toSet(); - if (!await checkStoragePermissionForAlbums(context, destinationAlbums)) return; + if (!await checkStoragePermissionForAlbums(context, destinationAlbums)) return false; // permission for modification at origins final originAlbums = entries.map((e) => e.directory).nonNulls.toSet(); - if ({MoveType.move, MoveType.toBin}.contains(moveType) && !await checkStoragePermissionForAlbums(context, originAlbums, entries: entries)) return; + if ({MoveType.move, MoveType.toBin}.contains(moveType) && !await checkStoragePermissionForAlbums(context, originAlbums, entries: entries)) return false; final hasEnoughSpaceByDestination = await Future.wait(destinationAlbums.map((destinationAlbum) { return checkFreeSpaceForMove(context, entries, destinationAlbum, moveType); })); - if (hasEnoughSpaceByDestination.any((v) => !v)) return; + if (hasEnoughSpaceByDestination.any((v) => !v)) return false; final l10n = context.l10n; var nameConflictStrategy = NameConflictStrategy.rename; @@ -217,12 +220,12 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { ), routeSettings: const RouteSettings(name: AvesSingleSelectionDialog.routeName), ); - if (value == null) return; + if (value == null) return false; nameConflictStrategy = value; } } - if ({MoveType.move, MoveType.copy}.contains(moveType) && !await _checkUndatedItems(context, entries)) return; + if ({MoveType.move, MoveType.copy}.contains(moveType) && !await _checkUndatedItems(context, entries)) return false; final source = context.read(); source.pauseMonitoring(); @@ -321,9 +324,11 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { } }, ); + return true; } - Future doMove( + // returns whether it completed the action (with or without failures) + Future doMove( BuildContext context, { required MoveType moveType, required Set entries, @@ -338,7 +343,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { message: l10n.binEntriesConfirmationDialogMessage(entries.length), confirmationButtonLabel: l10n.deleteButtonLabel, )) { - return; + return false; } } @@ -348,7 +353,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { case MoveType.move: case MoveType.export: final destinationAlbumFilter = await pickAlbum(context: context, moveType: moveType, storedAlbumsOnly: true); - if (destinationAlbumFilter == null || destinationAlbumFilter is! StoredAlbumFilter) return; + if (destinationAlbumFilter == null || destinationAlbumFilter is! StoredAlbumFilter) return false; final destinationAlbum = destinationAlbumFilter.album; settings.recentDestinationAlbums = settings.recentDestinationAlbums @@ -365,7 +370,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { }); } - await doQuickMove( + return await doQuickMove( context, moveType: moveType, entriesByDestination: entriesByDestination, @@ -373,7 +378,8 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { ); } - Future rename( + // returns whether it completed the action (with or without failures) + Future rename( BuildContext context, { required Map entriesToNewName, required bool persist, @@ -383,9 +389,9 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { final todoCount = entries.length; assert(todoCount > 0); - if (!await checkStoragePermission(context, entries)) return; + if (!await checkStoragePermission(context, entries)) return false; - if (!await _checkUndatedItems(context, entries)) return; + if (!await _checkUndatedItems(context, entries)) return false; final source = context.read(); source.pauseMonitoring(); @@ -420,6 +426,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { } }, ); + return true; } Future _checkUndatedItems(BuildContext context, Set entries) async {