drawer: navigate by country

This commit is contained in:
Thibault Deckers 2020-03-24 15:01:51 +09:00
parent 39185f5ba8
commit 2e5a2e7c91
4 changed files with 70 additions and 13 deletions

View file

@ -24,6 +24,15 @@ class TagFilter extends CollectionFilter {
bool filter(ImageEntry entry) => entry.xmpSubjects.contains(tag); bool filter(ImageEntry entry) => entry.xmpSubjects.contains(tag);
} }
class CountryFilter extends CollectionFilter {
final String country;
const CountryFilter(this.country);
@override
bool filter(ImageEntry entry) => entry.isLocated && entry.addressDetails.countryName == country;
}
class VideoFilter extends CollectionFilter { class VideoFilter extends CollectionFilter {
@override @override
bool filter(ImageEntry entry) => entry.isVideo; bool filter(ImageEntry entry) => entry.isVideo;

View file

@ -28,7 +28,7 @@ class CollectionLens with ChangeNotifier {
this.sortFactor = sortFactor ?? SortFactor.date { this.sortFactor = sortFactor ?? SortFactor.date {
_subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => onEntryAdded())); _subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => onEntryAdded()));
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entry))); _subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entry)));
_subscriptions.add(source.eventBus.on<MetadataChangedEvent>().listen((e) => onMetadataChanged())); _subscriptions.add(source.eventBus.on<CatalogMetadataChangedEvent>().listen((e) => onMetadataChanged()));
onEntryAdded(); onEntryAdded();
} }

View file

