From d8506a035f0c7ce107d34a6dacad82c9e261795e Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Thu, 13 Aug 2026 17:00:17 +0100 Subject: [PATCH] Small widget favourite picker: embed station name + multi-fuel flag in the WidgetFavourite entity so picker rows render with no storage read (config-sheet resolution can run without keychain/app-group access, which made every row show the 'Favourite' placeholder) --- FuelBoardWidgets/WidgetConfigIntent.swift | 49 ++++++++++++++++++----- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/FuelBoardWidgets/WidgetConfigIntent.swift b/FuelBoardWidgets/WidgetConfigIntent.swift index 4d55e58..2aeacff 100644 --- a/FuelBoardWidgets/WidgetConfigIntent.swift +++ b/FuelBoardWidgets/WidgetConfigIntent.swift @@ -72,24 +72,37 @@ struct WidgetDistanceQuery: EntityQuery { // station). Reuses FavouriteEntry's id scheme ("fuel|stationID") so the // provider can resolve the choice straight back to a stored favourite. Only // surfaced when Sort = Favourites. +// +// The display name is EMBEDDED in the entity (stationName + showsFuel): the +// config sheet can resolve entities in a process where keychain/app-group +// storage is unavailable, and a storage-backed lookup there made every picker +// row fall back to the "Favourite" placeholder. With the name carried on the +// value, rows render with no storage read at all. struct WidgetFavourite: AppEntity, Identifiable, Hashable, Codable { let fuel: FuelType let stationID: String + /// Station display name, embedded so picker rows render storage-free. + let stationName: String + /// True when favourites span more than one fuel type — embedded so the + /// fuel tag renders storage-free too. + let showsFuel: Bool var id: String { "\(fuel.rawValue)|\(stationID)" } var displayRepresentation: DisplayRepresentation { + if !stationName.isEmpty { + return DisplayRepresentation( + stringLiteral: showsFuel ? "\(stationName) · \(fuel.displayName)" : stationName + ) + } + // Id-only entity (e.g. a stored default): best-effort storage lookup. let favourites = FuelStore.loadFavourites() guard let entry = favourites.first(where: { $0.id == id }) else { return DisplayRepresentation(stringLiteral: "Favourite") } - let name = entry.station.name - // If favourites span more than one fuel type, tag EVERY option with its - // fuel so the picker stays unambiguous (different stations can share a - // name; the same station can be pinned for two fuels). let multiFuel = Set(favourites.map(\.fuel)).count > 1 return DisplayRepresentation( - stringLiteral: multiFuel ? "\\(name) · \\(entry.fuel.displayName)" : name + stringLiteral: multiFuel ? "\(entry.station.name) · \(entry.fuel.displayName)" : entry.station.name ) } @@ -99,15 +112,33 @@ struct WidgetFavourite: AppEntity, Identifiable, Hashable, Codable { struct WidgetFavouriteQuery: EntityQuery { func entities(for identifiers: [String]) async throws -> [WidgetFavourite] { - identifiers.map { id in + let favourites = FuelStore.loadFavourites() + let multiFuel = Set(favourites.map(\.fuel)).count > 1 + return identifiers.map { id in let parts = id.split(separator: "|", maxSplits: 1) let fuel = FuelType(rawValue: String(parts.first ?? "")) ?? .e10 - return WidgetFavourite(fuel: fuel, stationID: String(parts.last ?? "")) + let stationID = String(parts.last ?? "") + let entry = favourites.first { $0.id == id } + return WidgetFavourite( + fuel: fuel, + stationID: stationID, + stationName: entry?.station.name ?? "", + showsFuel: multiFuel + ) } } func suggestedEntities() async throws -> [WidgetFavourite] { - FuelStore.loadFavourites().map { WidgetFavourite(fuel: $0.fuel, stationID: $0.station.id) } + let favourites = FuelStore.loadFavourites() + let multiFuel = Set(favourites.map(\.fuel)).count > 1 + return favourites.map { entry in + WidgetFavourite( + fuel: entry.fuel, + stationID: entry.station.id, + stationName: entry.station.name, + showsFuel: multiFuel + ) + } } } @@ -184,7 +215,7 @@ struct FuelBoardSmallWidgetConfigurationIntent: WidgetConfigurationIntent, Widge @Parameter(title: "Distance", default: WidgetDistance(id: 5)) var distance: WidgetDistance - @Parameter(title: "Favourite", default: WidgetFavourite(fuel: .e10, stationID: "")) + @Parameter(title: "Favourite", default: WidgetFavourite(fuel: .e10, stationID: "", stationName: "", showsFuel: false)) var favourite: WidgetFavourite var favouriteChoice: WidgetFavourite? { favourite }