Merge main into feature/watch-app
Bring watch branch up to date with main (banner fade, onboarding timing, Live Activity full-width layout). Resolved the bundled fuel dump data-refresh conflict by taking main's newer snapshot (data_updated 2026-08-17T15:01, 8028 stations).
This commit is contained in:
@@ -216,10 +216,18 @@ enum FuelHistoryStore {
|
||||
// MARK: Network
|
||||
|
||||
/// The mirror pointer — used for the empty-state hint ("first snapshot
|
||||
/// landed …"). Non-fatal: nil just means no hint.
|
||||
static func fetchLatest(base: URL = mirrorBase,
|
||||
/// landed …"). Non-fatal: nil just means no hint. Deliberately bypasses
|
||||
/// the HTTP cache (reloadIgnoringLocalCacheData) so an OFFLINE fetch
|
||||
/// really fails instead of silently re-serving a cached pointer as a
|
||||
/// success — otherwise "no network" would look like fresh data and the
|
||||
/// offline banner would never fire. A short timeout surfaces the failure
|
||||
/// (and the banner) quickly on a connected-but-dead network.
|
||||
static func fetchLatest(base: URL = mirrorBase, timeout: TimeInterval = 10,
|
||||
session: URLSession = .shared) async -> MirrorLatest? {
|
||||
guard let (data, response) = try? await session.data(from: latestFileURL(base: base)),
|
||||
var req = URLRequest(url: latestFileURL(base: base))
|
||||
req.cachePolicy = .reloadIgnoringLocalCacheData
|
||||
req.timeoutInterval = timeout
|
||||
guard let (data, response) = try? await session.data(for: req),
|
||||
(response as? HTTPURLResponse)?.statusCode == 200,
|
||||
let latest = try? JSONDecoder().decode(MirrorLatest.self, from: data) else {
|
||||
return nil
|
||||
|
||||
@@ -242,21 +242,37 @@ struct FuelStation: Identifiable, Codable, Equatable {
|
||||
var brandImageName: String? {
|
||||
let raw = brand.uppercased()
|
||||
let known: [(String, String)] = [
|
||||
("CIRCLE K", "brand_circle_k"),
|
||||
("MAXOL", "brand_maxol"),
|
||||
("SPAR", "brand_spar"),
|
||||
("SHELL", "brand_shell"),
|
||||
("SAINSBURY", "brand_sainsburys"),
|
||||
("MORRISONS", "brand_morrisons"),
|
||||
("APPLEGREEN", "brand_applegreen"),
|
||||
("TESCO", "brand_tesco"),
|
||||
("TEXACO", "brand_texaco"),
|
||||
("ESSAR", "brand_essar"),
|
||||
("ESSO", "brand_esso"),
|
||||
("ASDA", "brand_asda"),
|
||||
("GULF", "brand_gulf"),
|
||||
("VALERO", "brand_valero"),
|
||||
("WELCOME BREAK", "brand_welcome_break"),
|
||||
("THE CO-OPERATIVE", "brand_the_co_operative"),
|
||||
("CO-OPERATIVE", "brand_the_co_operative"),
|
||||
("CO OPERATIVE", "brand_the_co_operative"),
|
||||
("CO-OP", "brand_the_co_operative"),
|
||||
("CO OP", "brand_the_co_operative"),
|
||||
("MURCO", "brand_murco"),
|
||||
("GLEANER", "brand_gleaner"),
|
||||
("HIGHLAND FUELS", "brand_highland_fuels"),
|
||||
("BP", "brand_bp"),
|
||||
("JET", "brand_jet"),
|
||||
]
|
||||
for (needle, asset) in known where raw.contains(needle) {
|
||||
return asset
|
||||
}
|
||||
if raw.contains("EG ON THE MOVE") { return "brand_esso" }
|
||||
if raw == "GO" || raw.contains("GO FORECOURT") || raw.contains("GO FUEL") { return "brand_gulf" }
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,9 +96,13 @@ struct MirrorFuelProvider: FuelPriceProviding {
|
||||
if Self.canReuseCache(cachedDay: cache?.day, latestDay: day), let cached = cache?.data {
|
||||
data = cached
|
||||
} else {
|
||||
let (fetched, response) = try await URLSession.shared.data(
|
||||
from: FuelHistoryStore.historyFileURL(day: day, base: baseURL)
|
||||
)
|
||||
// Bypass the HTTP cache + short timeout: an offline refresh must
|
||||
// FAIL (→ offline banner), never re-serve a stale cached dump as
|
||||
// a "successful" live fetch.
|
||||
var req = URLRequest(url: FuelHistoryStore.historyFileURL(day: day, base: baseURL))
|
||||
req.cachePolicy = .reloadIgnoringLocalCacheData
|
||||
req.timeoutInterval = 10
|
||||
let (fetched, response) = try await URLSession.shared.data(for: req)
|
||||
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
|
||||
throw FuelProviderError.mirrorUnavailable
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user