Add Best Value small widget and standardise Closest wording
This commit is contained in:
@@ -29,11 +29,12 @@ struct FuelPriceWidgetEntryData {
|
||||
let date: Date
|
||||
let stations: [FuelStation] // already filtered + sorted per config
|
||||
let fuel: FuelType
|
||||
let sort: SortMode // per-widget: cheapest | closest
|
||||
let isFavourites: Bool // favourites mode: pinned stations, cheapest-first
|
||||
let sort: SortMode // per-widget: cheapest | closest | Best Value
|
||||
let isFavourites: Bool // favourites mode: pinned stations
|
||||
let location: Coordinate?
|
||||
let locationSource: String // "live" | "cached" | "none"
|
||||
let unit: DistanceUnit // user's display unit for distances
|
||||
let bestValueResult: FuelSavingsResult? // only populated for the small Best Value face
|
||||
}
|
||||
|
||||
/// The widget face, parameterised by family so the app can preview it.
|
||||
@@ -67,9 +68,11 @@ struct FuelPriceWidgetContent: View {
|
||||
|
||||
private var heading: String {
|
||||
if entry.isFavourites { return "Favourite \(entry.fuel.displayName)" }
|
||||
return entry.sort == .closest
|
||||
? "Closest \(entry.fuel.displayName)"
|
||||
: "Cheapest \(entry.fuel.displayName)"
|
||||
switch entry.sort {
|
||||
case .closest: return "Closest \(entry.fuel.displayName)"
|
||||
case .worthTheDrive: return "Best Value \(entry.fuel.displayName)"
|
||||
case .cheapest: return "Cheapest \(entry.fuel.displayName)"
|
||||
}
|
||||
}
|
||||
|
||||
private var singleStation: some View {
|
||||
@@ -85,7 +88,15 @@ struct FuelPriceWidgetContent: View {
|
||||
Text(station.name)
|
||||
.font(.headline)
|
||||
.lineLimit(1)
|
||||
if let price = station.prices[entry.fuel] {
|
||||
if entry.sort == .worthTheDrive, let result = entry.bestValueResult {
|
||||
Text(result.headline(compact: true))
|
||||
.font(.title3.weight(.bold))
|
||||
.lineLimit(1)
|
||||
Text(result.status == .baseline ? "your baseline" : "vs closest station")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
} else if let price = station.prices[entry.fuel] {
|
||||
FuelStore.priceTextAttributed(price, size: 26, weight: .bold, color: .green)
|
||||
}
|
||||
if let location = entry.location {
|
||||
|
||||
@@ -144,10 +144,10 @@ enum FuelSavingsFormat {
|
||||
}
|
||||
|
||||
/// Compact comparison copy kept outside the coloured verdict capsule.
|
||||
/// "Nearest" refers to the existing shortest-journey baseline selection;
|
||||
/// "Closest" refers to the existing shortest-journey baseline selection;
|
||||
/// it does not claim a straight-line comparison.
|
||||
static func comparisonLabel(status: FuelSavingsStatus) -> String {
|
||||
status == .baseline ? "your baseline" : "vs nearest station"
|
||||
status == .baseline ? "your baseline" : "vs closest station"
|
||||
}
|
||||
|
||||
/// VoiceOver form of `extraTravelLabel`. The abbreviation and the unit suffix
|
||||
@@ -222,13 +222,11 @@ extension FuelSavingsResult {
|
||||
headline().replacingOccurrences(of: "Approx. ", with: "Approximately ")
|
||||
}
|
||||
|
||||
/// Row capsule copy: the headline, plus what the baseline row IS. Without the
|
||||
/// suffix the baseline capsule just repeated the status word above it
|
||||
/// ("Closest station" twice) and said nothing about the saving being measured
|
||||
/// against it.
|
||||
/// Row capsule copy. The baseline relationship is shown as secondary text
|
||||
/// beside the capsule in the station listing and detail summary, so the
|
||||
/// capsule itself contains only the verdict.
|
||||
func capsuleHeadline() -> String {
|
||||
let headline = headline(compact: true)
|
||||
return status == .baseline ? "\(headline) · your baseline" : headline
|
||||
headline(compact: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user