Test alert uses real data: actual cheapest station within radius

The first Settings test button no longer sends a hardcoded placeholder —
it runs the same criteria as a live alert (monitored fuel, alert radius,
current location) against the loaded stations and sends the REAL station
name, brand and price. Result line shown under the button; graceful
message when no stations or no sellers within radius.
This commit is contained in:
FuelBoard Contributor
2026-08-12 09:34:17 +01:00
parent ae37e224c6
commit 950844878c
3 changed files with 62 additions and 19 deletions
+42 -11
View File
@@ -11,6 +11,7 @@ import SwiftUI
final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLocationManagerDelegate, UNUserNotificationCenterDelegate {
@Published var monitoredStationIDs: [String] = []
@Published var lastAlert: String?
@Published private(set) var lastTestResult: String?
private let manager = CLLocationManager()
private var stations: [FuelStation] = []
@@ -176,19 +177,49 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
lastAlert = "\(brand) · \(String(format: "%.1fp", price)) · \(unit.format(radiusKM)) radius"
}
/// Fires a sample local notification shaped exactly like a real price
/// alert, for the Settings "test" button. Uses a placeholder station so
/// the user can see the alert format without a real entry event. Tapping
/// it does what tapping any FuelBoard alert does opens the app.
static func sendTestNotification(fuel: FuelType, radiusKM: Double) {
/// Fires a test alert built from REAL data: the cheapest station selling
/// the monitored fuel within the configured alert radius of the current
/// location the same criteria a live geofence entry would apply, minus
/// the geofence itself. Uses the monitor's live state (fuel, radius,
/// stations) so the notification reflects the Alerts-tab configuration.
func sendTestNotification() {
let unit = FuelStore.loadDistanceUnit()
let content = UNMutableNotificationContent()
content.title = "Cheapest \(fuel.displayName) nearby: Sample Filling Station"
content.body = "Sample Filling Station is the cheapest within \(unit.format(radiusKM)) at 132.9p. Tap to open."
content.sound = .default
let location = FuelStore.loadLocation()
let sellers = stations.filter { $0.prices[fuel] != nil }
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
// Cheapest within radius, judged around the current location
// identical to handleEntry's withinRadius logic.
let inRadius = sellers.filter { station in
guard let location else { return true }
return station.distanceKM(to: location.lat, lng2: location.lng) <= radiusKM
}
let candidate = inRadius.min { ($0.prices[fuel] ?? .infinity) < ($1.prices[fuel] ?? .infinity) }
if let candidate, let price = candidate.prices[fuel] {
let brand = candidate.brand.isEmpty ? candidate.name : candidate.brand
let content = UNMutableNotificationContent()
content.title = "Cheapest \(fuel.displayName) nearby: \(brand)"
content.body = "\(candidate.name) is the cheapest within \(unit.format(radiusKM)) at \(String(format: "%.1fp", price)). Tap to open."
content.sound = .default
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
lastTestResult = "\(fuel.displayName) · \(brand) · \(String(format: "%.1fp", price)) · radius \(unit.format(radiusKM))"
} else {
// No real candidate (no stations loaded yet, or none sell the
// monitored fuel) still fire so delivery is testable, but say
// why the real criteria found nothing.
let content = UNMutableNotificationContent()
content.title = "FuelBoard test alert"
content.body = sellers.isEmpty
? "No station data loaded yet — open the Stations tab first, then retry."
: "No \(fuel.displayName.lowercased()) station within \(unit.format(radiusKM)) of you."
content.sound = .default
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
lastTestResult = sellers.isEmpty
? "No stations loaded — open the Stations tab first"
: "No \(fuel.displayName.lowercased()) seller within \(unit.format(radiusKM))"
}
}
/// Fires a notification with NO criteria checks at all no geofence, no