Widget favourites defaults: auto-populate Fuel and Favourite rows when Sort = Favourites. FavouriteFuelQuery gains defaultResult() (first fuel with a favourite, Unleaded→Diesel order) and strict entities(for:) — a stored fuel that no longer has favourites fails resolution and falls back to the default. WidgetFavouriteQuery gains defaultResult() (first favourite of the Fuel row's fuel, else first favourite overall) and strict entities(for:) — the empty static default ('fuel|') and removed favourites now resolve to nil so the sheet pre-selects a real favourite instead of showing a placeholder. Provider fallback (cheapest favourite of the fuel) unchanged.

This commit is contained in:
FuelBoard Contributor
2026-08-13 18:28:23 +01:00
parent 906f8a8429
commit b1765b5611
+47 -9
View File
@@ -92,14 +92,31 @@ struct FavouriteFuel: AppEntity, Identifiable, Hashable, Codable {
} }
struct FavouriteFuelQuery: EntityQuery { 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] { 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] { func suggestedEntities() async throws -> [FavouriteFuel] {
let fuels = Set(FuelStore.loadFavourites().map(\.fuel)) let fuels = Set(FuelStore.loadFavourites().map(\.fuel))
return FuelType.allCases.filter { fuels.contains($0) }.map { FavouriteFuel(fuel: $0) } 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 // 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] { func entities(for identifiers: [String]) async throws -> [WidgetFavourite] {
let favourites = FuelStore.loadFavourites() let favourites = FuelStore.loadFavourites()
return identifiers.map { id in return identifiers.compactMap { id in
let parts = id.split(separator: "|", maxSplits: 1) // Strict: only real, existing favourites resolve. The empty
let fuel = FuelType(rawValue: String(parts.first ?? "")) ?? .e10 // static default (id "fuel|") and removed favourites fail
let stationID = String(parts.last ?? "") // resolution so the sheet falls back to defaultResult().
let entry = favourites.first { $0.id == id } guard !id.hasSuffix("|"),
let entry = favourites.first(where: { $0.id == id }) else { return nil }
return WidgetFavourite( return WidgetFavourite(
fuel: fuel, fuel: entry.fuel,
stationID: stationID, stationID: entry.station.id,
stationName: entry?.station.name ?? "" 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 // The knobs both the medium-list and small-single widget intents expose, so