diff --git a/FuelBoard/FavouritesView.swift b/FuelBoard/FavouritesView.swift index 818a6b4..22c4f2b 100644 --- a/FuelBoard/FavouritesView.swift +++ b/FuelBoard/FavouritesView.swift @@ -1,21 +1,39 @@ import SwiftUI -/// Favourites tab — starred stations, ranked cheapest-first for the selected -/// fuel, with the cheapest favourite called out at the top. +/// Favourites tab — starred stations, split by fuel type. Each fuel type +/// with at least one favourite appears as its own tab; that tab's favourites +/// are ranked cheapest-first for that fuel, with the cheapest called out. struct FavouritesView: View { let favourites: [FuelStation] + /// The app's currently selected fuel — used only as the initial tab. let selectedFuel: FuelType let location: Coordinate? let distanceUnit: DistanceUnit let favouriteIDs: Set var onToggleFavourite: (FuelStation) -> Void = { _ in } - /// Favourites that sell the selected fuel, cheapest first (distance tiebreak). + /// Fuel types that currently have at least one favourite selling them — + /// these are the only tabs shown. + private var availableFuels: [FuelType] { + FuelType.allCases.filter { fuel in + favourites.contains { $0.prices[fuel] != nil } + } + } + + /// The active tab. Falls back if the chosen fuel ever loses all its + /// favourites (e.g. the last one is un-starred while viewing it). + @State private var fuel: FuelType = .e10 + + private var activeFuel: FuelType { + availableFuels.contains(fuel) ? fuel : (availableFuels.first ?? .e10) + } + + /// Favourites that sell the active fuel, cheapest first (distance tiebreak). private var ranked: [FuelStation] { - let available = favourites.filter { $0.prices[selectedFuel] != nil } + let available = favourites.filter { $0.prices[activeFuel] != nil } return available.sorted { lhs, rhs in - let lPrice = lhs.prices[selectedFuel]! - let rPrice = rhs.prices[selectedFuel]! + let lPrice = lhs.prices[activeFuel]! + let rPrice = rhs.prices[activeFuel]! if lPrice != rPrice { return lPrice < rPrice } guard let location else { return false } return lhs.distanceKM(to: location.lat, lng2: location.lng) < @@ -24,7 +42,22 @@ struct FavouritesView: View { } private var cheapest: FuelStation? { ranked.first } - private var cheapestPrice: Double? { cheapest?.prices[selectedFuel] } + private var cheapestPrice: Double? { cheapest?.prices[activeFuel] } + + init(favourites: [FuelStation], + selectedFuel: FuelType, + location: Coordinate?, + distanceUnit: DistanceUnit, + favouriteIDs: Set, + onToggleFavourite: @escaping (FuelStation) -> Void = { _ in }) { + self.favourites = favourites + self.selectedFuel = selectedFuel + self.location = location + self.distanceUnit = distanceUnit + self.favouriteIDs = favouriteIDs + self.onToggleFavourite = onToggleFavourite + _fuel = State(initialValue: selectedFuel) + } var body: some View { NavigationStack { @@ -46,22 +79,31 @@ struct FavouritesView: View { .padding(.vertical, 24) } } else { + Section("Fuel type") { + Picker("Fuel type", selection: $fuel) { + ForEach(availableFuels) { fuel in + Text(fuel.shortName).tag(fuel) + } + } + .pickerStyle(.segmented) + } + Section { if let cheapest, let price = cheapestPrice { HStack(spacing: 10) { Image(systemName: "crown.fill") .foregroundStyle(.yellow) - Text("Cheapest favourite: \(cheapest.name) — \(String(format: "%.1fp", price))") + Text("Cheapest \(activeFuel.displayName) favourite: \(cheapest.name) — \(String(format: "%.1fp", price))") .font(.footnote) } } } - Section("Favourites — cheapest first") { + Section("\(activeFuel.displayName) favourites — cheapest first") { ForEach(Array(ranked.enumerated()), id: \.element.id) { index, station in StationRow( station: station, - fuel: selectedFuel, + fuel: activeFuel, location: location, distanceUnit: distanceUnit, baselinePrice: cheapestPrice, @@ -74,7 +116,7 @@ struct FavouritesView: View { if let cheapest = cheapest { Section { - Text("\(cheapest.name) is your cheapest favourite for \(selectedFuel.displayName). Favourites are monitored automatically — switch to the Alerts tab and enable alerts to get pinged when you approach one that's the cheapest within the radius.") + Text("\(cheapest.name) is your cheapest \(activeFuel.displayName) favourite. Favourites are monitored automatically — switch to the Alerts tab and enable alerts to get pinged when you approach one that's the cheapest within the radius.") .font(.caption) .foregroundStyle(.secondary) }