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:
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,9 +19,15 @@ struct StationsView: View {
|
|||||||
NavigationStack {
|
NavigationStack {
|
||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
Text("\(sortMode == .closest ? "Closest" : "Cheapest") \(selectedFuel.displayName) — tap a station for directions.")
|
if let location {
|
||||||
.font(.footnote)
|
Text("\(sortMode == .closest ? "Closest" : "Cheapest") \(selectedFuel.displayName) within \(Int(radiusKM)) km — tap a station for directions.")
|
||||||
.foregroundStyle(.secondary)
|
.font(.footnote)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
} else {
|
||||||
|
Text("\(sortMode == .closest ? "Closest" : "Cheapest") \(selectedFuel.displayName) — tap a station for directions.")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section("Fuel type") {
|
Section("Fuel type") {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user