search: on this day, month filters in date filter section

This commit is contained in:
Thibault Deckers 2022-06-27 16:20:45 +09:00
parent 76370b25b4
commit ccc99ed59a
19 changed files with 146 additions and 70 deletions

View file

@ -6,8 +6,8 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Search: `on this day` filter - Search: `on this day` and month filters in date filter section
- Stats: histogram and date filters - Stats: histogram and contextual date filters
- Screen saver - Screen saver
### Changed ### Changed

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "Sammlung durchsuchen", "searchCollectionFieldHint": "Sammlung durchsuchen",
"searchSectionRecent": "Neueste", "searchSectionRecent": "Neueste",
"searchSectionDate": "Datum",
"searchSectionAlbums": "Alben", "searchSectionAlbums": "Alben",
"searchSectionCountries": "Länder", "searchSectionCountries": "Länder",
"searchSectionPlaces": "Orte", "searchSectionPlaces": "Orte",

View file

@ -598,6 +598,7 @@
"searchCollectionFieldHint": "Search collection", "searchCollectionFieldHint": "Search collection",
"searchSectionRecent": "Recent", "searchSectionRecent": "Recent",
"searchSectionDate": "Date",
"searchSectionAlbums": "Albums", "searchSectionAlbums": "Albums",
"searchSectionCountries": "Countries", "searchSectionCountries": "Countries",
"searchSectionPlaces": "Places", "searchSectionPlaces": "Places",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "Buscar en colección", "searchCollectionFieldHint": "Buscar en colección",
"searchSectionRecent": "Reciente", "searchSectionRecent": "Reciente",
"searchSectionDate": "Fecha",
"searchSectionAlbums": "Álbumes", "searchSectionAlbums": "Álbumes",
"searchSectionCountries": "Países", "searchSectionCountries": "Países",
"searchSectionPlaces": "Lugares", "searchSectionPlaces": "Lugares",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "Recherche", "searchCollectionFieldHint": "Recherche",
"searchSectionRecent": "Historique", "searchSectionRecent": "Historique",
"searchSectionDate": "Date",
"searchSectionAlbums": "Albums", "searchSectionAlbums": "Albums",
"searchSectionCountries": "Pays", "searchSectionCountries": "Pays",
"searchSectionPlaces": "Lieux", "searchSectionPlaces": "Lieux",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "Cari koleksi", "searchCollectionFieldHint": "Cari koleksi",
"searchSectionRecent": "Terkini", "searchSectionRecent": "Terkini",
"searchSectionDate": "Tanggal",
"searchSectionAlbums": "Album", "searchSectionAlbums": "Album",
"searchSectionCountries": "Negara", "searchSectionCountries": "Negara",
"searchSectionPlaces": "Tempat", "searchSectionPlaces": "Tempat",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "Cerca raccolta", "searchCollectionFieldHint": "Cerca raccolta",
"searchSectionRecent": "Recenti", "searchSectionRecent": "Recenti",
"searchSectionDate": "Data",
"searchSectionAlbums": "Album", "searchSectionAlbums": "Album",
"searchSectionCountries": "Paesi", "searchSectionCountries": "Paesi",
"searchSectionPlaces": "Luoghi", "searchSectionPlaces": "Luoghi",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "コレクションを検索", "searchCollectionFieldHint": "コレクションを検索",
"searchSectionRecent": "最近", "searchSectionRecent": "最近",
"searchSectionDate": "日付",
"searchSectionAlbums": "アルバム", "searchSectionAlbums": "アルバム",
"searchSectionCountries": "国", "searchSectionCountries": "国",
"searchSectionPlaces": "場所", "searchSectionPlaces": "場所",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "미디어 검색", "searchCollectionFieldHint": "미디어 검색",
"searchSectionRecent": "최근 검색기록", "searchSectionRecent": "최근 검색기록",
"searchSectionDate": "날짜",
"searchSectionAlbums": "앨범", "searchSectionAlbums": "앨범",
"searchSectionCountries": "국가", "searchSectionCountries": "국가",
"searchSectionPlaces": "장소", "searchSectionPlaces": "장소",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "Pesquisar coleção", "searchCollectionFieldHint": "Pesquisar coleção",
"searchSectionRecent": "Recente", "searchSectionRecent": "Recente",
"searchSectionDate": "Data",
"searchSectionAlbums": "Álbuns", "searchSectionAlbums": "Álbuns",
"searchSectionCountries": "Países", "searchSectionCountries": "Países",
"searchSectionPlaces": "Locais", "searchSectionPlaces": "Locais",

View file

