From b1765b561122b7cbcc56cf11a1cd01109804ee39 Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Thu, 13 Aug 2026 18:28:23 +0100 Subject: [PATCH] =?UTF-8?q?Widget=20favourites=20defaults:=20auto-populate?= =?UTF-8?q?=20Fuel=20and=20Favourite=20rows=20when=20Sort=20=3D=20Favourit?= =?UTF-8?q?es.=20FavouriteFuelQuery=20gains=20defaultResult()=20(first=20f?= =?UTF-8?q?uel=20with=20a=20favourite,=20Unleaded=E2=86=92Diesel=20order)?= =?UTF-8?q?=20and=20strict=20entities(for:)=20=E2=80=94=20a=20stored=20fue?= =?UTF-8?q?l=20that=20no=20longer=20has=20favourites=20fails=20resolution?= =?UTF-8?q?=20and=20falls=20back=20to=20the=20default.=20WidgetFavouriteQu?= =?UTF-8?q?ery=20gains=20defaultResult()=20(first=20favourite=20of=20the?= =?UTF-8?q?=20Fuel=20row's=20fuel,=20else=20first=20favourite=20overall)?= =?UTF-8?q?=20and=20strict=20entities(for:)=20=E2=80=94=20the=20empty=20st?= =?UTF-8?q?atic=20default=20('fuel|')=20and=20removed=20favourites=20now?= =?UTF-8?q?=20resolve=20to=20nil=20so=20the=20sheet=20pre-selects=20a=20re?= =?UTF-8?q?al=20favourite=20instead=20of=20showing=20a=20placeholder.=20Pr?= =?UTF-8?q?ovider=20fallback=20(cheapest=20favourite=20of=20the=20fuel)=20?= =?UTF-8?q?unchanged.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FuelBoardWidgets/WidgetConfigIntent.swift | 56 +++++++++++++++++++---- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/FuelBoardWidgets/WidgetConfigIntent.swift b/FuelBoardWidgets/WidgetConfigIntent.swift index 1dae805..a3e4465 100644 --- a/FuelBoardWidgets/WidgetConfigIntent.swift +++ b/FuelBoardWidgets/WidgetConfigIntent.swift @@ -92,14 +92,31 @@ struct FavouriteFuel: AppEntity, Identifiable, Hashable, Codable { } struct FavouriteFuelQuery: EntityQuery { + /// Strict: a fuel only resolves if it ACTUALLY has favourites. A stored + /// value for a fuel that lost all its favourites (or the static .e10 + /// default on a fresh widget) fails resolution and falls back to + /// defaultResult() — the first fuel that has a favourite. func entities(for identifiers: [String]) async throws -> [FavouriteFuel] { - identifiers.compactMap { FuelType(rawValue: $0) }.map { FavouriteFuel(fuel: $0) } + let fuels = Set(FuelStore.loadFavourites().map(\.fuel)) + return identifiers.compactMap { id in + guard let fuel = FuelType(rawValue: id), fuels.contains(fuel) else { return nil } + return FavouriteFuel(fuel: fuel) + } } func suggestedEntities() async throws -> [FavouriteFuel] { let fuels = Set(FuelStore.loadFavourites().map(\.fuel)) return FuelType.allCases.filter { fuels.contains($0) }.map { FavouriteFuel(fuel: $0) } } + + /// Fresh widget / unresolved value → auto-populate the Fuel row with the + /// FIRST fuel that has a favourite (Unleaded, Premium, Diesel order). + func defaultResult() async -> FavouriteFuel? { + let fuels = Set(FuelStore.loadFavourites().map(\.fuel)) + return FuelType.allCases.first { fuels.contains($0) } + .map { FavouriteFuel(fuel: $0) } + ?? FavouriteFuel(fuel: .e10) + } } // A pinned favourite station, selectable on SMALL widgets (which show a single @@ -147,15 +164,16 @@ struct WidgetFavouriteQuery: EntityQuery { func entities(for identifiers: [String]) async throws -> [WidgetFavourite] { let favourites = FuelStore.loadFavourites() - return identifiers.map { id in - let parts = id.split(separator: "|", maxSplits: 1) - let fuel = FuelType(rawValue: String(parts.first ?? "")) ?? .e10 - let stationID = String(parts.last ?? "") - let entry = favourites.first { $0.id == id } + return identifiers.compactMap { id in + // Strict: only real, existing favourites resolve. The empty + // static default (id "fuel|") and removed favourites fail + // resolution so the sheet falls back to defaultResult(). + guard !id.hasSuffix("|"), + let entry = favourites.first(where: { $0.id == id }) else { return nil } return WidgetFavourite( - fuel: fuel, - stationID: stationID, - stationName: entry?.station.name ?? "" + fuel: entry.fuel, + stationID: entry.station.id, + stationName: entry.station.name ) } } @@ -176,6 +194,26 @@ struct WidgetFavouriteQuery: EntityQuery { ) } } + + /// Fresh widget / unresolved value → auto-populate the Favourite row with + /// the FIRST favourite of the Fuel row's fuel (falling back to the first + /// favourite of any fuel if the fuel has none, e.g. mid-change). + func defaultResult() async -> WidgetFavourite? { + let favourites = FuelStore.loadFavourites() + guard !favourites.isEmpty else { return nil } + let entry: FavouriteEntry + if let fuel = smallIntent?.favouriteFuel.fuel, + let scoped = favourites.first(where: { $0.fuel == fuel }) { + entry = scoped + } else { + entry = favourites[0] + } + return WidgetFavourite( + fuel: entry.fuel, + stationID: entry.station.id, + stationName: entry.station.name + ) + } } // The knobs both the medium-list and small-single widget intents expose, so