235 lines
6.4 KiB
Swift
235 lines
6.4 KiB
Swift
//
|
|
// HistoryViewModel.swift
|
|
// AutoCat
|
|
//
|
|
// Created by Selim Mustafaev on 15.01.2025.
|
|
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
import AutoCatCore
|
|
|
|
enum CheckType {
|
|
|
|
case normal
|
|
case update
|
|
case record(VehicleEventDto?)
|
|
}
|
|
|
|
@MainActor
|
|
@Observable
|
|
final class HistoryViewModel: ACHudContainer {
|
|
|
|
let apiService: ApiServiceProtocol
|
|
let storageService: StorageServiceProtocol
|
|
let vehicleService: VehicleServiceProtocol
|
|
|
|
var hud: ACHud?
|
|
|
|
var vehicles: [VehicleDto] = []
|
|
var vehiclesFiltered: [VehicleDto] = []
|
|
var vehicleSections: [DateSection<VehicleDto>] = []
|
|
|
|
var vehicleToOpen: VehicleDto?
|
|
var selectedVehicleId: VehicleDto.ID?
|
|
|
|
var selectedVehicle: VehicleDto? {
|
|
guard let id = selectedVehicleId else {
|
|
return nil
|
|
}
|
|
|
|
if let vehicle = vehicles.first(where: { $0.id == id }) {
|
|
return vehicle
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
var searchText: String = "" {
|
|
didSet {
|
|
if searchText != oldValue {
|
|
applyFilters()
|
|
}
|
|
}
|
|
}
|
|
|
|
var filter: HistoryFilter = .all
|
|
|
|
var vehiclesArchive: VehiclesArchive {
|
|
VehiclesArchive(vehiles: vehiclesFiltered)
|
|
}
|
|
|
|
var vehiclesCount: Int {
|
|
vehicleSections.reduce(0, { $0 + $1.elements.count })
|
|
}
|
|
|
|
var dbFileURL: URL?
|
|
|
|
init(apiService: ApiServiceProtocol,
|
|
storageService: StorageServiceProtocol,
|
|
vehicleService: VehicleServiceProtocol) {
|
|
|
|
self.apiService = apiService
|
|
self.storageService = storageService
|
|
self.vehicleService = vehicleService
|
|
}
|
|
|
|
func onAppear() async {
|
|
guard vehicles.isEmpty else {
|
|
return
|
|
}
|
|
|
|
async let loadTask: () = loadVehicles()
|
|
async let dbUrlTask = storageService.dbFileURL
|
|
|
|
await loadTask
|
|
dbFileURL = await dbUrlTask
|
|
}
|
|
|
|
func loadVehicles() async {
|
|
|
|
vehicles = await storageService.loadVehicles()
|
|
applyFilters()
|
|
}
|
|
|
|
func applyFilters() {
|
|
vehiclesFiltered = filterType(vehicles)
|
|
vehiclesFiltered = filterSearch(vehiclesFiltered)
|
|
vehicleSections = vehiclesFiltered.groupedByDate(type: .updatedDate)
|
|
}
|
|
|
|
func filterSearch(_ vehicles: [VehicleDto]) -> [VehicleDto] {
|
|
guard !searchText.isEmpty else {
|
|
return vehicles
|
|
}
|
|
|
|
let text = searchText.uppercased()
|
|
let regex = try? Regex(text)
|
|
|
|
return vehicles.filter { vehicle in
|
|
let number = vehicle.getNumber()
|
|
if let regex {
|
|
return number.contains(regex)
|
|
} else {
|
|
return number.contains(text)
|
|
}
|
|
}
|
|
}
|
|
|
|
func filterType(_ vehicles: [VehicleDto]) -> [VehicleDto] {
|
|
guard filter != .all else {
|
|
return vehicles
|
|
}
|
|
|
|
return vehicles.filter { vehicle in
|
|
switch filter {
|
|
case .unrecognized: vehicle.unrecognized
|
|
case .outdated: vehicle.outdated
|
|
case .notSynced: vehicle.needSync
|
|
case .all: true
|
|
}
|
|
}
|
|
}
|
|
|
|
func checkVehicle(
|
|
number: String,
|
|
numberType: VehicleNumberType = .plateNumber,
|
|
checkType: CheckType,
|
|
trackLocation: Bool = true
|
|
) async {
|
|
do {
|
|
hud = .progress
|
|
let (vehicle, errors) = switch checkType {
|
|
case .normal: try await vehicleService.check(number: number, numberType: numberType, trackLocation: trackLocation)
|
|
case .update: try await vehicleService.updateHistory(number: number)
|
|
case .record(let event): try await vehicleService.checkRecord(number: number, event: event)
|
|
}
|
|
|
|
await loadVehicles()
|
|
|
|
if errors.isEmpty {
|
|
hud = nil
|
|
if !vehicle.unrecognized {
|
|
openVehicleDetail(vehicle, numberType: numberType)
|
|
}
|
|
} else {
|
|
showErrors(errors)
|
|
}
|
|
} catch {
|
|
hud = .error(error)
|
|
}
|
|
}
|
|
|
|
func openVehicleDetail(_ vehicle: VehicleDto, numberType: VehicleNumberType) {
|
|
if Device.isIPhone {
|
|
switch numberType {
|
|
case .plateNumber:
|
|
Router.shared.openLocalReport(vehicle: vehicle)
|
|
case .vin:
|
|
Router.shared.openSharedReport(vehicle: vehicle)
|
|
}
|
|
} else {
|
|
selectedVehicleId = vehicle.id
|
|
}
|
|
}
|
|
|
|
func checkNewNumber(_ number: String, numberType: VehicleNumberType, enableLocation: Bool) async {
|
|
|
|
await checkVehicle(
|
|
number: number,
|
|
numberType: numberType,
|
|
checkType: .normal,
|
|
trackLocation: enableLocation
|
|
)
|
|
}
|
|
|
|
func deleteVehicle(_ vehicle: VehicleDto) async {
|
|
await wrapWithToast(showProgress: false) { [weak self] in
|
|
guard let self else { throw GenericError.somethingWentWrong }
|
|
try await storageService.deleteVehicle(number: vehicle.getNumber())
|
|
await loadVehicles()
|
|
}
|
|
}
|
|
|
|
func updateVehicle(_ vehicle: VehicleDto) async {
|
|
await checkVehicle(number: vehicle.getNumber(), checkType: .update)
|
|
}
|
|
|
|
func checkRecord(number: String, event: VehicleEventDto?) async {
|
|
await checkVehicle(number: number, checkType: .record(event))
|
|
}
|
|
|
|
func onVehicleChanged(_ vehicle: VehicleDto) {
|
|
|
|
Task {
|
|
await loadVehicles()
|
|
}
|
|
}
|
|
|
|
func onOpenUrl(_ url: URL) {
|
|
guard let param = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first,
|
|
let token = param.value,
|
|
let jwt = JWT<NumberPayload>(string: token)
|
|
else {
|
|
return
|
|
}
|
|
|
|
Task {
|
|
await openReport(number: jwt.payload.plateNumber)
|
|
}
|
|
}
|
|
|
|
func openReport(number: String) async {
|
|
await wrapWithToast { [weak self] in
|
|
guard let self else { return }
|
|
let vehicle = try await apiService.getReport(for: number)
|
|
|
|
if Device.isIPhone {
|
|
Router.shared.openSharedReport(vehicle: vehicle)
|
|
} else {
|
|
vehicleToOpen = vehicle
|
|
}
|
|
}
|
|
}
|
|
}
|