delete selected tags from all media in collection

This commit is contained in:
Thibault Deckers 2023-04-16 00:33:54 +02:00
parent 4866319a3b
commit 45a81e6522
4 changed files with 82 additions and 3 deletions

View file

@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- Info: improved state/place display (requires rescan, limited to AU/GB/IN/US)
- Info: edit tags with state placeholder
- Countries: show states for selected countries
- Tags: delete selected tags from all media in collection
- improved support for system font scale
### Changed

View file

@ -635,6 +635,14 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
await _edit(context, entries, (entry) => entry.editTags(newTagsByEntry[entry]!));
}
Future<void> removeTags(BuildContext context, {required Set<AvesEntry> entries, required Set<String> tags}) async {
final newTagsByEntry = Map.fromEntries(entries.map((v) {
return MapEntry(v, v.tags.whereNot(tags.contains).toSet());
}));
await _edit(context, entries, (entry) => entry.editTags(newTagsByEntry[entry]!));
}
Future<void> _removeMetadata(BuildContext context) async {
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canRemoveMetadata);
if (entries == null || entries.isEmpty) return;

View file

@ -83,9 +83,7 @@ mixin EntryEditorMixin {
if (entries.isEmpty) return null;
final filtersByEntry = Map.fromEntries(entries.map((v) {
// use `<CollectionFilter>{...}` instead of `toSet()` to circumvent an implicit typing issue, as of Dart v2.18.2
final filters = <CollectionFilter>{...v.tags.map(TagFilter.new)};
return MapEntry(v, filters);
return MapEntry(v, v.tags.map(TagFilter.new).toSet());
}));
await Navigator.maybeOf(context)?.push(
MaterialPageRoute(

View file

@ -1,9 +1,17 @@
import 'package:aves/app_mode.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class TagChipSetActionDelegate extends ChipSetActionDelegate<TagFilter> {
final Iterable<FilterGridItem<TagFilter>> _items;
@ -30,4 +38,68 @@ class TagChipSetActionDelegate extends ChipSetActionDelegate<TagFilter> {
@override
set tileLayout(TileLayout tileLayout) => settings.setTileLayout(TagListPage.routeName, tileLayout);
@override
bool isVisible(
ChipSetAction action, {
required AppMode appMode,
required bool isSelecting,
required int itemCount,
required Set<TagFilter> selectedFilters,
}) {
final isMain = appMode == AppMode.main;
switch (action) {
case ChipSetAction.delete:
return isMain && isSelecting && !settings.isReadOnly;
default:
return super.isVisible(
action,
appMode: appMode,
isSelecting: isSelecting,
itemCount: itemCount,
selectedFilters: selectedFilters,
);
}
}
@override
void onActionSelected(BuildContext context, Set<TagFilter> filters, ChipSetAction action) {
reportService.log('$action');
switch (action) {
// single/multiple filters
case ChipSetAction.delete:
_delete(context, filters);
break;
default:
break;
}
super.onActionSelected(context, filters, action);
}
Future<void> _delete(BuildContext context, Set<TagFilter> filters) async {
final source = context.read<CollectionSource>();
final todoEntries = source.visibleEntries.where((entry) => filters.any((f) => f.test(entry))).toSet();
final todoTags = filters.map((v) => v.tag).toSet();
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AvesDialog(
content: Text(context.l10n.genericDangerWarningDialogMessage),
actions: [
const CancelButton(),
TextButton(
onPressed: () => Navigator.maybeOf(context)?.pop(true),
child: Text(context.l10n.applyButtonLabel),
),
],
),
routeSettings: const RouteSettings(name: AvesDialog.warningRouteName),
);
if (confirmed == null || !confirmed) return;
await EntrySetActionDelegate().removeTags(context, entries: todoEntries, tags: todoTags);
browse(context);
}
}