Scope cheapest/RAG/TOP to alert trigger radius in app + widget; header shows 'within X km'; falls back to full fetch when radius pool empty

This commit is contained in:
FuelBoard Contributor
2026-08-11 17:21:36 +01:00
parent cde51622a3
commit 2d95c4e1d6
3 changed files with 34 additions and 6 deletions
+15 -2
View File
@@ -21,8 +21,20 @@ struct ContentView: View {
@State private var locationManager = LocationManager() @State private var locationManager = LocationManager()
@StateObject private var monitor = ProximityMonitor() @StateObject private var monitor = ProximityMonitor()
/// Stations within the alert trigger radius of the current location, used
/// as the pool for "cheapest" (RAG reference, TOP, sorting). Falls back to
/// the full fetch when location is unknown or the radius pool is empty
/// (rural areas) so the list is never blank.
private var radiusScopedStations: [FuelStation] {
guard let location else { return stations }
let within = stations.filter {
$0.distanceKM(to: location.lat, lng2: location.lng) <= alertsRadius
}
return within.isEmpty ? stations : within
}
private var cheapestPrice: Double? { private var cheapestPrice: Double? {
stations.compactMap { $0.prices[selectedFuel] }.min() radiusScopedStations.compactMap { $0.prices[selectedFuel] }.min()
} }
private var displayedStations: [FuelStation] { private var displayedStations: [FuelStation] {
@@ -30,7 +42,7 @@ struct ContentView: View {
} }
private var sortedStations: [FuelStation] { private var sortedStations: [FuelStation] {
let available = stations.filter { $0.prices[selectedFuel] != nil } let available = radiusScopedStations.filter { $0.prices[selectedFuel] != nil }
switch sortMode { switch sortMode {
case .closest: case .closest:
guard let location else { return available.sorted { $0.prices[selectedFuel]! < $1.prices[selectedFuel]! } } guard let location else { return available.sorted { $0.prices[selectedFuel]! < $1.prices[selectedFuel]! } }
@@ -73,6 +85,7 @@ struct ContentView: View {
stationLimit: $stationLimit, stationLimit: $stationLimit,
cheapestPrice: cheapestPrice, cheapestPrice: cheapestPrice,
location: location, location: location,
radiusKM: alertsRadius,
favouriteIDs: favouriteIDs, favouriteIDs: favouriteIDs,
onToggleFavourite: toggleFavourite onToggleFavourite: toggleFavourite
) )
+7
View File
@@ -11,6 +11,7 @@ struct StationsView: View {
@Binding var stationLimit: Int @Binding var stationLimit: Int
let cheapestPrice: Double? let cheapestPrice: Double?
let location: Coordinate? let location: Coordinate?
let radiusKM: Double
let favouriteIDs: Set<String> let favouriteIDs: Set<String>
var onToggleFavourite: (FuelStation) -> Void = { _ in } var onToggleFavourite: (FuelStation) -> Void = { _ in }
@@ -18,10 +19,16 @@ struct StationsView: View {
NavigationStack { NavigationStack {
List { List {
Section { Section {
if let location {
Text("\(sortMode == .closest ? "Closest" : "Cheapest") \(selectedFuel.displayName) within \(Int(radiusKM)) km — tap a station for directions.")
.font(.footnote)
.foregroundStyle(.secondary)
} else {
Text("\(sortMode == .closest ? "Closest" : "Cheapest") \(selectedFuel.displayName) — tap a station for directions.") Text("\(sortMode == .closest ? "Closest" : "Cheapest") \(selectedFuel.displayName) — tap a station for directions.")
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
}
Section("Fuel type") { Section("Fuel type") {
Picker("Fuel type", selection: $selectedFuel) { Picker("Fuel type", selection: $selectedFuel) {
+9 -1
View File
@@ -84,9 +84,17 @@ struct FuelPriceTimelineProvider: TimelineProvider {
FuelStore.saveLocation(lat: location.lat, lng: location.lng) FuelStore.saveLocation(lat: location.lat, lng: location.lng)
} }
// 3) Load stations, then sort by distance-weighted score. // 3) Load stations, then sort by price (distance tiebreak), scoped to
// the alert trigger radius so the widget's "cheapest" matches the app.
var stations = FuelStore.loadStations() var stations = FuelStore.loadStations()
if stations.isEmpty { stations = SampleFuelProvider.sampleStations } if stations.isEmpty { stations = SampleFuelProvider.sampleStations }
let radiusKM = FuelStore.loadAlertsRadius()
if let location {
let within = stations.filter {
$0.distanceKM(to: location.lat, lng2: location.lng) <= radiusKM
}
if !within.isEmpty { stations = within }
}
let filtered = stations.filter { $0.prices[fuel] != nil } let filtered = stations.filter { $0.prices[fuel] != nil }
let sorted: [FuelStation] let sorted: [FuelStation]
if let location { if let location {