193 lines
5.3 KiB
Swift
193 lines
5.3 KiB
Swift
//
|
|
// SearchViewModel.swift
|
|
// AutoCat
|
|
//
|
|
// Created by Selim Mustafaev on 17.02.2025.
|
|
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
|
//
|
|
|
|
import AutoCatCore
|
|
import SwiftUI
|
|
import Combine
|
|
|
|
@MainActor
|
|
@Observable
|
|
final class SearchViewModel: ACHudContainer {
|
|
|
|
let apiService: ApiServiceProtocol
|
|
let vehicleService: VehicleServiceProtocol
|
|
var coordinator: SearchCoordinator?
|
|
|
|
var hud: ACHud?
|
|
|
|
@ObservationIgnored
|
|
var vehicles: [VehicleDto] = []
|
|
var vehicleSections: [DateSection<VehicleDto>] = []
|
|
|
|
var filter = Filter()
|
|
var pageToken: String?
|
|
var hasMoreData: Bool = true
|
|
var vehiclesCount: Int = 0
|
|
|
|
var searchText: String = "" {
|
|
didSet {
|
|
if searchText != oldValue {
|
|
filter.searchString = searchText
|
|
searchTask = Task { [filter] in
|
|
do {
|
|
try await Task.sleep(for: .milliseconds(500))
|
|
await reloadData(with: filter)
|
|
} catch {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@ObservationIgnored
|
|
@AutoCancellable
|
|
var searchTask: Task<Void, Never>?
|
|
|
|
var vehiclesArchive: VehiclesArchive {
|
|
VehiclesArchive(apiService: apiService, filter: filter)
|
|
}
|
|
|
|
init(apiService: ApiServiceProtocol,
|
|
vehicleService: VehicleServiceProtocol) {
|
|
|
|
self.apiService = apiService
|
|
self.vehicleService = vehicleService
|
|
}
|
|
|
|
func onAppear() async {
|
|
guard vehicles.isEmpty else {
|
|
return
|
|
}
|
|
|
|
resetData()
|
|
await wrapWithToast { [weak self] in
|
|
guard let self else { return }
|
|
try await loadSearchResults(filter: filter)
|
|
}
|
|
}
|
|
|
|
func resetData() {
|
|
|
|
vehicles = []
|
|
vehicleSections = []
|
|
pageToken = nil
|
|
}
|
|
|
|
func loadSearchResults(filter: Filter) async throws {
|
|
|
|
let response = try await apiService.getVehicles(with: filter, pageToken: pageToken, pageSize: 20)
|
|
|
|
if response.items.isEmpty {
|
|
hasMoreData = false
|
|
} else {
|
|
hasMoreData = response.pageToken != nil
|
|
vehicles += response.items
|
|
pageToken = response.pageToken
|
|
vehiclesCount = response.count ?? 0
|
|
vehicleSections = vehicles.groupedByDate(type: .updatedDate)
|
|
}
|
|
}
|
|
|
|
func loadMoreData() async {
|
|
do {
|
|
try await loadSearchResults(filter: filter)
|
|
} catch {
|
|
if !error.isCanceled {
|
|
hasMoreData = false
|
|
hud = .error(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
func reloadData() async {
|
|
await reloadData(with: filter)
|
|
}
|
|
|
|
func reloadData(with filter: Filter) async {
|
|
resetData()
|
|
do {
|
|
try await loadSearchResults(filter: filter)
|
|
} catch {
|
|
if !error.isCanceled {
|
|
hasMoreData = false
|
|
hud = .error(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
func openReport(vehicle: VehicleDto) async {
|
|
guard let updatedVehicle = await coordinator?.openReport(vehicle: vehicle) else {
|
|
return
|
|
}
|
|
|
|
if let index = vehicles.firstIndex(where: { $0.number == updatedVehicle.number }) {
|
|
vehicles[index] = updatedVehicle
|
|
vehicleSections = vehicles.groupedByDate(type: .updatedDate)
|
|
}
|
|
}
|
|
|
|
func openFilterDetail() async {
|
|
guard let updatedFilter = await coordinator?.openFilterDetail(filter: filter) else {
|
|
return
|
|
}
|
|
|
|
filter = updatedFilter
|
|
resetData()
|
|
await wrapWithToast { [weak self] in
|
|
guard let self else { return }
|
|
try await loadSearchResults(filter: filter)
|
|
}
|
|
}
|
|
|
|
func showOnMap() {
|
|
coordinator?.showOnMap(filter: filter)
|
|
}
|
|
|
|
func exportSearchResults() async {
|
|
await wrapWithToast { [weak self] in
|
|
guard let self else {
|
|
return
|
|
}
|
|
|
|
let resp = try await apiService.getVehicles(with: filter, pageToken: nil, pageSize: 0)
|
|
|
|
let newLine = "\r\n"
|
|
var csvString = VehicleDto.csvHeader + newLine
|
|
|
|
for vehicle in resp.items {
|
|
csvString.append(vehicle.csvLine)
|
|
csvString.append(newLine)
|
|
}
|
|
|
|
let tmpUrl = FileManager.default.tmpUrl(name: "search", ext: "csv")
|
|
try csvString.write(to: tmpUrl, atomically: true, encoding: .utf8)
|
|
|
|
coordinator?.export(url: tmpUrl)
|
|
}
|
|
}
|
|
|
|
func updateVehicle(_ vehicle: VehicleDto) async {
|
|
do {
|
|
hud = .progress
|
|
let (updatedVehicle, errors) = try await vehicleService.updateSearch(number: vehicle.number)
|
|
|
|
if !errors.isEmpty {
|
|
showErrors(errors)
|
|
return
|
|
}
|
|
|
|
if let index = vehicles.firstIndex(where: { $0.number == updatedVehicle.number }) {
|
|
vehicles[index] = updatedVehicle
|
|
vehicleSections = vehicles.groupedByDate(type: .updatedDate)
|
|
}
|
|
hud = nil
|
|
} catch {
|
|
hud = .error(error)
|
|
}
|
|
}
|
|
}
|