From aa008d544351ba6c021de32b200ecffaa00b643c Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Tue, 3 Dec 2024 23:11:39 +0300 Subject: [PATCH] Showing debug info in new report screen --- AutoCat/Controllers/CheckController.swift | 2 +- .../ReportScreen/ReportCoordinator.swift | 6 ++- .../Screens/ReportScreen/ReportScreen.swift | 40 ++++++++++++++++++- .../ReportScreen/ReportViewModel.swift | 23 ++++++++++- AutoCat/SwiftUI/ACProgressHud/ACHud.swift | 5 +++ .../ACProgressHud/ACHudContainer.swift | 20 ++++++++++ .../ACProgressHud+Modifiers.swift | 5 +++ AutoCatCore/Models/Realm/Vehicle.swift | 1 + AutoCatTests/ReportTests.swift | 34 +++++++++++++--- 9 files changed, 125 insertions(+), 11 deletions(-) diff --git a/AutoCat/Controllers/CheckController.swift b/AutoCat/Controllers/CheckController.swift index de0cd92..a01c545 100644 --- a/AutoCat/Controllers/CheckController.swift +++ b/AutoCat/Controllers/CheckController.swift @@ -268,7 +268,7 @@ class CheckController: UIViewController, UITableViewDelegate, UISearchResultsUpd // } Task { - let coordinator = ReportCoordinator(splitController: splitViewController, vehicle: vehicle) + let coordinator = ReportCoordinator(splitController: splitViewController, vehicle: vehicle, isPersistent: true) try? await coordinator.start() } } diff --git a/AutoCat/Screens/ReportScreen/ReportCoordinator.swift b/AutoCat/Screens/ReportScreen/ReportCoordinator.swift index a282e24..a9f16ec 100644 --- a/AutoCat/Screens/ReportScreen/ReportCoordinator.swift +++ b/AutoCat/Screens/ReportScreen/ReportCoordinator.swift @@ -15,13 +15,15 @@ class ReportCoordinator: Coordinator { let viewController: UISplitViewController? let vehicle: VehicleDto + let isPersistent: Bool var navController: UINavigationController? - init(splitController: UISplitViewController?, vehicle: VehicleDto) { + init(splitController: UISplitViewController?, vehicle: VehicleDto, isPersistent: Bool) { self.viewController = splitController self.vehicle = vehicle + self.isPersistent = isPersistent } func start() async throws { @@ -29,7 +31,7 @@ class ReportCoordinator: Coordinator { if viewController?.viewControllers.count == 2 { navController = viewController?.viewControllers.last as? UINavigationController } else { - let viewModel = ReportViewModel(vehicle: vehicle) + let viewModel = ReportViewModel(vehicle: vehicle, isPersistent: isPersistent) viewModel.coordinator = self let controller = UIHostingController(rootView: ReportScreen(viewModel: viewModel)) navController = UINavigationController(rootViewController: controller) diff --git a/AutoCat/Screens/ReportScreen/ReportScreen.swift b/AutoCat/Screens/ReportScreen/ReportScreen.swift index 0b4edd8..466ac58 100644 --- a/AutoCat/Screens/ReportScreen/ReportScreen.swift +++ b/AutoCat/Screens/ReportScreen/ReportScreen.swift @@ -65,13 +65,49 @@ struct ReportScreen: View { LabeledContent("Notes", value: String(viewModel.vehicle.notes.count)) .navigationLink(onTap: viewModel.openNotes) } + + if viewModel.showDebugInfo { + Section("Debug info") { + makeDebugInfoCell(title: "Avtocod", model: viewModel.vehicle.debugInfo?.autocod) + makeDebugInfoCell(title: "Vin01 (VIN)", model: viewModel.vehicle.debugInfo?.vin01vin) + makeDebugInfoCell(title: "Vin01 (base)", model: viewModel.vehicle.debugInfo?.vin01base) + makeDebugInfoCell(title: "Vin01 (history)", model: viewModel.vehicle.debugInfo?.vin01history) + makeDebugInfoCell(title: "Nomerogram", model: viewModel.vehicle.debugInfo?.nomerogram) + } + } } .onAppear { - Task { await viewModel.loadVehicle() } + Task { await viewModel.onAppear() } + } + .hud($viewModel.hud) + } + + @ViewBuilder + func makeDebugInfoCell(title: String, model: DebugInfoEntryDto?) -> some View { + + let color: Color = switch model?.status { + case .success: .green + case .warning: .orange + case .error: .red + case .none: .gray + } + + let cell = LabeledContent(title) { + Circle() + .fill(color) + .frame(width: 16, height: 16) + } + + if let model, let error = model.error { + cell.navigationLink { + viewModel.showAlert(text: error, status: model.status) + } + } else { + cell } } } #Preview { - ReportScreen(viewModel: .init(vehicle: .preview)) + ReportScreen(viewModel: .init(vehicle: .preview, isPersistent: false)) } diff --git a/AutoCat/Screens/ReportScreen/ReportViewModel.swift b/AutoCat/Screens/ReportScreen/ReportViewModel.swift index 34322ec..263d5a7 100644 --- a/AutoCat/Screens/ReportScreen/ReportViewModel.swift +++ b/AutoCat/Screens/ReportScreen/ReportViewModel.swift @@ -15,9 +15,11 @@ class ReportViewModel: ACHudContainer { @ObservationIgnored @Service var api: ApiServiceProtocol @ObservationIgnored @Service var storageService: StorageServiceProtocol + @ObservationIgnored @Service var settings: SettingsServiceProtocol var coordinator: ReportCoordinator? + let isPersistent: Bool var vehicle: VehicleDto var hud: ACHud? @@ -41,8 +43,19 @@ class ReportViewModel: ACHudContainer { : NSLocalizedString("No", comment: "") } - init(vehicle: VehicleDto) { + var showDebugInfo: Bool { + settings.showDebugInfo + } + + init(vehicle: VehicleDto, isPersistent: Bool) { self.vehicle = vehicle + self.isPersistent = isPersistent + } + + func onAppear() async { + if isPersistent { + await loadVehicle() + } } func loadVehicle() async { @@ -53,6 +66,14 @@ class ReportViewModel: ACHudContainer { } } + func showAlert(text: String, status: DebugInfoStatus) { + switch status { + case .success: showSuccess(text: text) + case .warning: showWarning(text: text) + case .error: showError(text: text) + } + } + func openEvents() { coordinator?.openEvents(vehicle: vehicle) } diff --git a/AutoCat/SwiftUI/ACProgressHud/ACHud.swift b/AutoCat/SwiftUI/ACProgressHud/ACHud.swift index 416ce54..9144336 100644 --- a/AutoCat/SwiftUI/ACProgressHud/ACHud.swift +++ b/AutoCat/SwiftUI/ACProgressHud/ACHud.swift @@ -10,6 +10,7 @@ enum ACHud: Equatable, Identifiable, Sendable { case progress case error(Error) + case message(String, ACMessageView.MessageType = .info) var id: String { switch self { @@ -17,6 +18,8 @@ enum ACHud: Equatable, Identifiable, Sendable { "progress" case .error(let error): error.localizedDescription + case .message(let text, _): + text } } @@ -26,6 +29,8 @@ enum ACHud: Equatable, Identifiable, Sendable { true case (let .error(lerr), let .error(rerr)): lerr.localizedDescription == rerr.localizedDescription + case (let .message(lmsg, ltype), let .message(rmsg, rtype)): + lmsg == rmsg && ltype == rtype default: false } diff --git a/AutoCat/SwiftUI/ACProgressHud/ACHudContainer.swift b/AutoCat/SwiftUI/ACProgressHud/ACHudContainer.swift index 07e3aa7..9f78ac1 100644 --- a/AutoCat/SwiftUI/ACProgressHud/ACHudContainer.swift +++ b/AutoCat/SwiftUI/ACProgressHud/ACHudContainer.swift @@ -31,4 +31,24 @@ extension ACHudContainer where Self: AnyObject { hud = .error(error) } } + + func showInfo(text: String) { + hud = .message(text, .info) + } + + func showWarning(text: String) { + hud = .message(text, .warning) + } + + func showSuccess(text: String) { + hud = .message(text, .success) + } + + func showError(text: String) { + hud = .message(text, .error) + } + + func showError(_ error: Error) { + hud = .error(error) + } } diff --git a/AutoCat/SwiftUI/ACProgressHud/ACProgressHud+Modifiers.swift b/AutoCat/SwiftUI/ACProgressHud/ACProgressHud+Modifiers.swift index a300546..1fca688 100644 --- a/AutoCat/SwiftUI/ACProgressHud/ACProgressHud+Modifiers.swift +++ b/AutoCat/SwiftUI/ACProgressHud/ACProgressHud+Modifiers.swift @@ -38,6 +38,11 @@ struct ACProgressHudModifier: ViewModifier { type: .error) { self.item = nil } + case .message(let message, let type): + ACMessageView(message: message, + type: type) { + self.item = nil + } } } } diff --git a/AutoCatCore/Models/Realm/Vehicle.swift b/AutoCatCore/Models/Realm/Vehicle.swift index 5842e58..270f42c 100644 --- a/AutoCatCore/Models/Realm/Vehicle.swift +++ b/AutoCatCore/Models/Realm/Vehicle.swift @@ -165,6 +165,7 @@ extension Vehicle: DtoConvertible { notes.append(objectsIn: dto.notes.map(VehicleNote.init)) self.notes = notes + self.debugInfo = DebugInfo(dto: dto.debugInfo) self.synchronized = dto.synchronized } } diff --git a/AutoCatTests/ReportTests.swift b/AutoCatTests/ReportTests.swift index bb34e5b..9572d3b 100644 --- a/AutoCatTests/ReportTests.swift +++ b/AutoCatTests/ReportTests.swift @@ -12,21 +12,24 @@ import AutoCatCore @testable import AutoCat @MainActor -struct ReportTests { +class ReportTests { let existingVehicleNumber = "А123АА761" let nonExistingVehicleNumber = "А999АА761" let testColor = "testColor" var storageServiceMock: MockStorageServiceProtocol + var settingsServiceMock: MockSettingsServiceProtocol var viewModel: ReportViewModel init() { storageServiceMock = MockStorageServiceProtocol() + settingsServiceMock = MockSettingsServiceProtocol() ServiceContainer.shared.register(StorageServiceProtocol.self, instance: storageServiceMock) - viewModel = ReportViewModel(vehicle: VehicleDto()) + ServiceContainer.shared.register(SettingsServiceProtocol.self, instance: settingsServiceMock) + viewModel = ReportViewModel(vehicle: VehicleDto(), isPersistent: true) } @Test("Load vehicle detail") @@ -35,7 +38,7 @@ struct ReportTests { let incompleteVehicleModel = VehicleDto(number: existingVehicleNumber) let fullVehicleModel = VehicleDto(number: existingVehicleNumber, color: testColor) - viewModel.vehicle = incompleteVehicleModel + viewModel = ReportViewModel(vehicle: incompleteVehicleModel, isPersistent: true) #expect(viewModel.vehicle.color == nil) @@ -43,7 +46,11 @@ struct ReportTests { .loadVehicle(number: .value(existingVehicleNumber)) .willReturn(fullVehicleModel) - await viewModel.loadVehicle() + await viewModel.onAppear() + + verify(storageServiceMock) + .loadVehicle(number: .value(existingVehicleNumber)) + .called(.once) #expect(viewModel.vehicle.color == testColor) #expect(viewModel.hud == nil) @@ -52,12 +59,29 @@ struct ReportTests { @Test("Load vehicle error") func loadVehicleError() async throws { + viewModel = ReportViewModel(vehicle: VehicleDto(), isPersistent: true) + given(storageServiceMock) .loadVehicle(number: .any) .willThrow(StorageError.vehicleNotFound) - await viewModel.loadVehicle() + await viewModel.onAppear() + + verify(storageServiceMock) + .loadVehicle(number: .any) + .called(.once) #expect(viewModel.hud == .error(StorageError.vehicleNotFound)) } + + @Test("Show debug info", arguments: [true, false]) + func showDebugInfo(value: Bool) async throws { + + viewModel = ReportViewModel(vehicle: VehicleDto(), isPersistent: false) + + given(settingsServiceMock) + .showDebugInfo.willReturn(value) + + #expect(viewModel.showDebugInfo == value) + } }