From b36620eba0a3001153140ff5ffc03de5cc2f8f95 Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Fri, 13 Dec 2024 00:14:40 +0300 Subject: [PATCH] Adding events --- AutoCat.xcodeproj/project.pbxproj | 4 ++ .../EventsScreen/EventsCoordinator.swift | 7 ++++ .../Screens/EventsScreen/EventsScreen.swift | 6 +++ .../EventsScreen/EventsViewModel.swift | 37 ++++++++++++++++++- .../LocationEditCoordinator.swift | 4 +- .../ApiService/ApiServiceProtocol.swift | 4 ++ .../StorageService+Events.swift | 26 +++++++++++++ .../StorageServiceProtocol.swift | 3 ++ 8 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 AutoCatCore/Services/StorageService/StorageService+Events.swift diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index e744657..90dd136 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -136,6 +136,7 @@ 7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A96AE2E246B2BCD00297C33 /* WebKit.framework */; }; 7A99406426E4BFAE002E9CB6 /* VehicleNoteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A99406326E4BFAE002E9CB6 /* VehicleNoteCell.swift */; }; 7A9FEEC82529AB23001CA50E /* RxRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FEEC72529AB23001CA50E /* RxRealmDataSource.swift */; }; + 7AA514E02D0B75B3001CAC50 /* StorageService+Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA514DF2D0B75B3001CAC50 /* StorageService+Events.swift */; }; 7AA7BC3325A5DFB80053A5D5 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 7AF58D332402A91C00CE01A0 /* Kingfisher */; }; 7AA7BC3525A5DFB80053A5D5 /* ExceptionCatcher in Frameworks */ = {isa = PBXBuildFile; productRef = 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */; }; 7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABDE1C2532F3EB0041AFC6 /* PKHUD */; }; @@ -413,6 +414,7 @@ 7A96AE32246C095700297C33 /* Base64FS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base64FS.swift; sourceTree = ""; }; 7A99406326E4BFAE002E9CB6 /* VehicleNoteCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleNoteCell.swift; sourceTree = ""; }; 7A9FEEC72529AB23001CA50E /* RxRealmDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxRealmDataSource.swift; sourceTree = ""; }; + 7AA514DF2D0B75B3001CAC50 /* StorageService+Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorageService+Events.swift"; sourceTree = ""; }; 7AAAFAD22C4D0FD00050410D /* ACImageSliderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACImageSliderView.swift; sourceTree = ""; }; 7AAAFAD92C4D1AFE0050410D /* Zoomable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Zoomable.swift; sourceTree = ""; }; 7AAAFADB2C4D1E130050410D /* ACImageSliderView+Modifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ACImageSliderView+Modifier.swift"; sourceTree = ""; }; @@ -962,6 +964,7 @@ 7AB587312C42D38E00FA7B66 /* StorageServiceProtocol.swift */, 7A45FB372C27073700618694 /* StorageService.swift */, 7AB587332C42D3FA00FA7B66 /* StorageService+Notes.swift */, + 7AA514DF2D0B75B3001CAC50 /* StorageService+Events.swift */, ); path = StorageService; sourceTree = ""; @@ -1495,6 +1498,7 @@ 7A64A2032C19DA1000284124 /* VehicleDto.swift in Sources */, 7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */, 7A3E12D72C7B42B700EE710D /* UserDefaults+Settings.swift in Sources */, + 7AA514E02D0B75B3001CAC50 /* StorageService+Events.swift in Sources */, 7A64A2222C19E99E00284124 /* DebugInfoDto.swift in Sources */, 7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */, 7A64A2202C19E93500284124 /* VehicleNoteDto.swift in Sources */, diff --git a/AutoCat/Screens/EventsScreen/EventsCoordinator.swift b/AutoCat/Screens/EventsScreen/EventsCoordinator.swift index e72079b..6917afe 100644 --- a/AutoCat/Screens/EventsScreen/EventsCoordinator.swift +++ b/AutoCat/Screens/EventsScreen/EventsCoordinator.swift @@ -22,9 +22,16 @@ class EventsCoordinator { func start(vehicle: VehicleDto) async -> VehicleDto { let viewModel = EventsViewModel(vehicle: vehicle) + viewModel.coordinator = self let controller = CustomHostingController(rootView: EventsScreen(viewModel: viewModel)) navController.pushViewController(controller, animated: true) await controller.waitForDisappear() return viewModel.vehicle } + + func addNewEvent() async -> VehicleEventDto? { + + let coordinator = LocationEditCoordinator(navController: navController) + return await coordinator.start() + } } diff --git a/AutoCat/Screens/EventsScreen/EventsScreen.swift b/AutoCat/Screens/EventsScreen/EventsScreen.swift index 30e02fb..8c6d763 100644 --- a/AutoCat/Screens/EventsScreen/EventsScreen.swift +++ b/AutoCat/Screens/EventsScreen/EventsScreen.swift @@ -25,8 +25,14 @@ struct EventsScreen: View { list .zIndex(viewModel.mode == .list ? 1 : 0) } + .hud($viewModel.hud) .navigationTitle(viewModel.vehicle.number) .toolbar { + ToolbarItem(placement: .primaryAction) { + Button("", systemImage: "plus") { + Task { await viewModel.addNewEvent() } + } + } ToolbarItem(placement: .primaryAction) { Button("", systemImage: viewModel.mode.switchIcon) { viewModel.mode.toggle() diff --git a/AutoCat/Screens/EventsScreen/EventsViewModel.swift b/AutoCat/Screens/EventsScreen/EventsViewModel.swift index 31acf74..7671f8b 100644 --- a/AutoCat/Screens/EventsScreen/EventsViewModel.swift +++ b/AutoCat/Screens/EventsScreen/EventsViewModel.swift @@ -12,12 +12,16 @@ import CoreLocation @MainActor @Observable -class EventsViewModel { +class EventsViewModel: ACHudContainer { - @ObservationIgnored @Service var api: ApiServiceProtocol + 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 @@ -49,4 +53,33 @@ class EventsViewModel { let coordinate = CLLocationCoordinate2DMake(event.latitude, event.longitude) return MarkerModel(date: dateString, coordinate: coordinate) } + + func addNewEvent() async { + if let event = await coordinator?.addNewEvent() { + 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 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 + } + } } diff --git a/AutoCat/Screens/LocationEditScreen/LocationEditCoordinator.swift b/AutoCat/Screens/LocationEditScreen/LocationEditCoordinator.swift index e1539dc..9966069 100644 --- a/AutoCat/Screens/LocationEditScreen/LocationEditCoordinator.swift +++ b/AutoCat/Screens/LocationEditScreen/LocationEditCoordinator.swift @@ -11,7 +11,7 @@ import SwiftUI import AutoCatCore @MainActor -final class LocationEditCoordinator: Coordinator { +final class LocationEditCoordinator { let viewController: UINavigationController? let event: VehicleEventDto @@ -21,7 +21,7 @@ final class LocationEditCoordinator: Coordinator { self.event = event ?? VehicleEventDto(lat: 0, lon: 0) } - func start() async throws -> VehicleEventDto? { + func start() async -> VehicleEventDto? { let viewModel = LocationEditViewModel(event: event) viewModel.coordinator = self diff --git a/AutoCatCore/Services/ApiService/ApiServiceProtocol.swift b/AutoCatCore/Services/ApiService/ApiServiceProtocol.swift index d2b8770..2de47bc 100644 --- a/AutoCatCore/Services/ApiService/ApiServiceProtocol.swift +++ b/AutoCatCore/Services/ApiService/ApiServiceProtocol.swift @@ -15,6 +15,10 @@ public protocol ApiServiceProtocol: Sendable { func edit(note: VehicleNoteDto) async throws -> VehicleDto func remove(note id: String) async throws -> VehicleDto + func add(event: VehicleEventDto, to number: String) async throws -> VehicleDto + func remove(event id: String) async throws -> VehicleDto + func edit(event: VehicleEventDto) async throws -> VehicleDto + func getBrands() async throws -> [String] func getModels(of brand: String) async throws -> [String] func getColors() async throws -> [String] diff --git a/AutoCatCore/Services/StorageService/StorageService+Events.swift b/AutoCatCore/Services/StorageService/StorageService+Events.swift new file mode 100644 index 0000000..ab9ba4a --- /dev/null +++ b/AutoCatCore/Services/StorageService/StorageService+Events.swift @@ -0,0 +1,26 @@ +// +// StorageService+Events.swift +// AutoCatCore +// +// Created by Selim Mustafaev on 12.12.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import Foundation + +public extension StorageService { + + func add(event: VehicleEventDto, to number: String) async throws -> VehicleDto { + + guard let vehicle = realm.object(ofType: Vehicle.self, forPrimaryKey: number) else { + throw StorageError.vehicleNotFound + } + + try await realm.asyncWrite { + vehicle.events.append(VehicleEvent(dto: event)) + vehicle.updatedDate = Date().timeIntervalSince1970 + } + + return vehicle.dto + } +} diff --git a/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift b/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift index b602bcd..26005c8 100644 --- a/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift +++ b/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift @@ -16,4 +16,7 @@ public protocol StorageServiceProtocol: Sendable { func editNote(id: String, text: String, for number: String) async throws -> VehicleDto func updateVehicleIfExists(dto: VehicleDto) async throws func loadVehicle(number: String) async throws -> VehicleDto + func add(event: VehicleEventDto, to number: String) async throws -> VehicleDto +// func remove(event id: String) async throws -> VehicleDto +// func edit(event: VehicleEventDto) async throws -> VehicleDto }