Widget fetches its own data from the relay
The widget previously read stations only from the app-group cache, which SideStore free accounts don't provision — the widget process saw empty data and silently fell back to sample stations (stuck on Morrisons). Now the widget makes its own focused relay fetch around its location (radius + limit 500, 5s timeout) whenever it has a fix, falling back to the shared cache then samples. Widget data now reflects the relay independently of the app. 35 tests pass.
This commit is contained in:
@@ -101,13 +101,22 @@ struct FuelPriceTimelineProvider: AppIntentTimelineProvider {
|
|||||||
FuelStore.saveLocation(lat: location.lat, lng: location.lng)
|
FuelStore.saveLocation(lat: location.lat, lng: location.lng)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Load stations, then sort per this widget's config, scoped to
|
// 3) Load stations. The widget prefers its OWN focused fetch from the
|
||||||
// the chosen search radius so the widget's "cheapest" matches
|
// relay so it shows real prices even when the app-group cache isn't
|
||||||
// the app's list.
|
// shared (SideStore free accounts don't provision the group, which
|
||||||
var stations = FuelStore.loadStations()
|
// previously left the widget stuck on sample data). Falls back to the
|
||||||
|
// shared cache, then to samples.
|
||||||
|
var stations: [FuelStation] = []
|
||||||
|
if let location {
|
||||||
|
let radiusKM = unitToKM()
|
||||||
|
if let fetched = await Self.fetchFocused(near: location, fuel: fuel, radiusKM: radiusKM) {
|
||||||
|
stations = fetched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stations.isEmpty { stations = FuelStore.loadStations() }
|
||||||
if stations.isEmpty { stations = SampleFuelProvider.sampleStations }
|
if stations.isEmpty { stations = SampleFuelProvider.sampleStations }
|
||||||
let unit = FuelStore.loadDistanceUnit()
|
let unit = FuelStore.loadDistanceUnit()
|
||||||
let radiusKM = unit.toKM(Double(FuelStore.loadStationLimit())) // chosen units → km
|
let radiusKM = unitToKM()
|
||||||
if let location {
|
if let location {
|
||||||
// STRICT: cached data fetched around another location must never
|
// STRICT: cached data fetched around another location must never
|
||||||
// leak out-of-radius stations into the widget.
|
// leak out-of-radius stations into the widget.
|
||||||
@@ -149,6 +158,37 @@ struct FuelPriceTimelineProvider: AppIntentTimelineProvider {
|
|||||||
unit: FuelStore.loadDistanceUnit()
|
unit: FuelStore.loadDistanceUnit()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The search radius in km for the widget's own focused relay fetch.
|
||||||
|
private func unitToKM() -> Double {
|
||||||
|
FuelStore.loadDistanceUnit().toKM(Double(FuelStore.loadStationLimit()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One focused fetch from the relay around the widget's location — small
|
||||||
|
/// response (radius + limit 500), bounded so a dead relay can't stall the
|
||||||
|
/// timeline. Returns nil on any failure so callers fall back to cache.
|
||||||
|
private static func fetchFocused(near location: Coordinate, fuel: FuelType, radiusKM: Double) async -> [FuelStation]? {
|
||||||
|
var components = URLComponents(
|
||||||
|
url: RelayFuelProvider().baseURL.appendingPathComponent("api/v1/stations"),
|
||||||
|
resolvingAgainstBaseURL: false
|
||||||
|
)!
|
||||||
|
components.queryItems = [
|
||||||
|
URLQueryItem(name: "fuel", value: fuel.rawValue),
|
||||||
|
URLQueryItem(name: "lat", value: String(location.lat)),
|
||||||
|
URLQueryItem(name: "lng", value: String(location.lng)),
|
||||||
|
URLQueryItem(name: "radius", value: String(radiusKM)),
|
||||||
|
URLQueryItem(name: "limit", value: "500"),
|
||||||
|
]
|
||||||
|
var request = URLRequest(url: components.url!)
|
||||||
|
request.timeoutInterval = 5
|
||||||
|
do {
|
||||||
|
let (data, response) = try await URLSession.shared.data(for: request)
|
||||||
|
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { return nil }
|
||||||
|
return try? FuelPriceProvider.decodeStations(from: data)
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FuelPriceWidgetView: View {
|
struct FuelPriceWidgetView: View {
|
||||||
|
|||||||
Reference in New Issue
Block a user