How Best Value works: its own sheet, opened from the Key sheet and More info sheet

The explanation used to be a section inside the Key sheet. It is a standalone
HowBestValueSheet now, opened from a tappable row with a chevron: at the bottom of
the Key sheet, and at the bottom of the More info sheet below the Best Value card.

Gating: the More info row sits INSIDE the same `if let fuelSavings` as the card it
follows, so it disappears with the feature. Verified with Best Value toggled off in
Settings: the More info sheet then shows neither the card nor the row. The Key
sheet row is deliberately ungated — that sheet is the legend and is reachable
whatever the feature is doing.

Copy is unchanged: the same Localizable.strings strings are reused, and the old
section header became the sheet's title. A DEBUG-only `-showBestValueInfo` hook
opens the sheet without a tap.
This commit is contained in:
FuelBoard Contributor
2026-10-14 14:37:18 +01:00
parent 78b43ce6d7
commit 93df94ddc9
3 changed files with 106 additions and 9 deletions
+25 -9
View File
@@ -35,6 +35,11 @@ struct StationsView: View {
/// info button in the header — keeps the list focused on stations.
@State private var showKey = false
/// The "How Best Value works" sheet. The explanation used to be a section in
/// the Key sheet; it is its own sheet now, opened from a row at the bottom of
/// the Key sheet and from the More info sheet.
@State private var showBestValueInfo = false
/// Station picked for the More-info sheet (tap action = showDetails).
@State private var detailStation: FuelStation?
@@ -218,6 +223,18 @@ struct StationsView: View {
if ProcessInfo.processInfo.arguments.contains("-showKeySheet") {
showKey = true
}
// QA hook: `-showBestValueInfo` opens the Key sheet and then the
// explanation on top of it, so the copy can be screenshotted
// without a tap. It has to chain: the explanation's `.sheet` lives
// inside the Key sheet's content, so it cannot present while the
// Key sheet is shut.
if BestValueInfoQA.isRequested {
showKey = true
Task { @MainActor in
try? await Task.sleep(nanoseconds: 1_200_000_000)
showBestValueInfo = true
}
}
#endif
}
.task(id: stations.count) {
@@ -299,15 +316,12 @@ struct StationsView: View {
.listRowInsets(EdgeInsets(top: 4, leading: 16, bottom: 4, trailing: 16))
}
Section("How Best Value works") {
VStack(alignment: .leading, spacing: 10) {
Text("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.")
Text("Your fill-up amount and MPG are used to show whether you could save overall.")
Text("Results are marked “Approx.” until road distances are available.")
.foregroundStyle(.secondary)
}
.font(.subheadline)
.padding(.vertical, 4)
// The explanation is its own sheet now, opened from this
// row. Deliberately NOT gated by Best Value: the Key sheet
// is the legend and is reachable whatever the feature is
// doing.
Section {
HowBestValueRow { showBestValueInfo = true }
}
}
.navigationTitle("Key")
@@ -317,6 +331,8 @@ struct StationsView: View {
Button("Done") { showKey = false }
}
}
// Nested sheet: the Key sheet stays behind the explanation.
.sheet(isPresented: $showBestValueInfo) { HowBestValueSheet() }
}
.presentationDetents([.fraction(0.6)])
.presentationBackground(Color(UIColor.systemGroupedBackground))