Separate browsing filters from review radius
All checks were successful
Build and deploy Flutter Web / build (push) Successful in 4m2s

This commit is contained in:
Ruslan Bakiev
2026-05-14 22:34:59 +07:00
parent 0b1493a02e
commit 697f029ad2
2 changed files with 55 additions and 49 deletions

View File

@@ -8,6 +8,8 @@ import '../models/place_models.dart';
final placeControllerProvider =
AsyncNotifierProvider<PlaceController, PlaceState>(PlaceController.new);
const _unset = Object();
class PlaceState {
const PlaceState({
required this.selectedTrait,
@@ -20,9 +22,8 @@ class PlaceState {
});
static final _distance = Distance();
static const nearbyRecommendationRadiusMeters = 500.0;
final PlaceTrait selectedTrait;
final PlaceTrait? selectedTrait;
final List<PlaceRecommendation> places;
final String? selectedPlaceId;
final AppUser? currentUser;
@@ -31,55 +32,52 @@ class PlaceState {
final VoiceReviewDraft reviewDraft;
List<PlaceRecommendation> get recommendations {
final selected = selectedTrait;
final matchingPlaces = selected == null
? places
: places.where((place) => place.traits.contains(selected));
final coordinate = userCoordinate;
if (coordinate == null) {
return const [];
return matchingPlaces.toList();
}
final nearbyPlaces = places.where((place) {
return _distance(coordinate, place.coordinate) <=
nearbyRecommendationRadiusMeters;
});
final ranked = [...nearbyPlaces]
final ranked = [...matchingPlaces]
..sort((a, b) {
final aScore = a.traits.contains(selectedTrait) ? 1 : 0;
final bScore = b.traits.contains(selectedTrait) ? 1 : 0;
final scoreOrder = bScore.compareTo(aScore);
if (scoreOrder != 0) {
return scoreOrder;
}
return _distance(
coordinate,
a.coordinate,
).compareTo(_distance(coordinate, b.coordinate));
});
return ranked.take(4).toList();
return ranked;
}
PlaceRecommendation? get selectedPlace {
for (final place in places) {
final visiblePlaces = recommendations;
for (final place in visiblePlaces) {
if (place.id == selectedPlaceId) {
return place;
}
}
return recommendations.isEmpty ? null : recommendations.first;
return visiblePlaces.isEmpty ? null : visiblePlaces.first;
}
PlaceState copyWith({
PlaceTrait? selectedTrait,
Object? selectedTrait = _unset,
List<PlaceRecommendation>? places,
String? selectedPlaceId,
Object? selectedPlaceId = _unset,
AppUser? currentUser,
bool? hasTelegramAuth,
LatLng? userCoordinate,
VoiceReviewDraft? reviewDraft,
}) {
return PlaceState(
selectedTrait: selectedTrait ?? this.selectedTrait,
selectedTrait: identical(selectedTrait, _unset)
? this.selectedTrait
: selectedTrait as PlaceTrait?,
places: places ?? this.places,
selectedPlaceId: selectedPlaceId ?? this.selectedPlaceId,
selectedPlaceId: identical(selectedPlaceId, _unset)
? this.selectedPlaceId
: selectedPlaceId as String?,
currentUser: currentUser ?? this.currentUser,
hasTelegramAuth: hasTelegramAuth ?? this.hasTelegramAuth,
userCoordinate: userCoordinate ?? this.userCoordinate,
@@ -96,7 +94,7 @@ class PlaceController extends AsyncNotifier<PlaceState> {
Future<PlaceState> build() async {
if (!_api.hasTelegramAuth) {
return const PlaceState(
selectedTrait: PlaceTrait.calm,
selectedTrait: null,
places: [],
selectedPlaceId: null,
currentUser: null,
@@ -115,7 +113,7 @@ class PlaceController extends AsyncNotifier<PlaceState> {
final userCoordinate = await _location.resolve();
final places = await _api.fetchPlaces();
return PlaceState(
selectedTrait: PlaceTrait.calm,
selectedTrait: null,
places: places,
selectedPlaceId: places.isEmpty ? null : places.first.id,
currentUser: currentUser,
@@ -144,6 +142,14 @@ class PlaceController extends AsyncNotifier<PlaceState> {
);
}
void clearTrait() {
final value = state.requireValue;
final selectedPlaceId = value.places.isEmpty ? null : value.places.first.id;
state = AsyncData(
value.copyWith(selectedTrait: null, selectedPlaceId: selectedPlaceId),
);
}
void selectPlace(String placeId) {
final value = state.requireValue;
state = AsyncData(value.copyWith(selectedPlaceId: placeId));