Small widget: Favourites gains a picker to pin which favourite to show (separate FuelPriceWidgetSmall kind + AppEntity picker; medium keeps the list, no picker). Backlog: dual-unit widget picker labels
This commit is contained in:
@@ -68,7 +68,61 @@ struct WidgetDistanceQuery: EntityQuery {
|
||||
}
|
||||
}
|
||||
|
||||
struct FuelBoardWidgetConfigurationIntent: WidgetConfigurationIntent {
|
||||
// A pinned favourite station, selectable on SMALL widgets (which show a single
|
||||
// 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.
|
||||
struct WidgetFavourite: AppEntity, Identifiable, Hashable, Codable {
|
||||
let fuel: FuelType
|
||||
let stationID: String
|
||||
|
||||
var id: String { "\(fuel.rawValue)|\(stationID)" }
|
||||
|
||||
var displayRepresentation: DisplayRepresentation {
|
||||
let favourites = FuelStore.loadFavourites()
|
||||
guard let entry = favourites.first(where: { $0.id == id }) else {
|
||||
return DisplayRepresentation(stringLiteral: "Favourite")
|
||||
}
|
||||
let name = entry.station.name
|
||||
// Same station pinned for another fuel? Disambiguate with the fuel tag.
|
||||
let hasOtherFuel = favourites.contains {
|
||||
$0.station.id == entry.station.id && $0.fuel != entry.fuel
|
||||
}
|
||||
return DisplayRepresentation(
|
||||
stringLiteral: hasOtherFuel ? "\(name) · \(entry.fuel.displayName)" : name
|
||||
)
|
||||
}
|
||||
|
||||
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Favourite"
|
||||
static var defaultQuery = WidgetFavouriteQuery()
|
||||
}
|
||||
|
||||
struct WidgetFavouriteQuery: EntityQuery {
|
||||
func entities(for identifiers: [String]) async throws -> [WidgetFavourite] {
|
||||
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 ?? ""))
|
||||
}
|
||||
}
|
||||
|
||||
func suggestedEntities() async throws -> [WidgetFavourite] {
|
||||
FuelStore.loadFavourites().map { WidgetFavourite(fuel: $0.fuel, stationID: $0.station.id) }
|
||||
}
|
||||
}
|
||||
|
||||
// The knobs both the medium-list and small-single widget intents expose, so
|
||||
// one timeline provider can drive either.
|
||||
protocol WidgetConfigValues {
|
||||
var fuel: WidgetFuel { get }
|
||||
var sort: WidgetSort { get }
|
||||
var distance: WidgetDistance { get }
|
||||
/// The pinned favourite (small widget, Favourites sort) — nil when the
|
||||
/// widget lists favourites instead (medium).
|
||||
var favouriteChoice: WidgetFavourite? { get }
|
||||
}
|
||||
|
||||
struct FuelBoardWidgetConfigurationIntent: WidgetConfigurationIntent, WidgetConfigValues {
|
||||
static var title: LocalizedStringResource = "Fuel & Sort"
|
||||
static var description = IntentDescription("Which fuel, ordering and search radius this widget shows.")
|
||||
|
||||
@@ -81,6 +135,10 @@ struct FuelBoardWidgetConfigurationIntent: WidgetConfigurationIntent {
|
||||
@Parameter(title: "Distance", default: WidgetDistance(id: 5))
|
||||
var distance: WidgetDistance
|
||||
|
||||
// Medium/list widgets show ALL favourites for the chosen fuel — no
|
||||
// pinned-favourite knob.
|
||||
var favouriteChoice: WidgetFavourite? { nil }
|
||||
|
||||
/// Edit-Widget UI: the Distance picker only makes sense for Cheapest
|
||||
/// ordering — Closest is inherently "nearest within range" and Favourites
|
||||
/// is not radius-bound. Show it for Cheapest only; hide for both others.
|
||||
@@ -106,3 +164,49 @@ struct FuelBoardWidgetConfigurationIntent: WidgetConfigurationIntent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SMALL widget intent: same knobs as the list widget, PLUS a pinned-favourite
|
||||
// picker for Favourites sort (a small widget shows exactly ONE station, so the
|
||||
// user chooses which favourite). In Favourites mode the Fuel + Distance rows
|
||||
// are hidden — the chosen favourite carries its own fuel, and there is no
|
||||
// radius when a station is pinned.
|
||||
struct FuelBoardSmallWidgetConfigurationIntent: WidgetConfigurationIntent, WidgetConfigValues {
|
||||
static var title: LocalizedStringResource = "Fuel & Sort"
|
||||
static var description = IntentDescription("Which fuel, ordering, radius and pinned favourite this small widget shows.")
|
||||
|
||||
@Parameter(title: "Fuel", default: .e10)
|
||||
var fuel: WidgetFuel
|
||||
|
||||
@Parameter(title: "Sort by", default: .cheapest)
|
||||
var sort: WidgetSort
|
||||
|
||||
@Parameter(title: "Distance", default: WidgetDistance(id: 5))
|
||||
var distance: WidgetDistance
|
||||
|
||||
@Parameter(title: "Favourite", default: WidgetFavourite(fuel: .e10, stationID: ""))
|
||||
var favourite: WidgetFavourite
|
||||
|
||||
var favouriteChoice: WidgetFavourite? { favourite }
|
||||
|
||||
static var parameterSummary: some ParameterSummary {
|
||||
When(\.$sort, .equalTo, WidgetSort.favourites) {
|
||||
Summary("Show \(\.$favourite)") {
|
||||
\.$sort
|
||||
\.$favourite
|
||||
}
|
||||
} otherwise: {
|
||||
When(\.$sort, .equalTo, WidgetSort.cheapest) {
|
||||
Summary("Show \(\.$fuel) by \(\.$sort) within \(\.$distance)") {
|
||||
\.$fuel
|
||||
\.$sort
|
||||
\.$distance
|
||||
}
|
||||
} otherwise: {
|
||||
Summary("Show \(\.$fuel) by \(\.$sort)") {
|
||||
\.$fuel
|
||||
\.$sort
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user