warn and optionally set metadata date before moving undated items

This commit is contained in:
Thibault Deckers 2022-03-20 13:05:34 +09:00
parent 150e94cee5
commit 1101400ae2
14 changed files with 289 additions and 130 deletions

View file

@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
## <a id="unreleased"></a>[Unreleased]
### Added
- Theme: light/dark/black and poly/monochrome settings
- Video: speed and muted state indicators
- Info: option to set date from other item
- warn and optionally set metadata date before moving undated items
### Changed
- Viewer: quick action defaults
### Fixed
- app launch despite faulty storage volumes on Android 11+
## <a id="v1.6.2"></a>[v1.6.2] - 2022-03-07
### Added

View file

@ -286,6 +286,8 @@
"count": {}
}
},
"moveUndatedConfirmationDialogMessage": "Some items have no metadata date. Their current date will be reset by this operation unless a metadata date is set.",
"moveUndatedConfirmationDialogSetDate": "Set date",
"videoResumeDialogMessage": "Do you want to resume playing at {time}?",
"@videoResumeDialogMessage": {
@ -568,6 +570,7 @@
"settingsConfirmationDialogTitle": "Confirmation Dialogs",
"settingsConfirmationDialogDeleteItems": "Ask before deleting items forever",
"settingsConfirmationDialogMoveToBinItems": "Ask before moving items to the recycle bin",
"settingsConfirmationDialogMoveUndatedItems": "Ask before moving items without a metadata date",
"settingsNavigationDrawerTile": "Navigation menu",
"settingsNavigationDrawerEditorTitle": "Navigation Menu",

View file

@ -23,7 +23,10 @@ class SettingsDefaults {
static const mustBackTwiceToExit = true;
static const keepScreenOn = KeepScreenOn.viewerOnly;
static const homePage = HomePageSetting.collection;
static const confirmationDialogs = ConfirmationDialog.values;
static const confirmDeleteForever = true;
static const confirmMoveToBin = true;
static const confirmMoveUndatedItems = true;
static const setMetadataDateBeforeFileOp = false;
static final drawerTypeBookmarks = [
null,
MimeFilter.video,

View file

@ -1,15 +0,0 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/widgets.dart';
import 'enums.dart';
extension ExtraConfirmationDialog on ConfirmationDialog {
String getName(BuildContext context) {
switch (this) {
case ConfirmationDialog.delete:
return context.l10n.settingsConfirmationDialogDeleteItems;
case ConfirmationDialog.moveToBin:
return context.l10n.settingsConfirmationDialogMoveToBinItems;
}
}
}

View file

@ -6,7 +6,7 @@ enum AvesThemeColorMode { monochrome, polychrome }
enum AvesThemeBrightness { system, light, dark, black }
enum ConfirmationDialog { delete, moveToBin }
enum ConfirmationDialog { deleteForever, moveToBin, moveUndatedItems }
enum CoordinateFormat { dms, decimal }

View file

@ -52,7 +52,10 @@ class Settings extends ChangeNotifier {
static const mustBackTwiceToExitKey = 'must_back_twice_to_exit';
static const keepScreenOnKey = 'keep_screen_on';
static const homePageKey = 'home_page';
static const confirmationDialogsKey = 'confirmation_dialogs';
static const confirmDeleteForeverKey = 'confirm_delete_forever';
static const confirmMoveToBinKey = 'confirm_move_to_bin';
static const confirmMoveUndatedItemsKey = 'confirm_move_undated_items';
static const setMetadataDateBeforeFileOpKey = 'set_metadata_date_before_file_op';
static const drawerTypeBookmarksKey = 'drawer_type_bookmarks';
static const drawerAlbumBookmarksKey = 'drawer_album_bookmarks';
static const drawerPageBookmarksKey = 'drawer_page_bookmarks';
@ -279,9 +282,21 @@ class Settings extends ChangeNotifier {
set homePage(HomePageSetting newValue) => setAndNotify(homePageKey, newValue.toString());
List<ConfirmationDialog> get confirmationDialogs => getEnumListOrDefault(confirmationDialogsKey, SettingsDefaults.confirmationDialogs, ConfirmationDialog.values);
bool get confirmDeleteForever => getBoolOrDefault(confirmDeleteForeverKey, SettingsDefaults.confirmDeleteForever);
set confirmationDialogs(List<ConfirmationDialog> newValue) => setAndNotify(confirmationDialogsKey, newValue.map((v) => v.toString()).toList());
set confirmDeleteForever(bool newValue) => setAndNotify(confirmDeleteForeverKey, newValue);
bool get confirmMoveToBin => getBoolOrDefault(confirmMoveToBinKey, SettingsDefaults.confirmMoveToBin);
set confirmMoveToBin(bool newValue) => setAndNotify(confirmMoveToBinKey, newValue);
bool get confirmMoveUndatedItems => getBoolOrDefault(confirmMoveUndatedItemsKey, SettingsDefaults.confirmMoveUndatedItems);
set confirmMoveUndatedItems(bool newValue) => setAndNotify(confirmMoveUndatedItemsKey, newValue);
bool get setMetadataDateBeforeFileOp => getBoolOrDefault(setMetadataDateBeforeFileOpKey, SettingsDefaults.setMetadataDateBeforeFileOp);
set setMetadataDateBeforeFileOp(bool newValue) => setAndNotify(setMetadataDateBeforeFileOpKey, newValue);
List<CollectionFilter?> get drawerTypeBookmarks =>
(getStringList(drawerTypeBookmarksKey))?.map((v) {
@ -656,6 +671,10 @@ class Settings extends ChangeNotifier {
case isInstalledAppAccessAllowedKey:
case isErrorReportingAllowedKey:
case mustBackTwiceToExitKey:
case confirmDeleteForeverKey:
case confirmMoveToBinKey:
case confirmMoveUndatedItemsKey:
case setMetadataDateBeforeFileOpKey:
case showThumbnailFavouriteKey:
case showThumbnailLocationKey:
case showThumbnailMotionPhotoKey:
@ -710,7 +729,6 @@ class Settings extends ChangeNotifier {
debugPrint('failed to import key=$key, value=$newValue is not a string');
}
break;
case confirmationDialogsKey:
case drawerTypeBookmarksKey:
case drawerAlbumBookmarksKey:
case drawerPageBookmarksKey:

View file

@ -75,7 +75,7 @@ mixin LocationMixin on SourceBase {
// full reverse geocoding, requiring Play Services and some connectivity
Future<void> _locatePlaces(AnalysisController controller, Set<AvesEntry> candidateEntries) async {
if (controller.isStopping) return;
if (!(await availability.canLocatePlaces)) return;
if (!await availability.canLocatePlaces) return;
final force = controller.force;
final todo = (force ? candidateEntries.where((entry) => entry.hasGps) : candidateEntries.where(locatePlacesTest)).toSet();

View file

@ -8,6 +8,7 @@ import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_metadata_edition.dart';
import 'package:aves/model/favourites.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/model/query.dart';
import 'package:aves/model/selection.dart';
import 'package:aves/model/settings/enums/enums.dart';
@ -197,7 +198,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
_flip(context);
break;
case EntrySetAction.editDate:
_editDate(context);
editDate(context);
break;
case EntrySetAction.editLocation:
_editLocation(context);
@ -214,6 +215,11 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
}
}
void _leaveSelectionMode(BuildContext context) {
final selection = context.read<Selection<AvesEntry>?>();
selection?.browse();
}
Set<AvesEntry> _getTargetItems(BuildContext context) {
final selection = context.read<Selection<AvesEntry>>();
final groupedEntries = (selection.isSelecting ? selection.selectedItems : context.read<CollectionLens>().sortedEntries);
@ -234,8 +240,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
final collection = context.read<CollectionLens>();
collection.source.analyze(controller, entries: entries);
final selection = context.read<Selection<AvesEntry>>();
selection.browse();
_leaveSelectionMode(context);
}
Future<void> _toggleFavourite(BuildContext context) async {
@ -246,8 +251,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
await favourites.add(entries);
}
final selection = context.read<Selection<AvesEntry>>();
selection.browse();
_leaveSelectionMode(context);
}
Future<void> _delete(BuildContext context) async {
@ -264,12 +268,12 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
final selectionDirs = entries.map((e) => e.directory).whereNotNull().toSet();
final todoCount = entries.length;
if (!(await showConfirmationDialog(
if (!await showConfirmationDialog(
context: context,
type: ConfirmationDialog.delete,
type: ConfirmationDialog.deleteForever,
message: l10n.deleteEntriesConfirmationDialogMessage(todoCount),
confirmationButtonLabel: l10n.deleteButtonLabel,
))) return;
)) return;
if (!pureTrash && !await checkStoragePermissionForAlbums(context, selectionDirs, entries: entries)) return;
@ -298,23 +302,22 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
},
);
final selection = context.read<Selection<AvesEntry>>();
selection.browse();
_leaveSelectionMode(context);
}
Future<void> _move(BuildContext context, {required MoveType moveType}) async {
final entries = _getTargetItems(context);
await move(context, moveType: moveType, entries: entries);
final selection = context.read<Selection<AvesEntry>>();
selection.browse();
_leaveSelectionMode(context);
}
Future<void> _edit(
BuildContext context,
Set<AvesEntry> todoItems,
Future<Set<EntryDataType>> Function(AvesEntry entry) op,
) async {
Future<Set<EntryDataType>> Function(AvesEntry entry) op, {
bool showResult = true,
}) async {
final selectionDirs = todoItems.map((e) => e.directory).whereNotNull().toSet();
final todoCount = todoItems.length;
@ -355,19 +358,20 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
}
}));
final l10n = context.l10n;
final successCount = successOps.length;
if (successCount < todoCount) {
final count = todoCount - successCount;
showFeedback(context, l10n.collectionEditFailureFeedback(count));
} else {
final count = editedOps.length;
showFeedback(context, l10n.collectionEditSuccessFeedback(count));
if (showResult) {
final l10n = context.l10n;
final successCount = successOps.length;
if (successCount < todoCount) {
final count = todoCount - successCount;
showFeedback(context, l10n.collectionEditFailureFeedback(count));
} else {
final count = editedOps.length;
showFeedback(context, l10n.collectionEditSuccessFeedback(count));
}
}
},
);
final selection = context.read<Selection<AvesEntry>>();
selection.browse();
_leaveSelectionMode(context);
}
Future<Set<AvesEntry>?> _getEditableTargetItems(
@ -410,78 +414,80 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
}
Future<void> _rotate(BuildContext context, {required bool clockwise}) async {
final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canRotateAndFlip);
if (todoItems == null || todoItems.isEmpty) return;
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canRotateAndFlip);
if (entries == null || entries.isEmpty) return;
await _edit(context, todoItems, (entry) => entry.rotate(clockwise: clockwise));
await _edit(context, entries, (entry) => entry.rotate(clockwise: clockwise));
}
Future<void> _flip(BuildContext context) async {
final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canRotateAndFlip);
if (todoItems == null || todoItems.isEmpty) return;
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canRotateAndFlip);
if (entries == null || entries.isEmpty) return;
await _edit(context, todoItems, (entry) => entry.flip());
await _edit(context, entries, (entry) => entry.flip());
}
Future<void> _editDate(BuildContext context) async {
final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditDate);
if (todoItems == null || todoItems.isEmpty) return;
Future<void> editDate(BuildContext context, {Set<AvesEntry>? entries, DateModifier? modifier, bool showResult = true}) async {
entries ??= await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditDate);
if (entries == null || entries.isEmpty) return;
final collection = context.read<CollectionLens>();
final modifier = await selectDateModifier(context, todoItems, collection);
if (modifier == null) {
final collection = context.read<CollectionLens>();
modifier = await selectDateModifier(context, entries, collection);
}
if (modifier == null) return;
await _edit(context, todoItems, (entry) => entry.editDate(modifier));
await _edit(context, entries, (entry) => entry.editDate(modifier!), showResult: showResult);
}
Future<void> _editLocation(BuildContext context) async {
final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditLocation);
if (todoItems == null || todoItems.isEmpty) return;
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditLocation);
if (entries == null || entries.isEmpty) return;
final collection = context.read<CollectionLens>();
final location = await selectLocation(context, todoItems, collection);
final location = await selectLocation(context, entries, collection);
if (location == null) return;
await _edit(context, todoItems, (entry) => entry.editLocation(location));
await _edit(context, entries, (entry) => entry.editLocation(location));
}
Future<void> _editRating(BuildContext context) async {
final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditRating);
if (todoItems == null || todoItems.isEmpty) return;
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditRating);
if (entries == null || entries.isEmpty) return;
final rating = await selectRating(context, todoItems);
final rating = await selectRating(context, entries);
if (rating == null) return;
await _edit(context, todoItems, (entry) => entry.editRating(rating));
await _edit(context, entries, (entry) => entry.editRating(rating));
}
Future<void> _editTags(BuildContext context) async {
final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditTags);
if (todoItems == null || todoItems.isEmpty) return;
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditTags);
if (entries == null || entries.isEmpty) return;
final newTagsByEntry = await selectTags(context, todoItems);
final newTagsByEntry = await selectTags(context, entries);
if (newTagsByEntry == null) return;
// only process modified items
todoItems.removeWhere((entry) {
entries.removeWhere((entry) {
final newTags = newTagsByEntry[entry] ?? entry.tags;
final currentTags = entry.tags;
return newTags.length == currentTags.length && newTags.every(currentTags.contains);
});
if (todoItems.isEmpty) return;
if (entries.isEmpty) return;
await _edit(context, todoItems, (entry) => entry.editTags(newTagsByEntry[entry]!));
await _edit(context, entries, (entry) => entry.editTags(newTagsByEntry[entry]!));
}
Future<void> _removeMetadata(BuildContext context) async {
final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canRemoveMetadata);
if (todoItems == null || todoItems.isEmpty) return;
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canRemoveMetadata);
if (entries == null || entries.isEmpty) return;
final types = await selectMetadataToRemove(context, todoItems);
final types = await selectMetadataToRemove(context, entries);
if (types == null || types.isEmpty) return;
await _edit(context, todoItems, (entry) => entry.removeMetadata(types));
await _edit(context, entries, (entry) => entry.removeMetadata(types));
}
void _goToMap(BuildContext context) {

View file

@ -7,7 +7,10 @@ import 'package:aves/model/entry.dart';
import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/highlight.dart';
import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/model/metadata/enums.dart';
import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/services/common/image_op_events.dart';
@ -16,6 +19,7 @@ import 'package:aves/services/media/enums.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:aves/widgets/common/action_mixins/permission_aware.dart';
import 'package:aves/widgets/common/action_mixins/size_aware.dart';
@ -42,12 +46,12 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
final l10n = context.l10n;
if (toBin) {
if (!(await showConfirmationDialog(
if (!await showConfirmationDialog(
context: context,
type: ConfirmationDialog.moveToBin,
message: l10n.binEntriesConfirmationDialogMessage(todoCount),
confirmationButtonLabel: l10n.deleteButtonLabel,
))) return;
)) return;
}
final entriesByDestination = <String, Set<AvesEntry>>{};
@ -108,6 +112,8 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
}
}
if ({MoveType.move, MoveType.copy}.contains(moveType) && !await _checkUndatedItems(context, entries)) return;
final source = context.read<CollectionSource>();
source.pauseMonitoring();
final opId = mediaFileService.newOpId;
@ -196,4 +202,60 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
},
);
}
Future<bool> _checkUndatedItems(BuildContext context, Set<AvesEntry> entries) async {
final undatedItems = entries.where((entry) {
if (!entry.isCatalogued) return false;
final dateMillis = entry.catalogMetadata?.dateMillis;
return dateMillis == null || dateMillis == 0;
}).toSet();
if (undatedItems.isNotEmpty) {
if (!await showConfirmationDialog(
context: context,
type: ConfirmationDialog.moveUndatedItems,
delegate: MoveUndatedConfirmationDialogDelegate(),
confirmationButtonLabel: context.l10n.continueButtonLabel,
)) return false;
if (settings.setMetadataDateBeforeFileOp) {
final entriesToDate = undatedItems.where((entry) => entry.canEditDate).toSet();
if (entriesToDate.isNotEmpty) {
await EntrySetActionDelegate().editDate(
context,
entries: entriesToDate,
modifier: DateModifier.copyField(DateFieldSource.fileModifiedDate),
showResult: false,
);
}
}
}
return true;
}
}
class MoveUndatedConfirmationDialogDelegate extends ConfirmationDialogDelegate {
final ValueNotifier<bool> _setMetadataDate = ValueNotifier(false);
MoveUndatedConfirmationDialogDelegate() {
_setMetadataDate.value = settings.setMetadataDateBeforeFileOp;
}
@override
List<Widget> build(BuildContext context) => [
Padding(
padding: const EdgeInsets.all(16) + const EdgeInsets.only(top: 8),
child: Text(context.l10n.moveUndatedConfirmationDialogMessage),
),
ValueListenableBuilder<bool>(
valueListenable: _setMetadataDate,
builder: (context, flag, child) => SwitchListTile(
value: flag,
onChanged: (v) => _setMetadataDate.value = v,
title: Text(context.l10n.moveUndatedConfirmationDialogSetDate),
),
),
];
@override
void apply() => settings.setMetadataDateBeforeFileOp = _setMetadataDate.value;
}

