Tabs (Stations/Favourites/Alerts): favourites with cheapest-first ranking, proximity geofence alerts (favourites priority, 3km default radius, 1h dedup), Always location + background mode
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user