AutoCat/AutoCat/Controllers/Location/EventsController.swift

266 lines
9.8 KiB
Swift

import UIKit
import MapKit
import RxSwift
import Realm
import RealmSwift
import PKHUD
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
}
convenience init(event: VehicleEvent) {
let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
let address = event.address ?? "\(event.latitude), \(event.longitude)"
let date = Date(timeIntervalSince1970: event.date)
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
let dateStr = formatter.string(from: date)
if let number = event.number {
self.init(coordinate: coordinate, title: number, subtitle: dateStr)
} else {
self.init(coordinate: coordinate, title: dateStr, subtitle: address)
}
}
}
enum EventsMode {
case map
case list
}
class EventsController: UIViewController, UITableViewDataSource, UITableViewDelegate, MKMapViewDelegate {
@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 self.isViewLoaded {
self.updateInterface()
}
}
}
private var pins: [EventPin] = []
override func viewDidLoad() {
super.viewDidLoad()
self.title = self.vehicle?.getNumber() ?? NSLocalizedString("Events", comment: "")
#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.map.delegate = self
self.updateInterface()
}
func updateInterface() {
if let vehicle = self.vehicle {
self.pins = vehicle.events.map(EventPin.init(event:))
}
if !self.pins.isEmpty {
self.map.removeAnnotations(self.map.annotations)
self.map.addAnnotations(self.pins)
self.map.centerOnPins()
} else {
self.map.showsUserLocation = true
}
self.tableView.reloadData()
}
@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, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in
let edit = UIAction(title: NSLocalizedString("Edit", comment: ""), image: UIImage(systemName: "pencil")) { action in
self.editEvent(index: indexPath.row)
}
let delete = UIAction(title: NSLocalizedString("Delete", comment: ""), image: UIImage(systemName: "trash"), attributes: .destructive) { action in
self.deleteEvent(index: indexPath.row)
}
return UIMenu(title: NSLocalizedString("Actions", comment: ""), children: [edit, delete])
}
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .destructive, title: NSLocalizedString("Delete", comment: "")) { action, view, completion in
self.deleteEvent(index: indexPath.row, completion: completion)
}
delete.image = UIImage(systemName: "trash")
let edit = UIContextualAction(style: .normal, title: NSLocalizedString("Edit", comment: "")) { 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: ((Bool) -> Void)? = nil) {
guard let vehicle = self.vehicle else {
HUD.flash(.labeledError(title: nil, subtitle: "Unknown vehicle"))
return
}
let event = vehicle.events[index]
if let eventId = event.id {
HUD.show(.progress)
Api.remove(event: eventId).observeOn(MainScheduler.instance).subscribe(onSuccess: { vehicle in
completion?(self.update(vehicle: vehicle))
}, onError: { error in
completion?(false)
HUD.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 {
HUD.flash(.labeledError(title: nil, subtitle: "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 = NSLocalizedString("Edit event", comment: "")
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: {
HUD.show(.progress)
Api.edit(event: newEvent)
.observeOn(MainScheduler.instance)
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
{ error in
HUD.show(error: error)
})
.disposed(by: self.bag)
})
}
self.navigationController?.pushViewController(controller, animated: true)
}
@objc func addEvent(_ sender: UIBarButtonItem) {
guard let vehicle = self.vehicle else {
HUD.flash(.labeledError(title: nil, subtitle: "Unknown vehicle"))
return
}
let sb = UIStoryboard(name: "Main", bundle: nil)
let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController
controller.title = NSLocalizedString("Add new event", comment: "")
controller.onDone = { newEvent in
self.navigationController?.popViewController(animated: true, completion: {
HUD.show(.progress)
Api.add(event: newEvent, to: vehicle.getNumber())
.observeOn(MainScheduler.instance)
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
{ error in
HUD.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)
}
} else {
self.vehicle?.events.removeAll()
self.vehicle?.events.append(objectsIn: vehicle.events)
}
self.updateInterface()
HUD.hide()
return true
} catch {
HUD.show(error: error)
print(error)
return false
}
}
// MARK: - MKMapViewDelegate
public func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
let region = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
self.map.setRegion(region, animated: true)
self.map.showsUserLocation = false
}
}