View file

@ -5,56 +5,107 @@ import 'package:flutter/material.dart';
import 'aves_dialog.dart';
abstract class ConfirmationDialogDelegate {
List<Widget> build(BuildContext context);
void apply() {}
}
class MessageConfirmationDialogDelegate extends ConfirmationDialogDelegate {
final String message;
MessageConfirmationDialogDelegate(this.message);
@override
List<Widget> build(BuildContext context) => [
Padding(
padding: const EdgeInsets.all(16) + const EdgeInsets.only(top: 8),
child: Text(message),
),
];
}
Future<bool> showConfirmationDialog({
required BuildContext context,
required ConfirmationDialog type,
required String message,
String? message,
ConfirmationDialogDelegate? delegate,
required String confirmationButtonLabel,
}) async {
if (!settings.confirmationDialogs.contains(type)) return true;
if (!_shouldConfirm(type)) return true;
assert((message != null) ^ (delegate != null));
final effectiveDelegate = delegate ?? MessageConfirmationDialogDelegate(message!);
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AvesConfirmationDialog(
builder: (context) => _AvesConfirmationDialog(
type: type,
message: message,
delegate: effectiveDelegate,
confirmationButtonLabel: confirmationButtonLabel,
),
);
return confirmed == true;
if (confirmed == null) return false;
if (confirmed) {
effectiveDelegate.apply();
}
return confirmed;
}
class AvesConfirmationDialog extends StatefulWidget {
final ConfirmationDialog type;
final String message, confirmationButtonLabel;
bool _shouldConfirm(ConfirmationDialog type) {
switch (type) {
case ConfirmationDialog.deleteForever:
return settings.confirmDeleteForever;
case ConfirmationDialog.moveToBin:
return settings.confirmMoveToBin;
case ConfirmationDialog.moveUndatedItems:
return settings.confirmMoveUndatedItems;
}
}
const AvesConfirmationDialog({
void _skipConfirmation(ConfirmationDialog type) {
switch (type) {
case ConfirmationDialog.deleteForever:
settings.confirmDeleteForever = false;
break;
case ConfirmationDialog.moveToBin:
settings.confirmMoveToBin = false;
break;
case ConfirmationDialog.moveUndatedItems:
settings.confirmMoveUndatedItems = false;
break;
}
}
class _AvesConfirmationDialog extends StatefulWidget {
final ConfirmationDialog type;
final ConfirmationDialogDelegate delegate;
final String confirmationButtonLabel;
const _AvesConfirmationDialog({
Key? key,
required this.type,
required this.message,
required this.delegate,
required this.confirmationButtonLabel,
}) : super(key: key);
@override
State<AvesConfirmationDialog> createState() => _AvesConfirmationDialogState();
State<_AvesConfirmationDialog> createState() => _AvesConfirmationDialogState();
}
class _AvesConfirmationDialogState extends State<AvesConfirmationDialog> {
final ValueNotifier<bool> _skipConfirmation = ValueNotifier(false);
class _AvesConfirmationDialogState extends State<_AvesConfirmationDialog> {
final ValueNotifier<bool> _skip = ValueNotifier(false);
@override
Widget build(BuildContext context) {
return AvesDialog(
scrollableContent: [
Padding(
padding: const EdgeInsets.all(16) + const EdgeInsets.only(top: 8),
child: Text(widget.message),
),
...widget.delegate.build(context),
ValueListenableBuilder<bool>(
valueListenable: _skipConfirmation,
builder: (context, ask, child) => SwitchListTile(
value: ask,
onChanged: (v) => _skipConfirmation.value = v,
valueListenable: _skip,
builder: (context, flag, child) => SwitchListTile(
value: flag,
onChanged: (v) => _skip.value = v,
title: Text(context.l10n.doNotAskAgain),
),
),
@ -66,8 +117,8 @@ class _AvesConfirmationDialogState extends State<AvesConfirmationDialog> {
),
TextButton(
onPressed: () {
if (_skipConfirmation.value) {
settings.confirmationDialogs = settings.confirmationDialogs.toList()..remove(widget.type);
if (_skip.value) {
_skipConfirmation(widget.type);
}
Navigator.pop(context, true);
},

View file

@ -290,7 +290,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
if (!await checkStoragePermissionForAlbums(context, {album})) return;
if (!(await File(destinationAlbum).exists())) {
if (!await File(destinationAlbum).exists()) {
// access to the destination parent is required to create the underlying destination folder
if (!await checkStoragePermissionForAlbums(context, {destinationAlbumParent})) return;
}

View file

@ -1,9 +1,7 @@
import 'package:aves/model/settings/enums/confirmation_dialogs.dart';
import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/settings/common/tiles.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ConfirmationDialogTile extends StatelessWidget {
const ConfirmationDialogTile({Key? key}) : super(key: key);
@ -37,29 +35,23 @@ class ConfirmationDialogPage extends StatelessWidget {
title: Text(context.l10n.settingsConfirmationDialogTitle),
),
body: SafeArea(
child: Selector<Settings, List<ConfirmationDialog>>(
selector: (context, s) => s.confirmationDialogs,
builder: (context, current, child) => ListView(
children: [
ConfirmationDialog.moveToBin,
ConfirmationDialog.delete,
]
.map((dialog) => SwitchListTile(
value: current.contains(dialog),
onChanged: (v) {
final dialogs = current.toList();
if (v) {
dialogs.add(dialog);
} else {
dialogs.remove(dialog);
}
settings.confirmationDialogs = dialogs;
},
title: Text(dialog.getName(context)),
))
.toList(),
child: ListView(children: [
SettingsSwitchListTile(
selector: (context, s) => s.confirmMoveUndatedItems,
onChanged: (v) => settings.confirmMoveUndatedItems = v,
title: context.l10n.settingsConfirmationDialogMoveUndatedItems,
),
),
SettingsSwitchListTile(
selector: (context, s) => s.confirmMoveToBin,
onChanged: (v) => settings.confirmMoveToBin = v,
title: context.l10n.settingsConfirmationDialogMoveToBinItems,
),
SettingsSwitchListTile(
selector: (context, s) => s.confirmDeleteForever,
onChanged: (v) => settings.confirmDeleteForever = v,
title: context.l10n.settingsConfirmationDialogDeleteItems,
),
]),
),
);
}

View file

@ -187,12 +187,12 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
}
final l10n = context.l10n;
if (!(await showConfirmationDialog(
if (!await showConfirmationDialog(
context: context,
type: ConfirmationDialog.delete,
type: ConfirmationDialog.deleteForever,
message: l10n.deleteEntriesConfirmationDialogMessage(1),
confirmationButtonLabel: l10n.deleteButtonLabel,
))) return;
)) return;
if (!await checkStoragePermission(context, {entry})) return;

View file

@ -3,7 +3,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsSectionDisplay",
"settingsThemeBrightness",
"settingsThemeColorful"
@ -13,7 +16,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsSectionDisplay",
"settingsThemeBrightness",
"settingsThemeColorful"
@ -23,7 +29,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsSectionDisplay",
"settingsThemeBrightness",
"settingsThemeColorful"
@ -39,7 +48,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsViewerShowOverlayThumbnails",
"settingsVideoControlsTile",
"settingsVideoControlsTitle",
@ -56,7 +68,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsSectionDisplay",
"settingsThemeBrightness",
"settingsThemeColorful"
@ -66,7 +81,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsSectionDisplay",
"settingsThemeBrightness",
"settingsThemeColorful"
@ -76,7 +94,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsSectionDisplay",
"settingsThemeBrightness",
"settingsThemeColorful"
@ -86,7 +107,10 @@
"themeBrightnessLight",
"themeBrightnessDark",
"themeBrightnessBlack",
"moveUndatedConfirmationDialogMessage",
"moveUndatedConfirmationDialogSetDate",
"editEntryDateDialogCopyItem",
"settingsConfirmationDialogMoveUndatedItems",
"settingsSectionDisplay",
"settingsThemeBrightness",
"settingsThemeColorful"