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