From 2e5a2e7c91e0f05676e936fd7e8232eaa0efeca5 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Tue, 24 Mar 2020 15:01:51 +0900 Subject: [PATCH] drawer: navigate by country --- lib/model/collection_filters.dart | 9 +++++ lib/model/collection_lens.dart | 2 +- lib/model/collection_source.dart | 37 ++++++++++++++------ lib/widgets/album/all_collection_drawer.dart | 35 +++++++++++++++++- 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/lib/model/collection_filters.dart b/lib/model/collection_filters.dart index 940f091a0..31d616f9b 100644 --- a/lib/model/collection_filters.dart +++ b/lib/model/collection_filters.dart @@ -24,6 +24,15 @@ class TagFilter extends CollectionFilter { 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 { @override bool filter(ImageEntry entry) => entry.isVideo; diff --git a/lib/model/collection_lens.dart b/lib/model/collection_lens.dart index 7c9b1a1a9..ffc5befc5 100644 --- a/lib/model/collection_lens.dart +++ b/lib/model/collection_lens.dart @@ -28,7 +28,7 @@ class CollectionLens with ChangeNotifier { this.sortFactor = sortFactor ?? SortFactor.date { _subscriptions.add(source.eventBus.on().listen((e) => onEntryAdded())); _subscriptions.add(source.eventBus.on().listen((e) => onEntryRemoved(e.entry))); - _subscriptions.add(source.eventBus.on().listen((e) => onMetadataChanged())); + _subscriptions.add(source.eventBus.on().listen((e) => onMetadataChanged())); onEntryAdded(); } diff --git a/lib/model/collection_source.dart b/lib/model/collection_source.dart index fb8cc113d..aa95f1f9b 100644 --- a/lib/model/collection_source.dart +++ b/lib/model/collection_source.dart @@ -12,6 +12,7 @@ class CollectionSource { List sortedAlbums = List.unmodifiable(const Iterable.empty()); List sortedTags = List.unmodifiable(const Iterable.empty()); + List sortedCountries = List.unmodifiable(const Iterable.empty()); List 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'); - onMetadataChanged(); + onCatalogMetadataChanged(); } Future loadAddresses() async { @@ -48,6 +49,7 @@ class CollectionSource { } }); debugPrint('$runtimeType loadAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms with ${saved.length} saved entries'); + onAddressMetadataChanged(); } Future catalogEntries() async { @@ -65,15 +67,10 @@ class CollectionSource { if (newMetadata.isEmpty) return; await metadataDb.saveMetadata(List.unmodifiable(newMetadata)); - onMetadataChanged(); + onCatalogMetadataChanged(); debugPrint('$runtimeType catalogEntries complete in ${stopwatch.elapsed.inSeconds}s with ${newMetadata.length} new entries'); } - void onMetadataChanged() { - updateTags(); - eventBus.fire(MetadataChangedEvent()); - } - Future locateEntries() async { final stopwatch = Stopwatch()..start(); final unlocatedEntries = _rawEntries.where((entry) => entry.hasGps && !entry.isLocated).toList(); @@ -89,9 +86,20 @@ class CollectionSource { } }); await metadataDb.saveAddresses(List.unmodifiable(newAddresses)); + onAddressMetadataChanged(); debugPrint('$runtimeType locateEntries complete in ${stopwatch.elapsed.inMilliseconds}ms'); } + void onCatalogMetadataChanged() { + updateTags(); + eventBus.fire(CatalogMetadataChangedEvent()); + } + + void onAddressMetadataChanged() { + updateLocations(); + eventBus.fire(AddressMetadataChangedEvent()); + } + void updateAlbums() { final albums = _rawEntries.map((entry) => entry.directory).toSet(); final sorted = albums.toList() @@ -104,9 +112,14 @@ class CollectionSource { } void updateTags() { - final tags = _rawEntries.expand((entry) => entry.xmpSubjects).toSet(); - final sorted = tags.toList()..sort(compareAsciiUpperCase); - sortedTags = List.unmodifiable(sorted); + final tags = _rawEntries.expand((entry) => entry.xmpSubjects).toSet().toList()..sort(compareAsciiUpperCase); + sortedTags = List.unmodifiable(tags); + } + + 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) { @@ -140,7 +153,9 @@ class CollectionSource { } } -class MetadataChangedEvent {} +class AddressMetadataChangedEvent {} + +class CatalogMetadataChangedEvent {} class EntryAddedEvent { final ImageEntry entry; diff --git a/lib/widgets/album/all_collection_drawer.dart b/lib/widgets/album/all_collection_drawer.dart index 8877fac39..3d4df5ab4 100644 --- a/lib/widgets/album/all_collection_drawer.dart +++ b/lib/widgets/album/all_collection_drawer.dart @@ -20,7 +20,7 @@ class AllCollectionDrawer extends StatefulWidget { } class _AllCollectionDrawerState extends State { - bool _albumsExpanded = false, _tagsExpanded = false; + bool _albumsExpanded = false, _tagsExpanded = false, _countriesExpanded = false; @override Widget build(BuildContext context) { @@ -113,6 +113,16 @@ class _AllCollectionDrawerState extends State { dense: true, 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 = []; for (var album in source.sortedAlbums) { @@ -130,6 +140,7 @@ class _AllCollectionDrawerState extends State { } final tags = source.sortedTags; + final countries = source.sortedCountries; final drawerItems = [ header, gifEntry, @@ -186,6 +197,28 @@ class _AllCollectionDrawerState extends State { 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(