439 lines
21 KiB
Swift
439 lines
21 KiB
Swift
import UIKit
|
|
import Kingfisher
|
|
import LinkPresentation
|
|
import RealmSwift
|
|
import Eureka
|
|
import AutoCatCore
|
|
import SwiftEntryKit
|
|
import MobileCoreServices
|
|
import PKHUD
|
|
|
|
class ReportController: FormViewController, MediaBrowserViewControllerDataSource, MediaBrowserViewControllerDelegate {
|
|
|
|
@IBOutlet weak var actionBarItem: UIBarButtonItem!
|
|
@IBOutlet weak var copyBarItem: UIBarButtonItem!
|
|
|
|
private let logoPlaceholder = UIImage(named: "SteeringWheel")
|
|
|
|
private let copyableTags = ["Model", "Year", "Color", "Category", "STP", "Japanese",
|
|
"PlateNumber", "VIN", "STS", "PTS",
|
|
"EngineNumber", "FuelType", "Volume", "PowerHP", "PowerKw"];
|
|
|
|
var vehicle: VehicleDto? {
|
|
didSet {
|
|
if isViewLoaded && self.view.window != nil {
|
|
self.updateReport()
|
|
self.form.allSections.forEach { $0.reload() }
|
|
self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: false)
|
|
}
|
|
|
|
if let vehicle {
|
|
vehicleUpdated?(vehicle)
|
|
}
|
|
}
|
|
}
|
|
|
|
private var notificationToken: NotificationToken?
|
|
var number: String? {
|
|
didSet {
|
|
if let realm = try? Realm(), let num = number {
|
|
let vehicles = realm.objects(Vehicle.self).filter("number = %@", num)
|
|
self.notificationToken?.invalidate()
|
|
self.notificationToken = vehicles.observe { _ in self.vehicle = vehicles.first?.dto }
|
|
} else {
|
|
self.vehicle = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
public var vehicleUpdated: ((VehicleDto) -> Void)?
|
|
|
|
// MARK: - Lifecycle
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
if let vehicle = self.vehicle {
|
|
let urls = Array(vehicle.photos.compactMap { URL(string: $0.url) })
|
|
let prefetcher = ImagePrefetcher(urls: urls)
|
|
prefetcher.start()
|
|
}
|
|
|
|
form +++ Section()
|
|
<<< LabelRow("Model").cellUpdate { cell, _ in cell.imageView?.kf.setImage(with: URL(string: self.vehicle?.brand?.logo ?? ""), placeholder: self.logoPlaceholder) }
|
|
|
|
form +++ Section(NSLocalizedString("General", comment: ""))
|
|
<<< LabelRow("Year") { $0.title = NSLocalizedString("Year", comment: "") }
|
|
<<< LabelRow("Color") { $0.title = NSLocalizedString("Color", comment: "") }
|
|
<<< LabelRow("Category") { $0.title = NSLocalizedString("Category", comment: "") }
|
|
<<< LabelRow("STP") { $0.title = NSLocalizedString("Steering wheel position", comment: "") }
|
|
<<< LabelRow("Japanese") { $0.title = NSLocalizedString("Japanese", comment: "") }
|
|
|
|
form +++ Section(NSLocalizedString("Identifiers", comment: ""))
|
|
<<< LabelRow("PlateNumber") { $0.title = NSLocalizedString("Plate number", comment: "") }
|
|
<<< LabelRow("VIN") { $0.title = NSLocalizedString("VIN", comment: "") }
|
|
<<< LabelRow("STS") { $0.title = NSLocalizedString("STS", comment: "") }
|
|
<<< LabelRow("PTS") { $0.title = NSLocalizedString("PTS", comment: "") }
|
|
|
|
form +++ Section(NSLocalizedString("Engine", comment: ""))
|
|
<<< LabelRow("EngineNumber") { $0.title = NSLocalizedString("Number", comment: "") }
|
|
<<< LabelRow("FuelType") { $0.title = NSLocalizedString("Fuel type", comment: "") }
|
|
<<< LabelRow("Volume") { $0.title = NSLocalizedString("Volume (cm³)", comment: "") }
|
|
<<< LabelRow("PowerHP") { $0.title = NSLocalizedString("Power (HP)", comment: "") }
|
|
<<< LabelRow("PowerKw") { $0.title = NSLocalizedString("Power (kw)", comment: "") }
|
|
|
|
form +++ Section(NSLocalizedString("History", comment: ""))
|
|
<<< LabelRow("Events") { $0.title = NSLocalizedString("Events", comment: "") }
|
|
.cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator }
|
|
.onCellSelection { _, _ in
|
|
let sb = UIStoryboard(name: "Main", bundle: nil)
|
|
let controller = sb.instantiateViewController(identifier: "EventsController") as EventsController
|
|
controller.vehicle = self.vehicle
|
|
controller.vehicleUpdated = { vehicle in
|
|
self.vehicle = vehicle
|
|
}
|
|
self.navigationController?.pushViewController(controller, animated: true)
|
|
}
|
|
|
|
<<< LabelRow("OSAGO") { $0.title = NSLocalizedString("OSAGO", comment: "") }
|
|
.cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator }
|
|
.onCellSelection { _, _ in
|
|
if let contracts = self.vehicle?.osagoContracts, let navController = self.navigationController {
|
|
let coordinator = OsagoCoordinator(navController: navController, contracts: contracts)
|
|
Task { try await coordinator.start() }
|
|
}
|
|
}
|
|
|
|
<<< LabelRow("Owners") { row in
|
|
row.title = NSLocalizedString("Owners", comment: "")
|
|
row.disabled = "$Owners == '0'"
|
|
}
|
|
.cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator }
|
|
.onCellSelection { _, row in
|
|
if row.value != "0" {
|
|
if let ownerships = self.vehicle?.ownershipPeriods, let navController = self.navigationController {
|
|
let coordinator = OwnersCoordinator(navController: navController, ownerships: ownerships)
|
|
Task { try await coordinator.start() }
|
|
}
|
|
}
|
|
}
|
|
|
|
<<< LabelRow("Photos") { row in
|
|
row.title = NSLocalizedString("Photos", comment: "")
|
|
row.disabled = "$Photos == '0'"
|
|
}
|
|
.cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator }
|
|
.onCellSelection { _, row in
|
|
if row.value != "0" {
|
|
let mediaBrowser = MediaBrowserViewController(index: 0, dataSource: self, delegate: self)
|
|
mediaBrowser.shouldShowTitle = true
|
|
mediaBrowser.title = self.vehicle?.photos.first?.description
|
|
self.present(mediaBrowser, animated: true, completion: nil)
|
|
}
|
|
}
|
|
|
|
<<< LabelRow("Ads") { row in
|
|
row.title = NSLocalizedString("Ads", comment: "")
|
|
row.disabled = "$Ads == '0'"
|
|
}
|
|
.cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator }
|
|
.onCellSelection { _, row in
|
|
if let ads = self.vehicle?.ads, let navController = self.navigationController {
|
|
let coordinator = AdsCoordinator(navController: navController, ads: ads)
|
|
Task { try await coordinator.start() }
|
|
}
|
|
}
|
|
|
|
<<< LabelRow("Notes") { row in
|
|
row.title = NSLocalizedString("Notes", comment: "")
|
|
}
|
|
.cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator }
|
|
.onCellSelection { _, row in
|
|
if let vehicle = self.vehicle, let navController = self.navigationController {
|
|
let coordinator = NotesCoordinator(navController: navController, vehicle: vehicle)
|
|
Task { try await coordinator.start() }
|
|
}
|
|
}
|
|
|
|
if Settings.shared.showDebugInfo {
|
|
self.form +++ Section(NSLocalizedString("Debug info", comment: "noun"))
|
|
<<< SourceStatusRow("DebugAutocod") { $0.title = NSLocalizedString("Autocod", comment: "") }
|
|
<<< SourceStatusRow("DebugVin01Vin") { $0.title = NSLocalizedString("Vin01 (VIN)", comment: "") }
|
|
<<< SourceStatusRow("DebugVin01Base") { $0.title = NSLocalizedString("Vin01 (base)", comment: "base report") }
|
|
<<< SourceStatusRow("DebugVin01History") { $0.title = NSLocalizedString("Vin01 (history)", comment: "GIBDD registration history") }
|
|
<<< SourceStatusRow("DebugNomerogram") { $0.title = NSLocalizedString("Nomerogram", comment: "") }
|
|
}
|
|
|
|
form +++ Section("")
|
|
<<< ButtonRow("CheckGB") { $0.title = NSLocalizedString("Check GB", comment: "") }.onCellSelection { cell, row in
|
|
Task { await self.checkGB() }
|
|
}
|
|
|
|
setupCopyBehaviour()
|
|
}
|
|
|
|
func setupCopyBehaviour() {
|
|
|
|
for row in form.allRows {
|
|
if let labelRow = row as? LabelRow, copyableTags.contains(row.tag ?? "") {
|
|
let doubleTap = UITapGestureRecognizer { _ in
|
|
guard let text = labelRow.value else {
|
|
return
|
|
}
|
|
|
|
UIPasteboard.general.string = text
|
|
let generator = UIImpactFeedbackGenerator(style: .rigid)
|
|
generator.impactOccurred()
|
|
let toastMessage = NSLocalizedString("Copied: ", comment: "") + text
|
|
self.showToast(text: toastMessage)
|
|
}
|
|
|
|
doubleTap.numberOfTapsRequired = 2
|
|
doubleTap.delaysTouchesBegan = true
|
|
labelRow.cell.addGestureRecognizer(doubleTap)
|
|
}
|
|
}
|
|
}
|
|
|
|
func showToast(text: String) {
|
|
let style = EKProperty.LabelStyle(
|
|
font: .systemFont(ofSize: 14),
|
|
color: .white, //.black,
|
|
alignment: .center
|
|
)
|
|
let labelContent = EKProperty.LabelContent(
|
|
text: text,
|
|
style: style
|
|
)
|
|
let contentView = EKNoteMessageView(with: labelContent)
|
|
|
|
var attributes: EKAttributes = .bottomFloat //.toast
|
|
attributes.entryBackground = .visualEffect(style: EKAttributes.BackgroundStyle.BlurStyle(light: .dark, dark: .light)) //.color(color: .init(red: 0, green: 196, blue: 0))
|
|
SwiftEntryKit.display(entry: contentView, using: attributes)
|
|
}
|
|
|
|
func update(row tag: String, with value: String) {
|
|
if let row = self.form.rowBy(tag: tag) as? LabelRow {
|
|
row.value = value
|
|
row.reload()
|
|
}
|
|
}
|
|
|
|
func update(sourceStatusRow tag: String, with value: DebugInfoEntryDto) {
|
|
if let row = self.form.rowBy(tag: tag) as? SourceStatusRow {
|
|
row.value = value
|
|
row.reload()
|
|
}
|
|
}
|
|
|
|
func updateReport() {
|
|
self.update(row: "Model", with: self.vehicle?.brand?.name?.original ?? "<unknown>")
|
|
self.update(row: "Year", with: String(self.vehicle?.year ?? 0))
|
|
self.update(row: "Color", with: self.vehicle?.color ?? "<unknown>")
|
|
self.update(row: "Category", with: self.vehicle?.category ?? "<unknown>")
|
|
self.update(row: "STP", with: self.stringFromBool(self.vehicle?.isRightWheel, yes: NSLocalizedString("Right", comment: ""), no: NSLocalizedString("Left", comment: "")))
|
|
self.update(row: "Japanese", with: self.stringFromBool(self.vehicle?.isJapanese, yes: NSLocalizedString("Yes", comment: ""), no: NSLocalizedString("No", comment: "")))
|
|
|
|
var num = self.vehicle?.getNumber() ?? "<unknown>"
|
|
if self.vehicle?.outdated ?? false, let current = self.vehicle?.currentNumber {
|
|
num = "\(self.vehicle!.getNumber()) (\(current))"
|
|
}
|
|
self.update(row: "PlateNumber", with: num)
|
|
|
|
self.update(row: "VIN", with: self.vehicle?.vin1 ?? "<unknown>")
|
|
self.update(row: "STS", with: self.vehicle?.sts ?? "<unknown>")
|
|
self.update(row: "PTS", with: self.vehicle?.pts ?? "<unknown>")
|
|
self.update(row: "EngineNumber", with: self.vehicle?.engine?.number ?? "<unknown>")
|
|
self.update(row: "FuelType", with: self.vehicle?.engine?.fuelType ?? "<unknown>")
|
|
self.update(row: "Volume", with: String(self.vehicle?.engine?.volume ?? 0))
|
|
self.update(row: "PowerHP", with: String(self.vehicle?.engine?.powerHp ?? 0))
|
|
self.update(row: "PowerKw", with: String(self.vehicle?.engine?.powerKw ?? 0))
|
|
self.update(row: "Events", with: String(self.vehicle?.events.count ?? 0))
|
|
self.update(row: "OSAGO", with: String(self.vehicle?.osagoContracts.count ?? 0))
|
|
self.update(row: "Owners", with: String(self.vehicle?.ownershipPeriods.count ?? 0))
|
|
self.update(row: "Photos", with: String(self.vehicle?.photos.count ?? 0))
|
|
self.update(row: "Ads", with: String(self.vehicle?.ads.count ?? 0))
|
|
self.update(row: "Notes", with: String(self.vehicle?.notes.count ?? 0))
|
|
|
|
if let dInfo = self.vehicle?.debugInfo {
|
|
self.update(sourceStatusRow: "DebugAutocod", with: dInfo.autocod)
|
|
self.update(sourceStatusRow: "DebugVin01Vin", with: dInfo.vin01vin)
|
|
self.update(sourceStatusRow: "DebugVin01Base", with: dInfo.vin01base)
|
|
self.update(sourceStatusRow: "DebugVin01History", with: dInfo.vin01history)
|
|
self.update(sourceStatusRow: "DebugNomerogram", with: dInfo.nomerogram)
|
|
}
|
|
}
|
|
|
|
func stringFromBool(_ value: Bool?, yes: String, no: String) -> String {
|
|
guard let value = value else { return "<unknown>" }
|
|
return value ? yes : no
|
|
}
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
guard let ad = UIApplication.shared.delegate as? AppDelegate else { return }
|
|
|
|
self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: animated)
|
|
|
|
switch ad.quickAction {
|
|
case .check:
|
|
self.dismiss(animated: false, completion: nil)
|
|
default:
|
|
break
|
|
}
|
|
|
|
self.updateReport()
|
|
}
|
|
|
|
func checkGB() async {
|
|
guard let vehicle = self.vehicle else { return }
|
|
|
|
do {
|
|
HUD.show(.progress)
|
|
let newVehicle = try await ApiService.shared.checkVehicleGb(by: vehicle.getNumber())
|
|
|
|
let realm = try await Realm()
|
|
if let realmVehicle = realm.object(ofType: Vehicle.self, forPrimaryKey: vehicle.getNumber()) {
|
|
try? realm.write {
|
|
realm.add(Vehicle(dto: newVehicle), update: .all)
|
|
}
|
|
} else {
|
|
self.vehicle?.vin1 = newVehicle.vin1
|
|
self.vehicle?.color = newVehicle.color
|
|
self.vehicle?.sts = newVehicle.sts
|
|
}
|
|
|
|
self.updateReport()
|
|
self.form.allSections.forEach { $0.reload() }
|
|
HUD.hide()
|
|
} catch {
|
|
HUD.hide()
|
|
self.show(error: error)
|
|
}
|
|
}
|
|
|
|
// MARK: - MediaBrowserViewControllerDataSource & MediaBrowserViewControllerDelegate
|
|
|
|
func numberOfItems(in mediaBrowser: MediaBrowserViewController) -> Int {
|
|
guard let images = self.vehicle?.photos else { return 0 }
|
|
return images.count
|
|
}
|
|
|
|
func mediaBrowser(_ mediaBrowser: MediaBrowserViewController, imageAt index: Int, completion: @escaping MediaBrowserViewControllerDataSource.CompletionBlock) {
|
|
guard let images = self.vehicle?.photos, let url = URL(string: images[index].url) else {
|
|
completion(index, nil, ZoomScale.default, NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Image not found"]))
|
|
return
|
|
}
|
|
|
|
KingfisherManager.shared.retrieveImage(with: url) { result in
|
|
Task { @MainActor in
|
|
switch result {
|
|
case .success(let res):
|
|
completion(index, res.image, ZoomScale.default, nil)
|
|
break
|
|
case .failure(let error):
|
|
completion(index, nil, ZoomScale.default, error)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func mediaBrowser(_ mediaBrowser: MediaBrowserViewController, didChangeFocusTo index: Int) {
|
|
guard let photo = self.vehicle?.photos[index] else { return }
|
|
mediaBrowser.title = photo.description
|
|
}
|
|
|
|
// MARK: - Sharing
|
|
|
|
@IBAction func onShare(_ sender: UIBarButtonItem) {
|
|
guard let vehicle = self.vehicle else { return }
|
|
|
|
let sheet = UIAlertController(title: NSLocalizedString("Share report", comment: ""), message: nil, preferredStyle: .actionSheet)
|
|
sheet.popoverPresentationController?.barButtonItem = self.actionBarItem
|
|
|
|
let cancel = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { _ in sheet.dismiss(animated: true, completion: nil) }
|
|
let shareImage = UIAlertAction(title: NSLocalizedString("As one image", comment: ""), style: .default) { _ in
|
|
let image = vehicle.reportImage(width: self.tableView.contentSize.width)
|
|
|
|
do {
|
|
let fm = FileManager.default
|
|
let documentDirectory = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
|
|
let fileURL = documentDirectory.appendingPathComponent("report.png")
|
|
if let imageData = image.pngData() {
|
|
try imageData.write(to: fileURL)
|
|
}
|
|
|
|
let item = ActivityItemSource(url: fileURL, title: vehicle.getNumber())
|
|
|
|
let controller = UIActivityViewController(activityItems: [item], applicationActivities: nil)
|
|
controller.popoverPresentationController?.barButtonItem = sender
|
|
self.present(controller, animated: true)
|
|
} catch {
|
|
print(error)
|
|
}
|
|
}
|
|
|
|
let shareTextAndImage = UIAlertAction(title: NSLocalizedString("As text and photos", comment: ""), style: .default) { _ in
|
|
guard let vehicle = self.vehicle else { return }
|
|
var items: [Any] = [vehicle.reportText()]
|
|
for photo in vehicle.photos {
|
|
// TODO: Fix sharing
|
|
// if let url = URL(string: photo.url) {
|
|
// if let image = ImageCache.default.retrieveImageInDiskCache(forKey: url.cacheKey) {
|
|
// items.append(image)
|
|
// }
|
|
//
|
|
// }
|
|
}
|
|
|
|
let controller = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
|
controller.popoverPresentationController?.barButtonItem = sender
|
|
self.present(controller, animated: true)
|
|
}
|
|
|
|
let shareLink = UIAlertAction(title: NSLocalizedString("As link", comment: ""), style: .default) { _ in
|
|
guard let vehicle = self.vehicle else { return }
|
|
|
|
if let jwt = try? JWT<EmptyPayload>.generate(for: vehicle.getNumber()),
|
|
let url = URL(string: Constants.reportLinkBaseURL + "?token=" + jwt)
|
|
{
|
|
let controller = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
|
controller.popoverPresentationController?.barButtonItem = sender
|
|
self.present(controller, animated: true)
|
|
}
|
|
}
|
|
|
|
let copyLink = UIAlertAction(title: NSLocalizedString("Copy link to report", comment: ""), style: .default) { _ in
|
|
guard let vehicle = self.vehicle else { return }
|
|
|
|
if let jwt = try? JWT<EmptyPayload>.generate(for: vehicle.getNumber()),
|
|
let url = URL(string: Constants.reportLinkBaseURL + "?token=" + jwt)
|
|
{
|
|
UIPasteboard.general.string = url.absoluteString
|
|
}
|
|
}
|
|
|
|
sheet.addAction(shareImage)
|
|
sheet.addAction(shareTextAndImage)
|
|
sheet.addAction(shareLink)
|
|
sheet.addAction(copyLink)
|
|
sheet.addAction(cancel)
|
|
self.present(sheet, animated: true, completion: nil)
|
|
}
|
|
|
|
// MARK: - Copy
|
|
|
|
@IBAction func onCopy(_ sender: UIBarButtonItem) {
|
|
let sheet = UIAlertController(title: NSLocalizedString("Copy to pasteboard", comment: ""), message: nil, preferredStyle: .actionSheet)
|
|
sheet.popoverPresentationController?.barButtonItem = self.copyBarItem
|
|
let cancel = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { _ in sheet.dismiss(animated: true, completion: nil) }
|
|
let copyPlateNumber = UIAlertAction(title: NSLocalizedString("Plate number", comment: ""), style: .default) { _ in UIPasteboard.general.string = self.vehicle?.getNumber() }
|
|
let copyVin = UIAlertAction(title: NSLocalizedString("VIN", comment: ""), style: .default) { _ in UIPasteboard.general.string = self.vehicle?.vin1 }
|
|
sheet.addAction(copyPlateNumber)
|
|
sheet.addAction(copyVin)
|
|
sheet.addAction(cancel)
|
|
self.present(sheet, animated: true, completion: nil)
|
|
}
|
|
}
|