@ -12,6 +12,7 @@ class CollectionSource {
List<String> sortedAlbums = List.unmodifiable(const Iterable.empty()); List<String> sortedAlbums = List.unmodifiable(const Iterable.empty());
List<String> sortedTags = List.unmodifiable(const Iterable.empty()); List<String> sortedTags = List.unmodifiable(const Iterable.empty());
List<String> sortedCountries = List.unmodifiable(const Iterable.empty());
List<ImageEntry> get entries => List.unmodifiable(_rawEntries); List<ImageEntry> get entries => List.unmodifiable(_rawEntries);
@ -35,7 +36,7 @@ class CollectionSource {
} }
}); });
debugPrint('$runtimeType loadCatalogMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms with ${saved.length} saved entries'); debugPrint('$runtimeType loadCatalogMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms with ${saved.length} saved entries');
onMetadataChanged(); onCatalogMetadataChanged();
} }
Future<void> loadAddresses() async { Future<void> loadAddresses() async {
@ -48,6 +49,7 @@ class CollectionSource {
} }
}); });
debugPrint('$runtimeType loadAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms with ${saved.length} saved entries'); debugPrint('$runtimeType loadAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms with ${saved.length} saved entries');
onAddressMetadataChanged();
} }
Future<void> catalogEntries() async { Future<void> catalogEntries() async {
@ -65,15 +67,10 @@ class CollectionSource {
if (newMetadata.isEmpty) return; if (newMetadata.isEmpty) return;
await metadataDb.saveMetadata(List.unmodifiable(newMetadata)); await metadataDb.saveMetadata(List.unmodifiable(newMetadata));
onMetadataChanged(); onCatalogMetadataChanged();
debugPrint('$runtimeType catalogEntries complete in ${stopwatch.elapsed.inSeconds}s with ${newMetadata.length} new entries'); debugPrint('$runtimeType catalogEntries complete in ${stopwatch.elapsed.inSeconds}s with ${newMetadata.length} new entries');
} }
void onMetadataChanged() {
updateTags();
eventBus.fire(MetadataChangedEvent());
}
Future<void> locateEntries() async { Future<void> locateEntries() async {
final stopwatch = Stopwatch()..start(); final stopwatch = Stopwatch()..start();
final unlocatedEntries = _rawEntries.where((entry) => entry.hasGps && !entry.isLocated).toList(); final unlocatedEntries = _rawEntries.where((entry) => entry.hasGps && !entry.isLocated).toList();
@ -89,9 +86,20 @@ class CollectionSource {
} }
}); });
await metadataDb.saveAddresses(List.unmodifiable(newAddresses)); await metadataDb.saveAddresses(List.unmodifiable(newAddresses));
onAddressMetadataChanged();
debugPrint('$runtimeType locateEntries complete in ${stopwatch.elapsed.inMilliseconds}ms'); debugPrint('$runtimeType locateEntries complete in ${stopwatch.elapsed.inMilliseconds}ms');
} }
void onCatalogMetadataChanged() {
updateTags();
eventBus.fire(CatalogMetadataChangedEvent());
}
void onAddressMetadataChanged() {
updateLocations();
eventBus.fire(AddressMetadataChangedEvent());
}
void updateAlbums() { void updateAlbums() {
final albums = _rawEntries.map((entry) => entry.directory).toSet(); final albums = _rawEntries.map((entry) => entry.directory).toSet();
final sorted = albums.toList() final sorted = albums.toList()
@ -104,9 +112,14 @@ class CollectionSource {
} }
void updateTags() { void updateTags() {
final tags = _rawEntries.expand((entry) => entry.xmpSubjects).toSet(); final tags = _rawEntries.expand((entry) => entry.xmpSubjects).toSet().toList()..sort(compareAsciiUpperCase);
final sorted = tags.toList()..sort(compareAsciiUpperCase); sortedTags = List.unmodifiable(tags);
sortedTags = List.unmodifiable(sorted); }
void updateLocations() {
final locatedEntries = _rawEntries.where((entry) => entry.isLocated);
final countries = locatedEntries.map((entry) => entry.addressDetails.countryName).toSet().toList()..sort(compareAsciiUpperCase);
sortedCountries = List.unmodifiable(countries);
} }
void add(ImageEntry entry) { void add(ImageEntry entry) {
@ -140,7 +153,9 @@ class CollectionSource {
} }
} }
class MetadataChangedEvent {} class AddressMetadataChangedEvent {}
class CatalogMetadataChangedEvent {}
class EntryAddedEvent { class EntryAddedEvent {
final ImageEntry entry; final ImageEntry entry;

View file

@ -20,7 +20,7 @@ class AllCollectionDrawer extends StatefulWidget {
} }
class _AllCollectionDrawerState extends State<AllCollectionDrawer> { class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
bool _albumsExpanded = false, _tagsExpanded = false; bool _albumsExpanded = false, _tagsExpanded = false, _countriesExpanded = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -113,6 +113,16 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
dense: true, dense: true,
filter: TagFilter(tag), filter: TagFilter(tag),
); );
final buildCountryEntry = (country) => _FilteredCollectionNavTile(
collection: collection,
leading: Icon(
OMIcons.place,
color: stringToColor(country),
),
title: country,
dense: true,
filter: CountryFilter(country),
);
final regularAlbums = [], appAlbums = [], specialAlbums = []; final regularAlbums = [], appAlbums = [], specialAlbums = [];
for (var album in source.sortedAlbums) { for (var album in source.sortedAlbums) {
@ -130,6 +140,7 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
} }
final tags = source.sortedTags; final tags = source.sortedTags;
final countries = source.sortedCountries;
final drawerItems = [ final drawerItems = [
header, header,
gifEntry, gifEntry,
@ -186,6 +197,28 @@ class _AllCollectionDrawerState extends State<AllCollectionDrawer> {
children: tags.map(buildTagEntry).toList(), children: tags.map(buildTagEntry).toList(),
), ),
), ),
if (countries.isNotEmpty)
SafeArea(
top: false,
bottom: false,
child: ExpansionTile(
leading: const Icon(OMIcons.place),
title: Row(
children: [
const Text('Countries'),
const Spacer(),
Text(
'${countries.length}',
style: TextStyle(
color: (_countriesExpanded ? Theme.of(context).accentColor : Colors.white).withOpacity(.6),
),
),
],
),
onExpansionChanged: (expanded) => setState(() => _countriesExpanded = expanded),
children: countries.map(buildCountryEntry).toList(),
),
),
]; ];
return Drawer( return Drawer(