diff --git a/FuelBoardWidgets/FuelPriceWidget.swift b/FuelBoardWidgets/FuelPriceWidget.swift index 9eee29e..26df698 100644 --- a/FuelBoardWidgets/FuelPriceWidget.swift +++ b/FuelBoardWidgets/FuelPriceWidget.swift @@ -76,20 +76,24 @@ struct FuelPriceWidget: Widget { } // Small widget — exactly ONE station (the first of the configured result). -// Shares the medium widget's intent: the small intent with extra favourite -// AppEntity params failed fresh-widget default-config resolution at the -// system level (whole default config failed → no timeline → skeleton; proven -// by an A/B test where a small widget using this intent populated while the -// custom-intent small widget never even reached makeEntry). Favourites sort -// on the small face shows the cheapest favourite of the chosen fuel. +// Uses its OWN intent TYPE (FuelBoardSmallWidgetConfigurationIntent) with the +// IDENTICAL minimal parameter set as the medium intent. iOS keys the +// long-press app-icon contextual menu by configuration-intent TYPE, so two +// kinds sharing one intent collapsed there to the first-registered kind (the +// small widget won — medium vanished from the icon menu). Distinct intent +// TYPE, same minimal params: restores both entries in the icon menu AND keeps +// fresh-widget default-config resolution working (the skeleton root cause was +// the extra favourite AppEntity params, not intent separation — see the +// Fresh-widget defaults section). 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: FuelBoardWidgetConfigurationIntent.self, - provider: FuelPriceTimelineProvider( + intent: FuelBoardSmallWidgetConfigurationIntent.self, + provider: FuelPriceTimelineProvider( beaconTag: "FuelBoardSmallWidget" ) ) { entry in diff --git a/FuelBoardWidgets/WidgetConfigIntent.swift b/FuelBoardWidgets/WidgetConfigIntent.swift index 900fcce..99ba680 100644 --- a/FuelBoardWidgets/WidgetConfigIntent.swift +++ b/FuelBoardWidgets/WidgetConfigIntent.swift @@ -120,3 +120,51 @@ 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 + } + } + } + } +}