@ -399,6 +399,7 @@
"searchCollectionFieldHint": "Поиск по коллекции", "searchCollectionFieldHint": "Поиск по коллекции",
"searchSectionRecent": "Недавние", "searchSectionRecent": "Недавние",
"searchSectionDate": "Дата",
"searchSectionAlbums": "Альбомы", "searchSectionAlbums": "Альбомы",
"searchSectionCountries": "Страны", "searchSectionCountries": "Страны",
"searchSectionPlaces": "Локации", "searchSectionPlaces": "Локации",

View file

@ -418,6 +418,7 @@
"searchCollectionFieldHint": "Koleksiyonu ara", "searchCollectionFieldHint": "Koleksiyonu ara",
"searchSectionRecent": "Yakın zamanda", "searchSectionRecent": "Yakın zamanda",
"searchSectionDate": "Tarih",
"searchSectionAlbums": "Albümler", "searchSectionAlbums": "Albümler",
"searchSectionCountries": "Ülkeler", "searchSectionCountries": "Ülkeler",
"searchSectionPlaces": "Yerler", "searchSectionPlaces": "Yerler",

View file

@ -417,6 +417,7 @@
"searchCollectionFieldHint": "搜索媒体集", "searchCollectionFieldHint": "搜索媒体集",
"searchSectionRecent": "最近", "searchSectionRecent": "最近",
"searchSectionDate": "日期",
"searchSectionAlbums": "相册", "searchSectionAlbums": "相册",
"searchSectionCountries": "国家", "searchSectionCountries": "国家",
"searchSectionPlaces": "地点", "searchSectionPlaces": "地点",

View file

