diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index ca97bf4..9f034a3 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022762C557EC400B84627 /* LocationPickerScreen.swift */; }; 7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */; }; 7A11470A23FDE7E600B424AF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470923FDE7E600B424AF /* Assets.xcassets */; }; + 7A11BB002DE48FC500E3239F /* ProportionalHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11BAFF2DE48FC500E3239F /* ProportionalHStack.swift */; }; 7A131FD32D37B75500DC7755 /* HistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */; }; 7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */; }; 7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441652C297EDE00E79018 /* NotesScreen.swift */; }; @@ -40,6 +41,7 @@ 7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4927D42CCE438600851C01 /* OptionalBinding.swift */; }; 7A4955822D58CCF900912E66 /* HistoryFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4955812D58CCF900912E66 /* HistoryFilter.swift */; }; 7A54BFD32D43B95E00176D6D /* DbUpdatePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A54BFD22D43B95E00176D6D /* DbUpdatePolicy.swift */; }; + 7A5631CE2DE4B638001070D6 /* VehicleCheckType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5631CD2DE4B638001070D6 /* VehicleCheckType.swift */; }; 7A589E0F2D6B6E8E00EF3FBE /* NumberEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */; }; 7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5911ED2D63226F00EC51BA /* SearchScreen.swift */; }; 7A5911F02D63266B00EC51BA /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5911EF2D63266B00EC51BA /* SearchViewModel.swift */; }; @@ -97,6 +99,7 @@ 7A7AA2CB2DA2C85100276D83 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C92DA2C85100276D83 /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 7A7DADAC2D99738300F52F6C /* AudioRecordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7DADAB2D99738300F52F6C /* AudioRecordView.swift */; }; 7A809F392D66755B00CF1B3C /* Error+Canceled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A809F382D66755B00CF1B3C /* Error+Canceled.swift */; }; + 7A8652462DE4484700AEBF27 /* ACVinKeyboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8652452DE4484700AEBF27 /* ACVinKeyboardView.swift */; }; 7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */; }; 7A912F372D381B7400002938 /* LicensePlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A912F362D381B7400002938 /* LicensePlateView.swift */; }; 7A91CE952DC2422B00DBA953 /* HideTabBarModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A91CE942DC2422B00DBA953 /* HideTabBarModifier.swift */; }; @@ -260,6 +263,7 @@ 7A11474623FF2AA500B424AF /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 7A11474823FF2B2D00B424AF /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = ""; }; + 7A11BAFF2DE48FC500E3239F /* ProportionalHStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProportionalHStack.swift; sourceTree = ""; }; 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryScreen.swift; sourceTree = ""; }; 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewModel.swift; sourceTree = ""; }; 7A1441652C297EDE00E79018 /* NotesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesScreen.swift; sourceTree = ""; }; @@ -289,6 +293,7 @@ 7A52AB292580112E002CD910 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 7A530B7F2401803A00CBFE6E /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = ""; }; 7A54BFD22D43B95E00176D6D /* DbUpdatePolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DbUpdatePolicy.swift; sourceTree = ""; }; + 7A5631CD2DE4B638001070D6 /* VehicleCheckType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleCheckType.swift; sourceTree = ""; }; 7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberEditView.swift; sourceTree = ""; }; 7A5911ED2D63226F00EC51BA /* SearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchScreen.swift; sourceTree = ""; }; 7A5911EF2D63266B00EC51BA /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = ""; }; @@ -344,6 +349,7 @@ 7A7DADAB2D99738300F52F6C /* AudioRecordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordView.swift; sourceTree = ""; }; 7A809F382D66755B00CF1B3C /* Error+Canceled.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Canceled.swift"; sourceTree = ""; }; 7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AuthenticationServices.framework; sourceTree = DEVELOPER_DIR; }; + 7A8652452DE4484700AEBF27 /* ACVinKeyboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACVinKeyboardView.swift; sourceTree = ""; }; 7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleVersion.swift; sourceTree = ""; }; 7A8AB76725A0DC8200ECF2C1 /* DebugInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugInfo.swift; sourceTree = ""; }; 7A912F362D381B7400002938 /* LicensePlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensePlateView.swift; sourceTree = ""; }; @@ -992,8 +998,10 @@ children = ( 7AD176AF2DC127540023049D /* PNButtonType.swift */, 7AB490282D6B1217002F39C6 /* ACKeyboardView.swift */, + 7A8652452DE4484700AEBF27 /* ACVinKeyboardView.swift */, 7AB4902A2D6B1446002F39C6 /* ACKeyboardButton.swift */, 7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */, + 7A5631CD2DE4B638001070D6 /* VehicleCheckType.swift */, ); path = NumberEditView; sourceTree = ""; @@ -1076,6 +1084,7 @@ 7AB4E42B2D397D8E0006D052 /* VehicleCellView.swift */, 7AC8B2752D6A01C700190706 /* UISearchTextField+Dumb.swift */, 7AF231982DA27C1B00AE5EB3 /* ACButtonView.swift */, + 7A11BAFF2DE48FC500E3239F /* ProportionalHStack.swift */, 7A6974C62DCA5024008AA6A1 /* ListItemStyle.swift */, ); path = SwiftUI; @@ -1328,12 +1337,15 @@ 7A06E0AE2C7065C7005731AC /* SettingsViewModel.swift in Sources */, 7AB4E42C2D397D8E0006D052 /* VehicleCellView.swift in Sources */, 7A91CE9A2DC2470F00DBA953 /* TextFieldDumbModifier.swift in Sources */, + 7A5631CE2DE4B638001070D6 /* VehicleCheckType.swift in Sources */, 7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */, 7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */, 7AC44B822DB390B900ADC026 /* MainTabScreen.swift in Sources */, 7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */, 7AD176B02DC127540023049D /* PNButtonType.swift in Sources */, 7AF231952DA1C29300AE5EB3 /* AuthViewModel.swift in Sources */, + 7A8652462DE4484700AEBF27 /* ACVinKeyboardView.swift in Sources */, + 7A11BB002DE48FC500E3239F /* ProportionalHStack.swift in Sources */, 7AB4E4662D58A16C0006D052 /* GenericError.swift in Sources */, 7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */, 7A6974C72DCA5024008AA6A1 /* ListItemStyle.swift in Sources */, diff --git a/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift b/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift index 1cb93f3..66b8ff4 100644 --- a/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift +++ b/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift @@ -131,11 +131,11 @@ final class HistoryViewModel: ACHudContainer { } } - func checkVehicle(number: String, type: CheckType) async { + func checkVehicle(number: String, type: CheckType, trackLocation: Bool = true) async { do { hud = .progress let (vehicle, errors) = switch type { - case .normal: try await vehicleService.check(number: number) + case .normal: try await vehicleService.check(number: number, trackLocation: trackLocation) case .update: try await vehicleService.updateHistory(number: number) case .record(let event): try await vehicleService.checkRecord(number: number, event: event) } @@ -163,8 +163,13 @@ final class HistoryViewModel: ACHudContainer { } } - func checkNewNumber(_ number: String) async { - await checkVehicle(number: number, type: .normal) + func checkNewNumber(_ number: String, checkType: VehicleCheckType, enableLocation: Bool) async { + + await checkVehicle( + number: number, + type: .normal, + trackLocation: enableLocation + ) } func deleteVehicle(_ vehicle: VehicleDto) async { diff --git a/AutoCat/Screens/MainScreen/MainSplitScreen.swift b/AutoCat/Screens/MainScreen/MainSplitScreen.swift index 70035af..5a7c918 100644 --- a/AutoCat/Screens/MainScreen/MainSplitScreen.swift +++ b/AutoCat/Screens/MainScreen/MainSplitScreen.swift @@ -70,10 +70,17 @@ struct MainSplitScreen: View { } var numberEditSheet: some View { - NumberEditView { number in + NumberEditView { number, checkType, enableLocation in + checkNumberSheetOpened = false selection = .history - Task { await historyViewModel.checkNewNumber(number) } + Task { + await historyViewModel.checkNewNumber( + number, + checkType: checkType, + enableLocation: enableLocation + ) + } } .presentationDetents([.height(350)]) } diff --git a/AutoCat/Screens/MainScreen/MainTabScreen.swift b/AutoCat/Screens/MainScreen/MainTabScreen.swift index 1ab1345..9189e55 100644 --- a/AutoCat/Screens/MainScreen/MainTabScreen.swift +++ b/AutoCat/Screens/MainScreen/MainTabScreen.swift @@ -102,10 +102,17 @@ struct MainTabScreen: View { } var numberEditSheet: some View { - NumberEditView { number in + NumberEditView { number, checkType, enableLocation in + checkNumberSheetOpened = false selection = .history - Task { await historyViewModel.checkNewNumber(number) } + Task { + await historyViewModel.checkNewNumber( + number, + checkType: checkType, + enableLocation: enableLocation + ) + } } .presentationDetents([.height(350)]) } diff --git a/AutoCat/SwiftUI/NumberEditView/ACKeyboardButton.swift b/AutoCat/SwiftUI/NumberEditView/ACKeyboardButton.swift index 2a6f78b..713bd5a 100644 --- a/AutoCat/SwiftUI/NumberEditView/ACKeyboardButton.swift +++ b/AutoCat/SwiftUI/NumberEditView/ACKeyboardButton.swift @@ -41,6 +41,9 @@ struct ACKeyboardButton: View { Text(String(Constants.pnLettersMap[s] ?? s)) .font(.custom("RoadNumbers", size: 36 - 2*sizeDelta)) .offset(y: sizeDelta/2) + case .vin(let s): + Text(String(Constants.pnLettersMap[s] ?? s)) + .font(.system(size: 26, weight: .semibold)) case .backspace: Image(systemName: "delete.left") case .done: @@ -50,7 +53,7 @@ struct ACKeyboardButton: View { var backgroundColor: Color { switch type { - case .symbol: Color("KeyBackground") + case .symbol, .vin: Color("KeyBackground") case .backspace: Color("DarkKeyBackground") case .done: .blue } @@ -70,6 +73,7 @@ struct ACKeyboardButton: View { HStack(spacing: 0) { ACKeyboardButton(type: .symbol("A")) { _ in } + ACKeyboardButton(type: .vin("A")) { _ in } ACKeyboardButton(type: .symbol("1")) { _ in } ACKeyboardButton(type: .backspace) { _ in } ACKeyboardButton(type: .done) { _ in } diff --git a/AutoCat/SwiftUI/NumberEditView/ACVinKeyboardView.swift b/AutoCat/SwiftUI/NumberEditView/ACVinKeyboardView.swift new file mode 100644 index 0000000..b588a0e --- /dev/null +++ b/AutoCat/SwiftUI/NumberEditView/ACVinKeyboardView.swift @@ -0,0 +1,80 @@ +// +// ACVinKeyboardView.swift +// AutoCat +// +// Created by Selim Mustafaev on 26.05.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import SwiftUI + +struct ACVinKeyboardView: View { + + let buttonPressed: (PNButtonType) -> Void + + var body: some View { + ProportionalHStack(widthWeights: [2/3, 1/3], spacing: 16) { + Grid(horizontalSpacing: 0, verticalSpacing: 0) { + GridRow { + ACKeyboardButton(type: .vin("A"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("B"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("C"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("D"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("E"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("F"), onTap: buttonPressed) + } + GridRow { + ACKeyboardButton(type: .vin("G"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("H"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("J"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("K"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("L"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("M"), onTap: buttonPressed) + } + GridRow { + ACKeyboardButton(type: .vin("N"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("P"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("R"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("S"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("T"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("U"), onTap: buttonPressed) + } + GridRow { + ACKeyboardButton(type: .vin("V"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("W"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("X"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("Y"), onTap: buttonPressed) + ACKeyboardButton(type: .vin("Z"), onTap: buttonPressed) + } + } + Grid(horizontalSpacing: 0, verticalSpacing: 0) { + GridRow { + ACKeyboardButton(type: .symbol("1"), onTap: buttonPressed) + ACKeyboardButton(type: .symbol("2"), onTap: buttonPressed) + ACKeyboardButton(type: .symbol("3"), onTap: buttonPressed) + } + GridRow { + ACKeyboardButton(type: .symbol("4"), onTap: buttonPressed) + ACKeyboardButton(type: .symbol("5"), onTap: buttonPressed) + ACKeyboardButton(type: .symbol("6"), onTap: buttonPressed) + } + GridRow { + ACKeyboardButton(type: .symbol("7"), onTap: buttonPressed) + ACKeyboardButton(type: .symbol("8"), onTap: buttonPressed) + ACKeyboardButton(type: .symbol("9"), onTap: buttonPressed) + } + GridRow { + ACKeyboardButton(type: .symbol("0"), onTap: buttonPressed) + ACKeyboardButton(type: .backspace, onTap: buttonPressed) + ACKeyboardButton(type: .done, onTap: buttonPressed) + } + } + } + .font(.system(size: 24)) + } +} + +#Preview { + ACVinKeyboardView { _ in } + .frame(height: 240) +} diff --git a/AutoCat/SwiftUI/NumberEditView/NumberEditView.swift b/AutoCat/SwiftUI/NumberEditView/NumberEditView.swift index 7744ee4..1527c8e 100644 --- a/AutoCat/SwiftUI/NumberEditView/NumberEditView.swift +++ b/AutoCat/SwiftUI/NumberEditView/NumberEditView.swift @@ -11,9 +11,11 @@ import AutoCatCore struct NumberEditView: View { - let onCheck: (String) -> Void + let onCheck: (String, VehicleCheckType, Bool) -> Void @State var number = PlateNumber("") + @State var enableLocation: Bool = true + @State var checkType: VehicleCheckType = .plateNumber var body: some View { VStack(spacing: 16) { @@ -27,7 +29,7 @@ struct NumberEditView: View { .fixedSize(horizontal: true, vertical: false) Button { - onCheck(number.asString()) + onCheck(number.asString(), checkType, enableLocation) } label: { Text("Check") .frame(maxWidth: .infinity) @@ -43,11 +45,36 @@ struct NumberEditView: View { .frame(height: 50) .opacity(number.isValid ? 1 : 0.5) } - .padding(.leading, 4) - .padding(.trailing, 4) + .padding(.horizontal, 4) - ACKeyboardView(buttonPressed: buttonPressed) - .frame(height: 220) + HStack(spacing: 16) { + Picker("Location", selection: $enableLocation) { + Image(systemName: "location") + .tag(true) + Image(systemName: "location.slash") + .tag(false) + } + .pickerStyle(.segmented) + .frame(width: 120) + + Picker("Check type", selection: $checkType) { + ForEach(VehicleCheckType.allCases) { type in + Text(type.title) + .selectionDisabled(!type.enabled) + } + } + .pickerStyle(.segmented) + } + .padding(.horizontal, 4) + + switch checkType { + case .plateNumber: + ACKeyboardView(buttonPressed: buttonPressed) + .frame(height: 220) + case .vin: + ACVinKeyboardView(buttonPressed: buttonPressed) + .frame(height: 220) + } } .padding(16) .gesture(DragGesture(), including: .gesture) @@ -55,16 +82,16 @@ struct NumberEditView: View { func buttonPressed(type: PNButtonType) { switch type { - case .symbol(let s): + case .symbol(let s), .vin(let s): number.insertText(String(s)) case .backspace: number.deleteBackward() case .done: - onCheck(number.asString()) + onCheck(number.asString(), checkType, enableLocation) } } } #Preview { - NumberEditView() { _ in } + NumberEditView() { _ , _, _ in } } diff --git a/AutoCat/SwiftUI/NumberEditView/PNButtonType.swift b/AutoCat/SwiftUI/NumberEditView/PNButtonType.swift index 709a5ad..fcd87d2 100644 --- a/AutoCat/SwiftUI/NumberEditView/PNButtonType.swift +++ b/AutoCat/SwiftUI/NumberEditView/PNButtonType.swift @@ -9,6 +9,7 @@ enum PNButtonType { case symbol(Character) + case vin(Character) case backspace case done } diff --git a/AutoCat/SwiftUI/NumberEditView/VehicleCheckType.swift b/AutoCat/SwiftUI/NumberEditView/VehicleCheckType.swift new file mode 100644 index 0000000..4bec8ab --- /dev/null +++ b/AutoCat/SwiftUI/NumberEditView/VehicleCheckType.swift @@ -0,0 +1,33 @@ +// +// VehicleCheckType.swift +// AutoCat +// +// Created by Selim Mustafaev on 26.05.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import Foundation + +enum VehicleCheckType: Hashable, CaseIterable, Identifiable { + + case plateNumber + case vin + + var title: String { + switch self { + case .plateNumber: + return String(localized: "Plate number") + case .vin: + return String(localized: "VIN") + } + } + + var enabled: Bool { + switch self { + case .plateNumber: true + case .vin: true + } + } + + var id: Self { self } +} diff --git a/AutoCat/SwiftUI/ProportionalHStack.swift b/AutoCat/SwiftUI/ProportionalHStack.swift new file mode 100644 index 0000000..b74a123 --- /dev/null +++ b/AutoCat/SwiftUI/ProportionalHStack.swift @@ -0,0 +1,55 @@ +// +// ProportionalHStack.swift +// NavTest +// +// Created by Selim Mustafaev on 26.05.2025. +// + +import SwiftUI + +struct ProportionalHStack: Layout { + + let widthWeights: [CGFloat] + let spacing: CGFloat + + init(widthWeights: [CGFloat], spacing: CGFloat? = nil) { + self.widthWeights = widthWeights + self.spacing = spacing ?? .zero + } + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + let maxHeight = subviews + .map { proxy in + return proxy.sizeThatFits(.unspecified).height + } + .max() ?? .zero + + return CGSize(width: proposal.width ?? .zero, height: maxHeight) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + let subviewSizes = subviews.map { proxy in + return proxy.sizeThatFits(.infinity) + } + + var x = bounds.minX + let y = bounds.minY + let parentTotalWith = (proposal.width ?? .zero) - (CGFloat((widthWeights.count - 1)) * spacing) + + for index in subviews.indices { + let widthWeight = widthWeights[index] + let proposedWidth = widthWeight * parentTotalWith + + let sizeProposal = ProposedViewSize(width: proposedWidth, height: subviewSizes[index].height) + + subviews[index] + .place( + at: CGPoint(x: x, y: y), + anchor: .topLeading, + proposal: sizeProposal + ) + + x += (proposedWidth + spacing) + } + } +} diff --git a/AutoCatCore/Services/VehicleService/VehicleService.swift b/AutoCatCore/Services/VehicleService/VehicleService.swift index 0dac9f0..45f09af 100644 --- a/AutoCatCore/Services/VehicleService/VehicleService.swift +++ b/AutoCatCore/Services/VehicleService/VehicleService.swift @@ -87,7 +87,7 @@ extension VehicleService: VehicleServiceProtocol { return VehicleWithErrors(vehicle: vehicle, errors: errors) } - public func check(number: String) async throws -> VehicleWithErrors { + public func check(number: String, trackLocation: Bool) async throws -> VehicleWithErrors { #if targetEnvironment(macCatalyst) try await check( @@ -101,7 +101,7 @@ extension VehicleService: VehicleServiceProtocol { try await check( number: number, forceUpdate: false, - trackLocation: true, + trackLocation: trackLocation, additionalEvent: nil, dbUpdatePolicy: .always ) diff --git a/AutoCatCore/Services/VehicleService/VehicleServiceProtocol.swift b/AutoCatCore/Services/VehicleService/VehicleServiceProtocol.swift index 20b519a..5e7a5fd 100644 --- a/AutoCatCore/Services/VehicleService/VehicleServiceProtocol.swift +++ b/AutoCatCore/Services/VehicleService/VehicleServiceProtocol.swift @@ -11,7 +11,7 @@ import Mockable @Mockable public protocol VehicleServiceProtocol: Sendable { - func check(number: String) async throws -> VehicleWithErrors + func check(number: String, trackLocation: Bool) async throws -> VehicleWithErrors func updateHistory(number: String) async throws -> VehicleWithErrors func updateSearch(number: String) async throws -> VehicleWithErrors func checkRecord(number: String, event: VehicleEventDto?) async throws -> VehicleWithErrors diff --git a/AutoCatCoreTests/VehicleServiceTests.swift b/AutoCatCoreTests/VehicleServiceTests.swift index eff5130..d92c574 100644 --- a/AutoCatCoreTests/VehicleServiceTests.swift +++ b/AutoCatCoreTests/VehicleServiceTests.swift @@ -53,7 +53,7 @@ struct VehicleServiceTests { .willThrow(TestError.generic) await #expect(throws: TestError.generic) { - _ = try await vehicleService.check(number: existingVehicleNumber) + _ = try await vehicleService.check(number: existingVehicleNumber, trackLocation: true) } } @@ -80,7 +80,7 @@ struct VehicleServiceTests { .updateVehicle(dto: .any, policy: .any) .willReturn(true) - let result = try await vehicleService.check(number: existingVehicleNumber) + let result = try await vehicleService.check(number: existingVehicleNumber, trackLocation: true) #expect(result.vehicle.unrecognized) #expect(result.vehicle.events.isEmpty) @@ -113,7 +113,7 @@ struct VehicleServiceTests { .updateVehicle(dto: .any, policy: .any) .willReturn(true) - let result = try await vehicleService.check(number: existingVehicleNumber) + let result = try await vehicleService.check(number: existingVehicleNumber, trackLocation: true) #expect(result.vehicle.vin1 == testVin) #expect(result.errors.count == 2) @@ -145,7 +145,7 @@ struct VehicleServiceTests { .updateVehicle(dto: .any, policy: .any) .willReturn(true) - let result = try await vehicleService.check(number: VehicleDto.validNumber) + let result = try await vehicleService.check(number: VehicleDto.validNumber, trackLocation: true) #expect(result.vehicle.vin1 == testVin) #expect(result.errors.count == 1) @@ -181,7 +181,7 @@ struct VehicleServiceTests { .add(event: .any, to: .any) .willThrow(TestError.generic) - let result = try await vehicleService.check(number: VehicleDto.validNumber) + let result = try await vehicleService.check(number: VehicleDto.validNumber, trackLocation: true) #expect(result.vehicle.number == VehicleDto.validNumber) #expect(result.errors.count == 2) @@ -217,7 +217,7 @@ struct VehicleServiceTests { .add(event: .any, to: .any) .willReturn(.normal.addEvent(.valid)) - let result = try await vehicleService.check(number: VehicleDto.validNumber) + let result = try await vehicleService.check(number: VehicleDto.validNumber, trackLocation: true) #expect(result.vehicle.number == VehicleDto.validNumber) #expect(result.errors.count == 1) @@ -253,7 +253,7 @@ struct VehicleServiceTests { .add(event: .any, to: .any) .willReturn(.normal.addEvent(.valid)) - let result = try await vehicleService.check(number: VehicleDto.validNumber) + let result = try await vehicleService.check(number: VehicleDto.validNumber, trackLocation: true) #expect(result.vehicle.number == VehicleDto.validNumber) #expect(result.errors.count == 0) @@ -291,7 +291,7 @@ struct VehicleServiceTests { .add(event: .any, to: .any) .willReturn(vehicle.addEvent(.valid)) - let result = try await vehicleService.check(number: vehicle.number) + let result = try await vehicleService.check(number: vehicle.number, trackLocation: true) verify(apiServiceMock) .checkVehicle(by: .any, notes: .any, events: .any, force: .value(false))