info: changed date edit dialog

This commit is contained in:
Thibault Deckers 2021-12-30 18:19:53 +09:00
parent b5e4fecf2f
commit 30d875f1cf
12 changed files with 159 additions and 171 deletions

View file

@ -178,7 +178,7 @@
"renameEntryDialogLabel": "Neuer Name", "renameEntryDialogLabel": "Neuer Name",
"editEntryDateDialogTitle": "Datum & Uhrzeit", "editEntryDateDialogTitle": "Datum & Uhrzeit",
"editEntryDateDialogSet": "Festlegen", "editEntryDateDialogExtractFromTitle": "Auszug aus dem Titel",
"editEntryDateDialogShift": "Verschieben", "editEntryDateDialogShift": "Verschieben",
"editEntryDateDialogClear": "Aufräumen", "editEntryDateDialogClear": "Aufräumen",
"editEntryDateDialogHours": "Stunden", "editEntryDateDialogHours": "Stunden",

View file

@ -275,13 +275,12 @@
"renameEntryDialogLabel": "New name", "renameEntryDialogLabel": "New name",
"editEntryDateDialogTitle": "Date & Time", "editEntryDateDialogTitle": "Date & Time",
"editEntryDateDialogSet": "Set", "editEntryDateDialogSetCustom": "Set custom date",
"editEntryDateDialogCopyField": "Set from other date",
"editEntryDateDialogExtractFromTitle": "Extract from title",
"editEntryDateDialogShift": "Shift", "editEntryDateDialogShift": "Shift",
"editEntryDateDialogClear": "Clear", "editEntryDateDialogClear": "Clear",
"editEntryDateDialogSourceFieldLabel": "Value:", "editEntryDateDialogSourceFileModifiedDate": "File modified date",
"editEntryDateDialogSourceCustomDate": "custom date",
"editEntryDateDialogSourceTitle": "extracted from title",
"editEntryDateDialogSourceFileModifiedDate": "file modified date",
"editEntryDateDialogTargetFieldsHeader": "Fields to modify", "editEntryDateDialogTargetFieldsHeader": "Fields to modify",
"editEntryDateDialogHours": "Hours", "editEntryDateDialogHours": "Hours",
"editEntryDateDialogMinutes": "Minutes", "editEntryDateDialogMinutes": "Minutes",

View file

@ -180,13 +180,12 @@
"renameEntryDialogLabel": "Nouveau nom", "renameEntryDialogLabel": "Nouveau nom",
"editEntryDateDialogTitle": "Date & Heure", "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", "editEntryDateDialogShift": "Décaler",
"editEntryDateDialogClear": "Effacer", "editEntryDateDialogClear": "Effacer",
"editEntryDateDialogSourceFieldLabel": "Valeur :", "editEntryDateDialogSourceFileModifiedDate": "Date de modification du fichier",
"editEntryDateDialogSourceCustomDate": "date personnalisée",
"editEntryDateDialogSourceTitle": "extraite du titre",
"editEntryDateDialogSourceFileModifiedDate": "date de modification du fichier",
"editEntryDateDialogTargetFieldsHeader": "Champs à modifier", "editEntryDateDialogTargetFieldsHeader": "Champs à modifier",
"editEntryDateDialogHours": "Heures", "editEntryDateDialogHours": "Heures",
"editEntryDateDialogMinutes": "Minutes", "editEntryDateDialogMinutes": "Minutes",

View file

@ -180,12 +180,11 @@
"renameEntryDialogLabel": "이름", "renameEntryDialogLabel": "이름",
"editEntryDateDialogTitle": "날짜 및 시간", "editEntryDateDialogTitle": "날짜 및 시간",
"editEntryDateDialogSet": "편집", "editEntryDateDialogSetCustom": "지정 날짜로 편집",
"editEntryDateDialogCopyField": "다른 날짜에서 지정",
"editEntryDateDialogExtractFromTitle": "제목에서 추출",
"editEntryDateDialogShift": "시간 이동", "editEntryDateDialogShift": "시간 이동",
"editEntryDateDialogClear": "삭제", "editEntryDateDialogClear": "삭제",
"editEntryDateDialogSourceFieldLabel": "값:",
"editEntryDateDialogSourceCustomDate": "지정 날짜",
"editEntryDateDialogSourceTitle": "제목에서 추출",
"editEntryDateDialogSourceFileModifiedDate": "파일 수정한 날짜", "editEntryDateDialogSourceFileModifiedDate": "파일 수정한 날짜",
"editEntryDateDialogTargetFieldsHeader": "수정할 필드", "editEntryDateDialogTargetFieldsHeader": "수정할 필드",
"editEntryDateDialogHours": "시간", "editEntryDateDialogHours": "시간",

