diff --git a/lib/model/source/enums.dart b/lib/model/source/enums.dart index 7a49d1fc0..65b52917b 100644 --- a/lib/model/source/enums.dart +++ b/lib/model/source/enums.dart @@ -1,6 +1,6 @@ enum Activity { browse, select } -enum ChipSortFactor { date, name } +enum ChipSortFactor { date, name, count } enum EntrySortFactor { date, size, name } diff --git a/lib/widgets/filter_grids/albums_page.dart b/lib/widgets/filter_grids/albums_page.dart index 6a4c104cb..b10093d0e 100644 --- a/lib/widgets/filter_grids/albums_page.dart +++ b/lib/widgets/filter_grids/albums_page.dart @@ -62,42 +62,56 @@ class AlbumListPage extends StatelessWidget { final pinned = settings.pinnedFilters.whereType().map((f) => f.album); final entriesByDate = source.sortedEntriesForFilterList; - switch (settings.albumSortFactor) { - case ChipSortFactor.date: - final allAlbumMapEntries = source.sortedAlbums.map((album) => MapEntry( - album, - entriesByDate.firstWhere((entry) => entry.directory == album, orElse: () => null), - )); - final byPin = groupBy, bool>(allAlbumMapEntries, (e) => pinned.contains(e.key)); - final pinnedMapEntries = (byPin[true] ?? [])..sort(FilterNavigationPage.compareChipsByDate); - final unpinnedMapEntries = (byPin[false] ?? [])..sort(FilterNavigationPage.compareChipsByDate); - return Map.fromEntries([...pinnedMapEntries, ...unpinnedMapEntries]); - case ChipSortFactor.name: - default: - final pinnedAlbums = [], regularAlbums = [], appAlbums = [], specialAlbums = []; - for (var album in source.sortedAlbums) { - if (pinned.contains(album)) { - pinnedAlbums.add(album); - } else { - switch (androidFileUtils.getAlbumType(album)) { - case AlbumType.regular: - regularAlbums.add(album); - break; - case AlbumType.app: - appAlbums.add(album); - break; - default: - specialAlbums.add(album); - break; - } + // albums are initially sorted by name at the source level + var sortedAlbums = source.sortedAlbums; + + if (settings.albumSortFactor == ChipSortFactor.name) { + final pinnedAlbums = [], regularAlbums = [], appAlbums = [], specialAlbums = []; + for (var album in sortedAlbums) { + if (pinned.contains(album)) { + pinnedAlbums.add(album); + } else { + switch (androidFileUtils.getAlbumType(album)) { + case AlbumType.regular: + regularAlbums.add(album); + break; + case AlbumType.app: + appAlbums.add(album); + break; + default: + specialAlbums.add(album); + break; } } - return Map.fromEntries([...pinnedAlbums, ...specialAlbums, ...appAlbums, ...regularAlbums].map((album) { - return MapEntry( - album, - entriesByDate.firstWhere((entry) => entry.directory == album, orElse: () => null), - ); - })); + } + return Map.fromEntries([...pinnedAlbums, ...specialAlbums, ...appAlbums, ...regularAlbums].map((album) { + return MapEntry( + album, + entriesByDate.firstWhere((entry) => entry.directory == album, orElse: () => null), + ); + })); } + + if (settings.albumSortFactor == ChipSortFactor.count) { + CollectionFilter _buildFilter(String album) => AlbumFilter(album, source.getUniqueAlbumName(album)); + var filtersWithCount = List.of(sortedAlbums.map((s) => MapEntry(s, source.count(_buildFilter(s))))); + filtersWithCount.sort(FilterNavigationPage.compareChipsByEntryCount); + sortedAlbums = filtersWithCount.map((kv) => kv.key).toList(); + } + + final allMapEntries = sortedAlbums.map((album) => MapEntry( + album, + entriesByDate.firstWhere((entry) => entry.directory == album, orElse: () => null), + )); + final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); + final pinnedMapEntries = (byPin[true] ?? []); + final unpinnedMapEntries = (byPin[false] ?? []); + + if (settings.albumSortFactor == ChipSortFactor.date) { + pinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); + unpinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); + } + + return Map.fromEntries([...pinnedMapEntries, ...unpinnedMapEntries]); } } diff --git a/lib/widgets/filter_grids/common/chip_set_action_delegate.dart b/lib/widgets/filter_grids/common/chip_set_action_delegate.dart index 5112d7396..974f18018 100644 --- a/lib/widgets/filter_grids/common/chip_set_action_delegate.dart +++ b/lib/widgets/filter_grids/common/chip_set_action_delegate.dart @@ -46,6 +46,7 @@ abstract class ChipSetActionDelegate { options: { ChipSortFactor.date: 'By date', ChipSortFactor.name: 'By name', + ChipSortFactor.count: 'By entry count', }, title: 'Sort', ), diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index 89e6db5af..82bcc9084 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -153,6 +153,11 @@ class FilterNavigationPage extends StatelessWidget { final c = b.value.bestDate?.compareTo(a.value.bestDate) ?? -1; return c != 0 ? c : compareAsciiUpperCase(a.key, b.key); } + + static int compareChipsByEntryCount(MapEntry a, MapEntry b) { + final c = b.value.compareTo(a.value) ?? -1; + return c != 0 ? c : compareAsciiUpperCase(a.key, b.key); + } } class FilterGridPage extends StatelessWidget { diff --git a/lib/widgets/filter_grids/countries_page.dart b/lib/widgets/filter_grids/countries_page.dart index fae95bd1a..170c837f0 100644 --- a/lib/widgets/filter_grids/countries_page.dart +++ b/lib/widgets/filter_grids/countries_page.dart @@ -39,7 +39,7 @@ class CountryListPage extends StatelessWidget { settings.pinnedFilters.contains(filter) ? ChipAction.unpin : ChipAction.pin, ], filterEntries: _getCountryEntries(), - filterBuilder: (s) => LocationFilter(LocationLevel.country, s), + filterBuilder: _buildFilter, emptyBuilder: () => EmptyContent( icon: AIcons.location, text: 'No countries', @@ -50,12 +50,22 @@ class CountryListPage extends StatelessWidget { ); } + CollectionFilter _buildFilter(String location) => LocationFilter(LocationLevel.country, location); + Map _getCountryEntries() { final pinned = settings.pinnedFilters.whereType().map((f) => f.countryNameAndCode); final entriesByDate = source.sortedEntriesForFilterList; + // countries are initially sorted by name at the source level + var sortedCountries = source.sortedCountries; + if (settings.countrySortFactor == ChipSortFactor.count) { + var filtersWithCount = List.of(sortedCountries.map((s) => MapEntry(s, source.count(_buildFilter(s))))); + filtersWithCount.sort(FilterNavigationPage.compareChipsByEntryCount); + sortedCountries = filtersWithCount.map((kv) => kv.key).toList(); + } + final locatedEntries = entriesByDate.where((entry) => entry.isLocated); - final allMapEntries = source.sortedCountries.map((countryNameAndCode) { + final allMapEntries = sortedCountries.map((countryNameAndCode) { final split = countryNameAndCode.split(LocationFilter.locationSeparator); ImageEntry entry; if (split.length > 1) { @@ -63,21 +73,16 @@ class CountryListPage extends StatelessWidget { entry = locatedEntries.firstWhere((entry) => entry.addressDetails.countryCode == countryCode, orElse: () => null); } return MapEntry(countryNameAndCode, entry); - }).toList(); - + }); final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); final pinnedMapEntries = (byPin[true] ?? []); final unpinnedMapEntries = (byPin[false] ?? []); - switch (settings.countrySortFactor) { - case ChipSortFactor.date: - pinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); - unpinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); - break; - case ChipSortFactor.name: - // already sorted by name at the source level - break; + if (settings.countrySortFactor == ChipSortFactor.date) { + pinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); + unpinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); } + return Map.fromEntries([...pinnedMapEntries, ...unpinnedMapEntries]); } } diff --git a/lib/widgets/filter_grids/tags_page.dart b/lib/widgets/filter_grids/tags_page.dart index 0efce6eca..97ebc9468 100644 --- a/lib/widgets/filter_grids/tags_page.dart +++ b/lib/widgets/filter_grids/tags_page.dart @@ -39,7 +39,7 @@ class TagListPage extends StatelessWidget { settings.pinnedFilters.contains(filter) ? ChipAction.unpin : ChipAction.pin, ], filterEntries: _getTagEntries(), - filterBuilder: (s) => TagFilter(s), + filterBuilder: _buildFilter, emptyBuilder: () => EmptyContent( icon: AIcons.tag, text: 'No tags', @@ -50,30 +50,33 @@ class TagListPage extends StatelessWidget { ); } + CollectionFilter _buildFilter(String tag) => TagFilter(tag); + Map _getTagEntries() { final pinned = settings.pinnedFilters.whereType().map((f) => f.tag); final entriesByDate = source.sortedEntriesForFilterList; - final allMapEntries = source.sortedTags - .map((tag) => MapEntry( - tag, - entriesByDate.firstWhere((entry) => entry.xmpSubjects.contains(tag), orElse: () => null), - )) - .toList(); + // tags are initially sorted by name at the source level + var sortedTags = source.sortedTags; + if (settings.tagSortFactor == ChipSortFactor.count) { + var filtersWithCount = List.of(sortedTags.map((s) => MapEntry(s, source.count(_buildFilter(s))))); + filtersWithCount.sort(FilterNavigationPage.compareChipsByEntryCount); + sortedTags = filtersWithCount.map((kv) => kv.key).toList(); + } + final allMapEntries = sortedTags.map((tag) => MapEntry( + tag, + entriesByDate.firstWhere((entry) => entry.xmpSubjects.contains(tag), orElse: () => null), + )); final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); final pinnedMapEntries = (byPin[true] ?? []); final unpinnedMapEntries = (byPin[false] ?? []); - switch (settings.tagSortFactor) { - case ChipSortFactor.date: - pinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); - unpinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); - break; - case ChipSortFactor.name: - // already sorted by name at the source level - break; + if (settings.tagSortFactor == ChipSortFactor.date) { + pinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); + unpinnedMapEntries.sort(FilterNavigationPage.compareChipsByDate); } + return Map.fromEntries([...pinnedMapEntries, ...unpinnedMapEntries]); } }