feat(market): search + pull-to-refresh in the market list
Match the inventory patterns the market was missing: - Search: OffersState gains a `query` + `visibleOffers` getter and OffersCubit.search() filters the already-discovered offers locally by summary (case-insensitive), never re-hitting the transport. The filter survives a refresh. Rounded search field mirrors inventory's. - Pull-to-refresh: RefreshIndicator re-runs discovery for the current area (empty state included, so a swipe re-scans). Text filter kept. - i18n: market.searchHint / market.noMatches (en/es/pt). Note: the shared slang output (strings*.g.dart, incl. the generated Asturian locale) is regenerated wholesale, so it also carries the in-progress `ast` locale keys already present in the working tree.
This commit is contained in:
parent
bd18978aff
commit
beb29915d8
11 changed files with 1614 additions and 21 deletions
|
|
@ -16,6 +16,7 @@ class OffersState extends Equatable {
|
|||
const OffersState({
|
||||
this.offers = const [],
|
||||
this.areaGeohash = '',
|
||||
this.query = '',
|
||||
this.searching = false,
|
||||
this.publishing = false,
|
||||
this.hasSearched = false,
|
||||
|
|
@ -28,6 +29,9 @@ class OffersState extends Equatable {
|
|||
/// The coarse area currently being browsed.
|
||||
final String areaGeohash;
|
||||
|
||||
/// Free-text filter over [offers] typed in the search box (empty = show all).
|
||||
final String query;
|
||||
|
||||
final bool searching;
|
||||
final bool publishing;
|
||||
|
||||
|
|
@ -38,9 +42,18 @@ class OffersState extends Equatable {
|
|||
/// Last error, in human terms for the UI (null when fine).
|
||||
final String? error;
|
||||
|
||||
/// [offers] narrowed by [query], matched against the offer summary. Kept
|
||||
/// separate from [offers] so a search never drops the underlying discoveries.
|
||||
List<Offer> get visibleOffers {
|
||||
final q = query.trim().toLowerCase();
|
||||
if (q.isEmpty) return offers;
|
||||
return offers.where((o) => o.summary.toLowerCase().contains(q)).toList();
|
||||
}
|
||||
|
||||
OffersState copyWith({
|
||||
List<Offer>? offers,
|
||||
String? areaGeohash,
|
||||
String? query,
|
||||
bool? searching,
|
||||
bool? publishing,
|
||||
bool? hasSearched,
|
||||
|
|
@ -49,6 +62,7 @@ class OffersState extends Equatable {
|
|||
return OffersState(
|
||||
offers: offers ?? this.offers,
|
||||
areaGeohash: areaGeohash ?? this.areaGeohash,
|
||||
query: query ?? this.query,
|
||||
searching: searching ?? this.searching,
|
||||
publishing: publishing ?? this.publishing,
|
||||
hasSearched: hasSearched ?? this.hasSearched,
|
||||
|
|
@ -58,7 +72,7 @@ class OffersState extends Equatable {
|
|||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[offers, areaGeohash, searching, publishing, hasSearched, error];
|
||||
[offers, areaGeohash, query, searching, publishing, hasSearched, error];
|
||||
}
|
||||
|
||||
/// Drives offer discovery and publishing over an [OfferTransport]. Depends on
|
||||
|
|
@ -92,6 +106,7 @@ class OffersCubit extends Cubit<OffersState> {
|
|||
_searchTimeout?.cancel();
|
||||
emit(OffersState(
|
||||
areaGeohash: geohashPrefix,
|
||||
query: state.query, // keep the text filter across a refresh
|
||||
searching: true,
|
||||
hasSearched: true,
|
||||
));
|
||||
|
|
@ -111,6 +126,10 @@ class OffersCubit extends Cubit<OffersState> {
|
|||
});
|
||||
}
|
||||
|
||||
/// Narrows the visible offers to those whose summary matches [query]. Purely
|
||||
/// local over the already-discovered list; does not re-hit the transport.
|
||||
void search(String query) => emit(state.copyWith(query: query));
|
||||
|
||||
/// Publishes [offer]; returns the transport's verdict. No-op result when
|
||||
/// offline.
|
||||
Future<PublishResult> publish(Offer offer) async {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue