diff --git a/FuelBoardWidgets/FuelBoardLiveActivityView.swift b/FuelBoardWidgets/FuelBoardLiveActivityView.swift index 8d6a242..738eabe 100644 --- a/FuelBoardWidgets/FuelBoardLiveActivityView.swift +++ b/FuelBoardWidgets/FuelBoardLiveActivityView.swift @@ -49,12 +49,15 @@ struct FuelBoardLiveActivity: Widget { .font(.caption2.bold().monospacedDigit()) } } - // NOTE: deliberately NO `.supplementalActivityFamilies([.small])`. - // That modifier makes iOS eligible to render this activity in the - // narrow `.small` form on the iPhone/iPad Lock Screen, which is what - // produced the squeezed, small-text card. Dropping it keeps the - // full-width Lock Screen card on iPhone/iPad; CarPlay still shows a - // small form via the Dynamic Island compact closures below. + .supplementalActivityFamilies([.small]) + // Why `.small` is kept: it lets the SHARED body render a compact form + // in genuinely small slots (CarPlay small / Apple Watch smart stack) + // instead of falling back to the Dynamic Island compact closure — + // which could NOT show the station distance the user wants on CarPlay. + // The full-width iPhone/iPad card is protected by the `richMinWidth` + // gate on `richBody` + its flexible, truncating middle column, so + // iPhone/iPad still get the full card; only truly small space picks + // the compact strip below. } } @@ -62,19 +65,28 @@ struct FuelBoardLiveActivity: Widget { private struct FuelBoardLiveActivityView: View { let context: ActivityViewContext + /// Full card needs at least this much width; iPhone/iPad Lock Screen is + /// always wider, so they always get it. Only genuinely small slots + /// (CarPlay small / Watch smart stack) fall through to `compactBody`. + private let richMinWidth: CGFloat = 280 + /// Cap the compact strip so a mid-width surface can't sneak it in. + private let compactMaxWidth: CGFloat = 230 + var body: some View { Link(destination: context.state.mapsURL) { - // Always the full three-column card. The station caption is - // line-limited + scale-down + tail-truncated, so a LONG station - // name truncates in place instead of inflating this view's ideal - // width and tricking ViewThatFits into falling back to the compact - // strip (that is exactly what made 5-mi / long-named activities - // render small while 10-15-mi / short names stayed full). - // - // No ViewThatFits / compactBody: with `.supplementalActivityFamilies` - // removed, this body is only ever handed Lock-Screen width, so the - // compact fallback was both dead weight and the cause of the bug. - richBody + ViewThatFits(in: .horizontal) { + // rich FIRST — wins on the full-width iPhone/iPad Lock Screen. + // Its middle column is flexible + line-limited, so a LONG + // station name truncates in place and never inflates the ideal + // width (which used to trick ViewThatFits into falling back to + // compact on iPhone). + richBody + .frame(minWidth: richMinWidth) + // compact fallback — the small CarPlay / Watch smart-stack + // strip: fuel · price · station distance (no app name). + compactBody + .frame(maxWidth: compactMaxWidth) + } } } @@ -113,7 +125,31 @@ private struct FuelBoardLiveActivityView: View { .padding() } - // NOTE: `compactBody` was removed — always render `richBody` (see body). + /// Minimal strip for small space (CarPlay small / Watch smart stack): + /// fuel type + bold price on one line, station · distance below. + /// Deliberately no app name and no "Tap for directions" — CarPlay is + /// display-only, and the user's asks here are just fuel + price + distance. + private var compactBody: some View { + VStack(alignment: .leading, spacing: 3) { + HStack(spacing: 5) { + Text(context.state.fuel.displayName) + .font(.caption.bold()) + .lineLimit(1) + Spacer(minLength: 4) + FuelStore.priceTextAttributed(context.state.pricePence, + style: context.state.priceDisplayStyle, + size: 15, weight: .bold) + .lineLimit(1) + } + Text("\\(context.state.stationName) · \\(context.state.distanceText)") + .font(.system(size: 9)) + .foregroundStyle(.secondary) + .lineLimit(1) + .minimumScaleFactor(0.7) + .truncationMode(.tail) + } + .padding(8) + } } /// Dynamic Island expanded regions + compact trailing — price only.