Widgets: merge small+medium into one kind (both sizes, one gallery entry)
This commit is contained in:
@@ -4,8 +4,9 @@ import SwiftUI
|
||||
@main
|
||||
struct FuelBoardWidgetsBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
// Gallery order follows registration order: small first, then medium.
|
||||
FuelPriceSmallWidget()
|
||||
// ONE widget kind (both sizes) first — the iOS long-press app-icon
|
||||
// menu shows the FIRST-registered kind's sizes; this kind supports
|
||||
// .systemSmall + .systemMedium so both tiles appear.
|
||||
FuelPriceWidget()
|
||||
FuelBoardLiveActivity()
|
||||
}
|
||||
|
||||
@@ -17,13 +17,14 @@ private extension FuelType {
|
||||
}
|
||||
}
|
||||
|
||||
// FuelBoard widget — petrol stations near you.
|
||||
// FuelPriceWidget (medium): top 3 stations with price + distance, each row
|
||||
// opens Maps. Per-widget fuel + sort (+ distance for Cheapest).
|
||||
// FuelPriceSmallWidget (small): single station, whole widget opens Maps.
|
||||
// Same knobs as the medium — the small face shows the first of the
|
||||
// configured result (cheapest/closest station, or the cheapest favourite
|
||||
// of the chosen fuel in Favourites mode).
|
||||
// FuelBoard widget — petrol stations near you. ONE widget kind, TWO sizes:
|
||||
// .systemSmall: single station (the first of the configured result — the
|
||||
// cheapest/closest station, or the cheapest favourite of the chosen fuel
|
||||
// in Favourites mode); the whole face opens Maps.
|
||||
// .systemMedium: top 3 stations with price + distance, each row opens Maps.
|
||||
// Per-widget fuel + sort (+ distance for Cheapest) via one config intent.
|
||||
// The view branches on widgetFamily, not kind — the small face renders the
|
||||
// first station, the medium face the list.
|
||||
// Taps deep-link to Apple Maps directions (maps://?daddr=). On the Home
|
||||
// Screen the system either opens Maps directly or delivers the URL to
|
||||
// FuelBoard, whose onOpenURL forwards it (and also still handles legacy
|
||||
@@ -49,6 +50,13 @@ private extension FuelType {
|
||||
// (no permission yet / timeout) it falls back to the app's cached location,
|
||||
// then to price-only sorting. So the widget is location-driven the moment the
|
||||
// app has been opened once and permission granted.
|
||||
//
|
||||
// History: a separate "small" kind existed (FuelPriceWidgetSmall) and was
|
||||
// merged here on 2026-08-14. The small kind had gained .systemMedium so the
|
||||
// iOS long-press app-icon menu (which shows only the FIRST-registered kind's
|
||||
// sizes) would offer both tiles — but that duplicated the medium gallery
|
||||
// entry. Merging keeps both size tiles in the icon menu AND one gallery entry
|
||||
// with both sizes.
|
||||
|
||||
struct FuelPriceWidget: Widget {
|
||||
let kind = "FuelPriceWidget"
|
||||
@@ -70,49 +78,8 @@ struct FuelPriceWidget: Widget {
|
||||
}
|
||||
.configurationDisplayName("FuelBoard Prices")
|
||||
.description("Fuel prices near you. Configure fuel + sort per widget.")
|
||||
.supportedFamilies([.systemMedium])
|
||||
.disfavoredLocations([.carPlay], for: [.systemMedium])
|
||||
}
|
||||
}
|
||||
|
||||
// Small widget — exactly ONE station (the first of the configured result)
|
||||
// when rendered at systemSmall; the SAME kind also supports .systemMedium,
|
||||
// where the shared view renders the station list (identical content to the
|
||||
// medium widget — the view branches on family, not kind).
|
||||
//
|
||||
// WHY medium is here (2026-08-14): the iOS 18+ long-press app-icon menu
|
||||
// ("turn app icon into a widget") shows the supported SIZES of the
|
||||
// FIRST-registered widget kind only — it does NOT list widget kinds, and it
|
||||
// is NOT keyed by configuration-intent type (a two-intent experiment,
|
||||
// 6375af4, changed nothing on-device). So the bundle's first kind decides
|
||||
// which size tiles the icon menu offers: small-only meant the menu showed
|
||||
// only the small tile even though the app has a medium widget. Giving this
|
||||
// first-registered kind BOTH .systemSmall and .systemMedium puts both size
|
||||
// tiles in the icon menu (ChatGPT/Safari show multiple sizes the same way —
|
||||
// their first kind supports several families). The gallery still lists both
|
||||
// widget kinds in bundle order (small, then medium); this kind's entry just
|
||||
// gains a medium variant. Favourites sort on the small face shows the
|
||||
// cheapest favourite of the chosen fuel.
|
||||
struct FuelPriceSmallWidget: Widget {
|
||||
let kind = "FuelPriceWidgetSmall"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(
|
||||
kind: kind,
|
||||
intent: FuelBoardSmallWidgetConfigurationIntent.self,
|
||||
provider: FuelPriceTimelineProvider<FuelBoardSmallWidgetConfigurationIntent>(
|
||||
beaconTag: "FuelBoardSmallWidget"
|
||||
)
|
||||
) { entry in
|
||||
FuelPriceWidgetView(entry: entry)
|
||||
.containerBackground(for: .widget) {
|
||||
Color(.systemBackground)
|
||||
}
|
||||
}
|
||||
.configurationDisplayName("FuelBoard Small")
|
||||
.description("Fuel prices near you. Configure fuel + sort per widget.")
|
||||
.supportedFamilies([.systemSmall, .systemMedium])
|
||||
.disfavoredLocations([.carPlay], for: [.systemSmall])
|
||||
.disfavoredLocations([.carPlay], for: [.systemSmall, .systemMedium])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +217,7 @@ struct FuelPriceTimelineProvider<Configuration: WidgetConfigurationIntent & Widg
|
||||
// the cheapest favourite of the chosen fuel. (The old per-widget
|
||||
// pinned-favourite picker was removed: its AppEntity params made
|
||||
// fresh-widget default-config resolution fail at the system level
|
||||
// — see FuelPriceSmallWidget.)
|
||||
// — the merged widget uses the minimal fuel/sort/distance intent.)
|
||||
let fuelFavourites = favourites
|
||||
.filter { $0.fuel == fuel && $0.station.prices[fuel] != nil }
|
||||
.map(\.station)
|
||||
|
||||
@@ -120,51 +120,3 @@ struct FuelBoardWidgetConfigurationIntent: WidgetConfigurationIntent, WidgetConf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The SMALL widget's OWN intent TYPE. iOS keys the long-press app-icon
|
||||
// contextual menu (iOS 18+ "turn icon into a widget") by configuration-intent
|
||||
// TYPE, so two widget kinds sharing one intent collapse there to a single
|
||||
// entry — the first-registered kind. (The + gallery is kind-keyed, which is
|
||||
// why it still lists both.) This intent carries the IDENTICAL minimal
|
||||
// parameter set as the medium intent — fuel/sort/distance only, NO AppEntity
|
||||
// params (the favouriteFuel/favourite AppEntity params were what broke
|
||||
// fresh-widget default-config resolution, not intent separation; see
|
||||
// Fresh-widget defaults). Sharing the generic provider keeps the timeline
|
||||
// logic in one place.
|
||||
struct FuelBoardSmallWidgetConfigurationIntent: WidgetConfigurationIntent, WidgetConfigValues {
|
||||
static var title: LocalizedStringResource = "Fuel & Sort"
|
||||
static var description = IntentDescription("Which fuel, ordering and search radius this 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
|
||||
|
||||
/// Edit-Widget UI: same layout as the medium intent — Distance only for
|
||||
/// Cheapest ordering; hidden for Closest and Favourites.
|
||||
static var parameterSummary: some ParameterSummary {
|
||||
When(\.$sort, .equalTo, WidgetSort.cheapest) {
|
||||
Summary("Show \(\.$fuel) by \(\.$sort) within \(\.$distance)") {
|
||||
\.$fuel
|
||||
\.$sort
|
||||
\.$distance
|
||||
}
|
||||
} otherwise: {
|
||||
When(\.$sort, .equalTo, WidgetSort.favourites) {
|
||||
Summary("Show \(\.$fuel) favourites") {
|
||||
\.$fuel
|
||||
\.$sort
|
||||
}
|
||||
} otherwise: {
|
||||
Summary("Show \(\.$fuel) by \(\.$sort)") {
|
||||
\.$fuel
|
||||
\.$sort
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user