@ -7,7 +7,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class DateFilter extends CoveredCollectionFilter { class DateFilter extends CollectionFilter {
static const type = 'date'; static const type = 'date';
final DateLevel level; final DateLevel level;
@ -69,6 +69,32 @@ class DateFilter extends CoveredCollectionFilter {
@override @override
EntryFilter get test => _test; EntryFilter get test => _test;
@override
bool isCompatible(CollectionFilter other) {
if (other is DateFilter) {
return isCompatibleLevel(level, other.level);
} else {
return true;
}
}
static bool isCompatibleLevel(DateLevel a, DateLevel b) {
switch (a) {
case DateLevel.y:
return {DateLevel.md, DateLevel.m, DateLevel.d}.contains(b);
case DateLevel.ym:
return DateLevel.d == b;
case DateLevel.ymd:
return false;
case DateLevel.md:
return DateLevel.y == b;
case DateLevel.m:
return {DateLevel.y, DateLevel.d}.contains(b);
case DateLevel.d:
return {DateLevel.y, DateLevel.ym, DateLevel.m}.contains(b);
}
}
@override @override
String get universalLabel => _effectiveDate.toIso8601String(); String get universalLabel => _effectiveDate.toIso8601String();

View file

@ -89,7 +89,7 @@ abstract class CollectionFilter extends Equatable implements Comparable<Collecti
EntryFilter get test; EntryFilter get test;
bool get isUnique => true; bool isCompatible(CollectionFilter other) => category != other.category;
String get universalLabel; String get universalLabel;

View file

@ -75,7 +75,7 @@ class QueryFilter extends CollectionFilter {
EntryFilter get test => _test; EntryFilter get test => _test;
@override @override
bool get isUnique => false; bool isCompatible(CollectionFilter other) => true;
@override @override
String get universalLabel => query; String get universalLabel => query;

View file

@ -38,7 +38,7 @@ class TagFilter extends CoveredCollectionFilter {
EntryFilter get test => _test; EntryFilter get test => _test;
@override @override
bool get isUnique => false; bool isCompatible(CollectionFilter other) => true;
@override @override
String get universalLabel => tag; String get universalLabel => tag;

View file

@ -150,9 +150,7 @@ class CollectionLens with ChangeNotifier {
void addFilter(CollectionFilter filter) { void addFilter(CollectionFilter filter) {
if (filters.contains(filter)) return; if (filters.contains(filter)) return;
if (filter.isUnique) { filters.removeWhere((other) => !filter.isCompatible(other));
filters.removeWhere((old) => old.category == filter.category);
}
filters.add(filter); filters.add(filter);
_onFilterChanged(); _onFilterChanged();
} }

View file

@ -42,9 +42,10 @@ class CollectionSearchDelegate extends AvesSearchDelegate {
TypeFilter.geotiff, TypeFilter.geotiff,
TypeFilter.raw, TypeFilter.raw,
MimeFilter(MimeTypes.svg), MimeFilter(MimeTypes.svg),
DateFilter.onThisDay,
]; ];
static final _monthFilters = List.generate(12, (i) => DateFilter(DateLevel.m, DateTime(1, i + 1)));
CollectionSearchDelegate({ CollectionSearchDelegate({
required super.searchFieldLabel, required super.searchFieldLabel,
required this.source, required this.source,
@ -97,66 +98,12 @@ class CollectionSearchDelegate extends AvesSearchDelegate {
title: context.l10n.searchSectionRecent, title: context.l10n.searchSectionRecent,
filters: history, filters: history,
), ),
StreamBuilder( _buildDateFilters(context, containQuery),
stream: source.eventBus.on<AlbumsChangedEvent>(), _buildAlbumFilters(containQuery),
builder: (context, snapshot) { _buildCountryFilters(containQuery),
final filters = source.rawAlbums _buildPlaceFilters(containQuery),
.map((album) => AlbumFilter( _buildTagFilters(containQuery),
album, _buildRatingFilters(context, containQuery),
source.getAlbumDisplayName(context, album),
))
.where((filter) => containQuery(filter.displayName ?? filter.album))
.toList()
..sort();
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionAlbums,
filters: filters,
);
}),
StreamBuilder(
stream: source.eventBus.on<CountriesChangedEvent>(),
builder: (context, snapshot) {
final filters = source.sortedCountries.where(containQuery).map((s) => LocationFilter(LocationLevel.country, s)).toList();
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionCountries,
filters: filters,
);
}),
StreamBuilder(
stream: source.eventBus.on<PlacesChangedEvent>(),
builder: (context, snapshot) {
final filters = source.sortedPlaces.where(containQuery).map((s) => LocationFilter(LocationLevel.place, s));
final noFilter = LocationFilter(LocationLevel.place, '');
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionPlaces,
filters: [
if (containQuery(noFilter.getLabel(context))) noFilter,
...filters,
],
);
}),
StreamBuilder(
stream: source.eventBus.on<TagsChangedEvent>(),
builder: (context, snapshot) {
final filters = source.sortedTags.where(containQuery).map(TagFilter.new);
final noFilter = TagFilter('');
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionTags,
filters: [
if (containQuery(noFilter.getLabel(context))) noFilter,
...filters,
],
);
}),
_buildFilterRow(
context: context,
title: context.l10n.searchSectionRating,
filters: [0, 5, 4, 3, 2, 1, -1].map(RatingFilter.new).where((f) => containQuery(f.getLabel(context))).toList(),
),
], ],
); );
}); });
@ -180,6 +127,97 @@ class CollectionSearchDelegate extends AvesSearchDelegate {
); );
} }
Widget _buildDateFilters(BuildContext context, _ContainQuery containQuery) {
final filters = [
DateFilter.onThisDay,
..._monthFilters,
].where((f) => containQuery(f.getLabel(context))).toList();
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionDate,
filters: filters,
);
}
Widget _buildAlbumFilters(_ContainQuery containQuery) {
return StreamBuilder(
stream: source.eventBus.on<AlbumsChangedEvent>(),
builder: (context, snapshot) {
final filters = source.rawAlbums
.map((album) => AlbumFilter(
album,
source.getAlbumDisplayName(context, album),
))
.where((filter) => containQuery(filter.displayName ?? filter.album))
.toList()
..sort();
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionAlbums,
filters: filters,
);
},
);
}
Widget _buildCountryFilters(_ContainQuery containQuery) {
return StreamBuilder(
stream: source.eventBus.on<CountriesChangedEvent>(),
builder: (context, snapshot) {
final filters = source.sortedCountries.where(containQuery).map((s) => LocationFilter(LocationLevel.country, s)).toList();
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionCountries,
filters: filters,
);
},
);
}
Widget _buildPlaceFilters(_ContainQuery containQuery) {
return StreamBuilder(
stream: source.eventBus.on<PlacesChangedEvent>(),
builder: (context, snapshot) {
final filters = source.sortedPlaces.where(containQuery).map((s) => LocationFilter(LocationLevel.place, s));
final noFilter = LocationFilter(LocationLevel.place, '');
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionPlaces,
filters: [
if (containQuery(noFilter.getLabel(context))) noFilter,
...filters,
],
);
},
);
}
Widget _buildTagFilters(_ContainQuery containQuery) {
return StreamBuilder(
stream: source.eventBus.on<TagsChangedEvent>(),
builder: (context, snapshot) {
final filters = source.sortedTags.where(containQuery).map(TagFilter.new);
final noFilter = TagFilter('');
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionTags,
filters: [
if (containQuery(noFilter.getLabel(context))) noFilter,
...filters,
],
);
},
);
}
Widget _buildRatingFilters(BuildContext context, _ContainQuery containQuery) {
return _buildFilterRow(
context: context,
title: context.l10n.searchSectionRating,
filters: [0, 5, 4, 3, 2, 1, -1].map(RatingFilter.new).where((f) => containQuery(f.getLabel(context))).toList(),
);
}
@override @override
Widget buildResults(BuildContext context) { Widget buildResults(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@ -240,3 +278,5 @@ class CollectionSearchDelegate extends AvesSearchDelegate {
); );
} }
} }
typedef _ContainQuery = bool Function(String s);