215 lines
6.5 KiB
Swift
215 lines
6.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
|
|
import MapKit
|
|
|
|
extension UTType {
|
|
|
|
static var vehicleEvent: UTType {
|
|
UTType(exportedAs: "pro.aliencat.vehicle.event", conformingTo: .json)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
@Observable
|
|
class EventsViewModel: ACHudContainer {
|
|
|
|
typealias VehicleOperation = () async throws -> VehicleDto
|
|
|
|
let apiService: ApiServiceProtocol
|
|
let storageService: StorageServiceProtocol
|
|
let settingsService: SettingsServiceProtocol
|
|
|
|
var hud: ACHud?
|
|
|
|
var vehicle: VehicleDto
|
|
var mode: EventsScreenMode = .map
|
|
var events: [EventModel] = []
|
|
|
|
var showPasteAlert: Bool = false
|
|
var pastedEvent: VehicleEventDto?
|
|
|
|
var onUpdate: (VehicleDto) -> Void
|
|
|
|
var isPasteAvailable: Bool {
|
|
UIPasteboard.general.data(forPasteboardType: UTType.vehicleEvent.identifier) != nil
|
|
}
|
|
|
|
var shouldDisplayEventAuthors: Bool {
|
|
|
|
settingsService.user.hasPermission(.locationAuthor)
|
|
}
|
|
|
|
init(apiService: ApiServiceProtocol,
|
|
storageService: StorageServiceProtocol,
|
|
settingsService: SettingsServiceProtocol,
|
|
vehicle: VehicleDto,
|
|
onUpdate: @escaping (VehicleDto) -> Void) {
|
|
|
|
self.apiService = apiService
|
|
self.storageService = storageService
|
|
self.settingsService = settingsService
|
|
self.vehicle = vehicle
|
|
self.onUpdate = onUpdate
|
|
}
|
|
|
|
func onAppear() {
|
|
|
|
updateEvents(initialLoad: true)
|
|
}
|
|
|
|
func updateEvents(initialLoad: Bool = false) {
|
|
|
|
let email = settingsService.user.email
|
|
|
|
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 == email,
|
|
user: event.addedBy
|
|
)
|
|
}
|
|
|
|
if !initialLoad {
|
|
onUpdate(vehicle)
|
|
}
|
|
}
|
|
|
|
func onEventUpdated(_ event: VehicleEventDto) {
|
|
|
|
Task {
|
|
await eventOperation {
|
|
try await self.storageService.edit(event: event, for: self.vehicle.getNumber())
|
|
} apiOperation: {
|
|
try await self.apiService.edit(event: event)
|
|
}
|
|
|
|
updateEvents()
|
|
}
|
|
}
|
|
|
|
func onNewEvent(_ event: VehicleEventDto) {
|
|
|
|
Task {
|
|
await addEvent(event)
|
|
}
|
|
}
|
|
|
|
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 deleteEvent(_ event: EventModel) async {
|
|
|
|
await eventOperation {
|
|
try await self.storageService.remove(event: event.id, from: self.vehicle.getNumber())
|
|
} apiOperation: {
|
|
try await self.apiService.remove(event: event.id)
|
|
}
|
|
|
|
updateEvents()
|
|
}
|
|
|
|
func eventOperation(_ storageOperation: @escaping VehicleOperation, apiOperation: @escaping VehicleOperation) async {
|
|
|
|
if vehicle.unrecognized {
|
|
await wrapWithToast(showProgress: false) { [weak self] in
|
|
guard let self else { throw GenericError.somethingWentWrong }
|
|
vehicle = try await storageOperation()
|
|
}
|
|
return
|
|
}
|
|
|
|
await wrapWithToast { [weak self] in
|
|
guard let self else { throw GenericError.somethingWentWrong }
|
|
let vehicle = try await apiOperation()
|
|
try await storageService.updateVehicle(dto: vehicle, policy: .ifExists)
|
|
self.vehicle = vehicle
|
|
}
|
|
}
|
|
|
|
func getMapLink(for event: EventModel) -> URL? {
|
|
var urlString = "http://maps.apple.com/?sll=\(event.coordinate.latitude),\(event.coordinate.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: event),
|
|
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
|
|
}
|
|
|
|
func openMaps(_ event: EventModel) {
|
|
guard let url = URL(string: "yandexmaps://maps.yandex.ru/?pt=\(event.coordinate.longitude),\(event.coordinate.latitude)&z=12") else {
|
|
return
|
|
}
|
|
|
|
if UIApplication.shared.canOpenURL(url) {
|
|
UIApplication.shared.open(url)
|
|
} else {
|
|
let placemark = MKPlacemark(coordinate: event.coordinate)
|
|
let mapItem = MKMapItem(placemark: placemark)
|
|
mapItem.name = event.date
|
|
mapItem.openInMaps()
|
|
}
|
|
}
|
|
}
|