AutoCat/AutoCat/Screens/EventsScreen/EventsViewModel.swift
2024-12-16 22:14:43 +03:00

178 lines
5.5 KiB
Swift

//
// EventsViewModel.swift
// AutoCat
//
// Created by Selim Mustafaev on 10.12.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import SwiftUI
import AutoCatCore
import CoreLocation
import UniformTypeIdentifiers
import MobileCoreServices
extension UTType {
static var vehicleEvent: UTType {
UTType(exportedAs: "pro.aliencat.vehicle.event", conformingTo: .json)
}
}
@MainActor
@Observable
class EventsViewModel: ACHudContainer {
typealias VehicleOperation = () async throws -> VehicleDto
@ObservationIgnored @Service var apiService: ApiServiceProtocol
@ObservationIgnored @Service var storageService: StorageServiceProtocol
@ObservationIgnored @Service var settingsService: SettingsServiceProtocol
weak var coordinator: EventsCoordinator?
var hud: ACHud?
var vehicle: VehicleDto
var mode: EventsScreenMode = .map
var events: [EventModel] = []
var showPasteAlert: Bool = false
var pastedEvent: VehicleEventDto?
var isPasteAvailable: Bool {
UIPasteboard.general.data(forPasteboardType: UTType.vehicleEvent.identifier) != nil
}
init(vehicle: VehicleDto) {
self.vehicle = vehicle
updateEvents()
}
func updateEvents() {
events = vehicle.events.sorted { $0.date > $1.date }.map { event in
let date = Date(timeIntervalSince1970: event.date)
let dateString = Formatters.marker.string(from: date)
let coordinate = CLLocationCoordinate2DMake(event.latitude, event.longitude)
return EventModel(
id: event.id,
date: dateString,
coordinate: coordinate,
address: event.address ?? "Lat: \(event.latitude), Lon: \(event.longitude)",
isMe: event.addedBy == settingsService.user.email
)
}
}
func addEvent(_ event: VehicleEventDto) async {
await eventOperation {
try await self.storageService.add(event: event, to: self.vehicle.getNumber())
} apiOperation: {
try await self.apiService.add(event: event, to: self.vehicle.getNumber())
}
updateEvents()
}
func addNewEvent() async {
let newEvent = VehicleEventDto(lat: 0, lon: 0, addedBy: settingsService.user.email)
await addEvent(newEvent)
}
func deleteEvent(_ event: EventModel) async {
await eventOperation {
try await self.storageService.deleteNote(id: event.id, for: self.vehicle.getNumber())
} apiOperation: {
try await self.apiService.remove(event: event.id)
}
updateEvents()
}
func editEvent(_ event: EventModel) async {
guard let eventDto = vehicle.events.first(where: { $0.id == event.id }) else {
return
}
if let updatedEvent = await coordinator?.editEvent(event: eventDto) {
await eventOperation {
try await self.storageService.edit(event: updatedEvent, for: self.vehicle.getNumber())
} apiOperation: {
try await self.apiService.edit(event: updatedEvent)
}
updateEvents()
}
}
func eventOperation(_ storageOperation: @escaping VehicleOperation, apiOperation: @escaping VehicleOperation) async {
if vehicle.unrecognized {
await wrapWithToast(showProgress: false) { [weak self] in
guard let self else { return }
vehicle = try await storageOperation()
}
return
}
await wrapWithToast { [weak self] in
guard let self else { return }
let vehicle = try await apiOperation()
try await storageService.updateVehicleIfExists(dto: vehicle)
self.vehicle = vehicle
}
}
func getMapLink(for event: VehicleEventDto) -> URL? {
var urlString = "http://maps.apple.com/?sll=\(event.latitude),\(event.longitude)"
if let address = event.address?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
urlString = urlString + "&address=" + address
}
return URL(string: urlString)
}
func copyEvent(_ event: EventModel) {
guard let eventDto = vehicle.events.first(where: { $0.id == event.id }) else {
return
}
let sharedData: [String: Any?] = [
UTType.url.identifier: getMapLink(for: eventDto),
UTType.vehicleEvent.identifier: try? JSONEncoder().encode(eventDto)
]
let items = sharedData.compactMapValues { $0 }
if !items.isEmpty {
UIPasteboard.general.items = [items]
}
}
func confirmPaste() {
guard let data = UIPasteboard.general.data(forPasteboardType: UTType.vehicleEvent.identifier),
var eventDto = try? JSONDecoder().decode(VehicleEventDto.self, from: data)
else {
return
}
eventDto.id = UUID().uuidString
pastedEvent = eventDto
showPasteAlert = true
}
func getConfirmMessge(from event: VehicleEventDto) -> String {
let date = Formatters.marker.string(from: Date(timeIntervalSince1970: event.date))
let location = event.address ?? "\(event.latitude), \(event.longitude)"
return date + "\n" + location
}
}