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)
This commit is contained in:
@@ -72,24 +72,37 @@ struct WidgetDistanceQuery: EntityQuery {
|
|||||||
// station). Reuses FavouriteEntry's id scheme ("fuel|stationID") so the
|
// station). Reuses FavouriteEntry's id scheme ("fuel|stationID") so the
|
||||||
// provider can resolve the choice straight back to a stored favourite. Only
|
// provider can resolve the choice straight back to a stored favourite. Only
|
||||||
// surfaced when Sort = Favourites.
|
// 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 {
|
struct WidgetFavourite: AppEntity, Identifiable, Hashable, Codable {
|
||||||
let fuel: FuelType
|
let fuel: FuelType
|
||||||
let stationID: String
|
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 id: String { "\(fuel.rawValue)|\(stationID)" }
|
||||||
|
|
||||||
var displayRepresentation: DisplayRepresentation {
|
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()
|
let favourites = FuelStore.loadFavourites()
|
||||||
guard let entry = favourites.first(where: { $0.id == id }) else {
|
guard let entry = favourites.first(where: { $0.id == id }) else {
|
||||||
return DisplayRepresentation(stringLiteral: "Favourite")
|
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
|
let multiFuel = Set(favourites.map(\.fuel)).count > 1
|
||||||
return DisplayRepresentation(
|
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 {
|
struct WidgetFavouriteQuery: EntityQuery {
|
||||||
func entities(for identifiers: [String]) async throws -> [WidgetFavourite] {
|
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 parts = id.split(separator: "|", maxSplits: 1)
|
||||||
let fuel = FuelType(rawValue: String(parts.first ?? "")) ?? .e10
|
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] {
|
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))
|
@Parameter(title: "Distance", default: WidgetDistance(id: 5))
|
||||||
var distance: WidgetDistance
|
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 favourite: WidgetFavourite
|
||||||
|
|
||||||
var favouriteChoice: WidgetFavourite? { favourite }
|
var favouriteChoice: WidgetFavourite? { favourite }
|
||||||
|
|||||||
Reference in New Issue
Block a user