feat(social): add SavedSearch model with shared offer matcher

Extract the market's query/type/category/organic filter chain into
SavedSearch.matchesFilters so live results and saved-search alerts can
never disagree. OffersState.visibleOffers now delegates to it.
This commit is contained in:
vjrj 2026-07-17 12:35:56 +02:00
parent dd9c97a57a
commit 5b1066a224
4 changed files with 251 additions and 9 deletions

View file

@ -69,20 +69,21 @@ class OffersState extends Equatable {
/// [offers] narrowed by the text [query] and the chip filters (all ANDed).
/// Kept separate from [offers] so filtering never drops the discoveries.
List<Offer> get visibleOffers {
final q = query.trim().toLowerCase();
return offers.where((o) {
// Moderation state (block/hide) is app-side, not part of the search facets.
if (blockedAuthors.contains(o.authorPubkeyHex)) return false;
if (hiddenOfferKeys.contains('${o.authorPubkeyHex}:${o.id}')) {
return false;
}
if (q.isNotEmpty && !o.summary.toLowerCase().contains(q)) return false;
if (typeFilter.isNotEmpty && !typeFilter.contains(o.type)) return false;
if (categoryFilter.isNotEmpty &&
(o.category == null || !categoryFilter.contains(o.category))) {
return false;
}
if (organicOnly && !o.isOrganic) return false;
return true;
// The query/facet chain is shared with saved-search alerts so the two
// never disagree on what "matches".
return SavedSearch.matchesFilters(
o,
query: query,
types: typeFilter,
categories: categoryFilter,
organicOnly: organicOnly,
);
}).toList();
}