diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c51c196f..e8ff62849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. ## [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+ + ## [v1.6.2] - 2022-03-07 ### Added diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index be8b3564d..2c54d3c54 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -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", diff --git a/lib/model/settings/defaults.dart b/lib/model/settings/defaults.dart index 7f39bca08..38008b3d3 100644 --- a/lib/model/settings/defaults.dart +++ b/lib/model/settings/defaults.dart @@ -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, diff --git a/lib/model/settings/enums/confirmation_dialogs.dart b/lib/model/settings/enums/confirmation_dialogs.dart deleted file mode 100644 index 4f1a58742..000000000 --- a/lib/model/settings/enums/confirmation_dialogs.dart +++ /dev/null @@ -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; - } - } -} diff --git a/lib/model/settings/enums/enums.dart b/lib/model/settings/enums/enums.dart index 2c73ccec7..b0709fdf3 100644 --- a/lib/model/settings/enums/enums.dart +++ b/lib/model/settings/enums/enums.dart @@ -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 } diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index 443db7e31..02eaef8f8 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -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 get confirmationDialogs => getEnumListOrDefault(confirmationDialogsKey, SettingsDefaults.confirmationDialogs, ConfirmationDialog.values); + bool get confirmDeleteForever => getBoolOrDefault(confirmDeleteForeverKey, SettingsDefaults.confirmDeleteForever); - set confirmationDialogs(List 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 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: diff --git a/lib/model/source/location.dart b/lib/model/source/location.dart index deca24b85..8eb3f8fc7 100644 --- a/lib/model/source/location.dart +++ b/lib/model/source/location.dart @@ -75,7 +75,7 @@ mixin LocationMixin on SourceBase { // full reverse geocoding, requiring Play Services and some connectivity Future _locatePlaces(AnalysisController controller, Set 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(); diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index b8147e4cf..db4b5f609 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -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?.browse(); + } + Set _getTargetItems(BuildContext context) { final selection = context.read>(); final groupedEntries = (selection.isSelecting ? selection.selectedItems : context.read().sortedEntries); @@ -234,8 +240,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware final collection = context.read(); collection.source.analyze(controller, entries: entries); - final selection = context.read>(); - selection.browse(); + _leaveSelectionMode(context); } Future _toggleFavourite(BuildContext context) async { @@ -246,8 +251,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware await favourites.add(entries); } - final selection = context.read>(); - selection.browse(); + _leaveSelectionMode(context); } Future _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.browse(); + _leaveSelectionMode(context); } Future _move(BuildContext context, {required MoveType moveType}) async { final entries = _getTargetItems(context); await move(context, moveType: moveType, entries: entries); - final selection = context.read>(); - selection.browse(); + _leaveSelectionMode(context); } Future _edit( BuildContext context, Set todoItems, - Future> Function(AvesEntry entry) op, - ) async { + Future> 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.browse(); + _leaveSelectionMode(context); } Future?> _getEditableTargetItems( @@ -410,78 +414,80 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware } Future _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 _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 _editDate(BuildContext context) async { - final todoItems = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditDate); - if (todoItems == null || todoItems.isEmpty) return; + Future editDate(BuildContext context, {Set? 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(); - final modifier = await selectDateModifier(context, todoItems, collection); + if (modifier == null) { + final collection = context.read(); + 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 _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(); - 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 _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 _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 _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) { diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index 3cec4cea3..38f63b35d 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -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 = >{}; @@ -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(); source.pauseMonitoring(); final opId = mediaFileService.newOpId; @@ -196,4 +202,60 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { }, ); } + + Future _checkUndatedItems(BuildContext context, Set 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 _setMetadataDate = ValueNotifier(false); + + MoveUndatedConfirmationDialogDelegate() { + _setMetadataDate.value = settings.setMetadataDateBeforeFileOp; + } + + @override + List build(BuildContext context) => [ + Padding( + padding: const EdgeInsets.all(16) + const EdgeInsets.only(top: 8), + child: Text(context.l10n.moveUndatedConfirmationDialogMessage), + ), + ValueListenableBuilder( + 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; } diff --git a/lib/widgets/dialogs/aves_confirmation_dialog.dart b/lib/widgets/dialogs/aves_confirmation_dialog.dart index de287290a..36f77c408 100644 --- a/lib/widgets/dialogs/aves_confirmation_dialog.dart +++ b/lib/widgets/dialogs/aves_confirmation_dialog.dart @@ -5,56 +5,107 @@ import 'package:flutter/material.dart'; import 'aves_dialog.dart'; +abstract class ConfirmationDialogDelegate { + List build(BuildContext context); + + void apply() {} +} + +class MessageConfirmationDialogDelegate extends ConfirmationDialogDelegate { + final String message; + + MessageConfirmationDialogDelegate(this.message); + + @override + List build(BuildContext context) => [ + Padding( + padding: const EdgeInsets.all(16) + const EdgeInsets.only(top: 8), + child: Text(message), + ), + ]; +} + Future 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( 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 createState() => _AvesConfirmationDialogState(); + State<_AvesConfirmationDialog> createState() => _AvesConfirmationDialogState(); } -class _AvesConfirmationDialogState extends State { - final ValueNotifier _skipConfirmation = ValueNotifier(false); +class _AvesConfirmationDialogState extends State<_AvesConfirmationDialog> { + final ValueNotifier _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( - 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 { ), TextButton( onPressed: () { - if (_skipConfirmation.value) { - settings.confirmationDialogs = settings.confirmationDialogs.toList()..remove(widget.type); + if (_skip.value) { + _skipConfirmation(widget.type); } Navigator.pop(context, true); }, 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 bc49eebac..a0c5b2e6c 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -290,7 +290,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate 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; } diff --git a/lib/widgets/settings/navigation/confirmation_dialogs.dart b/lib/widgets/settings/navigation/confirmation_dialogs.dart index e46a7864a..e45a7890f 100644 --- a/lib/widgets/settings/navigation/confirmation_dialogs.dart +++ b/lib/widgets/settings/navigation/confirmation_dialogs.dart @@ -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>( - 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, + ), + ]), ), ); } diff --git a/lib/widgets/viewer/action/entry_action_delegate.dart b/lib/widgets/viewer/action/entry_action_delegate.dart index 5434ee6c1..affd156d3 100644 --- a/lib/widgets/viewer/action/entry_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_action_delegate.dart @@ -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; diff --git a/untranslated.json b/untranslated.json index 3ad3eea16..13e575b5d 100644 --- a/untranslated.json +++ b/untranslated.json @@ -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"