diff --git a/FuelBoard/FuelSavingsViews.swift b/FuelBoard/FuelSavingsViews.swift index 5391a8b..cbec107 100644 --- a/FuelBoard/FuelSavingsViews.swift +++ b/FuelBoard/FuelSavingsViews.swift @@ -78,6 +78,7 @@ struct FuelSavingsSummaryView: View { /// Sizes the "extra return travel" caption in the user's own unit (the /// stored figure is miles) — used by both the row banner and the detail sheet. var distanceUnit: DistanceUnit = .miles + var showInfoAction: (() -> Void)? = nil var body: some View { switch availability { @@ -166,6 +167,22 @@ struct FuelSavingsSummaryView: View { .font(.caption2) .foregroundStyle(.secondary) } + if let showInfoAction { + Rectangle() + .fill(Color(.separator)) + .frame(height: 0.5) + .padding(.vertical, 4) + Button(action: showInfoAction) { + HStack(spacing: 8) { + Text("How Best Value works") + Spacer(minLength: 8) + Image(systemName: "chevron.forward") + .font(.footnote.weight(.semibold)) + .foregroundStyle(.tertiary) + } + } + .buttonStyle(.plain) + } } } case .unavailable(let reason): diff --git a/FuelBoard/OnboardingView.swift b/FuelBoard/OnboardingView.swift index 314c782..ae9ed39 100644 --- a/FuelBoard/OnboardingView.swift +++ b/FuelBoard/OnboardingView.swift @@ -108,10 +108,10 @@ struct OnboardingView: View { .frame(width: 96, height: 96) .clipShape(RoundedRectangle(cornerRadius: 21, style: .continuous)) } - .padding(.bottom, 28) + .padding(.bottom, 16) - Text(String(localized: "Welcome to FuelBoard", comment: "Onboarding welcome title")) - .font(.largeTitle.bold()) + Text(String(localized: "Welcome to\nFuelBoard", comment: "Onboarding welcome title")) + .font(.title.bold()) .multilineTextAlignment(.center) Text(String(localized: "The cheapest petrol, diesel and premium fuel near you — from the official UK Fuel Finder data.", comment: "Onboarding welcome subtitle")) @@ -121,12 +121,13 @@ struct OnboardingView: View { .padding(.horizontal, 32) .padding(.top, 12) - featureRow(icon: "globe.europe.africa.fill", text: String(localized: "UK-wide prices — 8,000+ stations, updated twice a day", comment: "Onboarding feature row")) - featureRow(icon: "scope", text: String(localized: "Cheapest within your chosen radius, or closest station first", comment: "Onboarding feature row")) - featureRow(icon: "star.fill", text: String(localized: "Favourites with instant price comparison", comment: "Onboarding feature row")) - featureRow(icon: "chart.bar.fill", text: String(localized: "Green/amber/red rating vs the best nearby price", comment: "Onboarding feature row")) - featureRow(icon: "square.grid.2x2.fill", text: String(localized: "Home-screen widget showing the cheapest nearby", comment: "Onboarding feature row")) - featureRow(icon: "bell.fill", text: String(localized: "Alerts when you approach the cheapest station", comment: "Onboarding feature row")) + featureRow(icon: "map.fill", title: String(localized: "Compare fuel prices near you", comment: "Onboarding feature row title"), subtitle: String(localized: "Over 8,000 stations across Great Britain, with prices updated daily.", comment: "Onboarding feature row subtitle")) + + featureRow(icon: "chart.line.uptrend.xyaxis", title: String(localized: "See how much you could save", comment: "Onboarding feature row title"), subtitle: String(localized: "Compare fill-up costs and potential savings at different stations.", comment: "Onboarding feature row subtitle")) + + featureRow(icon: "car.fill", title: String(localized: "Know if it's worth the drive", comment: "Onboarding feature row title"), subtitle: String(localized: "Factor in your journey and fuel costs to find the best value.", comment: "Onboarding feature row subtitle")) + + featureRow(icon: "star.fill", title: String(localized: "Keep your favourites close", comment: "Onboarding feature row title"), subtitle: String(localized: "Save stations and quickly compare their latest prices.", comment: "Onboarding feature row subtitle")) Spacer() } @@ -298,18 +299,23 @@ struct OnboardingView: View { OnboardingPrimaryButton(title: title, action: action) } - private func featureRow(icon: String, text: String) -> some View { - HStack(spacing: 12) { + private func featureRow(icon: String, title: String, subtitle: String) -> some View { + HStack(alignment: .top, spacing: 12) { Image(systemName: icon) .font(.system(size: 20, weight: .semibold)) .foregroundStyle(Color.accentColor) .frame(width: 32) - Text(text) - .font(.subheadline) + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.subheadline.bold()) + Text(subtitle) + .font(.subheadline) + .foregroundStyle(.secondary) + } Spacer(minLength: 0) } .padding(.horizontal, 36) - .padding(.vertical, 10) + .padding(.vertical, 8) } private func permissionStatusLabel(granted: Bool, denied: Bool, deniedText: String) -> some View { diff --git a/FuelBoard/StationDetailView.swift b/FuelBoard/StationDetailView.swift index 41599e6..e0faed1 100644 --- a/FuelBoard/StationDetailView.swift +++ b/FuelBoard/StationDetailView.swift @@ -238,18 +238,12 @@ struct StationDetailView: View { availability: fuelSavings, amount: fuelSavingsAmount, baselineStationName: savingsBaselineStationName, - distanceUnit: distanceUnit + distanceUnit: distanceUnit, + showInfoAction: { showBestValueInfo = true } ) } header: { Text("Best Value") } - - // The explanation lives in its own sheet now, opened from this - // row. The row sits INSIDE the `if let` so it carries the same - // gate as the card above: no card, no row. - Section { - HowBestValueRow { showBestValueInfo = true } - } } #if DEBUG diff --git a/FuelBoard/StationsView.swift b/FuelBoard/StationsView.swift index 02a0671..4a16fc9 100644 --- a/FuelBoard/StationsView.swift +++ b/FuelBoard/StationsView.swift @@ -267,14 +267,14 @@ struct StationsView: View { keyCard( icon: "checkmark.circle.fill", color: .green, title: "Good price", blurb: "Within 1.5p of the cheapest", - threshold: "≤1.5p") + threshold: "0–1.5p") keyCard( icon: "equal.circle.fill", color: .orange, - title: "Okay", blurb: "Within 4p of the cheapest", - threshold: "≤4p") + title: "Okay", blurb: "More than 1.5p, up to 4p above the cheapest", + threshold: ">1.5–4p") keyCard( icon: "exclamationmark.circle.fill", color: .red, - title: "Pricey", blurb: "More than 4p over the cheapest", + title: "Pricey", blurb: "More than 4p above the cheapest", threshold: ">4p") Rectangle() .fill(Color(.separator)) @@ -306,10 +306,11 @@ struct StationsView: View { .padding(.horizontal, 14) .padding(.vertical, 12) .keyCardFill(color: .yellow) - Text("Colours match each station's rating on the list") + Text("Price differences are shown in pence per litre (p/litre). Colours match each station's rating on the list.") .font(.footnote) .foregroundStyle(.secondary) - .frame(maxWidth: .infinity) + .frame(maxWidth: .infinity, alignment: .center) + .multilineTextAlignment(.center) .padding(.top, 4) } .listRowBackground(Color.clear) @@ -377,6 +378,7 @@ struct StationsView: View { Text(LocalizedStringKey(blurb)) .font(.caption) .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) } Spacer(minLength: 8) HStack(spacing: 6) { diff --git a/FuelBoard/en.lproj/Localizable.strings b/FuelBoard/en.lproj/Localizable.strings index 8eec127..e5445e4 100644 --- a/FuelBoard/en.lproj/Localizable.strings +++ b/FuelBoard/en.lproj/Localizable.strings @@ -26,15 +26,15 @@ "Good price" = "Good price"; "Within 1.5p of the cheapest" = "Within 1.5p of the cheapest"; "Okay" = "Okay"; -"Within 4p of the cheapest" = "Within 4p of the cheapest"; +"More than 1.5p, up to 4p above the cheapest" = "More than 1.5p, up to 4p above the cheapest"; "Pricey" = "Pricey"; -"More than 4p over the cheapest" = "More than 4p over the cheapest"; +"More than 4p above the cheapest" = "More than 4p above the cheapest"; "Top result for the current sort" = "Top result for the current sort"; "Star a station to add it to Favourites" = "Star a station to add it to Favourites"; /* Star button in the More info sheet heading (accessibility labels) */ "Add to favourites" = "Add to favourites"; "Remove from favourites" = "Remove from favourites"; -"Colours match each station's rating on the list" = "Colours match each station's rating on the list"; +"Price differences are shown in pence per litre (p/litre). Colours match each station's rating on the list." = "Price differences are shown in pence per litre (p/litre). Colours match each station's rating on the list."; "How Best Value works" = "How Best Value works"; "Best Value compares each station with the closest one. It works out what you could save at the pump, then takes off the fuel used for the extra return journey." = "Best Value compares each station with the closest one. It works out what you could save at the pump, then takes off the fuel used for the extra return journey."; "Your fill-up amount and MPG are used to show whether you could save overall." = "Your fill-up amount and MPG are used to show whether you could save overall.";