Both test buttons carry real station coords; map status surfaced
- Plain test notification now picks the nearest station selling the monitored fuel and carries its real name, price, distance and coordinates (was a static no-station notification) - addAlertRequest reports whether the map snapshot attached or why it failed, appended to the Settings result line instead of silently delivering a notification without the embedded map - Settings routes both test buttons through the live monitor
This commit is contained in:
@@ -195,7 +195,13 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
/// station's coordinates ride in `userInfo` so the tap handler can route
|
||||
/// to Apple Maps (or an in-app fallback), and a map snapshot with a pin is
|
||||
/// attached so the expanded notification shows where the station is.
|
||||
private func addAlertRequest(content: UNMutableNotificationContent, station: FuelStation) {
|
||||
/// `mapStatus` reports whether the snapshot attached (or why not), so the
|
||||
/// test UI can show it instead of failing silently.
|
||||
private func addAlertRequest(
|
||||
content: UNMutableNotificationContent,
|
||||
station: FuelStation,
|
||||
mapStatus: ((String) -> Void)? = nil
|
||||
) {
|
||||
content.userInfo = [
|
||||
"stationName": station.name,
|
||||
"stationLat": station.lat,
|
||||
@@ -212,17 +218,20 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
snapshotter.start { [content] snapshot, error in
|
||||
Task { @MainActor in
|
||||
let finalContent = content
|
||||
var status = "map: no image"
|
||||
if let snapshot, error == nil,
|
||||
let image = Self.mapImage(snapshot: snapshot, station: station) {
|
||||
let url = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("alert-map-\(UUID().uuidString).png")
|
||||
if let data = image.pngData() {
|
||||
try? data.write(to: url)
|
||||
if let attachment = try? UNNotificationAttachment(identifier: "map", url: url, options: nil) {
|
||||
finalContent.attachments = [attachment]
|
||||
}
|
||||
if let data = image.pngData(), (try? data.write(to: url)) != nil,
|
||||
let attachment = try? UNNotificationAttachment(identifier: "map", url: url, options: nil) {
|
||||
finalContent.attachments = [attachment]
|
||||
status = "map: attached"
|
||||
}
|
||||
} else if let error {
|
||||
status = "map: snapshot failed (\(error.localizedDescription))"
|
||||
}
|
||||
mapStatus?(status)
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: finalContent, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
}
|
||||
@@ -272,8 +281,11 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
content.title = "Cheapest \(fuel.displayName) nearby: \(brand)"
|
||||
content.body = "\(candidate.name) · \(distanceText) away · \(String(format: "%.1fp", price)). Tap for directions."
|
||||
content.sound = .default
|
||||
addAlertRequest(content: content, station: candidate)
|
||||
lastTestResult = "\(fuel.displayName) · \(brand) · \(String(format: "%.1fp", price)) · \(distanceText) away · radius \(unit.format(radiusKM))"
|
||||
let baseResult = "\(fuel.displayName) · \(brand) · \(String(format: "%.1fp", price)) · \(distanceText) away · radius \(unit.format(radiusKM))"
|
||||
addAlertRequest(content: content, station: candidate) { status in
|
||||
self.lastTestResult = "\(baseResult) · \(status)"
|
||||
}
|
||||
lastTestResult = baseResult
|
||||
} else {
|
||||
// No real candidate (no stations loaded yet, or none sell the
|
||||
// monitored fuel) — still fire so delivery is testable, but say
|
||||
@@ -294,17 +306,39 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
|
||||
/// Fires a notification with NO criteria checks at all — no geofence, no
|
||||
/// cheapest-within-radius, no dedup, no permission gate, no fuel/radius
|
||||
/// coupling. Purely tests "does a notification appear, and does tapping
|
||||
/// it open the app". If notifications are denied system-wide nothing can
|
||||
/// display, but this fires regardless of every FuelBoard condition.
|
||||
static func sendPlainTestNotification() {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = "FuelBoard test notification"
|
||||
content.body = "This is a plain test alert — no criteria were checked. Tap to open FuelBoard."
|
||||
content.sound = .default
|
||||
/// coupling. Still carries a REAL station (the nearest one selling the
|
||||
/// monitored fuel) with its real name, price, distance and coordinates, so
|
||||
/// the tap action — directions to that station — is testable regardless of
|
||||
/// every alert condition. If notifications are denied system-wide nothing
|
||||
/// can display, but this fires regardless of every FuelBoard condition.
|
||||
func sendPlainTestNotification() {
|
||||
let unit = FuelStore.loadDistanceUnit()
|
||||
let location = FuelStore.loadLocation()
|
||||
let sellers = stations.filter { $0.prices[fuel] != nil }
|
||||
let nearest = sellers.min { lhs, rhs in
|
||||
let ld = location.map { lhs.distanceKM(to: $0.lat, lng2: $0.lng) } ?? 0
|
||||
let rd = location.map { rhs.distanceKM(to: $0.lat, lng2: $0.lng) } ?? 0
|
||||
return ld < rd
|
||||
}
|
||||
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
let content = UNMutableNotificationContent()
|
||||
if let nearest, let price = nearest.prices[fuel] {
|
||||
let brand = nearest.brand.isEmpty ? nearest.name : nearest.brand
|
||||
let distanceText: String = {
|
||||
guard let location else { return unit.format(0) }
|
||||
return unit.format(nearest.distanceKM(to: location.lat, lng2: location.lng))
|
||||
}()
|
||||
content.title = "\(brand) — plain test notification"
|
||||
content.body = "\(nearest.name) · \(distanceText) away · \(String(format: "%.1fp", price)). Tap for directions."
|
||||
content.sound = .default
|
||||
addAlertRequest(content: content, station: nearest)
|
||||
} else {
|
||||
content.title = "FuelBoard test notification"
|
||||
content.body = "No station data loaded yet — open the Stations tab first."
|
||||
content.sound = .default
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Notification presentation + tap
|
||||
|
||||
Reference in New Issue
Block a user