315 lines
11 KiB
Swift
315 lines
11 KiB
Swift
import UIKit
|
|
import RealmSwift
|
|
import RxSwift
|
|
import SwiftDate
|
|
import RxRealm
|
|
import PKHUD
|
|
|
|
enum EventAction {
|
|
case doNotSend
|
|
case receiveAndSend
|
|
case sendSpecific(VehicleEvent)
|
|
}
|
|
|
|
class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegate, PNKeyboardDelegate {
|
|
|
|
@IBOutlet weak var number: SwiftMaskTextfield!
|
|
@IBOutlet weak var check: UIButton!
|
|
@IBOutlet weak var history: UITableView!
|
|
|
|
let bag = DisposeBag()
|
|
var historyDataSource: RealmSectionedDataSource<Vehicle, VehicleCell>!
|
|
|
|
// MARK: - Lifecycle
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
guard let realm = try? Realm() else { return }
|
|
|
|
self.hideKeyboardWhenTappedAround()
|
|
let keyboard = PNKeyboard(target: self.number)
|
|
keyboard.delegate = self
|
|
self.number.formatPattern = "@###@@###"
|
|
self.number.inputView = keyboard
|
|
self.check.isEnabled = false
|
|
|
|
DispatchQueue.main.async {
|
|
self.historyDataSource = RealmSectionedDataSource(table: self.history, data: realm.objects(Vehicle.self).sorted(byKeyPath: "updatedDate", ascending: false))
|
|
}
|
|
|
|
self.history.delegate = self
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
self.navigationController?.setNavigationBarHidden(true, animated: false)
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
if let index = self.history.indexPathForSelectedRow {
|
|
self.history.deselectRow(at: index, animated: true)
|
|
}
|
|
}
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
self.handleQuickActions()
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
func handleQuickActions() {
|
|
guard let ad = UIApplication.shared.delegate as? AppDelegate else { return }
|
|
|
|
switch ad.quickAction {
|
|
case .check:
|
|
ad.quickAction = .none
|
|
self.number.becomeFirstResponder()
|
|
break
|
|
case .checkNumber(let number, let event):
|
|
ad.quickAction = .none
|
|
var action: EventAction = .receiveAndSend
|
|
if let event = event {
|
|
action = .sendSpecific(event)
|
|
}
|
|
//self.check(number: number, action: action).subscribe().disposed(by: self.bag)
|
|
HUD.show(.progress)
|
|
self.check(number: number, action: action).subscribe { vehicle in
|
|
self.updateDetailController(with: vehicle)
|
|
HUD.hide()
|
|
} onError: { error in
|
|
HUD.hide()
|
|
self.show(error: error)
|
|
//HUD.show(error: error)
|
|
}
|
|
.disposed(by: self.bag)
|
|
break
|
|
case .addVoiceRecord:
|
|
self.tabBarController?.selectedIndex = 1
|
|
break
|
|
case .openReport(let number):
|
|
ad.quickAction = .none
|
|
if let sd = self.view.window?.windowScene?.delegate as? SceneDelegate {
|
|
sd.openReport(with: number)
|
|
}
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// MARK: - Checking new number
|
|
|
|
@IBAction func checkTapped(_ sender: UIButton) {
|
|
guard let number = self.number.text else { return }
|
|
|
|
let numberNormalized = number.filter { !$0.isWhitespace }.uppercased()
|
|
self.number.resignFirstResponder()
|
|
self.number.text = nil
|
|
self.check.isEnabled = false
|
|
|
|
HUD.show(.progress)
|
|
self.check(number: numberNormalized, action: .receiveAndSend).subscribe { vehicle in
|
|
self.updateDetailController(with: vehicle)
|
|
HUD.hide()
|
|
} onError: { error in
|
|
HUD.hide()
|
|
self.show(error: error)
|
|
//HUD.show(error: error)
|
|
}
|
|
.disposed(by: self.bag)
|
|
|
|
}
|
|
|
|
func updateDetailController(with vehicle: Vehicle) {
|
|
if let splitViewController = self.view.window?.rootViewController as? UISplitViewController
|
|
{
|
|
var detail: UINavigationController?
|
|
if splitViewController.viewControllers.count == 2 {
|
|
detail = splitViewController.viewControllers.last as? UINavigationController
|
|
} else {
|
|
let storyboard = UIStoryboard(name: "Main", bundle: nil)
|
|
detail = storyboard.instantiateViewController(identifier: "ReportNavController")
|
|
}
|
|
|
|
if let detail = detail {
|
|
detail.popToRootViewController(animated: true)
|
|
let report = detail.viewControllers.first as? ReportController
|
|
report?.number = vehicle.getNumber()
|
|
splitViewController.showDetailViewController(detail, sender: self)
|
|
//self.performSegue(withIdentifier: "OpenDetailSegue", sender: self)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - UITextFieldDelegate
|
|
|
|
@IBAction func textFieldDidBeginEditing(_ textField: UITextField) {
|
|
LocationManager.requestCurrentLocation().subscribe().disposed(by: self.bag)
|
|
}
|
|
|
|
@IBAction func textFieldDidChange(_ textField: UITextField) {
|
|
guard let text = textField.text else {
|
|
self.check.isEnabled = false
|
|
return
|
|
}
|
|
|
|
self.check.isEnabled = text.count >= 8
|
|
}
|
|
|
|
// MARK: - PNKeyboardDelegate
|
|
|
|
func returnClicked() {
|
|
if self.check.isEnabled {
|
|
self.checkTapped(self.check)
|
|
}
|
|
}
|
|
|
|
// MARK: - UITableViewDelegate
|
|
|
|
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
|
let vehicle = self.historyDataSource.item(at: indexPath)
|
|
|
|
let updateAction = UIContextualAction(style: .normal, title: "Update") { action, view, completion in
|
|
self.update(vehicle: vehicle)
|
|
completion(true)
|
|
}
|
|
updateAction.image = UIImage(systemName: "arrow.2.circlepath")
|
|
updateAction.backgroundColor = .systemBlue
|
|
|
|
let removeAction = UIContextualAction(style: .destructive, title: "Remove") { action, view, completion in
|
|
self.remove(vehicle: vehicle)
|
|
}
|
|
removeAction.image = UIImage(systemName: "trash")
|
|
|
|
let configuration = UISwipeActionsConfiguration(actions: [updateAction, removeAction])
|
|
configuration.performsFirstActionWithFullSwipe = false
|
|
return configuration
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
let vehicle = self.historyDataSource.item(at: indexPath)
|
|
self.updateDetailController(with: vehicle)
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
|
let vehicle = self.historyDataSource.item(at: indexPath)
|
|
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in
|
|
let update = UIAction(title: "Update", image: UIImage(systemName: "arrow.2.circlepath")) { action in
|
|
self.update(vehicle: vehicle)
|
|
}
|
|
let remove = UIAction(title: "Remove", image: UIImage(systemName: "trash"), attributes: [.destructive]) { action in
|
|
self.remove(vehicle: vehicle)
|
|
}
|
|
|
|
return UIMenu(title: "Actions", children: [update, remove])
|
|
}
|
|
}
|
|
|
|
// MARK: - Contextual actions
|
|
|
|
func update(vehicle: Vehicle) {
|
|
HUD.show(.progress)
|
|
|
|
var eventAction: EventAction = .doNotSend
|
|
if vehicle.unrecognized, let savedEvent = vehicle.events.first {
|
|
eventAction = .sendSpecific(savedEvent)
|
|
}
|
|
|
|
self.check(number: vehicle.getNumber(), action: eventAction, force: true).subscribe { vehicle in
|
|
self.updateDetailController(with: vehicle)
|
|
HUD.hide()
|
|
} onError: { error in
|
|
HUD.hide()
|
|
self.show(error: error)
|
|
//HUD.show(error: error)
|
|
}
|
|
.disposed(by: self.bag)
|
|
}
|
|
|
|
func remove(vehicle: Vehicle) {
|
|
if let realm = vehicle.realm {
|
|
do {
|
|
try realm.write {
|
|
realm.delete(vehicle)
|
|
}
|
|
} catch {
|
|
print(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Checking number
|
|
|
|
func save(vehicle: Vehicle) throws {
|
|
let realm = try Realm()
|
|
try realm.write {
|
|
realm.add(vehicle, update: .all)
|
|
}
|
|
}
|
|
|
|
func getEvent(for action: EventAction) -> Single<VehicleEvent> {
|
|
if case .sendSpecific(let event) = action {
|
|
return Single.just(event)
|
|
}
|
|
|
|
if let event = LocationManager.lastEvent, (Date().timeIntervalSince1970 - event.date) < 60 {
|
|
return Single<VehicleEvent>.just(event)
|
|
} else {
|
|
return LocationManager.requestCurrentLocation()
|
|
}
|
|
}
|
|
|
|
func check(number: String, action: EventAction, force: Bool = false) -> Single<Vehicle> {
|
|
let checkSingle = Api.checkVehicle(by: number, force: force)
|
|
.observeOn(MainScheduler.instance)
|
|
.map { (vehicle: Vehicle) -> Vehicle in
|
|
try self.save(vehicle: vehicle)
|
|
return vehicle
|
|
}
|
|
.do (onError: { err in
|
|
let realm = try Realm()
|
|
if realm.object(ofType: Vehicle.self, forPrimaryKey: number) == nil {
|
|
let vehicle = Vehicle(number)
|
|
try realm.write { realm.add(vehicle, update: .all) }
|
|
|
|
self.getEvent(for: action)
|
|
.flatMap { event in event.findAddress().map{ [event] }.catchErrorJustReturn([event]) }
|
|
.catchErrorJustReturn([])
|
|
.map { events in
|
|
try realm.write { vehicle.events.append(objectsIn: events) }
|
|
}
|
|
.subscribe()
|
|
.disposed(by: self.bag)
|
|
}
|
|
})
|
|
|
|
|
|
if case .doNotSend = action {
|
|
return checkSingle
|
|
} else {
|
|
return checkSingle
|
|
.flatMap { self.addEvent(to: $0, action: action) }
|
|
}
|
|
}
|
|
|
|
func addEvent(to vehicle: Vehicle, action: EventAction) -> Single<Vehicle> {
|
|
return self.getEvent(for: action).flatMap { event in
|
|
return event
|
|
.findAddress()
|
|
.map{ event }
|
|
.catchErrorJustReturn(event)
|
|
.flatMap { Api.add(event: $0, to: vehicle.freeze().getNumber()) }
|
|
.observeOn(MainScheduler.instance)
|
|
.map {
|
|
try self.save(vehicle: $0)
|
|
return $0
|
|
}
|
|
.catchErrorJustReturn(vehicle)
|
|
}
|
|
.catchErrorJustReturn(vehicle)
|
|
}
|
|
}
|