Checking number from audio record

This commit is contained in:
Selim Mustafaev 2025-04-02 23:56:17 +03:00
parent ecf64d3280
commit 87074fa61a
10 changed files with 139 additions and 33 deletions

View File

@ -30,6 +30,7 @@
7A11470D23FDE7E600B424AF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470B23FDE7E600B424AF /* LaunchScreen.storyboard */; }; 7A11470D23FDE7E600B424AF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470B23FDE7E600B424AF /* LaunchScreen.storyboard */; };
7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11471523FDEB2A00B424AF /* MainSplitController.swift */; }; 7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11471523FDEB2A00B424AF /* MainSplitController.swift */; };
7A11471A23FE839000B424AF /* AuthController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11471923FE839000B424AF /* AuthController.swift */; }; 7A11471A23FE839000B424AF /* AuthController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11471923FE839000B424AF /* AuthController.swift */; };
7A123C742D9DC40A00781F24 /* RecordScreenOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A123C732D9DC40A00781F24 /* RecordScreenOutput.swift */; };
7A131FD32D37B75500DC7755 /* HistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */; }; 7A131FD32D37B75500DC7755 /* HistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */; };
7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */; }; 7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */; };
7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */; }; 7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */; };
@ -318,6 +319,7 @@
7A11474623FF2AA500B424AF /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; }; 7A11474623FF2AA500B424AF /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
7A11474823FF2B2D00B424AF /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = "<group>"; }; 7A11474823FF2B2D00B424AF /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = "<group>"; };
7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; }; 7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; };
7A123C732D9DC40A00781F24 /* RecordScreenOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordScreenOutput.swift; sourceTree = "<group>"; };
7A131FD22D37B75500DC7755 /* HistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryScreen.swift; sourceTree = "<group>"; }; 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryScreen.swift; sourceTree = "<group>"; };
7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewModel.swift; sourceTree = "<group>"; }; 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewModel.swift; sourceTree = "<group>"; };
7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCoordinator.swift; sourceTree = "<group>"; }; 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCoordinator.swift; sourceTree = "<group>"; };
@ -1046,6 +1048,7 @@
7A95197F2D80B6C100E69883 /* RecordsScreen.swift */, 7A95197F2D80B6C100E69883 /* RecordsScreen.swift */,
7A9519812D80B6E500E69883 /* RecordsViewModel.swift */, 7A9519812D80B6E500E69883 /* RecordsViewModel.swift */,
7A9519832D80B72B00E69883 /* RecordsCoordinator.swift */, 7A9519832D80B72B00E69883 /* RecordsCoordinator.swift */,
7A123C732D9DC40A00781F24 /* RecordScreenOutput.swift */,
); );
path = RecordsScreen; path = RecordsScreen;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1585,6 +1588,7 @@
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */, 7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */, 7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */,
7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */, 7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */,
7A123C742D9DC40A00781F24 /* RecordScreenOutput.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -15,8 +15,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/Kolos65/Mockable", "location" : "https://github.com/Kolos65/Mockable",
"state" : { "state" : {
"revision" : "a9e5e1d222035567069ed6fff8429c327229b5f6", "revision" : "68f3ed6c4b62afab27a84425494cb61421a61ac1",
"version" : "0.0.11" "version" : "0.3.1"
} }
}, },
{ {
@ -33,8 +33,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/realm/realm-core.git", "location" : "https://github.com/realm/realm-core.git",
"state" : { "state" : {
"revision" : "85eeca41654cc9070ad81a21a4b1d153ad467c33", "revision" : "cccb3ca9e26ec452a29f2f0d4050d1e38b8a3d43",
"version" : "14.13.1" "version" : "14.14.0"
} }
}, },
{ {
@ -42,17 +42,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/realm/realm-swift.git", "location" : "https://github.com/realm/realm-swift.git",
"state" : { "state" : {
"revision" : "5553cfd1c789efdb3d6daf7f0cc0733cfe601846", "revision" : "8cb364a6a63695df296f05b53f7c3f3b1dda6af3",
"version" : "10.54.1" "version" : "10.54.3"
}
},
{
"identity" : "swift-issue-reporting",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-issue-reporting",
"state" : {
"revision" : "770f990d3e4eececb57ac04a6076e22f8c97daeb",
"version" : "1.4.2"
} }
}, },
{ {
@ -60,8 +51,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax.git", "location" : "https://github.com/swiftlang/swift-syntax.git",
"state" : { "state" : {
"revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82", "revision" : "0687f71944021d616d34d922343dcef086855920",
"version" : "510.0.3" "version" : "600.0.1"
} }
}, },
{ {
@ -90,6 +81,15 @@
"revision" : "010073e62cea4daefea61042a51b8619d23cdc35", "revision" : "010073e62cea4daefea61042a51b8619d23cdc35",
"version" : "6.0.0" "version" : "6.0.0"
} }
},
{
"identity" : "xctest-dynamic-overlay",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state" : {
"revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4",
"version" : "1.5.2"
}
} }
], ],
"version" : 3 "version" : 3

View File

@ -40,7 +40,7 @@ class MainTabController: UITabBarController, UITabBarControllerDelegate {
func addRecordsTab() { func addRecordsTab() {
let coordinator = RecordsCoordinator() let coordinator = RecordsCoordinator()
let controller = coordinator.start() let controller = coordinator.start(output: self)
controller.tabBarItem = UITabBarItem(title: NSLocalizedString("Records", comment: ""), controller.tabBarItem = UITabBarItem(title: NSLocalizedString("Records", comment: ""),
image: UIImage(named: "record"), tag: 0) image: UIImage(named: "record"), tag: 0)
viewControllers?[1] = controller viewControllers?[1] = controller
@ -108,3 +108,11 @@ class MainTabController: UITabBarController, UITabBarControllerDelegate {
Task { try? await RxLocationManager.requestCurrentLocation() } Task { try? await RxLocationManager.requestCurrentLocation() }
} }
} }
extension MainTabController: RecordScreenOutput {
func checkRecord(number: String, event: VehicleEventDto?) {
selectedIndex = 0
Task { await historyViewModel?.checkRecord(number: number, event: event) }
}
}

View File

@ -9,6 +9,13 @@
import SwiftUI import SwiftUI
import AutoCatCore import AutoCatCore
enum CheckType {
case normal
case update
case record(VehicleEventDto?)
}
@MainActor @MainActor
@Observable @Observable
final class HistoryViewModel: ACHudContainer { final class HistoryViewModel: ACHudContainer {
@ -112,11 +119,15 @@ final class HistoryViewModel: ACHudContainer {
} }
} }
func checkVehicle(number: String, isUpdate: Bool) async { func checkVehicle(number: String, type: CheckType) async {
do { do {
hud = .progress hud = .progress
let (vehicle, errors) = isUpdate ? try await vehicleService.updateHistory(number: number) let (vehicle, errors) = switch type {
: try await vehicleService.check(number: number) case .normal: try await vehicleService.check(number: number)
case .update: try await vehicleService.updateHistory(number: number)
case .record(let event): try await vehicleService.checkRecord(number: number, event: event)
}
await loadVehicles() await loadVehicles()
if errors.isEmpty { if errors.isEmpty {
@ -133,7 +144,7 @@ final class HistoryViewModel: ACHudContainer {
} }
func checkNewNumber(_ number: String) async { func checkNewNumber(_ number: String) async {
await checkVehicle(number: number, isUpdate: false) await checkVehicle(number: number, type: .normal)
} }
func deleteVehicle(_ vehicle: VehicleDto) async { func deleteVehicle(_ vehicle: VehicleDto) async {
@ -145,6 +156,10 @@ final class HistoryViewModel: ACHudContainer {
} }
func updateVehicle(_ vehicle: VehicleDto) async { func updateVehicle(_ vehicle: VehicleDto) async {
await checkVehicle(number: vehicle.getNumber(), isUpdate: true) await checkVehicle(number: vehicle.getNumber(), type: .update)
}
func checkRecord(number: String, event: VehicleEventDto?) async {
await checkVehicle(number: number, type: .record(event))
} }
} }

View File

@ -0,0 +1,15 @@
//
// RecordScreenOutput.swift
// AutoCat
//
// Created by Selim Mustafaev on 02.04.2025.
// Copyright © 2025 Selim Mustafaev. All rights reserved.
//
import AutoCatCore
@MainActor
protocol RecordScreenOutput: AnyObject {
func checkRecord(number: String, event: VehicleEventDto?)
}

View File

@ -15,7 +15,7 @@ final class RecordsCoordinator {
var navController = UINavigationController() var navController = UINavigationController()
func start() -> UIViewController { func start(output: RecordScreenOutput?) -> UIViewController {
let resolver = ServiceContainer.shared let resolver = ServiceContainer.shared
let viewModel = RecordsViewModel( let viewModel = RecordsViewModel(
@ -25,6 +25,7 @@ final class RecordsCoordinator {
) )
viewModel.coordinator = self viewModel.coordinator = self
viewModel.output = output
let view = RecordsScreen(viewModel: viewModel) let view = RecordsScreen(viewModel: viewModel)
let controller = UIHostingController(rootView: view) let controller = UIHostingController(rootView: view)

View File

@ -100,5 +100,11 @@ struct RecordsScreen: View {
} label: { } label: {
Label("Show on map", systemImage: "map") Label("Show on map", systemImage: "map")
} }
Button {
viewModel.check(id: record.id)
} label: {
Label("Check", systemImage: "eye")
}
} }
} }

View File

@ -17,6 +17,7 @@ final class RecordsViewModel: ACHudContainer {
let storageService: StorageServiceProtocol let storageService: StorageServiceProtocol
let recordPlayer: RecordPlayerServiceProtocol let recordPlayer: RecordPlayerServiceProtocol
var coordinator: RecordsCoordinator? var coordinator: RecordsCoordinator?
weak var output: RecordScreenOutput?
var hud: ACHud? var hud: ACHud?
var showRecordingAlert: Bool = false var showRecordingAlert: Bool = false
@ -122,4 +123,14 @@ final class RecordsViewModel: ACHudContainer {
coordinator?.showOnMap(event: event) coordinator?.showOnMap(event: event)
} }
func check(id: String) {
guard let record = records.first(where: { $0.id == id }),
let number = record.number
else {
return
}
output?.checkRecord(number: number, event: record.event)
}
} }

View File

@ -31,17 +31,27 @@ public final class VehicleService {
extension VehicleService: VehicleServiceProtocol { extension VehicleService: VehicleServiceProtocol {
func check(number: String, func check(
number: String,
forceUpdate: Bool, forceUpdate: Bool,
trackLocation: Bool, trackLocation: Bool,
dbUpdatePolicy: DbUpdatePolicy) async throws -> VehicleWithErrors { additionalEvents: [VehicleEventDto],
dbUpdatePolicy: DbUpdatePolicy
) async throws -> VehicleWithErrors {
var vehicle = (try? await storageService.loadVehicle(number: number)) ?? VehicleDto(number: number) var vehicle = (try? await storageService.loadVehicle(number: number)) ?? VehicleDto(number: number)
var errors: [Error] = [] var errors: [Error] = []
let events = vehicle.events // TODO: Check if additionalEvents already was added earlier (avoid duplication)
let events = vehicle.events + additionalEvents
let notes = vehicle.notes let notes = vehicle.notes
if !additionalEvents.isEmpty {
vehicle.events = events
vehicle.updatedDate = Date().timeIntervalSince1970
vehicle.synchronized = false
}
async let locationTask = trackLocation ? locationService.getRecentLocation() : nil async let locationTask = trackLocation ? locationService.getRecentLocation() : nil
async let vehicleTask = apiService.checkVehicle(by: number, notes: notes, events: events, force: forceUpdate) async let vehicleTask = apiService.checkVehicle(by: number, notes: notes, events: events, force: forceUpdate)
@ -75,19 +85,54 @@ extension VehicleService: VehicleServiceProtocol {
public func check(number: String) async throws -> VehicleWithErrors { public func check(number: String) async throws -> VehicleWithErrors {
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
try await check(number: number, forceUpdate: false, trackLocation: false, dbUpdatePolicy: .always) try await check(
number: number,
forceUpdate: false,
trackLocation: false,
additionalEvents: [],
dbUpdatePolicy: .always
)
#else #else
try await check(number: number, forceUpdate: false, trackLocation: true, dbUpdatePolicy: .always) try await check(
number: number,
forceUpdate: false,
trackLocation: true,
additionalEvents: [],
dbUpdatePolicy: .always
)
#endif #endif
} }
public func updateHistory(number: String) async throws -> VehicleWithErrors { public func updateHistory(number: String) async throws -> VehicleWithErrors {
try await check(number: number, forceUpdate: true, trackLocation: false, dbUpdatePolicy: .always) try await check(
number: number,
forceUpdate: true,
trackLocation: false,
additionalEvents: [],
dbUpdatePolicy: .always
)
} }
public func updateSearch(number: String) async throws -> VehicleWithErrors { public func updateSearch(number: String) async throws -> VehicleWithErrors {
try await check(number: number, forceUpdate: true, trackLocation: false, dbUpdatePolicy: .ifExists) try await check(
number: number,
forceUpdate: true,
trackLocation: false,
additionalEvents: [],
dbUpdatePolicy: .ifExists
)
}
public func checkRecord(number: String, event: VehicleEventDto?) async throws -> VehicleWithErrors {
try await check(
number: number,
forceUpdate: false,
trackLocation: false,
additionalEvents: event.flatMap { [$0] } ?? [],
dbUpdatePolicy: .always
)
} }
} }

View File

@ -14,4 +14,5 @@ public protocol VehicleServiceProtocol: Sendable {
func check(number: String) async throws -> VehicleWithErrors func check(number: String) async throws -> VehicleWithErrors
func updateHistory(number: String) async throws -> VehicleWithErrors func updateHistory(number: String) async throws -> VehicleWithErrors
func updateSearch(number: String) async throws -> VehicleWithErrors func updateSearch(number: String) async throws -> VehicleWithErrors
func checkRecord(number: String, event: VehicleEventDto?) async throws -> VehicleWithErrors
} }