diff --git a/FuelBoard/ContentView.swift b/FuelBoard/ContentView.swift index eb79eb1..a979e88 100644 --- a/FuelBoard/ContentView.swift +++ b/FuelBoard/ContentView.swift @@ -39,7 +39,9 @@ struct ContentView: View { } private var displayedStations: [FuelStation] { - Array(sortedStations.prefix(stationLimit)) + // Relay already returns every station within the selected miles radius + // (sorted nearest-first); no local cap needed. + sortedStations } private var sortedStations: [FuelStation] { @@ -145,6 +147,10 @@ struct ContentView: View { // No re-fetch needed — one response carries E5/E10/DIESEL prices. WidgetCenter.shared.reloadAllTimelines() } + .onChange(of: stationLimit) { _, _ in + // Search radius changed (miles) — cached stations may not cover it. + Task { await refresh(force: true) } + } .onChange(of: alertsEnabled) { _, newValue in FuelStore.saveAlertsEnabled(newValue) monitor.setEnabled(newValue) @@ -182,7 +188,11 @@ struct ContentView: View { isLoading = true defer { isLoading = false } do { - let fetched = try await FuelPriceProvider.active.fetchStations(near: location?.lat, lng: location?.lng, fuel: selectedFuel) + let fetched = try await FuelPriceProvider.active.fetchStations( + near: location?.lat, lng: location?.lng, + fuel: selectedFuel, + radiusKM: Double(stationLimit) * 1.60934 // miles → km + ) stations = fetched FuelStore.saveStations(fetched) FuelStore.saveLastRefresh() diff --git a/FuelBoard/StationsView.swift b/FuelBoard/StationsView.swift index 17dd28e..5dc963d 100644 --- a/FuelBoard/StationsView.swift +++ b/FuelBoard/StationsView.swift @@ -80,9 +80,9 @@ struct StationsView: View { if !stations.isEmpty { Divider() - Picker("Show", selection: $stationLimit) { - ForEach([10, 25, 50, 75, 100], id: \.self) { count in - Text("\(count)").tag(count) + Picker("Within", selection: $stationLimit) { + ForEach(FuelStore.stationRadiusOptions, id: \.self) { miles in + Text("\(miles) miles").tag(miles) } } .pickerStyle(.segmented) @@ -90,7 +90,7 @@ struct StationsView: View { FuelStore.saveStationLimit(newValue) } .padding(.vertical, 2) - Text("Showing \(stations.count) of \(totalCount) stations") + Text("\(totalCount) stations within \(stationLimit) miles") .font(.caption2) .foregroundStyle(.secondary) } diff --git a/Shared/FuelPriceProvider.swift b/Shared/FuelPriceProvider.swift index 090d2fb..586497a 100644 --- a/Shared/FuelPriceProvider.swift +++ b/Shared/FuelPriceProvider.swift @@ -13,8 +13,9 @@ import Foundation protocol FuelPriceProviding { /// Fetch stations with prices. `location` may be nil (sort by price only). - /// Throws on failure so callers can fall back to cached/sample data. - func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType) async throws -> [FuelStation] + /// `radiusKM` bounds the search area (used by the relay). Throws on failure + /// so callers can fall back to cached/sample data. + func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType, radiusKM: Double) async throws -> [FuelStation] } enum FuelPriceProvider { @@ -31,15 +32,15 @@ enum FuelPriceProvider { struct RelayFuelProvider: FuelPriceProviding { var baseURL = URL(string: "http://192.168.1.131:8788")! - func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType) async throws -> [FuelStation] { + func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType, radiusKM: Double) async throws -> [FuelStation] { var components = URLComponents(url: baseURL.appendingPathComponent("api/v1/stations"), resolvingAgainstBaseURL: false)! var query: [URLQueryItem] = [URLQueryItem(name: "fuel", value: fuel.rawValue)] if let lat, let lng { query.append(URLQueryItem(name: "lat", value: String(lat))) query.append(URLQueryItem(name: "lng", value: String(lng))) - query.append(URLQueryItem(name: "radius", value: "50")) + query.append(URLQueryItem(name: "radius", value: String(radiusKM))) } - query.append(URLQueryItem(name: "limit", value: "100")) + query.append(URLQueryItem(name: "limit", value: "500")) components.queryItems = query let (data, response) = try await URLSession.shared.data(from: components.url!) @@ -106,7 +107,7 @@ private struct RelayResponse: Codable { /// Real England-wide data comes from the relay (full-UK Fuel Finder CSV/API). /// Prices in pence/litre. struct SampleFuelProvider: FuelPriceProviding { - func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType) async throws -> [FuelStation] { + func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType, radiusKM: Double) async throws -> [FuelStation] { try await Task.sleep(nanoseconds: 300_000_000) // simulate fetch return Self.sampleStations } @@ -200,7 +201,7 @@ struct FuelFinderProvider: FuelPriceProviding { let clientID: String let clientSecret: String - func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType) async throws -> [FuelStation] { + func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType, radiusKM: Double) async throws -> [FuelStation] { // TODO: OAuth token → GET /v1/prices → map to FuelStation. // The live API requires authentication; see notes above. throw FuelProviderError.notImplemented diff --git a/Shared/FuelStore.swift b/Shared/FuelStore.swift index 69b89c1..9d47e5b 100644 --- a/Shared/FuelStore.swift +++ b/Shared/FuelStore.swift @@ -140,7 +140,7 @@ struct FuelStore { static let locationKey = "fuelboard.lastLocation" // "lat,lng,timestamp" static let fuelKey = "fuelboard.selectedFuel" // FuelType raw value static let sortModeKey = "fuelboard.sortMode" // SortMode raw value - static let stationLimitKey = "fuelboard.stationLimit" // Int (10/25/50/75/100) + static let stationLimitKey = "fuelboard.stationLimitMiles" // Int miles (5/10/15) static let favouritesKey = "fuelboard.favourites" // [FuelStation] JSON static let alertsEnabledKey = "fuelboard.alertsEnabled" // Bool static let alertsRadiusKey = "fuelboard.alertsRadius" // Double km @@ -209,17 +209,21 @@ struct FuelStore { saveString(mode.rawValue, service: sortModeKey) } - // MARK: Station count limit + // MARK: Station search radius (miles) + + /// Search radius options — the results filter shows stations within this + /// many miles of the current location. + static let stationRadiusOptions = [5, 10, 15] static func loadStationLimit() -> Int { - if let raw = loadString(service: stationLimitKey), let value = Int(raw), [10, 25, 50, 75, 100].contains(value) { + if let raw = loadString(service: stationLimitKey), let value = Int(raw), stationRadiusOptions.contains(value) { return value } - return 25 + return 5 } - static func saveStationLimit(_ limit: Int) { - saveString(String(limit), service: stationLimitKey) + static func saveStationLimit(_ miles: Int) { + saveString(String(miles), service: stationLimitKey) } // MARK: Favourites