132 lines
3.9 KiB
Dart
132 lines
3.9 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../../../core/models/marketplace.dart';
|
|
import '../../../core/services/marketplace_service.dart';
|
|
|
|
final marketplaceServiceProvider = Provider((_) => MarketplaceService());
|
|
|
|
final citiesProvider = FutureProvider<List<String>>((ref) {
|
|
return ref.read(marketplaceServiceProvider).getCities();
|
|
});
|
|
|
|
final offersProvider = FutureProvider<List<MarketplaceOffer>>((ref) {
|
|
return ref.read(marketplaceServiceProvider).getOffers();
|
|
});
|
|
|
|
class SearchParams {
|
|
final String? city;
|
|
final DateTime? startDate;
|
|
final DateTime? endDate;
|
|
final Set<String>? categories; // null = all; multi-select
|
|
final String? transmission;
|
|
final int? maxPriceCents;
|
|
final int? minPriceCents; // client-side filter only
|
|
final int page;
|
|
|
|
const SearchParams({
|
|
this.city,
|
|
this.startDate,
|
|
this.endDate,
|
|
this.categories,
|
|
this.transmission,
|
|
this.maxPriceCents,
|
|
this.minPriceCents,
|
|
this.page = 1,
|
|
});
|
|
|
|
bool get hasDateRange => startDate != null && endDate != null;
|
|
|
|
bool get hasFilters =>
|
|
city != null ||
|
|
(categories != null && categories!.isNotEmpty) ||
|
|
transmission != null ||
|
|
maxPriceCents != null ||
|
|
minPriceCents != null ||
|
|
hasDateRange;
|
|
|
|
SearchParams copyWith({
|
|
Object? city = _sentinel,
|
|
DateTime? startDate,
|
|
DateTime? endDate,
|
|
Object? categories = _sentinel,
|
|
Object? transmission = _sentinel,
|
|
Object? maxPriceCents = _sentinel,
|
|
Object? minPriceCents = _sentinel,
|
|
int? page,
|
|
bool clearDates = false,
|
|
}) =>
|
|
SearchParams(
|
|
city: city == _sentinel ? this.city : city as String?,
|
|
startDate: clearDates ? null : (startDate ?? this.startDate),
|
|
endDate: clearDates ? null : (endDate ?? this.endDate),
|
|
categories: categories == _sentinel
|
|
? this.categories
|
|
: categories as Set<String>?,
|
|
transmission: transmission == _sentinel
|
|
? this.transmission
|
|
: transmission as String?,
|
|
maxPriceCents: maxPriceCents == _sentinel
|
|
? this.maxPriceCents
|
|
: maxPriceCents as int?,
|
|
minPriceCents: minPriceCents == _sentinel
|
|
? this.minPriceCents
|
|
: minPriceCents as int?,
|
|
page: page ?? this.page,
|
|
);
|
|
|
|
static bool _setsEqual(Set<String>? a, Set<String>? b) {
|
|
if (a == null && b == null) return true;
|
|
if (a == null || b == null) return false;
|
|
if (a.length != b.length) return false;
|
|
return a.containsAll(b);
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
other is SearchParams &&
|
|
other.city == city &&
|
|
other.startDate == startDate &&
|
|
other.endDate == endDate &&
|
|
_setsEqual(other.categories, categories) &&
|
|
other.transmission == transmission &&
|
|
other.maxPriceCents == maxPriceCents &&
|
|
other.minPriceCents == minPriceCents &&
|
|
other.page == page;
|
|
|
|
@override
|
|
int get hashCode {
|
|
final catKey = categories == null
|
|
? null
|
|
: (categories!.toList()..sort()).join(',');
|
|
return Object.hash(city, startDate, endDate, catKey, transmission,
|
|
maxPriceCents, minPriceCents, page);
|
|
}
|
|
}
|
|
|
|
const _sentinel = Object();
|
|
|
|
final searchParamsProvider = StateProvider<SearchParams>((_) {
|
|
final now = DateTime.now();
|
|
return SearchParams(
|
|
startDate: now,
|
|
endDate: now.add(const Duration(days: 1)),
|
|
);
|
|
});
|
|
|
|
final vehicleSearchProvider =
|
|
FutureProvider.family<MarketplaceSearchResult, SearchParams>(
|
|
(ref, params) {
|
|
// Only send a single category to the API; multi-category is filtered client-side
|
|
final apiCategory =
|
|
params.categories?.length == 1 ? params.categories!.first : null;
|
|
|
|
return ref.read(marketplaceServiceProvider).search(
|
|
city: params.city,
|
|
startDate: params.startDate,
|
|
endDate: params.endDate,
|
|
category: apiCategory,
|
|
transmission: params.transmission,
|
|
maxPriceCents: params.maxPriceCents,
|
|
page: params.page,
|
|
);
|
|
});
|