diff --git a/FuelBoard/ProximityMonitor.swift b/FuelBoard/ProximityMonitor.swift index 99bb2d4..c298ff7 100644 --- a/FuelBoard/ProximityMonitor.swift +++ b/FuelBoard/ProximityMonitor.swift @@ -259,12 +259,25 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca } let identifier = UUID().uuidString - // Render the map FIRST, then deliver ONE notification with the map - // attached. (Earlier, delivering immediately and "replacing" via a - // second add() showed up as two notifications in LiveContainer — the - // replace-in-place contract isn't honoured there.) A timeout - // guarantees the alert still fires, without the map, if the snapshot - // hangs. + // Tie case: no map. A snapshot would only show one arbitrary station + // (the primary), which is misleading — the user must pick from the + // action buttons. Deliver immediately with the choice buttons. + if !ties.isEmpty { + self.registerTieCategory(identifier: identifier, ties: ties) + content.categoryIdentifier = "tie-\(identifier)" + mapStatus?("map: none (tie — pick a station)") + UNUserNotificationCenter.current().add( + UNNotificationRequest(identifier: identifier, content: content, trigger: nil) + ) + return + } + + // Single-station case: render the map FIRST, then deliver ONE + // notification with the map attached. (Earlier, delivering + // immediately and "replacing" via a second add() showed up as two + // notifications in LiveContainer — the replace-in-place contract + // isn't honoured there.) A timeout guarantees the alert still fires, + // without the map, if the snapshot hangs. mapStatus?("map: preparing…") let options = MKMapSnapshotter.Options() @@ -280,10 +293,6 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca let deliver: (String) -> Void = { status in guard !finished else { return } finished = true - if !ties.isEmpty { - self.registerTieCategory(identifier: identifier, ties: ties) - content.categoryIdentifier = "tie-\(identifier)" - } mapStatus?(status) UNUserNotificationCenter.current().add( UNNotificationRequest(identifier: identifier, content: content, trigger: nil) @@ -491,14 +500,16 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca /// /// When the notification carried tie-choice action buttons, the tapped /// button's identifier ("tie_0", "tie_1", …) selects the corresponding - /// station from userInfo["tieStations"]; tapping the body itself falls - /// back to the primary station. + /// station from userInfo["tieStations"]. Tapping the body of a tie + /// notification does NOT open directions — nothing has been chosen yet; + /// only the option buttons make a choice. nonisolated func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { let userInfo = response.notification.request.content.userInfo + let isTie = userInfo["tieStations"] != nil let name = userInfo["stationName"] as? String let lat = userInfo["stationLat"] as? Double let lng = userInfo["stationLng"] as? Double @@ -511,11 +522,13 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca let tieLat = ties[index]["lat"] as? Double, let tieLng = ties[index]["lng"] as? Double { openDirections(to: tieName, latitude: tieLat, longitude: tieLng) - } else if let name, let lat, let lng { + } else if !isTie, let name, let lat, let lng { openDirections(to: name, latitude: lat, longitude: lng) } + // Tie body tap: intentionally no directions — the user must pick + // an option button to choose a station. + completionHandler() } - completionHandler() } /// Opens Apple Maps with driving directions to the station; falls back to