feet/lib/api/cache.dart
Open Source Contributor afa1127125 Add group dropdown to post filter dialog
Users can now restrict the Feed/Unread/Saved post lists to a specific
Fever group from the filter dialog. The app already fetched and cached
groups and feeds_groups, but nothing in the UI ever exposed them, so
posts could only be filtered by individually selected feeds.

- Add APICache.feedsInGroup() to resolve a group to the feeds that
  belong to it, skipping feed ids that no longer exist in the cache
- Add an "All groups"/group dropdown to FilterMenu; choosing a group
  limits the feed checklist to that group's feeds, and the dialog now
  reports the selected group together with the selected feeds
- Wire a new _groupFilter into the post filtering (ANDed with the feed
  filter), surface the active group in the app-bar entry, and reset it
  on a full refresh
- Cover the new behavior with unit tests for feedsInGroup() and widget
  tests for the group dropdown

Closes #16

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 14:43:50 +00:00

82 lines
2 KiB
Dart

part of fever_api;
/// Caches items, feeds and groups
class APICache {
final FeverAPI api;
late final ItemCache items;
late final ObjectCache<int, Feed> feeds;
late final ObjectCache<int, Group> groups;
late final ObjectCache<int, Favicon> favicons;
final List<FeedsGroup> feedsGroups = [];
APICache(this.api) {
items = ItemCache(api);
feeds = ObjectCache(api);
groups = ObjectCache(api);
favicons = ObjectCache(api);
}
/// Returns the ids of all feeds that belong to the given group.
///
/// Falls back to an empty list if the group exists but no feeds have been
/// fetched yet. Feeds that do not exist in the cache (deleted or not fetched
/// yet) are skipped, so the result never contains stale feed ids.
List<int> feedsInGroup(int groupId) {
final feedIds = <int>{};
for (final feedsGroup in feedsGroups) {
if (feedsGroup.groupId != groupId) continue;
for (final feedId in feedsGroup.feedIds) {
if (feeds.get(feedId) != null) feedIds.add(feedId);
}
}
return feedIds.toList();
}
void clear() {
items._items.clear();
feeds._items.clear();
groups._items.clear();
favicons._items.clear();
feedsGroups.clear();
}
}
class ObjectCache<K, T> {
final FeverAPI api;
final Map<K, T> _items = {};
ObjectCache(this.api);
/// Returns an item from the cache, or null if it doesn't exist
T? get(K key) {
return _items.containsKey(key) ? _items[key] : null;
}
/// Returns a copy of the cache content
Map<K, T> getAll() {
return {..._items};
}
void set(K key, T value) {
_items[key] = value;
}
int get size => _items.length;
}
class ItemCache extends ObjectCache<int, Item> {
/// Get all items for a given feed
List<Item> getForFeed(int feed) {
if (api.cache.feeds.get(feed) == null) {
throw Exception('Feed $feed doesn\'t exist');
}
return _items.values.where((item) => item.feedId == feed).toList();
}
ItemCache(FeverAPI api) : super(api);
}