diff --git a/lib/widgets/collection/thumbnail_collection.dart b/lib/widgets/collection/thumbnail_collection.dart index e0c230aff..be2943041 100644 --- a/lib/widgets/collection/thumbnail_collection.dart +++ b/lib/widgets/collection/thumbnail_collection.dart @@ -43,7 +43,7 @@ class ThumbnailCollection extends StatelessWidget { if (viewportSize.isEmpty) return SizedBox.shrink(); final tileExtentManager = TileExtentManager( - routeName: context.currentRouteName, + settingsRouteKey: context.currentRouteName, columnCountMin: columnCountMin, columnCountDefault: columnCountDefault, extentMin: extentMin, diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart index bbcfb83c4..50fd94e47 100644 --- a/lib/widgets/common/identity/aves_filter_chip.dart +++ b/lib/widgets/common/identity/aves_filter_chip.dart @@ -18,8 +18,9 @@ class AvesFilterChip extends StatefulWidget { final HeroType heroType; final FilterCallback onTap; final OffsetFilterCallback onLongPress; + final BorderRadius borderRadius; - static final BorderRadius borderRadius = BorderRadius.circular(32); + static const double defaultRadius = 32; static const double outlineWidth = 2; static const double minChipHeight = kMinInteractiveDimension; static const double minChipWidth = 80; @@ -33,6 +34,7 @@ class AvesFilterChip extends StatefulWidget { this.showGenericIcon = true, this.background, this.details, + this.borderRadius = const BorderRadius.all(Radius.circular(defaultRadius)), this.padding = 6.0, this.heroType = HeroType.onTap, this.onTap, @@ -52,6 +54,8 @@ class _AvesFilterChipState extends State { CollectionFilter get filter => widget.filter; + BorderRadius get borderRadius => widget.borderRadius; + double get padding => widget.padding; @override @@ -141,8 +145,6 @@ class _AvesFilterChipState extends State { ); } - final borderRadius = AvesFilterChip.borderRadius; - Widget chip = Container( constraints: BoxConstraints( minWidth: AvesFilterChip.minChipWidth, diff --git a/lib/widgets/common/tile_extent_manager.dart b/lib/widgets/common/tile_extent_manager.dart index 7e54ebab3..5001f84a2 100644 --- a/lib/widgets/common/tile_extent_manager.dart +++ b/lib/widgets/common/tile_extent_manager.dart @@ -4,13 +4,13 @@ import 'package:aves/model/settings/settings.dart'; import 'package:flutter/widgets.dart'; class TileExtentManager { - final String routeName; + final String settingsRouteKey; final int columnCountMin, columnCountDefault; final double spacing, extentMin; final ValueNotifier extentNotifier; const TileExtentManager({ - @required this.routeName, + @required this.settingsRouteKey, @required this.columnCountMin, @required this.columnCountDefault, @required this.extentMin, @@ -26,7 +26,7 @@ class TileExtentManager { final viewportSizeMin = Size.square(extentMin * columnCountMin); viewportSize = Size(max(viewportSize.width, viewportSizeMin.width), max(viewportSize.height, viewportSizeMin.height)); - final oldUserPreferredExtent = settings.getTileExtent(routeName); + final oldUserPreferredExtent = settings.getTileExtent(settingsRouteKey); final currentExtent = extentNotifier.value; final targetExtent = userPreferredExtent > 0 ? userPreferredExtent @@ -38,7 +38,7 @@ class TileExtentManager { final newExtent = _extentForColumnCount(viewportSize, columnCount); if (userPreferredExtent > 0 || oldUserPreferredExtent == 0) { - settings.setTileExtent(routeName, newExtent); + settings.setTileExtent(settingsRouteKey, newExtent); } if (extentNotifier.value != newExtent) { extentNotifier.value = newExtent; diff --git a/lib/widgets/filter_grids/album_pick.dart b/lib/widgets/filter_grids/album_pick.dart index 0ac6b4413..2828a8266 100644 --- a/lib/widgets/filter_grids/album_pick.dart +++ b/lib/widgets/filter_grids/album_pick.dart @@ -32,7 +32,7 @@ class AlbumPickPage extends StatefulWidget { } class _AlbumPickPageState extends State { - final _filterNotifier = ValueNotifier(''); + final _queryNotifier = ValueNotifier(''); CollectionSource get source => widget.source; @@ -41,26 +41,29 @@ class _AlbumPickPageState extends State { Widget appBar = AlbumPickAppBar( copy: widget.copy, actionDelegate: AlbumChipSetActionDelegate(source: source), - filterNotifier: _filterNotifier, + queryNotifier: _queryNotifier, ); return Selector( selector: (context, s) => s.albumSortFactor, builder: (context, sortFactor, child) { - return ValueListenableBuilder( - valueListenable: _filterNotifier, - builder: (context, filter, child) => FilterGridPage( - source: source, - appBar: appBar, - filterEntries: AlbumListPage.getAlbumEntries(source, filter: filter), - filterBuilder: (s) => AlbumFilter(s, source.getUniqueAlbumName(s)), - emptyBuilder: () => EmptyContent( - icon: AIcons.album, - text: 'No albums', - ), - appBarHeight: AlbumPickAppBar.preferredHeight, - onTap: (filter) => Navigator.pop(context, (filter as AlbumFilter)?.album), + return FilterGridPage( + source: source, + appBar: appBar, + filterEntries: AlbumListPage.getAlbumEntries(source), + applyQuery: (filters, query) { + if (query == null || query.isEmpty) return filters; + query = query.toUpperCase(); + return filters.where((filter) => filter.uniqueName.toUpperCase().contains(query)).toList(); + }, + queryNotifier: _queryNotifier, + emptyBuilder: () => EmptyContent( + icon: AIcons.album, + text: 'No albums', ), + settingsRouteKey: AlbumListPage.routeName, + appBarHeight: AlbumPickAppBar.preferredHeight, + onTap: (filter) => Navigator.pop(context, (filter as AlbumFilter)?.album), ); }, ); @@ -70,14 +73,14 @@ class _AlbumPickPageState extends State { class AlbumPickAppBar extends StatelessWidget { final bool copy; final AlbumChipSetActionDelegate actionDelegate; - final ValueNotifier filterNotifier; + final ValueNotifier queryNotifier; static const preferredHeight = kToolbarHeight + AlbumFilterBar.preferredHeight; const AlbumPickAppBar({ @required this.copy, @required this.actionDelegate, - @required this.filterNotifier, + @required this.queryNotifier, }); @override @@ -86,7 +89,7 @@ class AlbumPickAppBar extends StatelessWidget { leading: BackButton(), title: Text(copy ? 'Copy to Album' : 'Move to Album'), bottom: AlbumFilterBar( - filterNotifier: filterNotifier, + filterNotifier: queryNotifier, ), actions: [ IconButton( diff --git a/lib/widgets/filter_grids/albums_page.dart b/lib/widgets/filter_grids/albums_page.dart index 642957d60..4c78eac83 100644 --- a/lib/widgets/filter_grids/albums_page.dart +++ b/lib/widgets/filter_grids/albums_page.dart @@ -33,7 +33,7 @@ class AlbumListPage extends StatelessWidget { animation: androidFileUtils.appNameChangeNotifier, builder: (context, child) => StreamBuilder( stream: source.eventBus.on(), - builder: (context, snapshot) => FilterNavigationPage( + builder: (context, snapshot) => FilterNavigationPage( source: source, title: 'Albums', chipSetActionDelegate: AlbumChipSetActionDelegate(source: source), @@ -44,7 +44,6 @@ class AlbumListPage extends StatelessWidget { ChipAction.delete, ], filterEntries: getAlbumEntries(source), - filterBuilder: (album) => AlbumFilter(album, source.getUniqueAlbumName(album)), emptyBuilder: () => EmptyContent( icon: AIcons.album, text: 'No albums', @@ -58,56 +57,53 @@ class AlbumListPage extends StatelessWidget { // common with album selection page to move/copy entries - static Map getAlbumEntries(CollectionSource source, {String filter}) { - final pinned = settings.pinnedFilters.whereType().map((f) => f.album); + static Map getAlbumEntries(CollectionSource source) { + final pinned = settings.pinnedFilters.whereType(); final entriesByDate = source.sortedEntriesForFilterList; + AlbumFilter _buildFilter(String album) => AlbumFilter(album, source.getUniqueAlbumName(album)); + // albums are initially sorted by name at the source level - var sortedAlbums = source.sortedAlbums; - if (filter != null && filter.isNotEmpty) { - filter = filter.toUpperCase(); - sortedAlbums = sortedAlbums.where((album) => source.getUniqueAlbumName(album).toUpperCase().contains(filter)).toList(); - } + var sortedFilters = source.sortedAlbums.map(_buildFilter); if (settings.albumSortFactor == ChipSortFactor.name) { - final pinnedAlbums = [], regularAlbums = [], appAlbums = [], specialAlbums = []; - for (var album in sortedAlbums) { - if (pinned.contains(album)) { - pinnedAlbums.add(album); + final pinnedAlbums = [], regularAlbums = [], appAlbums = [], specialAlbums = []; + for (var filter in sortedFilters) { + if (pinned.contains(filter)) { + pinnedAlbums.add(filter); } else { - switch (androidFileUtils.getAlbumType(album)) { + switch (androidFileUtils.getAlbumType(filter.album)) { case AlbumType.regular: - regularAlbums.add(album); + regularAlbums.add(filter); break; case AlbumType.app: - appAlbums.add(album); + appAlbums.add(filter); break; default: - specialAlbums.add(album); + specialAlbums.add(filter); break; } } } - return Map.fromEntries([...pinnedAlbums, ...specialAlbums, ...appAlbums, ...regularAlbums].map((album) { + return Map.fromEntries([...pinnedAlbums, ...specialAlbums, ...appAlbums, ...regularAlbums].map((filter) { return MapEntry( - album, - entriesByDate.firstWhere((entry) => entry.directory == album, orElse: () => null), + filter, + entriesByDate.firstWhere((entry) => entry.directory == filter.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))))); + final filtersWithCount = List.of(sortedFilters.map((filter) => MapEntry(filter, source.count(filter)))); filtersWithCount.sort(FilterNavigationPage.compareChipsByEntryCount); - sortedAlbums = filtersWithCount.map((kv) => kv.key).toList(); + sortedFilters = filtersWithCount.map((kv) => kv.key).toList(); } - final allMapEntries = sortedAlbums.map((album) => MapEntry( - album, - entriesByDate.firstWhere((entry) => entry.directory == album, orElse: () => null), + final allMapEntries = sortedFilters.map((filter) => MapEntry( + filter, + entriesByDate.firstWhere((entry) => entry.directory == filter.album, orElse: () => null), )); - final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); + final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); final pinnedMapEntries = (byPin[true] ?? []); final unpinnedMapEntries = (byPin[false] ?? []); diff --git a/lib/widgets/filter_grids/common/decorated_filter_chip.dart b/lib/widgets/filter_grids/common/decorated_filter_chip.dart index f7690b276..c2aa2d52f 100644 --- a/lib/widgets/filter_grids/common/decorated_filter_chip.dart +++ b/lib/widgets/filter_grids/common/decorated_filter_chip.dart @@ -49,12 +49,14 @@ class DecoratedFilterChip extends StatelessWidget { entry: entry, extent: extent, ); + final borderRadius = min(AvesFilterChip.defaultRadius, extent / 4); final titlePadding = min(6.0, extent / 16); return AvesFilterChip( filter: filter, showGenericIcon: false, background: backgroundImage, details: _buildDetails(filter), + borderRadius: BorderRadius.all(Radius.circular(borderRadius)), padding: titlePadding, onTap: onTap, onLongPress: onLongPress, diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index ee75af44a..6240c3a39 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -20,12 +20,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:provider/provider.dart'; -class FilterGridPage extends StatelessWidget { +class FilterGridPage extends StatelessWidget { final CollectionSource source; final Widget appBar; - final Map filterEntries; - final CollectionFilter Function(String key) filterBuilder; + final Map filterEntries; + final ValueNotifier queryNotifier; final Widget Function() emptyBuilder; + final String settingsRouteKey; + final Iterable Function(Iterable filters, String query) applyQuery; final FilterCallback onTap; final OffsetFilterCallback onLongPress; @@ -39,8 +41,10 @@ class FilterGridPage extends StatelessWidget { @required this.source, @required this.appBar, @required this.filterEntries, - @required this.filterBuilder, + @required this.queryNotifier, + this.applyQuery, @required this.emptyBuilder, + this.settingsRouteKey, double appBarHeight = kToolbarHeight, @required this.onTap, this.onLongPress, @@ -48,13 +52,8 @@ class FilterGridPage extends StatelessWidget { _appBarHeightNotifier.value = appBarHeight; } - List get filterKeys => filterEntries.keys.toList(); - static const Color detailColor = Color(0xFFE0E0E0); - // TODO TLAD enforce max extent? - // static const double maxCrossAxisExtent = 180; - @override Widget build(BuildContext context) { return MediaQueryDataProvider( @@ -68,7 +67,7 @@ class FilterGridPage extends StatelessWidget { if (viewportSize.isEmpty) return SizedBox.shrink(); final tileExtentManager = TileExtentManager( - routeName: context.currentRouteName, + settingsRouteKey: settingsRouteKey ?? context.currentRouteName, columnCountMin: 2, columnCountDefault: 2, extentMin: 60, @@ -80,38 +79,51 @@ class FilterGridPage extends StatelessWidget { valueListenable: _tileExtentNotifier, builder: (context, tileExtent, child) { final columnCount = tileExtentManager.getEffectiveColumnCountForExtent(viewportSize, tileExtent); - final scrollView = AnimationLimiter( - child: _buildDraggableScrollView(_buildScrollView(context, columnCount)), - ); - return GridScaleGestureDetector( - tileExtentManager: tileExtentManager, - scrollableKey: _scrollableKey, - appBarHeightNotifier: _appBarHeightNotifier, - viewportSize: viewportSize, - showScaledGrid: false, - scaledBuilder: (item, extent) { - final filter = item.filter; - return SizedBox( - width: extent, - height: extent, - child: DecoratedFilterChip( - source: source, - filter: filter, - entry: item.entry, - extent: extent, - pinned: settings.pinnedFilters.contains(filter), - ), + return ValueListenableBuilder( + valueListenable: queryNotifier, + builder: (context, query, child) { + final allFilters = filterEntries.keys; + final visibleFilters = (applyQuery != null ? applyQuery(allFilters, query) : allFilters).toList(); + + final scrollView = AnimationLimiter( + child: _buildDraggableScrollView(_buildScrollView(context, columnCount, visibleFilters)), + ); + + return GridScaleGestureDetector( + tileExtentManager: tileExtentManager, + scrollableKey: _scrollableKey, + appBarHeightNotifier: _appBarHeightNotifier, + viewportSize: viewportSize, + showScaledGrid: false, + scaledBuilder: (item, extent) { + final filter = item.filter; + return SizedBox( + width: extent, + height: extent, + child: DecoratedFilterChip( + source: source, + filter: filter, + entry: item.entry, + extent: extent, + pinned: settings.pinnedFilters.contains(filter), + ), + ); + }, + getScaledItemTileRect: (context, item) { + final index = visibleFilters.indexOf(item.filter); + final column = index % columnCount; + final row = (index / columnCount).floor(); + final left = tileExtent * column + spacing * (column - 1); + final top = tileExtent * row + spacing * (row - 1); + return Rect.fromLTWH(left, top, tileExtent, tileExtent); + }, + onScaled: (item) { + // TODO TLAD highlight scaled item + }, + child: scrollView, ); }, - getScaledItemTileRect: (context, item) { - // TODO TLAD - return Rect.zero; - }, - onScaled: (item) { - // TODO TLAD - }, - child: scrollView, ); }, ); @@ -148,14 +160,14 @@ class FilterGridPage extends StatelessWidget { ); } - ScrollView _buildScrollView(BuildContext context, int columnCount) { + ScrollView _buildScrollView(BuildContext context, int columnCount, List visibleFilters) { final pinnedFilters = settings.pinnedFilters; return CustomScrollView( key: _scrollableKey, controller: PrimaryScrollController.of(context), slivers: [ appBar, - filterKeys.isEmpty + visibleFilters.isEmpty ? SliverFillRemaining( child: Selector( selector: (context, mq) => mq.viewInsets.bottom, @@ -171,13 +183,12 @@ class FilterGridPage extends StatelessWidget { : SliverGrid( delegate: SliverChildBuilderDelegate( (context, i) { - final key = filterKeys[i]; - final filter = filterBuilder(key); - final entry = filterEntries[key]; + final filter = visibleFilters[i]; + final entry = filterEntries[filter]; final child = MetaData( metaData: ScalerMetadata(FilterGridItem(filter, entry)), child: DecoratedFilterChip( - key: Key(key), + key: Key(filter.key), source: source, filter: filter, entry: entry, @@ -200,7 +211,7 @@ class FilterGridPage extends StatelessWidget { ), ); }, - childCount: filterKeys.length, + childCount: visibleFilters.length, ), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: columnCount, @@ -221,8 +232,8 @@ class FilterGridPage extends StatelessWidget { } } -class FilterGridItem { - final CollectionFilter filter; +class FilterGridItem { + final T filter; final ImageEntry entry; const FilterGridItem(this.filter, this.entry); diff --git a/lib/widgets/filter_grids/common/filter_nav_page.dart b/lib/widgets/filter_grids/common/filter_nav_page.dart index 36d65821b..d2a66d8bf 100644 --- a/lib/widgets/filter_grids/common/filter_nav_page.dart +++ b/lib/widgets/filter_grids/common/filter_nav_page.dart @@ -18,20 +18,18 @@ import 'package:aves/widgets/filter_grids/common/chip_set_action_delegate.dart'; import 'package:aves/widgets/filter_grids/common/filter_grid_page.dart'; import 'package:aves/widgets/search/search_button.dart'; import 'package:aves/widgets/search/search_delegate.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -class FilterNavigationPage extends StatelessWidget { +class FilterNavigationPage extends StatelessWidget { final CollectionSource source; final String title; final ChipSetActionDelegate chipSetActionDelegate; final ChipActionDelegate chipActionDelegate; - final Map filterEntries; - final CollectionFilter Function(String key) filterBuilder; + final Map filterEntries; final Widget Function() emptyBuilder; - final List Function(CollectionFilter filter) chipActionsBuilder; + final List Function(T filter) chipActionsBuilder; const FilterNavigationPage({ @required this.source, @@ -40,13 +38,12 @@ class FilterNavigationPage extends StatelessWidget { @required this.chipActionDelegate, @required this.chipActionsBuilder, @required this.filterEntries, - @required this.filterBuilder, @required this.emptyBuilder, }); @override Widget build(BuildContext context) { - return FilterGridPage( + return FilterGridPage( source: source, appBar: SliverAppBar( title: TappableAppBarTitle( @@ -61,7 +58,7 @@ class FilterNavigationPage extends StatelessWidget { floating: true, ), filterEntries: filterEntries, - filterBuilder: filterBuilder, + queryNotifier: ValueNotifier(''), emptyBuilder: () => ValueListenableBuilder( valueListenable: source.stateNotifier, builder: (context, sourceState, child) { @@ -84,7 +81,7 @@ class FilterNavigationPage extends StatelessWidget { ); } - Future _showMenu(BuildContext context, CollectionFilter filter, Offset tapPosition) async { + Future _showMenu(BuildContext context, T filter, Offset tapPosition) async { final RenderBox overlay = Overlay.of(context).context.findRenderObject(); final touchArea = Size(40, 40); final selectedAction = await showMenu( @@ -144,13 +141,13 @@ class FilterNavigationPage extends StatelessWidget { )); } - static int compareChipsByDate(MapEntry a, MapEntry b) { + static int compareChipsByDate(MapEntry a, MapEntry b) { final c = b.value.bestDate?.compareTo(a.value.bestDate) ?? -1; - return c != 0 ? c : compareAsciiUpperCase(a.key, b.key); + return c != 0 ? c : a.key.compareTo(b.key); } - static int compareChipsByEntryCount(MapEntry a, MapEntry b) { + static int compareChipsByEntryCount(MapEntry a, MapEntry b) { final c = b.value.compareTo(a.value) ?? -1; - return c != 0 ? c : compareAsciiUpperCase(a.key, b.key); + return c != 0 ? c : a.key.compareTo(b.key); } } diff --git a/lib/widgets/filter_grids/countries_page.dart b/lib/widgets/filter_grids/countries_page.dart index 1c8587477..5f3dd6ce4 100644 --- a/lib/widgets/filter_grids/countries_page.dart +++ b/lib/widgets/filter_grids/countries_page.dart @@ -39,7 +39,6 @@ class CountryListPage extends StatelessWidget { settings.pinnedFilters.contains(filter) ? ChipAction.unpin : ChipAction.pin, ], filterEntries: _getCountryEntries(), - filterBuilder: _buildFilter, emptyBuilder: () => EmptyContent( icon: AIcons.location, text: 'No countries', @@ -50,31 +49,29 @@ class CountryListPage extends StatelessWidget { ); } - CollectionFilter _buildFilter(String location) => LocationFilter(LocationLevel.country, location); - - Map _getCountryEntries() { - final pinned = settings.pinnedFilters.whereType().map((f) => f.countryNameAndCode); + Map _getCountryEntries() { + final pinned = settings.pinnedFilters.whereType(); final entriesByDate = source.sortedEntriesForFilterList; // countries are initially sorted by name at the source level - var sortedCountries = source.sortedCountries; + var sortedFilters = source.sortedCountries.map((location) => LocationFilter(LocationLevel.country, location)); if (settings.countrySortFactor == ChipSortFactor.count) { - var filtersWithCount = List.of(sortedCountries.map((s) => MapEntry(s, source.count(_buildFilter(s))))); + final filtersWithCount = List.of(sortedFilters.map((filter) => MapEntry(filter, source.count(filter)))); filtersWithCount.sort(FilterNavigationPage.compareChipsByEntryCount); - sortedCountries = filtersWithCount.map((kv) => kv.key).toList(); + sortedFilters = filtersWithCount.map((kv) => kv.key).toList(); } final locatedEntries = entriesByDate.where((entry) => entry.isLocated); - final allMapEntries = sortedCountries.map((countryNameAndCode) { - final split = countryNameAndCode.split(LocationFilter.locationSeparator); + final allMapEntries = sortedFilters.map((filter) { + final split = filter.countryNameAndCode.split(LocationFilter.locationSeparator); ImageEntry entry; if (split.length > 1) { final countryCode = split[1]; entry = locatedEntries.firstWhere((entry) => entry.addressDetails.countryCode == countryCode, orElse: () => null); } - return MapEntry(countryNameAndCode, entry); + return MapEntry(filter, entry); }); - final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); + final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); final pinnedMapEntries = (byPin[true] ?? []); final unpinnedMapEntries = (byPin[false] ?? []); diff --git a/lib/widgets/filter_grids/tags_page.dart b/lib/widgets/filter_grids/tags_page.dart index 1de87a33d..7d29f6f7c 100644 --- a/lib/widgets/filter_grids/tags_page.dart +++ b/lib/widgets/filter_grids/tags_page.dart @@ -39,7 +39,6 @@ class TagListPage extends StatelessWidget { settings.pinnedFilters.contains(filter) ? ChipAction.unpin : ChipAction.pin, ], filterEntries: _getTagEntries(), - filterBuilder: _buildFilter, emptyBuilder: () => EmptyContent( icon: AIcons.tag, text: 'No tags', @@ -50,25 +49,23 @@ class TagListPage extends StatelessWidget { ); } - CollectionFilter _buildFilter(String tag) => TagFilter(tag); - - Map _getTagEntries() { - final pinned = settings.pinnedFilters.whereType().map((f) => f.tag); + Map _getTagEntries() { + final pinned = settings.pinnedFilters.whereType(); final entriesByDate = source.sortedEntriesForFilterList; // tags are initially sorted by name at the source level - var sortedTags = source.sortedTags; + var sortedFilters = source.sortedTags.map((tag) => TagFilter(tag)); if (settings.tagSortFactor == ChipSortFactor.count) { - var filtersWithCount = List.of(sortedTags.map((s) => MapEntry(s, source.count(_buildFilter(s))))); + final filtersWithCount = List.of(sortedFilters.map((filter) => MapEntry(filter, source.count(filter)))); filtersWithCount.sort(FilterNavigationPage.compareChipsByEntryCount); - sortedTags = filtersWithCount.map((kv) => kv.key).toList(); + sortedFilters = filtersWithCount.map((kv) => kv.key).toList(); } - final allMapEntries = sortedTags.map((tag) => MapEntry( - tag, - entriesByDate.firstWhere((entry) => entry.xmpSubjects.contains(tag), orElse: () => null), + final allMapEntries = sortedFilters.map((filter) => MapEntry( + filter, + entriesByDate.firstWhere((entry) => entry.xmpSubjects.contains(filter.tag), orElse: () => null), )); - final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); + final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); final pinnedMapEntries = (byPin[true] ?? []); final unpinnedMapEntries = (byPin[false] ?? []);