View file

@ -178,7 +178,7 @@
"renameEntryDialogLabel": "Новое название", "renameEntryDialogLabel": "Новое название",
"editEntryDateDialogTitle": "Дата и время", "editEntryDateDialogTitle": "Дата и время",
"editEntryDateDialogSet": "Задать", "editEntryDateDialogExtractFromTitle": "Извлечь из названия",
"editEntryDateDialogShift": "Сдвиг", "editEntryDateDialogShift": "Сдвиг",
"editEntryDateDialogClear": "Очистить", "editEntryDateDialogClear": "Очистить",
"editEntryDateDialogHours": "Часов", "editEntryDateDialogHours": "Часов",

View file

@ -675,55 +675,42 @@ class AvesEntry {
} }
Future<Set<EntryDataType>> editDate(DateModifier modifier) async { Future<Set<EntryDataType>> editDate(DateModifier modifier) async {
final action = modifier.action; switch (modifier.action) {
if (action == DateEditAction.set) { case DateEditAction.copyField:
final source = modifier.setSource; DateTime? date;
if (source == null) { final source = modifier.copyFieldSource;
await reportService.recordError('edit date with action=$action but source is null', null); if (source != null) {
return {}; switch (source) {
} case DateFieldSource.fileModifiedDate:
try {
switch (source) { date = path != null ? await File(path!).lastModified() : null;
case DateSetSource.title: } on FileSystemException catch (_) {}
final _title = bestTitle; break;
if (_title == null) return {}; default:
final date = parseUnknownDateFormat(_title); date = await metadataFetchService.getDate(this, source.toMetadataField()!);
if (date == null) { break;
await reportService.recordError('failed to parse date from title=$_title', null);
return {};
} }
modifier = DateModifier(DateEditAction.set, modifier.fields, setDateTime: date); }
break; if (date != null) {
case DateSetSource.fileModifiedDate: modifier = DateModifier.setCustom(modifier.fields, date);
final _path = path; } else {
if (_path == null) { await reportService.recordError('failed to get date for modifier=$modifier, uri=$uri', null);
await reportService.recordError('edit date with action=$action, source=$source but entry has no path, uri=$uri', null); return {};
return {}; }
} break;
try { case DateEditAction.extractFromTitle:
final fileModifiedDate = await File(_path).lastModified(); final date = parseUnknownDateFormat(bestTitle);
modifier = DateModifier(DateEditAction.set, modifier.fields, setDateTime: fileModifiedDate); if (date != null) {
} on FileSystemException catch (error, stack) { modifier = DateModifier.setCustom(modifier.fields, date);
await reportService.recordError(error, stack); } else {
return {}; await reportService.recordError('failed to get date for modifier=$modifier, uri=$uri', null);
} return {};
break; }
case DateSetSource.custom: break;
break; case DateEditAction.setCustom:
default: case DateEditAction.shift:
final field = source.toMetadataField(); case DateEditAction.clear:
if (field == null) { break;
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;
}
} }
final newFields = await metadataEditService.editDate(this, modifier); final newFields = await metadataEditService.editDate(this, modifier);
return newFields.isEmpty return newFields.isEmpty
@ -746,10 +733,9 @@ class AvesEntry {
final metadataDate = catalogMetadata?.dateMillis; final metadataDate = catalogMetadata?.dateMillis;
if (metadataDate != null && metadataDate > 0) return {}; if (metadataDate != null && metadataDate > 0) return {};
return await editDate(const DateModifier( return await editDate(DateModifier.copyField(
DateEditAction.set, const {MetadataField.exifDateOriginal},
{MetadataField.exifDateOriginal}, DateFieldSource.fileModifiedDate,
setSource: DateSetSource.fileModifiedDate,
)); ));
} }

View file

@ -13,15 +13,35 @@ class DateModifier {
final DateEditAction action; final DateEditAction action;
final Set<MetadataField> fields; final Set<MetadataField> fields;
final DateSetSource? setSource;
final DateTime? setDateTime; final DateTime? setDateTime;
final DateFieldSource? copyFieldSource;
final int? shiftMinutes; final int? shiftMinutes;
const DateModifier( const DateModifier._private(
this.action, this.action,
this.fields, { this.fields, {
this.setSource,
this.setDateTime, this.setDateTime,
this.copyFieldSource,
this.shiftMinutes, this.shiftMinutes,
}); });
factory DateModifier.setCustom(Set<MetadataField> fields, DateTime dateTime) {
return DateModifier._private(DateEditAction.setCustom, fields, setDateTime: dateTime);
}
factory DateModifier.copyField(Set<MetadataField> fields, DateFieldSource copyFieldSource) {
return DateModifier._private(DateEditAction.copyField, fields, copyFieldSource: copyFieldSource);
}
factory DateModifier.extractFromTitle(Set<MetadataField> fields) {
return DateModifier._private(DateEditAction.extractFromTitle, fields);
}
factory DateModifier.shift(Set<MetadataField> fields, int shiftMinutes) {
return DateModifier._private(DateEditAction.shift, fields, shiftMinutes: shiftMinutes);
}
factory DateModifier.clear(Set<MetadataField> fields) {
return DateModifier._private(DateEditAction.clear, fields);
}
} }

View file

@ -6,14 +6,14 @@ enum MetadataField {
} }
enum DateEditAction { enum DateEditAction {
set, setCustom,
copyField,
extractFromTitle,
shift, shift,
clear, clear,
} }
enum DateSetSource { enum DateFieldSource {
custom,
title,
fileModifiedDate, fileModifiedDate,
exifDate, exifDate,
exifDateOriginal, exifDateOriginal,
@ -105,20 +105,18 @@ extension ExtraMetadataField on MetadataField {
} }
} }
extension ExtraDateSetSource on DateSetSource { extension ExtraDateFieldSource on DateFieldSource {
MetadataField? toMetadataField() { MetadataField? toMetadataField() {
switch (this) { switch (this) {
case DateSetSource.custom: case DateFieldSource.fileModifiedDate:
case DateSetSource.title:
case DateSetSource.fileModifiedDate:
return null; return null;
case DateSetSource.exifDate: case DateFieldSource.exifDate:
return MetadataField.exifDate; return MetadataField.exifDate;
case DateSetSource.exifDateOriginal: case DateFieldSource.exifDateOriginal:
return MetadataField.exifDateOriginal; return MetadataField.exifDateOriginal;
case DateSetSource.exifDateDigitized: case DateFieldSource.exifDateDigitized:
return MetadataField.exifDateDigitized; return MetadataField.exifDateDigitized;
case DateSetSource.exifGpsDate: case DateFieldSource.exifGpsDate:
return MetadataField.exifGpsDate; return MetadataField.exifGpsDate;
} }
} }

View file

@ -18,7 +18,9 @@ final _unixStampMillisPattern = RegExp(r'\d{13}');
final _unixStampSecPattern = RegExp(r'\d{10}'); final _unixStampSecPattern = RegExp(r'\d{10}');
final _plainPattern = RegExp(r'(\d{8})([_-\s](\d{6})([_-\s](\d{3}))?)?'); 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); var match = _unixStampMillisPattern.firstMatch(s);
if (match != null) { if (match != null) {
final stampString = match.group(0); final stampString = match.group(0);

View file

@ -24,8 +24,8 @@ class EditEntryDateDialog extends StatefulWidget {
} }
class _EditEntryDateDialogState extends State<EditEntryDateDialog> { class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
DateEditAction _action = DateEditAction.set; DateEditAction _action = DateEditAction.setCustom;
DateSetSource _setSource = DateSetSource.custom; DateFieldSource _copyFieldSource = DateFieldSource.fileModifiedDate;
late DateTime _setDateTime; late DateTime _setDateTime;
late ValueNotifier<int> _shiftHour, _shiftMinute; late ValueNotifier<int> _shiftHour, _shiftMinute;
late ValueNotifier<String> _shiftSign; late ValueNotifier<String> _shiftSign;
@ -96,7 +96,8 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
key: ValueKey(_action), key: ValueKey(_action),
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ 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), if (_action == DateEditAction.shift) _buildShiftContent(context),
], ],
), ),
@ -128,67 +129,52 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
), ),
); );
List<Widget> _buildSetContent(BuildContext context) { Widget _buildSetCustomContent(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
final locale = l10n.localeName; final locale = l10n.localeName;
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat); final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
return [ return Padding(
Padding( padding: const EdgeInsets.only(left: 16, top: 4, right: 12),
padding: const EdgeInsets.only(left: 16, right: 16), child: Row(
child: Row( children: [
children: [ Expanded(child: Text(formatDateTime(_setDateTime, locale, use24hour))),
Text(l10n.editEntryDateDialogSourceFieldLabel), IconButton(
const SizedBox(width: 8), icon: const Icon(AIcons.edit),
Expanded( onPressed: _editDate,
child: DropdownButton<DateSetSource>( tooltip: l10n.changeTooltip,
items: DateSetSource.values ),
.map((v) => DropdownMenuItem<DateSetSource>( ],
value: v,
child: Text(_setSourceText(context, v)),
))
.toList(),
selectedItemBuilder: (context) => DateSetSource.values
.map((v) => DropdownMenuItem<DateSetSource>(
value: v,
child: Text(
_setSourceText(context, v),
softWrap: false,
overflow: TextOverflow.fade,
),
))
.toList(),
value: _setSource,
onChanged: (v) => setState(() => _setSource = v!),
isExpanded: true,
dropdownColor: dropdownColor,
),
),
],
),
), ),
AnimatedSwitcher( );
duration: context.read<DurationsData>().formTransition, }
switchInCurve: Curves.easeInOutCubic,
switchOutCurve: Curves.easeInOutCubic, Widget _buildCopyFieldContent(BuildContext context) {
transitionBuilder: _formTransitionBuilder, return Padding(
child: _setSource == DateSetSource.custom padding: const EdgeInsets.symmetric(horizontal: 16),
? Padding( child: DropdownButton<DateFieldSource>(
padding: const EdgeInsets.only(left: 16, right: 12), items: DateFieldSource.values
child: Row( .map((v) => DropdownMenuItem<DateFieldSource>(
children: [ value: v,
Expanded(child: Text(formatDateTime(_setDateTime, locale, use24hour))), child: Text(_setSourceText(context, v)),
IconButton( ))
icon: const Icon(AIcons.edit), .toList(),
onPressed: _editDate, selectedItemBuilder: (context) => DateFieldSource.values
tooltip: l10n.changeTooltip, .map((v) => DropdownMenuItem<DateFieldSource>(
), value: v,
], child: Text(
), _setSourceText(context, v),
) softWrap: false,
: const SizedBox(), overflow: TextOverflow.fade,
),
))
.toList(),
value: _copyFieldSource,
onChanged: (v) => setState(() => _copyFieldSource = v!),
isExpanded: true,
dropdownColor: dropdownColor,
), ),
]; );
} }
Widget _buildShiftContent(BuildContext context) { Widget _buildShiftContent(BuildContext context) {
@ -283,8 +269,12 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
String _actionText(BuildContext context, DateEditAction action) { String _actionText(BuildContext context, DateEditAction action) {
final l10n = context.l10n; final l10n = context.l10n;
switch (action) { switch (action) {
case DateEditAction.set: case DateEditAction.setCustom:
return l10n.editEntryDateDialogSet; return l10n.editEntryDateDialogSetCustom;
case DateEditAction.copyField:
return l10n.editEntryDateDialogCopyField;
case DateEditAction.extractFromTitle:
return l10n.editEntryDateDialogExtractFromTitle;
case DateEditAction.shift: case DateEditAction.shift:
return l10n.editEntryDateDialogShift; return l10n.editEntryDateDialogShift;
case DateEditAction.clear: case DateEditAction.clear:
@ -292,22 +282,18 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
} }
} }
String _setSourceText(BuildContext context, DateSetSource source) { String _setSourceText(BuildContext context, DateFieldSource source) {
final l10n = context.l10n; final l10n = context.l10n;
switch (source) { switch (source) {
case DateSetSource.custom: case DateFieldSource.fileModifiedDate:
return l10n.editEntryDateDialogSourceCustomDate;
case DateSetSource.title:
return l10n.editEntryDateDialogSourceTitle;
case DateSetSource.fileModifiedDate:
return l10n.editEntryDateDialogSourceFileModifiedDate; return l10n.editEntryDateDialogSourceFileModifiedDate;
case DateSetSource.exifDate: case DateFieldSource.exifDate:
return 'Exif date'; return 'Exif date';
case DateSetSource.exifDateOriginal: case DateFieldSource.exifDateOriginal:
return 'Exif original date'; return 'Exif original date';
case DateSetSource.exifDateDigitized: case DateFieldSource.exifDateDigitized:
return 'Exif digitized date'; return 'Exif digitized date';
case DateSetSource.exifGpsDate: case DateFieldSource.exifGpsDate:
return 'Exif GPS date'; return 'Exif GPS date';
} }
} }
@ -350,20 +336,21 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
)); ));
} }
void _submit(BuildContext context) { DateModifier _getModifier() {
late DateModifier modifier;
switch (_action) { switch (_action) {
case DateEditAction.set: case DateEditAction.setCustom:
modifier = DateModifier(_action, _fields, setSource: _setSource, setDateTime: _setDateTime); return DateModifier.setCustom(_fields, _setDateTime);
break; case DateEditAction.copyField:
return DateModifier.copyField(_fields, _copyFieldSource);
case DateEditAction.extractFromTitle:
return DateModifier.extractFromTitle(_fields);
case DateEditAction.shift: case DateEditAction.shift:
final shiftTotalMinutes = (_shiftHour.value * 60 + _shiftMinute.value) * (_shiftSign.value == '+' ? 1 : -1); final shiftTotalMinutes = (_shiftHour.value * 60 + _shiftMinute.value) * (_shiftSign.value == '+' ? 1 : -1);
modifier = DateModifier(_action, _fields, shiftMinutes: shiftTotalMinutes); return DateModifier.shift(_fields, shiftTotalMinutes);
break;
case DateEditAction.clear: case DateEditAction.clear:
modifier = DateModifier(_action, _fields); return DateModifier.clear(_fields);
break;
} }
Navigator.pop(context, modifier);
} }
void _submit(BuildContext context) => Navigator.pop(context, _getModifier());
} }

