130 lines
5.4 KiB
Swift
130 lines
5.4 KiB
Swift
import SwiftUI
|
|
import WidgetKit
|
|
|
|
/// Stations tab — the main list with fuel picker, sort, station count limit.
|
|
struct StationsView: View {
|
|
let stations: [FuelStation]
|
|
let totalCount: Int
|
|
let isLoading: Bool
|
|
@Binding var selectedFuel: FuelType
|
|
@Binding var sortMode: SortMode
|
|
@Binding var stationLimit: Int
|
|
let cheapestPrice: Double?
|
|
let location: Coordinate?
|
|
let favouriteIDs: Set<String>
|
|
var onToggleFavourite: (FuelStation) -> Void = { _ in }
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List {
|
|
Section {
|
|
Text("\(sortMode == .closest ? "Closest" : "Cheapest") \(selectedFuel.displayName) — tap a station for directions.")
|
|
.font(.footnote)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
Section("Fuel type") {
|
|
Picker("Fuel type", selection: $selectedFuel) {
|
|
ForEach(FuelType.allCases) { fuel in
|
|
Text(fuel.displayName).tag(fuel)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.onChange(of: selectedFuel) { _, newValue in
|
|
FuelStore.saveSelectedFuel(newValue)
|
|
WidgetCenter.shared.reloadAllTimelines()
|
|
}
|
|
}
|
|
|
|
Section("Stations") {
|
|
Picker("Sort by", selection: $sortMode) {
|
|
ForEach(SortMode.allCases) { mode in
|
|
Text(mode.displayName).tag(mode)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.onChange(of: sortMode) { _, newValue in
|
|
FuelStore.saveSortMode(newValue)
|
|
}
|
|
.padding(.vertical, 2)
|
|
|
|
if isLoading {
|
|
HStack(spacing: 10) {
|
|
ProgressView()
|
|
Text("Fetching prices…")
|
|
}
|
|
} else if stations.isEmpty {
|
|
Text("No \(selectedFuel.displayName) stations found.")
|
|
.foregroundStyle(.secondary)
|
|
} else {
|
|
ForEach(Array(stations.enumerated()), id: \.element.id) { index, station in
|
|
StationRow(
|
|
station: station,
|
|
fuel: selectedFuel,
|
|
location: location,
|
|
cheapestPrice: cheapestPrice,
|
|
isTopResult: index == 0,
|
|
isFavourite: favouriteIDs.contains(station.id),
|
|
onToggleFavourite: { onToggleFavourite(station) }
|
|
)
|
|
}
|
|
}
|
|
|
|
if !stations.isEmpty {
|
|
Divider()
|
|
Picker("Show", selection: $stationLimit) {
|
|
ForEach([10, 25, 50, 75, 100], id: \.self) { count in
|
|
Text("\(count)").tag(count)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.onChange(of: stationLimit) { _, newValue in
|
|
FuelStore.saveStationLimit(newValue)
|
|
}
|
|
.padding(.vertical, 2)
|
|
Text("Showing \(stations.count) of \(totalCount) stations")
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
|
|
Section("Key") {
|
|
HStack(spacing: 8) {
|
|
Circle().fill(.green).frame(width: 12, height: 12)
|
|
Text("Best value — within 1.5p of the cheapest")
|
|
.font(.caption)
|
|
}
|
|
HStack(spacing: 8) {
|
|
Circle().fill(.orange).frame(width: 12, height: 12)
|
|
Text("Okay — within 4p of the cheapest")
|
|
.font(.caption)
|
|
}
|
|
HStack(spacing: 8) {
|
|
Circle().fill(.red).frame(width: 12, height: 12)
|
|
Text("Pricey — more than 4p over the cheapest")
|
|
.font(.caption)
|
|
}
|
|
HStack(spacing: 8) {
|
|
Text("TOP")
|
|
.font(.caption2.bold())
|
|
.padding(.horizontal, 5)
|
|
.padding(.vertical, 1)
|
|
.background(Capsule().fill(.blue.opacity(0.15)))
|
|
.foregroundStyle(.blue)
|
|
Text("Top result for the current sort")
|
|
.font(.caption)
|
|
}
|
|
HStack(spacing: 8) {
|
|
Image(systemName: "star.fill")
|
|
.font(.caption2)
|
|
.foregroundStyle(.yellow)
|
|
Text("Star a station to add it to Favourites")
|
|
.font(.caption)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("FuelBoard")
|
|
}
|
|
}
|
|
}
|