diff --git a/FuelBoardWidgets/FuelPriceWidget.swift b/FuelBoardWidgets/FuelPriceWidget.swift index e358979..68e87e9 100644 --- a/FuelBoardWidgets/FuelPriceWidget.swift +++ b/FuelBoardWidgets/FuelPriceWidget.swift @@ -165,16 +165,29 @@ struct FuelPriceTimelineProvider [FavouriteFuel] { identifiers.compactMap { FuelType(rawValue: $0) }.map { FavouriteFuel(fuel: $0) } } @@ -106,15 +112,6 @@ struct FavouriteFuelQuery: EntityQuery { 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 @@ -164,13 +161,27 @@ struct WidgetFavouriteQuery: EntityQuery { // WidgetFavourite (best-effort name lookup). NEVER return nil here — the // timeline request resolves the whole configuration through this query // and a failed resolution strands a default-config widget on the skeleton - // placeholder. Fresh widgets auto-populate via defaultResult(). + // placeholder. + // + // NO defaultResult(): fresh/default configs resolve from the static + // @Parameter(default:) value (empty id "e10|") with NO storage reads — + // the old keychain-backed defaultResult() ran in the gallery-preview / + // fresh-add resolution path and failed on-device. The sheet's + // auto-populate moved to entry time (makeEntry pins the first favourite + // of the fuel); the dependent picker keeps working via suggestedEntities(). 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 ?? "") + // Id-only value (fresh widget's static default): resolve with + // zero keychain access. displayRepresentation falls back to a + // storage lookup if the name is empty, so stored picks still + // render their name. + guard !stationID.isEmpty else { + return WidgetFavourite(fuel: fuel, stationID: stationID, stationName: "") + } + let favourites = FuelStore.loadFavourites() let entry = favourites.first { $0.id == id } return WidgetFavourite( fuel: fuel, @@ -196,26 +207,6 @@ 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