View file

@ -183,7 +183,7 @@ class CollectionSearchDelegate {
_buildFilterRow( _buildFilterRow(
context: context, context: context,
title: context.l10n.searchSectionRating, 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(),
), ),
], ],
); );

View file

@ -4,9 +4,8 @@
"filterRatingRejectedLabel", "filterRatingRejectedLabel",
"missingSystemFilePickerDialogTitle", "missingSystemFilePickerDialogTitle",
"missingSystemFilePickerDialogMessage", "missingSystemFilePickerDialogMessage",
"editEntryDateDialogSourceFieldLabel", "editEntryDateDialogSetCustom",
"editEntryDateDialogSourceCustomDate", "editEntryDateDialogCopyField",
"editEntryDateDialogSourceTitle",
"editEntryDateDialogSourceFileModifiedDate", "editEntryDateDialogSourceFileModifiedDate",
"editEntryDateDialogTargetFieldsHeader", "editEntryDateDialogTargetFieldsHeader",
"collectionSortRating", "collectionSortRating",
@ -29,9 +28,8 @@
"filterRatingRejectedLabel", "filterRatingRejectedLabel",
"missingSystemFilePickerDialogTitle", "missingSystemFilePickerDialogTitle",
"missingSystemFilePickerDialogMessage", "missingSystemFilePickerDialogMessage",
"editEntryDateDialogSourceFieldLabel", "editEntryDateDialogSetCustom",
"editEntryDateDialogSourceCustomDate", "editEntryDateDialogCopyField",
"editEntryDateDialogSourceTitle",
"editEntryDateDialogSourceFileModifiedDate", "editEntryDateDialogSourceFileModifiedDate",
"editEntryDateDialogTargetFieldsHeader", "editEntryDateDialogTargetFieldsHeader",
"collectionSortRating", "collectionSortRating",