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"