252 lines
9.2 KiB
Swift
252 lines
9.2 KiB
Swift
import UIKit
|
|
import MapKit
|
|
import RxSwift
|
|
import Realm
|
|
import RealmSwift
|
|
|
|
class EventPin: NSObject, MKAnnotation {
|
|
var coordinate: CLLocationCoordinate2D
|
|
var title: String?
|
|
var subtitle: String?
|
|
|
|
init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) {
|
|
self.coordinate = coordinate
|
|
self.title = title
|
|
self.subtitle = subtitle
|
|
}
|
|
}
|
|
|
|
enum EventsMode {
|
|
case map
|
|
case list
|
|
}
|
|
|
|
class EventsController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
|
|
|
@IBOutlet weak var map: MKMapView!
|
|
@IBOutlet weak var tableView: UITableView!
|
|
|
|
let bag = DisposeBag()
|
|
var modeButton: UIBarButtonItem!
|
|
var addButton: UIBarButtonItem!
|
|
var mode: EventsMode = .map
|
|
|
|
public var vehicle: Vehicle? {
|
|
didSet {
|
|
if let vehicle = self.vehicle {
|
|
self.pins = vehicle.events.map { event in
|
|
let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
|
|
let subtitle = event.address ?? "\(event.latitude), \(event.longitude)"
|
|
let date = Date(timeIntervalSince1970: event.date)
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .medium
|
|
formatter.timeStyle = .short
|
|
let title = formatter.string(from: date)
|
|
return EventPin(coordinate: coordinate, title: title, subtitle: subtitle)
|
|
}
|
|
} else {
|
|
|
|
}
|
|
|
|
if self.isViewLoaded {
|
|
self.updateInterface()
|
|
}
|
|
}
|
|
}
|
|
|
|
private var pins: [EventPin] = []
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
self.title = self.vehicle?.number ?? "Events"
|
|
|
|
#if targetEnvironment(macCatalyst)
|
|
self.map.showsZoomControls = true
|
|
#endif
|
|
|
|
self.modeButton = UIBarButtonItem(image: UIImage(systemName: "list.bullet"), style: .plain, target: self, action: #selector(switchMode(_:)))
|
|
self.addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addEvent(_:)))
|
|
self.navigationItem.rightBarButtonItems = [self.modeButton, self.addButton]
|
|
|
|
self.updateInterface()
|
|
}
|
|
|
|
func updateInterface() {
|
|
self.map.removeAnnotations(self.map.annotations)
|
|
self.map.addAnnotations(self.pins)
|
|
self.centerMap()
|
|
self.tableView.reloadData()
|
|
}
|
|
|
|
func centerMap() {
|
|
guard let vehicle = self.vehicle else { return }
|
|
|
|
var minLat = 0.0, maxLat = 0.0, minLon = 0.0, maxLon = 0.0
|
|
for event in vehicle.events {
|
|
if event.latitude < minLat || minLat == 0 { minLat = event.latitude }
|
|
if event.latitude > maxLat || maxLat == 0 { maxLat = event.latitude }
|
|
if event.longitude < minLon || minLon == 0 { minLon = event.longitude }
|
|
if event.longitude > maxLon || maxLon == 0 { maxLon = event.longitude }
|
|
}
|
|
|
|
let center = CLLocationCoordinate2D(latitude: (minLat + maxLat)/2, longitude: (minLon + maxLon)/2)
|
|
let leftTop = CLLocation(latitude: minLat, longitude: minLon)
|
|
let rightBottom = CLLocation(latitude: maxLat, longitude: maxLon)
|
|
var diagonal = leftTop.distance(from: rightBottom)*1.2
|
|
if diagonal < 1000 {
|
|
diagonal = 1000
|
|
}
|
|
|
|
let region = MKCoordinateRegion(center: center, latitudinalMeters: diagonal, longitudinalMeters: diagonal)
|
|
self.map.setRegion(region, animated: true)
|
|
}
|
|
|
|
@objc func switchMode(_ sender: UIBarButtonItem) {
|
|
switch self.mode {
|
|
case .map:
|
|
self.mode = .list
|
|
self.tableView.reloadData()
|
|
self.tableView.isHidden = false
|
|
self.map.isHidden = true
|
|
self.modeButton.image = UIImage(systemName: "map")
|
|
case .list:
|
|
self.mode = .map
|
|
self.tableView.isHidden = true
|
|
self.map.isHidden = false
|
|
self.modeButton.image = UIImage(systemName: "list.bullet")
|
|
}
|
|
}
|
|
|
|
// MARK: - UITableViewDataSource
|
|
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
return self.vehicle?.events.count ?? 0
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: "EventCell", for: indexPath) as? EventCell else {
|
|
return UITableViewCell()
|
|
}
|
|
|
|
if let event = self.vehicle?.events[indexPath.row] {
|
|
cell.configure(with: event)
|
|
}
|
|
|
|
return cell
|
|
}
|
|
|
|
// MARK: - UITableViewDelegate
|
|
|
|
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
|
let delete = UIContextualAction(style: .destructive, title: "Delete") { action, view, completion in
|
|
self.deleteEvent(index: indexPath.row, completion: completion)
|
|
}
|
|
delete.image = UIImage(systemName: "trash")
|
|
|
|
let edit = UIContextualAction(style: .normal, title: "Edit") { action, view, completion in
|
|
self.editEvent(index: indexPath.row)
|
|
completion(true)
|
|
}
|
|
edit.image = UIImage(systemName: "pencil")
|
|
edit.backgroundColor = .systemBlue
|
|
|
|
let configuration = UISwipeActionsConfiguration(actions: [delete, edit])
|
|
configuration.performsFirstActionWithFullSwipe = false
|
|
return configuration
|
|
}
|
|
|
|
// MARK: - Event actions
|
|
|
|
func deleteEvent(index: Int, completion: @escaping (Bool) -> Void) {
|
|
guard let vehicle = self.vehicle else {
|
|
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
|
return
|
|
}
|
|
|
|
let event = vehicle.events[index]
|
|
if let eventId = event.id {
|
|
IHProgressHUD.show()
|
|
Api.remove(event: eventId).observeOn(MainScheduler.instance).subscribe(onSuccess: { vehicle in
|
|
completion(self.update(vehicle: vehicle))
|
|
}, onError: { error in
|
|
completion(false)
|
|
IHProgressHUD.show(error: error)
|
|
print(error)
|
|
}).disposed(by: self.bag)
|
|
} else {
|
|
self.showAlert(title: "Error", message: "Event ID is not found. Please try to update vehicle record, containing this event.")
|
|
}
|
|
}
|
|
|
|
func editEvent(index: Int) {
|
|
guard let vehicle = self.vehicle else {
|
|
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
|
return
|
|
}
|
|
|
|
let event = vehicle.events[index]
|
|
let sb = UIStoryboard(name: "Main", bundle: nil)
|
|
let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController
|
|
controller.title = "Edit event"
|
|
controller.date = Date(timeIntervalSince1970: event.date)
|
|
controller.placemark = Placemark(latitude: event.latitude, longitude: event.longitude, address: event.address)
|
|
controller.onDone = { newEvent in
|
|
newEvent.id = event.id
|
|
self.navigationController?.popViewController(animated: true, completion: {
|
|
IHProgressHUD.show()
|
|
Api.edit(event: newEvent)
|
|
.observeOn(MainScheduler.instance)
|
|
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
|
|
{ error in
|
|
IHProgressHUD.show(error: error)
|
|
})
|
|
.disposed(by: self.bag)
|
|
})
|
|
}
|
|
self.navigationController?.pushViewController(controller, animated: true)
|
|
}
|
|
|
|
@objc func addEvent(_ sender: UIBarButtonItem) {
|
|
guard let vehicle = self.vehicle else {
|
|
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
|
return
|
|
}
|
|
|
|
let sb = UIStoryboard(name: "Main", bundle: nil)
|
|
let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController
|
|
controller.title = "Add new event"
|
|
controller.onDone = { newEvent in
|
|
self.navigationController?.popViewController(animated: true, completion: {
|
|
IHProgressHUD.show()
|
|
Api.add(event: newEvent, to: vehicle.number)
|
|
.observeOn(MainScheduler.instance)
|
|
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
|
|
{ error in
|
|
IHProgressHUD.show(error: error)
|
|
})
|
|
.disposed(by: self.bag)
|
|
})
|
|
}
|
|
self.navigationController?.pushViewController(controller, animated: true)
|
|
}
|
|
|
|
@discardableResult
|
|
func update(vehicle: Vehicle) -> Bool {
|
|
do {
|
|
if let realm = self.vehicle?.realm {
|
|
try realm.write {
|
|
realm.add(vehicle, update: .all)
|
|
}
|
|
}
|
|
self.vehicle = vehicle
|
|
IHProgressHUD.dismiss()
|
|
return true
|
|
} catch {
|
|
IHProgressHUD.show(error: error)
|
|
print(error)
|
|
return false
|
|
}
|
|
}
|
|
}
|