Show full fetch in list (count picker respected); radius scope only for cheapest/RAG reference; row = logo | name/address/distance | £price + delta | star

This commit is contained in:
FuelBoard Contributor
2026-08-11 17:54:04 +01:00
parent 8230daa249
commit 810cc2c006
+23 -17
View File
@@ -21,10 +21,11 @@ struct ContentView: View {
@State private var locationManager = LocationManager()
@StateObject private var monitor = ProximityMonitor()
/// Stations within the alert trigger radius of the current location, used
/// as the pool for "cheapest" (RAG reference, TOP, sorting). Falls back to
/// the full fetch when location is unknown or the radius pool is empty
/// (rural areas) so the list is never blank.
/// Stations within the alert trigger radius of the current location used
/// ONLY as the "cheapest" reference (RAG, TOP, deltas), so the cheapest
/// label matches what alerts would flag. The list itself shows the full
/// fetch so the count picker works. Falls back to the full fetch when
/// location is unknown or the radius pool is empty.
private var radiusScopedStations: [FuelStation] {
guard let location else { return stations }
let within = stations.filter {
@@ -42,7 +43,9 @@ struct ContentView: View {
}
private var sortedStations: [FuelStation] {
let available = radiusScopedStations.filter { $0.prices[selectedFuel] != nil }
// Full fetch pool NOT radius-scoped, so the results count picker
// (10/25/50/75/100) is respected.
let available = stations.filter { $0.prices[selectedFuel] != nil }
switch sortMode {
case .closest:
guard let location else { return available.sorted { $0.prices[selectedFuel]! < $1.prices[selectedFuel]! } }
@@ -224,7 +227,7 @@ struct StationRow: View {
var body: some View {
HStack(spacing: 12) {
// Round brand logo (or generic fuel pump fallback)
// LEFT round brand logo (or generic fuel pump fallback)
Group {
if let asset = station.brandImageName {
Image(asset)
@@ -243,10 +246,9 @@ struct StationRow: View {
.overlay(Circle().stroke(Color.primary.opacity(0.08), lineWidth: 1))
.shadow(color: .black.opacity(0.08), radius: 2, y: 1)
// Name at natural width (does NOT fill the row truncates);
// price + details stay on the row below, right-aligned.
VStack(alignment: .leading, spacing: 3) {
HStack(alignment: .firstTextBaseline, spacing: 6) {
// MIDDLE name, full address, distance
VStack(alignment: .leading, spacing: 2) {
HStack(spacing: 6) {
Text(station.name)
.font(.headline)
.lineLimit(1)
@@ -259,28 +261,32 @@ struct StationRow: View {
.background(Capsule().fill(.blue.opacity(0.15)))
.foregroundStyle(.blue)
}
Spacer(minLength: 6)
}
HStack(spacing: 5) {
Text("\(station.address), \(station.postcode)")
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
Spacer(minLength: 8)
.truncationMode(.tail)
if let location {
Text(String(format: "%.1f km", station.distanceKM(to: location.lat, lng2: location.lng)))
.font(.caption2)
.foregroundStyle(.secondary)
.monospacedDigit()
}
}
.frame(maxWidth: .infinity, alignment: .leading)
// RIGHT price in £ (no p) + difference vs cheapest
VStack(alignment: .trailing, spacing: 2) {
if let price = station.prices[fuel] {
HStack(spacing: 5) {
Circle()
.fill(ragColor)
.frame(width: 10, height: 10)
Text(String(format: "%.1fp", price))
.frame(width: 8, height: 8)
Text(String(format: "£%.3f", price / 100))
.font(.title3.bold())
.monospacedDigit()
}
if let deltaText {
Text(deltaText)
.font(.caption2.bold())
@@ -288,8 +294,8 @@ struct StationRow: View {
}
}
}
}
// FAR RIGHT favourite star
Button {
onToggleFavourite()
} label: {