search: on this day, month filters in date filter section
This commit is contained in:
parent
76370b25b4
commit
ccc99ed59a
19 changed files with 146 additions and 70 deletions
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -417,6 +417,7 @@
|
||||||
|
|
||||||
"searchCollectionFieldHint": "コレクションを検索",
|
"searchCollectionFieldHint": "コレクションを検索",
|
||||||
"searchSectionRecent": "最近",
|
"searchSectionRecent": "最近",
|
||||||
|
"searchSectionDate": "日付",
|
||||||
"searchSectionAlbums": "アルバム",
|
"searchSectionAlbums": "アルバム",
|
||||||
"searchSectionCountries": "国",
|
"searchSectionCountries": "国",
|
||||||
"searchSectionPlaces": "場所",
|
"searchSectionPlaces": "場所",
|
||||||
|
|
|
@ -417,6 +417,7 @@
|
||||||
|
|
||||||
"searchCollectionFieldHint": "미디어 검색",
|
"searchCollectionFieldHint": "미디어 검색",
|
||||||
"searchSectionRecent": "최근 검색기록",
|
"searchSectionRecent": "최근 검색기록",
|
||||||
|
"searchSectionDate": "날짜",
|
||||||
"searchSectionAlbums": "앨범",
|
"searchSectionAlbums": "앨범",
|
||||||
"searchSectionCountries": "국가",
|
"searchSectionCountries": "국가",
|
||||||
"searchSectionPlaces": "장소",
|
"searchSectionPlaces": "장소",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -399,6 +399,7 @@
|
||||||
|
|
||||||
"searchCollectionFieldHint": "Поиск по коллекции",
|
"searchCollectionFieldHint": "Поиск по коллекции",
|
||||||
"searchSectionRecent": "Недавние",
|
"searchSectionRecent": "Недавние",
|
||||||
|
"searchSectionDate": "Дата",
|
||||||
"searchSectionAlbums": "Альбомы",
|
"searchSectionAlbums": "Альбомы",
|
||||||
"searchSectionCountries": "Страны",
|
"searchSectionCountries": "Страны",
|
||||||
"searchSectionPlaces": "Локации",
|
"searchSectionPlaces": "Локации",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -417,6 +417,7 @@
|
||||||
|
|
||||||
"searchCollectionFieldHint": "搜索媒体集",
|
"searchCollectionFieldHint": "搜索媒体集",
|
||||||
"searchSectionRecent": "最近",
|
"searchSectionRecent": "最近",
|
||||||
|
"searchSectionDate": "日期",
|
||||||
"searchSectionAlbums": "相册",
|
"searchSectionAlbums": "相册",
|
||||||
"searchSectionCountries": "国家",
|
"searchSectionCountries": "国家",
|
||||||
"searchSectionPlaces": "地点",
|
"searchSectionPlaces": "地点",
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue