From 598b705b366a4ca07a6581a6599bfd1fee0bce9c Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Mon, 3 Feb 2025 19:12:46 +0100 Subject: [PATCH] #1323 preserve favourite status when converting items --- CHANGELOG.md | 7 ++-- .../collection/entry_set_action_delegate.dart | 12 +++--- .../common/action_mixins/entry_storage.dart | 41 +++++++++++++------ .../common/action_delegates/album_set.dart | 10 ++--- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73e9bf0c3..f2854897e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,10 @@ 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 +- Viewer: improved subsampling and filter quality strategy +- Collection: ignore moving an item to its current directory +- Collection: keep selection when action on several items is interrupted before processing +- Collection: preserve favourite status when converting items - 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 0822a4d5b..884b5fe0d 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -341,9 +341,9 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware itemCount: todoCount, onCancel: () => mediaEditService.cancelFileOp(opId), onDone: (processed) async { - final successOps = processed.where((e) => e.success).toSet(); - final deletedOps = successOps.where((e) => !e.skipped).toSet(); - final deletedUris = deletedOps.map((event) => event.uri).toSet(); + final successOps = processed.where((op) => op.success).toSet(); + final deletedOps = successOps.where((op) => !op.skipped).toSet(); + final deletedUris = deletedOps.map((op) => op.uri).toSet(); await source.removeEntries(deletedUris, includeTrash: true); source.resumeMonitoring(); @@ -460,11 +460,11 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware itemCount: todoCount, onCancel: () => cancelled = true, onDone: (processed) async { - final successOps = processed.where((e) => e.success).toSet(); - final editedOps = successOps.where((e) => !e.skipped).toSet(); + final successOps = processed.where((op) => op.success).toSet(); + final editedOps = successOps.where((op) => !op.skipped).toSet(); source.resumeMonitoring(); - unawaited(source.refreshUris(editedOps.map((v) => v.uri).toSet()).then((_) { + unawaited(source.refreshUris(editedOps.map((op) => op.uri).toSet()).then((_) { // invalidate filters derived from values before edition // this invalidation must happen after the source is refreshed, // otherwise filter chips may eagerly rebuild in between with the old state diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index bd060720e..35731d63c 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -3,8 +3,10 @@ import 'dart:io'; import 'package:aves/app_mode.dart'; import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/props.dart'; +import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/highlight.dart'; @@ -107,13 +109,28 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { ), itemCount: selectionCount, onDone: (processed) async { - final successOps = processed.where((e) => e.success).toSet(); - final exportedOps = successOps.where((e) => !e.skipped).toSet(); - final newUris = exportedOps.map((v) => v.newFields['uri'] as String?).nonNulls.toSet(); + final successOps = processed.where((op) => op.success).toSet(); + final exportedOps = successOps.where((op) => !op.skipped && op.newFields['uri'] != null).toSet(); + final newUris = exportedOps.map((op) => op.newFields['uri'] as String).toSet(); final isMainMode = context.read>().value == AppMode.main; + // check source favourite status + final favouriteSourceUris = selection.where((entry) => entry.isFavourite).map((entry) => entry.uri).toSet(); + final favouriteNewUris = {}; + exportedOps.forEach((op) { + final sourceUri = op.uri; + if (favouriteSourceUris.contains(sourceUri)) { + final newUri = op.newFields['uri'] as String; + favouriteNewUris.add(newUri); + } + }); + source.resumeMonitoring(); - unawaited(source.refreshUris(newUris)); + unawaited(source.refreshUris(newUris).then((_) { + // transfer favourite status on exports + final newFavouriteEntries = source.allEntries.where((entry) => favouriteNewUris.contains(entry.uri)).toSet(); + favourites.add(newFavouriteEntries); + })); // get navigator beforehand because // local context may be deactivated when action is triggered after navigation @@ -241,11 +258,11 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { itemCount: todoCount, onCancel: () => mediaEditService.cancelFileOp(opId), onDone: (processed) async { - final successOps = processed.where((v) => v.success).toSet(); + final successOps = processed.where((op) => op.success).toSet(); // move - final movedOps = successOps.where((v) => !v.skipped && !v.deleted).toSet(); - final movedEntries = movedOps.map((v) => v.uri).map((uri) => entries.firstWhereOrNull((entry) => entry.uri == uri)).nonNulls.toSet(); + final movedOps = successOps.where((op) => !op.skipped && !op.deleted).toSet(); + final movedEntries = movedOps.map((op) => op.uri).map((uri) => entries.firstWhereOrNull((entry) => entry.uri == uri)).nonNulls.toSet(); await source.updateAfterMove( todoEntries: entries, moveType: moveType, @@ -254,8 +271,8 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { ); // delete (when trying to move to bin obsolete entries) - final deletedOps = successOps.where((v) => v.deleted).toSet(); - final deletedUris = deletedOps.map((event) => event.uri).toSet(); + final deletedOps = successOps.where((op) => op.deleted).toSet(); + final deletedUris = deletedOps.map((op) => op.uri).toSet(); await source.removeEntries(deletedUris, includeTrash: true); source.resumeMonitoring(); @@ -405,8 +422,8 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { itemCount: todoCount, onCancel: () => mediaEditService.cancelFileOp(opId), onDone: (processed) async { - final successOps = processed.where((e) => e.success).toSet(); - final movedOps = successOps.where((e) => !e.skipped).toSet(); + final successOps = processed.where((op) => op.success).toSet(); + final movedOps = successOps.where((op) => !op.skipped).toSet(); await source.updateAfterRename( todoEntries: entries, movedOps: movedOps, @@ -466,7 +483,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { Set destinationAlbums, Set movedOps, ) async { - final newUris = movedOps.map((v) => v.newFields['uri'] as String?).toSet(); + final newUris = movedOps.map((op) => op.newFields['uri'] as String?).toSet(); bool highlightTest(AvesEntry entry) => newUris.contains(entry.uri); final collection = context.read(); diff --git a/lib/widgets/filter_grids/common/action_delegates/album_set.dart b/lib/widgets/filter_grids/common/action_delegates/album_set.dart index 8fa81c36c..7c8736e5e 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -382,9 +382,9 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate itemCount: todoCount, onCancel: () => mediaEditService.cancelFileOp(opId), onDone: (processed) async { - final successOps = processed.where((event) => event.success); - final deletedOps = successOps.where((e) => !e.skipped).toSet(); - final deletedUris = deletedOps.map((event) => event.uri).toSet(); + final successOps = processed.where((op) => op.success); + final deletedOps = successOps.where((op) => !op.skipped).toSet(); + final deletedUris = deletedOps.map((op) => op.uri).toSet(); await source.removeEntries(deletedUris, includeTrash: true); browse(context); source.resumeMonitoring(); @@ -547,8 +547,8 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate itemCount: todoCount, onCancel: () => mediaEditService.cancelFileOp(opId), onDone: (processed) async { - final successOps = processed.where((e) => e.success).toSet(); - final movedOps = successOps.where((e) => !e.skipped).toSet(); + final successOps = processed.where((op) => op.success).toSet(); + final movedOps = successOps.where((op) => !op.skipped).toSet(); await source.renameStoredAlbum(album, destinationAlbum, todoEntries, movedOps); browse(context); source.resumeMonitoring();