AutoCat/AutoCat/Controllers/ReportController.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)
}
}