// // NotificationContentViewController.swift // EQNNotificationContent // // Created by Andrea Busi on 24/07/25. // Copyright © 2025 Earthquake Network. All rights reserved. // import UIKit import MapKit import UserNotifications import UserNotificationsUI class NotificationContentViewController: UIViewController { // MARK: - UI @IBOutlet private weak var titleLabel: UILabel! @IBOutlet private weak var descriptionLabel: UILabel! @IBOutlet private weak var waveLabel: UILabel! @IBOutlet private weak var mapView: MKMapView! @IBOutlet private weak var tapToOpenButton: UIButton! private var animator: MapSeismicWaveAnimator? // MARK: - View override func viewDidLoad() { super.viewDidLoad() let tapRecognizer = UITapGestureRecognizer( target: self, action: #selector(openAppTapped(_:)) ) view.addGestureRecognizer(tapRecognizer) mapView .register( EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SingleLineIdentifier ) mapView .register( EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SmallIdentifier ) tapToOpenButton.setTitle("tap_to_open".localized, for: .normal) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) animator?.stop() } // MARK: - Actions @IBAction private func openAppTapped(_ sender: Any) { extensionContext?.performNotificationDefaultAction() } } extension NotificationContentViewController: UNNotificationContentExtension { func didReceive(_ notification: UNNotification) { let content = notification.request.content titleLabel.text = content.title descriptionLabel.text = content.body let notification = EQNRealtimePushNotification.from( userInfo: content.userInfo, title: "", displayTitle: content.title, displayBody: content.body ) if let notification { let coordinate = notification.coordinate.coordinate let span = MKCoordinateSpan(latitudeDelta: 6, longitudeDelta: 6) let region = MKCoordinateRegion( center: coordinate, span: span ) mapView.setCenter(coordinate, animated: false) mapView.setRegion(region, animated: true) switch notification.type.lowercased() { case "eqn": let annotation = EQNMapAnnotationPastquake( title: "", coordinate: coordinate, intensity: notification.intensity ) mapView.addAnnotation(annotation) case "manual": let annotation = EQNMapAnnotationUserReport( magnitude: notification.magnitude, coordinate: coordinate ) mapView.addAnnotation(annotation) default: break } // create animator to manage wave animation and countdown animator = MapSeismicWaveAnimator( realtimeAlert: notification, mapView: mapView, waveTimeLabel: waveLabel ) animator?.start() // set color based on intensity descriptionLabel.textColor = notification.relativeIntensityColor } } } extension NotificationContentViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, viewFor annotation: any MKAnnotation) -> MKAnnotationView? { switch annotation { case let pastquake as EQNMapAnnotationPastquake: let annotationView = mapView.dequeueReusableAnnotationView( withIdentifier: EQNCustomAnnotationView.SingleLineIdentifier ) as! EQNCustomAnnotationView annotationView.image = pastquake.image annotationView.title = pastquake.title return annotationView case let report as EQNMapAnnotationUserReport: let annotationView = mapView.dequeueReusableAnnotationView( withIdentifier: EQNCustomAnnotationView.SmallIdentifier ) as! EQNCustomAnnotationView annotationView.image = report.image(with: EQNCustomAnnotationView.SmallViewImageHeight) annotationView.title = report.timeDifference return annotationView default: return nil } } func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { animator?.getOverlayRenderer(for: overlay) ?? MKOverlayRenderer(overlay: overlay) } }