#1201 keep selection when action on several items is interrupted before processing
This commit is contained in:
parent
16da0ec3f5
commit
0a3a792a7e
3 changed files with 57 additions and 39 deletions
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
var completed = true;
|
||||
await Future.forEach(byBinUsage.entries, (kv) async {
|
||||
completed &= await doDelete(
|
||||
context: context,
|
||||
entries: kv.value.toSet(),
|
||||
enableBin: kv.key,
|
||||
));
|
||||
);
|
||||
});
|
||||
|
||||
if (completed) {
|
||||
_browse(context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> doDelete({
|
||||
// returns whether it completed the action (with or without failures)
|
||||
Future<bool> doDelete({
|
||||
required BuildContext context,
|
||||
required Set<AvesEntry> 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,14 +357,17 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
|||
await storageService.deleteEmptyRegularDirectories(storageDirs);
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _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);
|
||||
|
||||
if (completed) {
|
||||
_browse(context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _rename(BuildContext context) async {
|
||||
final entries = _getTargetItems(context).toList();
|
||||
|
@ -381,10 +387,12 @@ 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);
|
||||
|
||||
if (completed) {
|
||||
_browse(context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _convert(BuildContext context) async {
|
||||
final entries = _getTargetItems(context);
|
||||
|
@ -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<void> _toggleFavourite(BuildContext context) async {
|
||||
|
|
|
@ -37,14 +37,15 @@ import 'package:flutter/material.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
|
||||
mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
||||
Future<void> doExport(BuildContext context, Set<AvesEntry> targetEntries, EntryConvertOptions options) async {
|
||||
// returns whether it completed the action (with or without failures)
|
||||
Future<bool> doExport(BuildContext context, Set<AvesEntry> 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 = <MultiPageInfo>{};
|
||||
final selection = <AvesEntry>{};
|
||||
|
@ -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<void> doQuickMove(
|
||||
// returns whether it completed the action (with or without failures)
|
||||
Future<bool> doQuickMove(
|
||||
BuildContext context, {
|
||||
required MoveType moveType,
|
||||
required Map<String, Set<AvesEntry>> 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<CollectionSource>();
|
||||
source.pauseMonitoring();
|
||||
|
@ -321,9 +324,11 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
|||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> doMove(
|
||||
// returns whether it completed the action (with or without failures)
|
||||
Future<bool> doMove(
|
||||
BuildContext context, {
|
||||
required MoveType moveType,
|
||||
required Set<AvesEntry> 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<void> rename(
|
||||
// returns whether it completed the action (with or without failures)
|
||||
Future<bool> rename(
|
||||
BuildContext context, {
|
||||
required Map<AvesEntry, String> 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<CollectionSource>();
|
||||
source.pauseMonitoring();
|
||||
|
@ -420,6 +426,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
|||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> _checkUndatedItems(BuildContext context, Set<AvesEntry> entries) async {
|
||||
|
|
Loading…
Reference in a new issue