From ab93f6286acc936d0e3699dd77abb523fbd44ae4 Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Sun, 18 Oct 2026 20:16:30 +0100 Subject: [PATCH] Add the Key button to the Favourites tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Favourites gains the same info button the Stations list has, last in the trailing toolbar group so it sits to the right of Edit. It stays visible when the list is empty, because the legend explains the prices and labels before anything is starred — Trends and Edit still hide with no favourites. The legend itself is now one view, KeySheetView, lifted out of StationsView's private computed property so both tabs present the same sheet and cannot drift apart. It lives in StationsView.swift because the key's row types are file-private there, which keeps the Xcode project untouched. Stations passes its selected fuel's tint and Favourites its active tab's, so the station-label pump badge matches the rows on screen; everything else — detents, the two nested sheets, and the -showKeySheet / -showBestValueInfo / -showImportantInfo QA launch hooks — behaves exactly as before. --- FuelBoard/FavouritesView.swift | 29 +++++- FuelBoard/StationsView.swift | 162 +++++++++++++++++++-------------- 2 files changed, 123 insertions(+), 68 deletions(-) diff --git a/FuelBoard/FavouritesView.swift b/FuelBoard/FavouritesView.swift index 6c4abd6..b9f7d92 100644 --- a/FuelBoard/FavouritesView.swift +++ b/FuelBoard/FavouritesView.swift @@ -48,6 +48,14 @@ struct FavouritesView: View { /// Trends sheet (price history chart) presentation state. @State private var showTrends = false + /// The Key (legend) sheet — the same sheet the Stations tab presents, opened + /// from the info button beside Edit. + @State private var showKey = false + /// The Key sheet's two nested sheets, owned here because its rows set them + /// (same arrangement as StationsView, so the QA hooks behave identically). + @State private var showBestValueInfo = false + @State private var showImportantInformation = false + /// Station picked for the More-info sheet (tap action = showDetails). @State private var detailStation: FuelStation? @@ -206,8 +214,8 @@ struct FavouritesView: View { } .navigationTitle(String(localized: "Favourites", comment: "Favourites tab title")) .toolbar { - if !favourites.isEmpty { - ToolbarItemGroup(placement: .topBarTrailing) { + ToolbarItemGroup(placement: .topBarTrailing) { + if !favourites.isEmpty { Button { showTrends = true } label: { @@ -216,8 +224,25 @@ struct FavouritesView: View { } EditButton() } + // Same button and same slot as the Stations tab: last in the + // group, so it sits to the right of Edit. Shown even with no + // favourites — the legend explains the prices and labels, which + // matter before anything is starred. + Button { + showKey = true + } label: { + Image(systemName: "info.circle") + } + .accessibilityLabel(String(localized: "Key", comment: "Key button accessibility label")) } } + .sheet(isPresented: $showKey) { + KeySheetView( + fuelTint: activeFuel.tintColor, + showBestValueInfo: $showBestValueInfo, + showImportantInformation: $showImportantInformation + ) + } .sheet(isPresented: $showTrends) { TrendsView( favourites: favourites, diff --git a/FuelBoard/StationsView.swift b/FuelBoard/StationsView.swift index cc87de8..f7ca052 100644 --- a/FuelBoard/StationsView.swift +++ b/FuelBoard/StationsView.swift @@ -1,6 +1,94 @@ import SwiftUI import WidgetKit +/// The "Key" legend: price ratings, savings capsules, station labels, the +/// favourite glyph, then the two deeper explanations. One sheet for both list +/// tabs — Stations opens it from the info button in the navigation bar, +/// Favourites from the button beside Edit — so the legend cannot drift between +/// them. +/// +/// Dismisses through the environment rather than the presenting view's state, and +/// takes the two nested-sheet flags as bindings, so each tab keeps owning its own +/// QA launch-argument hooks. +struct KeySheetView: View { + /// The selected fuel's colour. `KeyStationLabelIndicator` draws its pump badge + /// in it, so the legend matches the rows the reader is looking at. + let fuelTint: Color + @Binding var showBestValueInfo: Bool + @Binding var showImportantInformation: Bool + + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationStack { + List { + Section { + KeySheetDescriptionRow(item: KeySheetContent.priceRatings[0]) { + KeyPriceRatingIndicator(item: KeySheetContent.priceRatings[0], color: .green) + } + KeySheetDescriptionRow(item: KeySheetContent.priceRatings[1]) { + KeyPriceRatingIndicator(item: KeySheetContent.priceRatings[1], color: .orange) + } + KeySheetDescriptionRow(item: KeySheetContent.priceRatings[2]) { + KeyPriceRatingIndicator(item: KeySheetContent.priceRatings[2], color: .red) + } + } header: { + Text("Price ratings") + } + + Section { + ForEach(KeySheetContent.savings, id: \.label) { item in + KeySheetDescriptionRow(item: item) { + KeySavingsCapsule(label: item.label) + } + } + } header: { + Text("Savings") + } + + Section { + ForEach(KeySheetContent.stationLabels, id: \.label) { item in + KeySheetDescriptionRow(item: item) { + KeyStationLabelIndicator(label: item.label, fuelColor: fuelTint) + } + } + } header: { + Text("Station labels") + } + + Section { + KeySheetDescriptionRow(item: KeySheetContent.favourite) { + FavouriteStarGlyph(isFavourite: true) + } + } header: { + Text("Favourites") + } + + Section { + HowBestValueRow { showBestValueInfo = true } + } + + Section { + ImportantInformationRow { showImportantInformation = true } + } + } + .navigationTitle("Key") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { dismiss() } + } + } + // Nested sheet: the Key sheet stays behind the explanation. + .sheet(isPresented: $showBestValueInfo) { HowBestValueSheet() } + // Nested sheet: the Key sheet stays behind the disclaimer. + .importantInformationSheet(isPresented: $showImportantInformation) + } + .presentationDetents([.fraction(0.6), .large]) + .presentationBackground(Color(UIColor.systemGroupedBackground)) + } +} + /// Stations tab — the main list with fuel picker, distance filter, sort, pagination. struct StationsView: View { let stations: [FuelStation] @@ -296,73 +384,15 @@ struct StationsView: View { ) } + /// The legend sheet, shared with the Favourites tab. Only the fuel differs: + /// its colour tints the station-label row, so the legend matches the rows the + /// reader is actually looking at. private var keySheet: some View { - NavigationStack { - List { - Section { - KeySheetDescriptionRow(item: KeySheetContent.priceRatings[0]) { - KeyPriceRatingIndicator(item: KeySheetContent.priceRatings[0], color: .green) - } - KeySheetDescriptionRow(item: KeySheetContent.priceRatings[1]) { - KeyPriceRatingIndicator(item: KeySheetContent.priceRatings[1], color: .orange) - } - KeySheetDescriptionRow(item: KeySheetContent.priceRatings[2]) { - KeyPriceRatingIndicator(item: KeySheetContent.priceRatings[2], color: .red) - } - } header: { - Text("Price ratings") - } - - Section { - ForEach(KeySheetContent.savings, id: \.label) { item in - KeySheetDescriptionRow(item: item) { - KeySavingsCapsule(label: item.label) - } - } - } header: { - Text("Savings") - } - - Section { - ForEach(KeySheetContent.stationLabels, id: \.label) { item in - KeySheetDescriptionRow(item: item) { - KeyStationLabelIndicator(label: item.label, fuelColor: selectedFuel.tintColor) - } - } - } header: { - Text("Station labels") - } - - Section { - KeySheetDescriptionRow(item: KeySheetContent.favourite) { - FavouriteStarGlyph(isFavourite: true) - } - } header: { - Text("Favourites") - } - - Section { - HowBestValueRow { showBestValueInfo = true } - } - - Section { - ImportantInformationRow { showImportantInformation = true } - } - } - .navigationTitle("Key") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .confirmationAction) { - Button("Done") { showKey = false } - } - } - // Nested sheet: the Key sheet stays behind the explanation. - .sheet(isPresented: $showBestValueInfo) { HowBestValueSheet() } - // Nested sheet: the Key sheet stays behind the disclaimer. - .importantInformationSheet(isPresented: $showImportantInformation) - } - .presentationDetents([.fraction(0.6), .large]) - .presentationBackground(Color(UIColor.systemGroupedBackground)) + KeySheetView( + fuelTint: selectedFuel.tintColor, + showBestValueInfo: $showBestValueInfo, + showImportantInformation: $showImportantInformation + ) } private func stationRow(_ station: FuelStation) -> some View {