From 30d875f1cf1e9988434b2eecf67c94e6cf731dfe Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Thu, 30 Dec 2021 18:19:53 +0900 Subject: [PATCH] info: changed date edit dialog --- lib/l10n/app_de.arb | 2 +- lib/l10n/app_en.arb | 9 +- lib/l10n/app_fr.arb | 9 +- lib/l10n/app_ko.arb | 7 +- lib/l10n/app_ru.arb | 2 +- lib/model/entry.dart | 90 +++++------ lib/model/metadata/date_modifier.dart | 26 +++- lib/model/metadata/enums.dart | 22 ++- lib/utils/time_utils.dart | 4 +- .../entry_editors/edit_entry_date_dialog.dart | 147 ++++++++---------- lib/widgets/search/search_delegate.dart | 2 +- untranslated.json | 10 +- 12 files changed, 159 insertions(+), 171 deletions(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 1ab2eea74..20d207b42 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -178,7 +178,7 @@ "renameEntryDialogLabel": "Neuer Name", "editEntryDateDialogTitle": "Datum & Uhrzeit", - "editEntryDateDialogSet": "Festlegen", + "editEntryDateDialogExtractFromTitle": "Auszug aus dem Titel", "editEntryDateDialogShift": "Verschieben", "editEntryDateDialogClear": "Aufräumen", "editEntryDateDialogHours": "Stunden", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f5354e8e1..aec69c717 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -275,13 +275,12 @@ "renameEntryDialogLabel": "New name", "editEntryDateDialogTitle": "Date & Time", - "editEntryDateDialogSet": "Set", + "editEntryDateDialogSetCustom": "Set custom date", + "editEntryDateDialogCopyField": "Set from other date", + "editEntryDateDialogExtractFromTitle": "Extract from title", "editEntryDateDialogShift": "Shift", "editEntryDateDialogClear": "Clear", - "editEntryDateDialogSourceFieldLabel": "Value:", - "editEntryDateDialogSourceCustomDate": "custom date", - "editEntryDateDialogSourceTitle": "extracted from title", - "editEntryDateDialogSourceFileModifiedDate": "file modified date", + "editEntryDateDialogSourceFileModifiedDate": "File modified date", "editEntryDateDialogTargetFieldsHeader": "Fields to modify", "editEntryDateDialogHours": "Hours", "editEntryDateDialogMinutes": "Minutes", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index a704e788c..2971c6bec 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -180,13 +180,12 @@ "renameEntryDialogLabel": "Nouveau nom", "editEntryDateDialogTitle": "Date & Heure", - "editEntryDateDialogSet": "Régler", + "editEntryDateDialogSetCustom": "Régler une date personnalisée", + "editEntryDateDialogCopyField": "Copier d'une autre date", + "editEntryDateDialogExtractFromTitle": "Extraire du titre", "editEntryDateDialogShift": "Décaler", "editEntryDateDialogClear": "Effacer", - "editEntryDateDialogSourceFieldLabel": "Valeur :", - "editEntryDateDialogSourceCustomDate": "date personnalisée", - "editEntryDateDialogSourceTitle": "extraite du titre", - "editEntryDateDialogSourceFileModifiedDate": "date de modification du fichier", + "editEntryDateDialogSourceFileModifiedDate": "Date de modification du fichier", "editEntryDateDialogTargetFieldsHeader": "Champs à modifier", "editEntryDateDialogHours": "Heures", "editEntryDateDialogMinutes": "Minutes", diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index 89c5a9017..c5cd70c88 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -180,12 +180,11 @@ "renameEntryDialogLabel": "이름", "editEntryDateDialogTitle": "날짜 및 시간", - "editEntryDateDialogSet": "편집", + "editEntryDateDialogSetCustom": "지정 날짜로 편집", + "editEntryDateDialogCopyField": "다른 날짜에서 지정", + "editEntryDateDialogExtractFromTitle": "제목에서 추출", "editEntryDateDialogShift": "시간 이동", "editEntryDateDialogClear": "삭제", - "editEntryDateDialogSourceFieldLabel": "값:", - "editEntryDateDialogSourceCustomDate": "지정 날짜", - "editEntryDateDialogSourceTitle": "제목에서 추출", "editEntryDateDialogSourceFileModifiedDate": "파일 수정한 날짜", "editEntryDateDialogTargetFieldsHeader": "수정할 필드", "editEntryDateDialogHours": "시간", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index f85ede8ec..e851d2fe2 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -178,7 +178,7 @@ "renameEntryDialogLabel": "Новое название", "editEntryDateDialogTitle": "Дата и время", - "editEntryDateDialogSet": "Задать", + "editEntryDateDialogExtractFromTitle": "Извлечь из названия", "editEntryDateDialogShift": "Сдвиг", "editEntryDateDialogClear": "Очистить", "editEntryDateDialogHours": "Часов", diff --git a/lib/model/entry.dart b/lib/model/entry.dart index 0153f4590..9d2e83afe 100644 --- a/lib/model/entry.dart +++ b/lib/model/entry.dart @@ -675,55 +675,42 @@ class AvesEntry { } Future> editDate(DateModifier modifier) async { - final action = modifier.action; - if (action == DateEditAction.set) { - final source = modifier.setSource; - if (source == null) { - await reportService.recordError('edit date with action=$action but source is null', null); - return {}; - } - - switch (source) { - case DateSetSource.title: - final _title = bestTitle; - if (_title == null) return {}; - final date = parseUnknownDateFormat(_title); - if (date == null) { - await reportService.recordError('failed to parse date from title=$_title', null); - return {}; + switch (modifier.action) { + case DateEditAction.copyField: + DateTime? date; + final source = modifier.copyFieldSource; + if (source != null) { + switch (source) { + case DateFieldSource.fileModifiedDate: + try { + date = path != null ? await File(path!).lastModified() : null; + } on FileSystemException catch (_) {} + break; + default: + date = await metadataFetchService.getDate(this, source.toMetadataField()!); + break; } - modifier = DateModifier(DateEditAction.set, modifier.fields, setDateTime: date); - break; - case DateSetSource.fileModifiedDate: - final _path = path; - if (_path == null) { - await reportService.recordError('edit date with action=$action, source=$source but entry has no path, uri=$uri', null); - return {}; - } - try { - final fileModifiedDate = await File(_path).lastModified(); - modifier = DateModifier(DateEditAction.set, modifier.fields, setDateTime: fileModifiedDate); - } on FileSystemException catch (error, stack) { - await reportService.recordError(error, stack); - return {}; - } - break; - case DateSetSource.custom: - break; - default: - final field = source.toMetadataField(); - if (field == null) { - await reportService.recordError('failed to get field for action=$action, source=$source, uri=$uri', null); - return {}; - } - final fieldDate = await metadataFetchService.getDate(this, field); - if (fieldDate == null) { - await reportService.recordError('failed to get date for field=$field, source=$source, uri=$uri', null); - return {}; - } - modifier = DateModifier(DateEditAction.set, modifier.fields, setDateTime: fieldDate); - break; - } + } + if (date != null) { + modifier = DateModifier.setCustom(modifier.fields, date); + } else { + await reportService.recordError('failed to get date for modifier=$modifier, uri=$uri', null); + return {}; + } + break; + case DateEditAction.extractFromTitle: + final date = parseUnknownDateFormat(bestTitle); + if (date != null) { + modifier = DateModifier.setCustom(modifier.fields, date); + } else { + await reportService.recordError('failed to get date for modifier=$modifier, uri=$uri', null); + return {}; + } + break; + case DateEditAction.setCustom: + case DateEditAction.shift: + case DateEditAction.clear: + break; } final newFields = await metadataEditService.editDate(this, modifier); return newFields.isEmpty @@ -746,10 +733,9 @@ class AvesEntry { final metadataDate = catalogMetadata?.dateMillis; if (metadataDate != null && metadataDate > 0) return {}; - return await editDate(const DateModifier( - DateEditAction.set, - {MetadataField.exifDateOriginal}, - setSource: DateSetSource.fileModifiedDate, + return await editDate(DateModifier.copyField( + const {MetadataField.exifDateOriginal}, + DateFieldSource.fileModifiedDate, )); } diff --git a/lib/model/metadata/date_modifier.dart b/lib/model/metadata/date_modifier.dart index 2924306eb..176d403e7 100644 --- a/lib/model/metadata/date_modifier.dart +++ b/lib/model/metadata/date_modifier.dart @@ -13,15 +13,35 @@ class DateModifier { final DateEditAction action; final Set fields; - final DateSetSource? setSource; final DateTime? setDateTime; + final DateFieldSource? copyFieldSource; final int? shiftMinutes; - const DateModifier( + const DateModifier._private( this.action, this.fields, { - this.setSource, this.setDateTime, + this.copyFieldSource, this.shiftMinutes, }); + + factory DateModifier.setCustom(Set fields, DateTime dateTime) { + return DateModifier._private(DateEditAction.setCustom, fields, setDateTime: dateTime); + } + + factory DateModifier.copyField(Set fields, DateFieldSource copyFieldSource) { + return DateModifier._private(DateEditAction.copyField, fields, copyFieldSource: copyFieldSource); + } + + factory DateModifier.extractFromTitle(Set fields) { + return DateModifier._private(DateEditAction.extractFromTitle, fields); + } + + factory DateModifier.shift(Set fields, int shiftMinutes) { + return DateModifier._private(DateEditAction.shift, fields, shiftMinutes: shiftMinutes); + } + + factory DateModifier.clear(Set fields) { + return DateModifier._private(DateEditAction.clear, fields); + } } diff --git a/lib/model/metadata/enums.dart b/lib/model/metadata/enums.dart index dc148cd49..d5592e9dc 100644 --- a/lib/model/metadata/enums.dart +++ b/lib/model/metadata/enums.dart @@ -6,14 +6,14 @@ enum MetadataField { } enum DateEditAction { - set, + setCustom, + copyField, + extractFromTitle, shift, clear, } -enum DateSetSource { - custom, - title, +enum DateFieldSource { fileModifiedDate, exifDate, exifDateOriginal, @@ -105,20 +105,18 @@ extension ExtraMetadataField on MetadataField { } } -extension ExtraDateSetSource on DateSetSource { +extension ExtraDateFieldSource on DateFieldSource { MetadataField? toMetadataField() { switch (this) { - case DateSetSource.custom: - case DateSetSource.title: - case DateSetSource.fileModifiedDate: + case DateFieldSource.fileModifiedDate: return null; - case DateSetSource.exifDate: + case DateFieldSource.exifDate: return MetadataField.exifDate; - case DateSetSource.exifDateOriginal: + case DateFieldSource.exifDateOriginal: return MetadataField.exifDateOriginal; - case DateSetSource.exifDateDigitized: + case DateFieldSource.exifDateDigitized: return MetadataField.exifDateDigitized; - case DateSetSource.exifGpsDate: + case DateFieldSource.exifGpsDate: return MetadataField.exifGpsDate; } } diff --git a/lib/utils/time_utils.dart b/lib/utils/time_utils.dart index cf36f57c6..6980430ba 100644 --- a/lib/utils/time_utils.dart +++ b/lib/utils/time_utils.dart @@ -18,7 +18,9 @@ final _unixStampMillisPattern = RegExp(r'\d{13}'); final _unixStampSecPattern = RegExp(r'\d{10}'); final _plainPattern = RegExp(r'(\d{8})([_-\s](\d{6})([_-\s](\d{3}))?)?'); -DateTime? parseUnknownDateFormat(String s) { +DateTime? parseUnknownDateFormat(String? s) { + if (s == null) return null; + var match = _unixStampMillisPattern.firstMatch(s); if (match != null) { final stampString = match.group(0); diff --git a/lib/widgets/dialogs/entry_editors/edit_entry_date_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_entry_date_dialog.dart index 9341213d8..9fb584ac3 100644 --- a/lib/widgets/dialogs/entry_editors/edit_entry_date_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_entry_date_dialog.dart @@ -24,8 +24,8 @@ class EditEntryDateDialog extends StatefulWidget { } class _EditEntryDateDialogState extends State { - DateEditAction _action = DateEditAction.set; - DateSetSource _setSource = DateSetSource.custom; + DateEditAction _action = DateEditAction.setCustom; + DateFieldSource _copyFieldSource = DateFieldSource.fileModifiedDate; late DateTime _setDateTime; late ValueNotifier _shiftHour, _shiftMinute; late ValueNotifier _shiftSign; @@ -96,7 +96,8 @@ class _EditEntryDateDialogState extends State { key: ValueKey(_action), mainAxisSize: MainAxisSize.min, children: [ - if (_action == DateEditAction.set) ..._buildSetContent(context), + if (_action == DateEditAction.setCustom) _buildSetCustomContent(context), + if (_action == DateEditAction.copyField) _buildCopyFieldContent(context), if (_action == DateEditAction.shift) _buildShiftContent(context), ], ), @@ -128,67 +129,52 @@ class _EditEntryDateDialogState extends State { ), ); - List _buildSetContent(BuildContext context) { + Widget _buildSetCustomContent(BuildContext context) { final l10n = context.l10n; final locale = l10n.localeName; final use24hour = context.select((v) => v.alwaysUse24HourFormat); - return [ - Padding( - padding: const EdgeInsets.only(left: 16, right: 16), - child: Row( - children: [ - Text(l10n.editEntryDateDialogSourceFieldLabel), - const SizedBox(width: 8), - Expanded( - child: DropdownButton( - items: DateSetSource.values - .map((v) => DropdownMenuItem( - value: v, - child: Text(_setSourceText(context, v)), - )) - .toList(), - selectedItemBuilder: (context) => DateSetSource.values - .map((v) => DropdownMenuItem( - value: v, - child: Text( - _setSourceText(context, v), - softWrap: false, - overflow: TextOverflow.fade, - ), - )) - .toList(), - value: _setSource, - onChanged: (v) => setState(() => _setSource = v!), - isExpanded: true, - dropdownColor: dropdownColor, - ), - ), - ], - ), + return Padding( + padding: const EdgeInsets.only(left: 16, top: 4, right: 12), + child: Row( + children: [ + Expanded(child: Text(formatDateTime(_setDateTime, locale, use24hour))), + IconButton( + icon: const Icon(AIcons.edit), + onPressed: _editDate, + tooltip: l10n.changeTooltip, + ), + ], ), - AnimatedSwitcher( - duration: context.read().formTransition, - switchInCurve: Curves.easeInOutCubic, - switchOutCurve: Curves.easeInOutCubic, - transitionBuilder: _formTransitionBuilder, - child: _setSource == DateSetSource.custom - ? Padding( - padding: const EdgeInsets.only(left: 16, right: 12), - child: Row( - children: [ - Expanded(child: Text(formatDateTime(_setDateTime, locale, use24hour))), - IconButton( - icon: const Icon(AIcons.edit), - onPressed: _editDate, - tooltip: l10n.changeTooltip, - ), - ], - ), - ) - : const SizedBox(), + ); + } + + Widget _buildCopyFieldContent(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: DropdownButton( + items: DateFieldSource.values + .map((v) => DropdownMenuItem( + value: v, + child: Text(_setSourceText(context, v)), + )) + .toList(), + selectedItemBuilder: (context) => DateFieldSource.values + .map((v) => DropdownMenuItem( + value: v, + child: Text( + _setSourceText(context, v), + softWrap: false, + overflow: TextOverflow.fade, + ), + )) + .toList(), + value: _copyFieldSource, + onChanged: (v) => setState(() => _copyFieldSource = v!), + isExpanded: true, + dropdownColor: dropdownColor, ), - ]; + ); } Widget _buildShiftContent(BuildContext context) { @@ -283,8 +269,12 @@ class _EditEntryDateDialogState extends State { String _actionText(BuildContext context, DateEditAction action) { final l10n = context.l10n; switch (action) { - case DateEditAction.set: - return l10n.editEntryDateDialogSet; + case DateEditAction.setCustom: + return l10n.editEntryDateDialogSetCustom; + case DateEditAction.copyField: + return l10n.editEntryDateDialogCopyField; + case DateEditAction.extractFromTitle: + return l10n.editEntryDateDialogExtractFromTitle; case DateEditAction.shift: return l10n.editEntryDateDialogShift; case DateEditAction.clear: @@ -292,22 +282,18 @@ class _EditEntryDateDialogState extends State { } } - String _setSourceText(BuildContext context, DateSetSource source) { + String _setSourceText(BuildContext context, DateFieldSource source) { final l10n = context.l10n; switch (source) { - case DateSetSource.custom: - return l10n.editEntryDateDialogSourceCustomDate; - case DateSetSource.title: - return l10n.editEntryDateDialogSourceTitle; - case DateSetSource.fileModifiedDate: + case DateFieldSource.fileModifiedDate: return l10n.editEntryDateDialogSourceFileModifiedDate; - case DateSetSource.exifDate: + case DateFieldSource.exifDate: return 'Exif date'; - case DateSetSource.exifDateOriginal: + case DateFieldSource.exifDateOriginal: return 'Exif original date'; - case DateSetSource.exifDateDigitized: + case DateFieldSource.exifDateDigitized: return 'Exif digitized date'; - case DateSetSource.exifGpsDate: + case DateFieldSource.exifGpsDate: return 'Exif GPS date'; } } @@ -350,20 +336,21 @@ class _EditEntryDateDialogState extends State { )); } - void _submit(BuildContext context) { - late DateModifier modifier; + DateModifier _getModifier() { switch (_action) { - case DateEditAction.set: - modifier = DateModifier(_action, _fields, setSource: _setSource, setDateTime: _setDateTime); - break; + case DateEditAction.setCustom: + return DateModifier.setCustom(_fields, _setDateTime); + case DateEditAction.copyField: + return DateModifier.copyField(_fields, _copyFieldSource); + case DateEditAction.extractFromTitle: + return DateModifier.extractFromTitle(_fields); case DateEditAction.shift: final shiftTotalMinutes = (_shiftHour.value * 60 + _shiftMinute.value) * (_shiftSign.value == '+' ? 1 : -1); - modifier = DateModifier(_action, _fields, shiftMinutes: shiftTotalMinutes); - break; + return DateModifier.shift(_fields, shiftTotalMinutes); case DateEditAction.clear: - modifier = DateModifier(_action, _fields); - break; + return DateModifier.clear(_fields); } - Navigator.pop(context, modifier); } + + void _submit(BuildContext context) => Navigator.pop(context, _getModifier()); } diff --git a/lib/widgets/search/search_delegate.dart b/lib/widgets/search/search_delegate.dart index 07247ac09..5d224993f 100644 --- a/lib/widgets/search/search_delegate.dart +++ b/lib/widgets/search/search_delegate.dart @@ -183,7 +183,7 @@ class CollectionSearchDelegate { _buildFilterRow( context: context, title: context.l10n.searchSectionRating, - filters: [0, 5, 4, 3, 2, 1, -1].map((rating) => RatingFilter(rating)).toList(), + filters: [0, 5, 4, 3, 2, 1, -1].map((rating) => RatingFilter(rating)).where((f) => containQuery(f.getLabel(context))).toList(), ), ], ); diff --git a/untranslated.json b/untranslated.json index a0db63d02..bc3e1d959 100644 --- a/untranslated.json +++ b/untranslated.json @@ -4,9 +4,8 @@ "filterRatingRejectedLabel", "missingSystemFilePickerDialogTitle", "missingSystemFilePickerDialogMessage", - "editEntryDateDialogSourceFieldLabel", - "editEntryDateDialogSourceCustomDate", - "editEntryDateDialogSourceTitle", + "editEntryDateDialogSetCustom", + "editEntryDateDialogCopyField", "editEntryDateDialogSourceFileModifiedDate", "editEntryDateDialogTargetFieldsHeader", "collectionSortRating", @@ -29,9 +28,8 @@ "filterRatingRejectedLabel", "missingSystemFilePickerDialogTitle", "missingSystemFilePickerDialogMessage", - "editEntryDateDialogSourceFieldLabel", - "editEntryDateDialogSourceCustomDate", - "editEntryDateDialogSourceTitle", + "editEntryDateDialogSetCustom", + "editEntryDateDialogCopyField", "editEntryDateDialogSourceFileModifiedDate", "editEntryDateDialogTargetFieldsHeader", "collectionSortRating",