diff --git a/lib/model/filters/location.dart b/lib/model/filters/location.dart index 02c5f7b81..50f77dd9b 100644 --- a/lib/model/filters/location.dart +++ b/lib/model/filters/location.dart @@ -27,9 +27,11 @@ class LocationFilter extends CollectionFilter { Map toMap() => { 'type': type, 'level': level.toString(), - 'location': _countryCode != null ? '$_location$locationSeparator$_countryCode' : _location, + 'location': _countryCode != null ? countryNameAndCode : _location, }; + String get countryNameAndCode => '$_location$locationSeparator$_countryCode'; + @override bool filter(ImageEntry entry) => entry.isLocated && ((level == LocationLevel.country && entry.addressDetails.countryCode == _countryCode) || (level == LocationLevel.place && entry.addressDetails.place == _location)); diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index e9b2fe665..e29302f42 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -19,7 +19,7 @@ import 'package:aves/widgets/common/data_providers/media_store_collection_provid import 'package:aves/widgets/common/entry_actions.dart'; import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/common/menu_row.dart'; -import 'package:aves/widgets/filter_grids/search_button.dart'; +import 'package:aves/widgets/common/search_button.dart'; import 'package:aves/widgets/stats/stats.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/common/action_delegates/selection_action_delegate.dart b/lib/widgets/common/action_delegates/selection_action_delegate.dart index 3b84c3525..59e039547 100644 --- a/lib/widgets/common/action_delegates/selection_action_delegate.dart +++ b/lib/widgets/common/action_delegates/selection_action_delegate.dart @@ -18,7 +18,7 @@ import 'package:aves/widgets/common/aves_dialog.dart'; import 'package:aves/widgets/common/entry_actions.dart'; import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; -import 'package:aves/widgets/filter_grids/filter_grid_page.dart'; +import 'package:aves/widgets/filter_grids/common/filter_grid_page.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; diff --git a/lib/widgets/filter_grids/search_button.dart b/lib/widgets/common/search_button.dart similarity index 100% rename from lib/widgets/filter_grids/search_button.dart rename to lib/widgets/common/search_button.dart diff --git a/lib/widgets/filter_grids/albums_page.dart b/lib/widgets/filter_grids/albums_page.dart index 14ac5ccd1..34b406322 100644 --- a/lib/widgets/filter_grids/albums_page.dart +++ b/lib/widgets/filter_grids/albums_page.dart @@ -8,10 +8,10 @@ import 'package:aves/model/source/enums.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/collection/empty.dart'; import 'package:aves/widgets/common/icons.dart'; -import 'package:aves/widgets/common/menu_row.dart'; -import 'package:aves/widgets/filter_grids/chip_action_delegate.dart'; -import 'package:aves/widgets/filter_grids/chip_actions.dart'; -import 'package:aves/widgets/filter_grids/filter_grid_page.dart'; +import 'package:aves/widgets/filter_grids/common/chip_action_delegate.dart'; +import 'package:aves/widgets/filter_grids/common/chip_actions.dart'; +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:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -22,6 +22,7 @@ class AlbumListPage extends StatelessWidget { final CollectionSource source; + static final ChipSetActionDelegate setActionDelegate = AlbumChipSetActionDelegate(); static final ChipActionDelegate actionDelegate = AlbumChipActionDelegate(); const AlbumListPage({@required this.source}); @@ -38,14 +39,18 @@ class AlbumListPage extends StatelessWidget { builder: (context, snapshot) => FilterNavigationPage( source: source, title: 'Albums', - actionDelegate: actionDelegate, + chipSetActionDelegate: setActionDelegate, + chipActionDelegate: actionDelegate, + chipActionsBuilder: (filter) => [ + settings.pinnedFilters.contains(filter) ? ChipAction.unpin : ChipAction.pin, + ChipAction.rename, + ], filterEntries: getAlbumEntries(source), filterBuilder: (album) => AlbumFilter(album, source.getUniqueAlbumName(album)), emptyBuilder: () => EmptyContent( icon: AIcons.album, text: 'No albums', ), - onLongPress: (filter, tapPosition) => _showMenu(context, filter, tapPosition), ), ), ); @@ -53,38 +58,6 @@ class AlbumListPage extends StatelessWidget { ); } - Future _showMenu(BuildContext context, CollectionFilter filter, Offset tapPosition) async { - final RenderBox overlay = Overlay.of(context).context.findRenderObject(); - final touchArea = Size(40, 40); - final selectedAction = await showMenu( - context: context, - position: RelativeRect.fromRect(tapPosition & touchArea, Offset.zero & overlay.size), - items: [settings.pinnedFilters.contains(filter) ? AlbumAction.unpin : AlbumAction.pin, AlbumAction.rename] - .map((action) => PopupMenuItem( - value: action, - child: MenuRow(text: action.getText(), icon: action.getIcon()), - )) - .toList(), - ); - if (selectedAction != null) { - switch (selectedAction) { - case AlbumAction.pin: - final pinnedFilters = settings.pinnedFilters..add(filter); - settings.pinnedFilters = pinnedFilters; - break; - case AlbumAction.unpin: - final pinnedFilters = settings.pinnedFilters..remove(filter); - settings.pinnedFilters = pinnedFilters; - break; - case AlbumAction.rename: - // TODO TLAD rename album - break; - default: - break; - } - } - } - // common with album selection page to move/copy entries static Map getAlbumEntries(CollectionSource source) { @@ -98,9 +71,9 @@ class AlbumListPage extends StatelessWidget { entriesByDate.firstWhere((entry) => entry.directory == album, orElse: () => null), )); final byPin = groupBy, bool>(allAlbumMapEntries, (e) => pinned.contains(e.key)); - final pinnedAlbumMapEntries = (byPin[true] ?? [])..sort(FilterNavigationPage.compareChipByDate); - final unpinnedAlbumMapEntries = (byPin[false] ?? [])..sort(FilterNavigationPage.compareChipByDate); - return Map.fromEntries([...pinnedAlbumMapEntries, ...unpinnedAlbumMapEntries]); + final pinnedMapEntries = (byPin[true] ?? [])..sort(FilterNavigationPage.compareChipByDate); + final unpinnedMapEntries = (byPin[false] ?? [])..sort(FilterNavigationPage.compareChipByDate); + return Map.fromEntries([...pinnedMapEntries, ...unpinnedMapEntries]); case ChipSortFactor.name: default: final pinnedAlbums = [], regularAlbums = [], appAlbums = [], specialAlbums = []; diff --git a/lib/widgets/filter_grids/common/chip_action_delegate.dart b/lib/widgets/filter_grids/common/chip_action_delegate.dart new file mode 100644 index 000000000..0a1af6670 --- /dev/null +++ b/lib/widgets/filter_grids/common/chip_action_delegate.dart @@ -0,0 +1,40 @@ +import 'package:aves/model/filters/filters.dart'; +import 'package:aves/model/settings/settings.dart'; +import 'package:aves/utils/durations.dart'; +import 'package:aves/widgets/filter_grids/common/chip_actions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +class ChipActionDelegate { + Future onActionSelected(BuildContext context, CollectionFilter filter, ChipAction action) async { + // wait for the popup menu to hide before proceeding with the action + await Future.delayed(Durations.popupMenuAnimation * timeDilation); + + switch (action) { + case ChipAction.pin: + final pinnedFilters = settings.pinnedFilters..add(filter); + settings.pinnedFilters = pinnedFilters; + break; + case ChipAction.unpin: + final pinnedFilters = settings.pinnedFilters..remove(filter); + settings.pinnedFilters = pinnedFilters; + break; + default: + break; + } + } +} + +class AlbumChipActionDelegate extends ChipActionDelegate { + @override + Future onActionSelected(BuildContext context, CollectionFilter filter, ChipAction action) async { + await super.onActionSelected(context, filter, action); + switch (action) { + case ChipAction.rename: + // TODO TLAD rename album + break; + default: + break; + } + } +} diff --git a/lib/widgets/filter_grids/chip_actions.dart b/lib/widgets/filter_grids/common/chip_actions.dart similarity index 64% rename from lib/widgets/filter_grids/chip_actions.dart rename to lib/widgets/filter_grids/common/chip_actions.dart index 0b0ae1de0..e26455793 100644 --- a/lib/widgets/filter_grids/chip_actions.dart +++ b/lib/widgets/filter_grids/common/chip_actions.dart @@ -1,24 +1,24 @@ import 'package:aves/widgets/common/icons.dart'; import 'package:flutter/widgets.dart'; -enum ChipAction { +enum ChipSetAction { sort, } -enum AlbumAction { +enum ChipAction { pin, unpin, rename, } -extension ExtraAlbumAction on AlbumAction { +extension ExtraChipAction on ChipAction { String getText() { switch (this) { - case AlbumAction.pin: + case ChipAction.pin: return 'Pin to top'; - case AlbumAction.unpin: + case ChipAction.unpin: return 'Unpin from top'; - case AlbumAction.rename: + case ChipAction.rename: return 'Rename'; } return null; @@ -26,10 +26,10 @@ extension ExtraAlbumAction on AlbumAction { IconData getIcon() { switch (this) { - case AlbumAction.pin: - case AlbumAction.unpin: + case ChipAction.pin: + case ChipAction.unpin: return AIcons.pin; - case AlbumAction.rename: + case ChipAction.rename: return AIcons.rename; } return null; diff --git a/lib/widgets/filter_grids/chip_action_delegate.dart b/lib/widgets/filter_grids/common/chip_set_action_delegate.dart similarity index 78% rename from lib/widgets/filter_grids/chip_action_delegate.dart rename to lib/widgets/filter_grids/common/chip_set_action_delegate.dart index 7eb741d7b..e053ee92a 100644 --- a/lib/widgets/filter_grids/chip_action_delegate.dart +++ b/lib/widgets/filter_grids/common/chip_set_action_delegate.dart @@ -2,21 +2,21 @@ import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/enums.dart'; import 'package:aves/utils/durations.dart'; import 'package:aves/widgets/common/aves_selection_dialog.dart'; -import 'package:aves/widgets/filter_grids/chip_actions.dart'; +import 'package:aves/widgets/filter_grids/common/chip_actions.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -abstract class ChipActionDelegate { +abstract class ChipSetActionDelegate { ChipSortFactor get sortFactor; set sortFactor(ChipSortFactor factor); - Future onChipActionSelected(BuildContext context, ChipAction action) async { + Future onActionSelected(BuildContext context, ChipSetAction action) async { // wait for the popup menu to hide before proceeding with the action await Future.delayed(Durations.popupMenuAnimation * timeDilation); switch (action) { - case ChipAction.sort: + case ChipSetAction.sort: await _showSortDialog(context); break; } @@ -40,7 +40,7 @@ abstract class ChipActionDelegate { } } -class AlbumChipActionDelegate extends ChipActionDelegate { +class AlbumChipSetActionDelegate extends ChipSetActionDelegate { @override ChipSortFactor get sortFactor => settings.albumSortFactor; @@ -48,7 +48,7 @@ class AlbumChipActionDelegate extends ChipActionDelegate { set sortFactor(ChipSortFactor factor) => settings.albumSortFactor = factor; } -class CountryChipActionDelegate extends ChipActionDelegate { +class CountryChipSetActionDelegate extends ChipSetActionDelegate { @override ChipSortFactor get sortFactor => settings.countrySortFactor; @@ -56,7 +56,7 @@ class CountryChipActionDelegate extends ChipActionDelegate { set sortFactor(ChipSortFactor factor) => settings.countrySortFactor = factor; } -class TagChipActionDelegate extends ChipActionDelegate { +class TagChipSetActionDelegate extends ChipSetActionDelegate { @override ChipSortFactor get sortFactor => settings.tagSortFactor; diff --git a/lib/widgets/filter_grids/decorated_filter_chip.dart b/lib/widgets/filter_grids/common/decorated_filter_chip.dart similarity index 97% rename from lib/widgets/filter_grids/decorated_filter_chip.dart rename to lib/widgets/filter_grids/common/decorated_filter_chip.dart index 228650c2d..8103d2c68 100644 --- a/lib/widgets/filter_grids/decorated_filter_chip.dart +++ b/lib/widgets/filter_grids/common/decorated_filter_chip.dart @@ -9,7 +9,7 @@ import 'package:aves/widgets/collection/thumbnail/raster.dart'; import 'package:aves/widgets/collection/thumbnail/vector.dart'; import 'package:aves/widgets/common/aves_filter_chip.dart'; import 'package:aves/widgets/common/icons.dart'; -import 'package:aves/widgets/filter_grids/filter_grid_page.dart'; +import 'package:aves/widgets/filter_grids/common/filter_grid_page.dart'; import 'package:flutter/material.dart'; class DecoratedFilterChip extends StatelessWidget { diff --git a/lib/widgets/filter_grids/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart similarity index 82% rename from lib/widgets/filter_grids/filter_grid_page.dart rename to lib/widgets/filter_grids/common/filter_grid_page.dart index 908eaa6ee..64f2ee751 100644 --- a/lib/widgets/filter_grids/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -15,33 +15,37 @@ import 'package:aves/widgets/common/data_providers/media_query_data_provider.dar import 'package:aves/widgets/common/double_back_pop.dart'; import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/common/menu_row.dart'; +import 'package:aves/widgets/common/search_button.dart'; import 'package:aves/widgets/drawer/app_drawer.dart'; -import 'package:aves/widgets/filter_grids/chip_action_delegate.dart'; -import 'package:aves/widgets/filter_grids/chip_actions.dart'; -import 'package:aves/widgets/filter_grids/decorated_filter_chip.dart'; -import 'package:aves/widgets/filter_grids/search_button.dart'; +import 'package:aves/widgets/filter_grids/common/chip_action_delegate.dart'; +import 'package:aves/widgets/filter_grids/common/chip_actions.dart'; +import 'package:aves/widgets/filter_grids/common/chip_set_action_delegate.dart'; +import 'package:aves/widgets/filter_grids/common/decorated_filter_chip.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:pedantic/pedantic.dart'; import 'package:provider/provider.dart'; class FilterNavigationPage extends StatelessWidget { final CollectionSource source; final String title; - final ChipActionDelegate actionDelegate; + final ChipSetActionDelegate chipSetActionDelegate; + final ChipActionDelegate chipActionDelegate; final Map filterEntries; final CollectionFilter Function(String key) filterBuilder; final Widget Function() emptyBuilder; - final OffsetFilterCallback onLongPress; + final List Function(CollectionFilter filter) chipActionsBuilder; const FilterNavigationPage({ @required this.source, @required this.title, - @required this.actionDelegate, + @required this.chipSetActionDelegate, + @required this.chipActionDelegate, + @required this.chipActionsBuilder, @required this.filterEntries, @required this.filterBuilder, @required this.emptyBuilder, - this.onLongPress, }); @override @@ -81,25 +85,43 @@ class FilterNavigationPage extends StatelessWidget { ), settings.navRemoveRoutePredicate(CollectionPage.routeName), ), - onLongPress: onLongPress, + onLongPress: (filter, tapPosition) => _showMenu(context, filter, tapPosition), ); } + Future _showMenu(BuildContext context, CollectionFilter filter, Offset tapPosition) async { + final RenderBox overlay = Overlay.of(context).context.findRenderObject(); + final touchArea = Size(40, 40); + final selectedAction = await showMenu( + context: context, + position: RelativeRect.fromRect(tapPosition & touchArea, Offset.zero & overlay.size), + items: chipActionsBuilder(filter) + .map((action) => PopupMenuItem( + value: action, + child: MenuRow(text: action.getText(), icon: action.getIcon()), + )) + .toList(), + ); + if (selectedAction != null) { + unawaited(chipActionDelegate.onActionSelected(context, filter, selectedAction)); + } + } + List _buildActions(BuildContext context) { return [ SearchButton(source), - PopupMenuButton( + PopupMenuButton( key: Key('appbar-menu-button'), itemBuilder: (context) { return [ PopupMenuItem( key: Key('menu-sort'), - value: ChipAction.sort, + value: ChipSetAction.sort, child: MenuRow(text: 'Sort...', icon: AIcons.sort), ), ]; }, - onSelected: (action) => actionDelegate.onChipActionSelected(context, action), + onSelected: (action) => chipSetActionDelegate.onActionSelected(context, action), ), ]; } diff --git a/lib/widgets/filter_grids/countries_page.dart b/lib/widgets/filter_grids/countries_page.dart index 8a2362a74..ad505976f 100644 --- a/lib/widgets/filter_grids/countries_page.dart +++ b/lib/widgets/filter_grids/countries_page.dart @@ -1,3 +1,4 @@ +import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/location.dart'; import 'package:aves/model/image_entry.dart'; import 'package:aves/model/settings/settings.dart'; @@ -6,31 +7,40 @@ import 'package:aves/model/source/enums.dart'; import 'package:aves/model/source/location.dart'; import 'package:aves/widgets/collection/empty.dart'; import 'package:aves/widgets/common/icons.dart'; -import 'package:aves/widgets/filter_grids/chip_action_delegate.dart'; -import 'package:aves/widgets/filter_grids/filter_grid_page.dart'; +import 'package:aves/widgets/filter_grids/common/chip_action_delegate.dart'; +import 'package:aves/widgets/filter_grids/common/chip_actions.dart'; +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:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:tuple/tuple.dart'; class CountryListPage extends StatelessWidget { static const routeName = '/countries'; final CollectionSource source; - static final ChipActionDelegate actionDelegate = CountryChipActionDelegate(); + static final ChipSetActionDelegate setActionDelegate = CountryChipSetActionDelegate(); + static final ChipActionDelegate actionDelegate = ChipActionDelegate(); const CountryListPage({@required this.source}); @override Widget build(BuildContext context) { - return Selector( - selector: (context, s) => s.countrySortFactor, - builder: (context, sortFactor, child) { + return Selector>>( + selector: (context, s) => Tuple2(s.countrySortFactor, s.pinnedFilters), + builder: (context, s, child) { return StreamBuilder( stream: source.eventBus.on(), builder: (context, snapshot) => FilterNavigationPage( source: source, title: 'Countries', - actionDelegate: actionDelegate, + chipSetActionDelegate: setActionDelegate, + chipActionDelegate: actionDelegate, + chipActionsBuilder: (filter) => [ + settings.pinnedFilters.contains(filter) ? ChipAction.unpin : ChipAction.pin, + ], filterEntries: _getCountryEntries(), filterBuilder: (s) => LocationFilter(LocationLevel.country, s), emptyBuilder: () => EmptyContent( @@ -44,9 +54,11 @@ class CountryListPage extends StatelessWidget { } Map _getCountryEntries() { + final pinned = settings.pinnedFilters.whereType().map((f) => f.countryNameAndCode); + final entriesByDate = source.sortedEntriesForFilterList; final locatedEntries = entriesByDate.where((entry) => entry.isLocated); - final countries = source.sortedCountries.map((countryNameAndCode) { + final allMapEntries = source.sortedCountries.map((countryNameAndCode) { final split = countryNameAndCode.split(LocationFilter.locationSeparator); ImageEntry entry; if (split.length > 1) { @@ -56,12 +68,19 @@ class CountryListPage extends StatelessWidget { 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: - countries.sort(FilterNavigationPage.compareChipByDate); + pinnedMapEntries.sort(FilterNavigationPage.compareChipByDate); + unpinnedMapEntries.sort(FilterNavigationPage.compareChipByDate); break; case ChipSortFactor.name: + // already sorted by name at the source level + break; } - return Map.fromEntries(countries); + return Map.fromEntries([...pinnedMapEntries, ...unpinnedMapEntries]); } } diff --git a/lib/widgets/filter_grids/tags_page.dart b/lib/widgets/filter_grids/tags_page.dart index 90533f253..af9179d83 100644 --- a/lib/widgets/filter_grids/tags_page.dart +++ b/lib/widgets/filter_grids/tags_page.dart @@ -1,3 +1,4 @@ +import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/image_entry.dart'; import 'package:aves/model/settings/settings.dart'; @@ -6,31 +7,40 @@ import 'package:aves/model/source/enums.dart'; import 'package:aves/model/source/tag.dart'; import 'package:aves/widgets/collection/empty.dart'; import 'package:aves/widgets/common/icons.dart'; -import 'package:aves/widgets/filter_grids/chip_action_delegate.dart'; -import 'package:aves/widgets/filter_grids/filter_grid_page.dart'; +import 'package:aves/widgets/filter_grids/common/chip_action_delegate.dart'; +import 'package:aves/widgets/filter_grids/common/chip_actions.dart'; +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:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:tuple/tuple.dart'; class TagListPage extends StatelessWidget { static const routeName = '/tags'; final CollectionSource source; - static final ChipActionDelegate actionDelegate = TagChipActionDelegate(); + static final ChipSetActionDelegate setActionDelegate = TagChipSetActionDelegate(); + static final ChipActionDelegate actionDelegate = ChipActionDelegate(); const TagListPage({@required this.source}); @override Widget build(BuildContext context) { - return Selector( - selector: (context, s) => s.tagSortFactor, - builder: (context, sortFactor, child) { + return Selector>>( + selector: (context, s) => Tuple2(s.tagSortFactor, s.pinnedFilters), + builder: (context, s, child) { return StreamBuilder( stream: source.eventBus.on(), builder: (context, snapshot) => FilterNavigationPage( source: source, title: 'Tags', - actionDelegate: actionDelegate, + chipSetActionDelegate: setActionDelegate, + chipActionDelegate: actionDelegate, + chipActionsBuilder: (filter) => [ + settings.pinnedFilters.contains(filter) ? ChipAction.unpin : ChipAction.pin, + ], filterEntries: _getTagEntries(), filterBuilder: (s) => TagFilter(s), emptyBuilder: () => EmptyContent( @@ -44,20 +54,29 @@ class TagListPage extends StatelessWidget { } Map _getTagEntries() { + final pinned = settings.pinnedFilters.whereType().map((f) => f.tag); + final entriesByDate = source.sortedEntriesForFilterList; - final tags = source.sortedTags + final allMapEntries = source.sortedTags .map((tag) => MapEntry( tag, entriesByDate.firstWhere((entry) => entry.xmpSubjects.contains(tag), orElse: () => null), )) .toList(); + final byPin = groupBy, bool>(allMapEntries, (e) => pinned.contains(e.key)); + final pinnedMapEntries = (byPin[true] ?? []); + final unpinnedMapEntries = (byPin[false] ?? []); + switch (settings.tagSortFactor) { case ChipSortFactor.date: - tags.sort(FilterNavigationPage.compareChipByDate); + pinnedMapEntries.sort(FilterNavigationPage.compareChipByDate); + unpinnedMapEntries.sort(FilterNavigationPage.compareChipByDate); break; case ChipSortFactor.name: + // already sorted by name at the source level + break; } - return Map.fromEntries(tags); + return Map.fromEntries([...pinnedMapEntries, ...unpinnedMapEntries]); } }