Full migration to SwiftUI
This commit is contained in:
parent
544f81be5e
commit
72ce7ec798
@ -17,16 +17,11 @@
|
||||
7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022762C557EC400B84627 /* LocationPickerScreen.swift */; };
|
||||
7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */; };
|
||||
7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */; };
|
||||
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470023FDE7E500B424AF /* AppDelegate.swift */; };
|
||||
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470223FDE7E500B424AF /* SceneDelegate.swift */; };
|
||||
7A11470823FDE7E500B424AF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470623FDE7E500B424AF /* Main.storyboard */; };
|
||||
7A11470A23FDE7E600B424AF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470923FDE7E600B424AF /* Assets.xcassets */; };
|
||||
7A11470D23FDE7E600B424AF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470B23FDE7E600B424AF /* LaunchScreen.storyboard */; };
|
||||
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 */; };
|
||||
7A1441682C297EFD00E79018 /* NotesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441672C297EFD00E79018 /* NotesViewModel.swift */; };
|
||||
7A14416E2C297F7C00E79018 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A14416D2C297F7C00E79018 /* Coordinator.swift */; };
|
||||
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE492A2E820300626A6E /* UIStackView.swift */; };
|
||||
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */; };
|
||||
7A1CF81629A42117007962DA /* Realm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1CF81529A42117007962DA /* Realm.swift */; };
|
||||
@ -102,14 +97,12 @@
|
||||
7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A15051124DB3E3000F39631 /* AnyEncodable.swift */; };
|
||||
7A761C08267E8EA20005F28F /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A43F9F7246C8A6200BA5B49 /* JWT.swift */; };
|
||||
7A761C09267E8EE40005F28F /* Base64FS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE32246C095700297C33 /* Base64FS.swift */; };
|
||||
7A761C0B267E8FF90005F28F /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A761C0A267E8FF90005F28F /* Error.swift */; };
|
||||
7A7AA2C42DA2A3CB00276D83 /* LocationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AA2C32DA2A3CB00276D83 /* LocationError.swift */; };
|
||||
7A7AA2C72DA2A45600276D83 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C62DA2A45600276D83 /* RealmSwift */; };
|
||||
7A7AA2CA2DA2C85100276D83 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C92DA2C85100276D83 /* RealmSwift */; };
|
||||
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 */; };
|
||||
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; };
|
||||
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */; };
|
||||
7A912F372D381B7400002938 /* LicensePlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A912F362D381B7400002938 /* LicensePlateView.swift */; };
|
||||
7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A91894E29A2BD8700519C74 /* GestureRecognizers.swift */; };
|
||||
@ -165,15 +158,14 @@
|
||||
7AC355592969746600889457 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC355582969746600889457 /* UIControl.swift */; };
|
||||
7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3555A296995B200889457 /* UIEdgeInsets.swift */; };
|
||||
7AC44B822DB390B900ADC026 /* MainTabScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B812DB390B900ADC026 /* MainTabScreen.swift */; };
|
||||
7AC44B842DB3EF7A00ADC026 /* HistorySplitScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B832DB3EF7A00ADC026 /* HistorySplitScreen.swift */; };
|
||||
7AC44B862DB40A5C00ADC026 /* HideTabBarModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B852DB40A5C00ADC026 /* HideTabBarModifier.swift */; };
|
||||
7AC44B882DB438F200ADC026 /* UIView+layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B872DB438F200ADC026 /* UIView+layout.swift */; };
|
||||
7AC44B8A2DB4395300ADC026 /* SearchSplitScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC44B892DB4395300ADC026 /* SearchSplitScreen.swift */; };
|
||||
7AC76D7B270083AE0084DB27 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC76D7A270083AE0084DB27 /* TextView.swift */; };
|
||||
7AC8B2762D6A01C700190706 /* UISearchTextField+Dumb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC8B2752D6A01C700190706 /* UISearchTextField+Dumb.swift */; };
|
||||
7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91D2CB9B155005A5168 /* Mockable */; };
|
||||
7ACBB9202CB9B16C005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91F2CB9B16C005A5168 /* Mockable */; };
|
||||
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; };
|
||||
7AD176AD2DC110830023049D /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD176AC2DC110830023049D /* Environment.swift */; };
|
||||
7ADCBC572DB51739002522C0 /* AutoCatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADCBC562DB51739002522C0 /* AutoCatApp.swift */; };
|
||||
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */; };
|
||||
7ADF6C99250F872C00F237B2 /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */; };
|
||||
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6CA02512244400F237B2 /* MapExt.swift */; };
|
||||
@ -181,11 +173,12 @@
|
||||
7ADFC9592DAD1C3D001A43E3 /* GoogleAuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADFC9582DAD1C3D001A43E3 /* GoogleAuthViewModel.swift */; };
|
||||
7ADFC95B2DAD1F45001A43E3 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADFC95A2DAD1F45001A43E3 /* WebView.swift */; };
|
||||
7AE24C5F251F1B4E00758E39 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE24C5E251F1B4E00758E39 /* Buttons.swift */; };
|
||||
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
|
||||
7AE8CBB32DBA1475005EF1AB /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE8CBB22DBA1475005EF1AB /* UIDevice.swift */; };
|
||||
7AE8CBB52DBA3B55005EF1AB /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE8CBB42DBA3B55005EF1AB /* Router.swift */; };
|
||||
7AE8CBB72DBA3E4E005EF1AB /* MainSplitScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE8CBB62DBA3E4E005EF1AB /* MainSplitScreen.swift */; };
|
||||
7AEAA2A12DAD9C00009954F0 /* TokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEAA2A02DAD9C00009954F0 /* TokenResponse.swift */; };
|
||||
7AF231932DA1C28100AE5EB3 /* AuthScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF231922DA1C28100AE5EB3 /* AuthScreen.swift */; };
|
||||
7AF231952DA1C29300AE5EB3 /* AuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF231942DA1C29300AE5EB3 /* AuthViewModel.swift */; };
|
||||
7AF231972DA1C30000AE5EB3 /* AuthCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF231962DA1C30000AE5EB3 /* AuthCoordinator.swift */; };
|
||||
7AF231992DA27C1B00AE5EB3 /* ACButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF231982DA27C1B00AE5EB3 /* ACButtonView.swift */; };
|
||||
7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; };
|
||||
7AF6D2052677C03B0086EA64 /* AutoCatCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@ -212,6 +205,7 @@
|
||||
7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE8C92C3081C7003C491D /* ACProgressHud+Modifiers.swift */; };
|
||||
7AFBE8CC2C3085C6003C491D /* ACProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE8CB2C3085C6003C491D /* ACProgressView.swift */; };
|
||||
7AFBE8CE2C308B53003C491D /* ACMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE8CD2C308B53003C491D /* ACMessageView.swift */; };
|
||||
7AFFF7A02DBAAFF300EE2DEE /* SideBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFF79F2DBAAFF300EE2DEE /* SideBarItem.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -278,11 +272,7 @@
|
||||
7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerViewModel.swift; sourceTree = "<group>"; };
|
||||
7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellProgressView.swift; sourceTree = "<group>"; };
|
||||
7A1146FD23FDE7E500B424AF /* AutoCat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7A11470023FDE7E500B424AF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7A11470223FDE7E500B424AF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
7A11470723FDE7E500B424AF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
7A11470923FDE7E600B424AF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
7A11470C23FDE7E600B424AF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
7A11470E23FDE7E600B424AF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7A11474323FF06CA00B424AF /* ApiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiService.swift; sourceTree = "<group>"; };
|
||||
7A11474623FF2AA500B424AF /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
|
||||
@ -292,7 +282,6 @@
|
||||
7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewModel.swift; sourceTree = "<group>"; };
|
||||
7A1441652C297EDE00E79018 /* NotesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesScreen.swift; sourceTree = "<group>"; };
|
||||
7A1441672C297EFD00E79018 /* NotesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesViewModel.swift; sourceTree = "<group>"; };
|
||||
7A14416D2C297F7C00E79018 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = "<group>"; };
|
||||
7A15051124DB3E3000F39631 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
||||
7A17CE492A2E820300626A6E /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = "<group>"; };
|
||||
7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = "<group>"; };
|
||||
@ -337,12 +326,10 @@
|
||||
7A60D24C2C5A9D4900D13F7B /* LocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = "<group>"; };
|
||||
7A60D24E2C5A9DA800D13F7B /* LocationServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationServiceProtocol.swift; sourceTree = "<group>"; };
|
||||
7A60D2502C5A9E4200D13F7B /* GeocoderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeocoderProtocol.swift; sourceTree = "<group>"; };
|
||||
7A61FF8325759DE700D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
|
||||
7A61FF8A2575A2CD00D905D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
7A61FF8D2575A2F900D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
7A61FF902575A5B300D905D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
7A61FF932575A5B600D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
7A61FF962576C16400D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
7A61FFA1257D3CFC00D905D5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
7A61FFA4257D3D0200D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
7A64A2022C19DA1000284124 /* VehicleDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleDto.swift; sourceTree = "<group>"; };
|
||||
@ -375,12 +362,10 @@
|
||||
7A71580B2C44453200852088 /* AdsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsScreen.swift; sourceTree = "<group>"; };
|
||||
7A7158112C444A6400852088 /* AdsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsViewModel.swift; sourceTree = "<group>"; };
|
||||
7A71EF562D0A26B200943129 /* EventModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventModel.swift; sourceTree = "<group>"; };
|
||||
7A761C0A267E8FF90005F28F /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
|
||||
7A7AA2C32DA2A3CB00276D83 /* LocationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationError.swift; sourceTree = "<group>"; };
|
||||
7A7DADAB2D99738300F52F6C /* AudioRecordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordView.swift; sourceTree = "<group>"; };
|
||||
7A809F382D66755B00CF1B3C /* Error+Canceled.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Canceled.swift"; sourceTree = "<group>"; };
|
||||
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; };
|
||||
7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = "<group>"; };
|
||||
7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleVersion.swift; sourceTree = "<group>"; };
|
||||
7A8AB76725A0DC8200ECF2C1 /* DebugInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugInfo.swift; sourceTree = "<group>"; };
|
||||
7A912F362D381B7400002938 /* LicensePlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensePlateView.swift; sourceTree = "<group>"; };
|
||||
@ -438,13 +423,12 @@
|
||||
7AC355582969746600889457 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
|
||||
7AC3555A296995B200889457 /* UIEdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIEdgeInsets.swift; sourceTree = "<group>"; };
|
||||
7AC44B812DB390B900ADC026 /* MainTabScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabScreen.swift; sourceTree = "<group>"; };
|
||||
7AC44B832DB3EF7A00ADC026 /* HistorySplitScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySplitScreen.swift; sourceTree = "<group>"; };
|
||||
7AC44B852DB40A5C00ADC026 /* HideTabBarModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideTabBarModifier.swift; sourceTree = "<group>"; };
|
||||
7AC44B872DB438F200ADC026 /* UIView+layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+layout.swift"; sourceTree = "<group>"; };
|
||||
7AC44B892DB4395300ADC026 /* SearchSplitScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSplitScreen.swift; sourceTree = "<group>"; };
|
||||
7AC76D7A270083AE0084DB27 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
|
||||
7AC8B2752D6A01C700190706 /* UISearchTextField+Dumb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISearchTextField+Dumb.swift"; sourceTree = "<group>"; };
|
||||
7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = "<group>"; };
|
||||
7AD176AC2DC110830023049D /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; };
|
||||
7ADCBC562DB51739002522C0 /* AutoCatApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCatApp.swift; sourceTree = "<group>"; };
|
||||
7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNKeyboard.swift; sourceTree = "<group>"; };
|
||||
7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = "<group>"; };
|
||||
7ADF6CA02512244400F237B2 /* MapExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapExt.swift; sourceTree = "<group>"; };
|
||||
@ -452,12 +436,13 @@
|
||||
7ADFC9582DAD1C3D001A43E3 /* GoogleAuthViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleAuthViewModel.swift; sourceTree = "<group>"; };
|
||||
7ADFC95A2DAD1F45001A43E3 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = "<group>"; };
|
||||
7AE24C5E251F1B4E00758E39 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; };
|
||||
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = "<group>"; };
|
||||
7AE8424D26109F78002F6B31 /* Exportable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Exportable.swift; sourceTree = "<group>"; };
|
||||
7AE8CBB22DBA1475005EF1AB /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
||||
7AE8CBB42DBA3B55005EF1AB /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
||||
7AE8CBB62DBA3E4E005EF1AB /* MainSplitScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitScreen.swift; sourceTree = "<group>"; };
|
||||
7AEAA2A02DAD9C00009954F0 /* TokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenResponse.swift; sourceTree = "<group>"; };
|
||||
7AF231922DA1C28100AE5EB3 /* AuthScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthScreen.swift; sourceTree = "<group>"; };
|
||||
7AF231942DA1C29300AE5EB3 /* AuthViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewModel.swift; sourceTree = "<group>"; };
|
||||
7AF231962DA1C30000AE5EB3 /* AuthCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthCoordinator.swift; sourceTree = "<group>"; };
|
||||
7AF231982DA27C1B00AE5EB3 /* ACButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACButtonView.swift; sourceTree = "<group>"; };
|
||||
7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AutoCatCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7AF6D1F12677C03B0086EA64 /* AutoCatCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutoCatCore.h; sourceTree = "<group>"; };
|
||||
@ -468,6 +453,7 @@
|
||||
7AFBE8C92C3081C7003C491D /* ACProgressHud+Modifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ACProgressHud+Modifiers.swift"; sourceTree = "<group>"; };
|
||||
7AFBE8CB2C3085C6003C491D /* ACProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACProgressView.swift; sourceTree = "<group>"; };
|
||||
7AFBE8CD2C308B53003C491D /* ACMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACMessageView.swift; sourceTree = "<group>"; };
|
||||
7AFFF79F2DBAAFF300EE2DEE /* SideBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideBarItem.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
@ -611,14 +597,11 @@
|
||||
7A6DD901242BF48D009DE740 /* Views */,
|
||||
7A64AE6B2469DC6900ABE48E /* AutoCat.entitlements */,
|
||||
7A11470E23FDE7E600B424AF /* Info.plist */,
|
||||
7A11470023FDE7E500B424AF /* AppDelegate.swift */,
|
||||
7A11470223FDE7E500B424AF /* SceneDelegate.swift */,
|
||||
7A11470923FDE7E600B424AF /* Assets.xcassets */,
|
||||
7A61FF8F2575A5B300D905D5 /* InfoPlist.strings */,
|
||||
7A11470B23FDE7E600B424AF /* LaunchScreen.storyboard */,
|
||||
7A61FF892575A2CD00D905D5 /* Localizable.strings */,
|
||||
7A61FFA2257D3CFC00D905D5 /* Localizable.stringsdict */,
|
||||
7A11470623FDE7E500B424AF /* Main.storyboard */,
|
||||
7ADCBC562DB51739002522C0 /* AutoCatApp.swift */,
|
||||
);
|
||||
path = AutoCat;
|
||||
sourceTree = "<group>";
|
||||
@ -642,8 +625,8 @@
|
||||
7A11474223FF06B600B424AF /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A14416D2C297F7C00E79018 /* Coordinator.swift */,
|
||||
7AB4E4652D58A16C0006D052 /* GenericError.swift */,
|
||||
7AE8CBB42DBA3B55005EF1AB /* Router.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
@ -680,7 +663,6 @@
|
||||
7A131FD12D37B74100DC7755 /* HistoryScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC44B832DB3EF7A00ADC026 /* HistorySplitScreen.swift */,
|
||||
7A131FD22D37B75500DC7755 /* HistoryScreen.swift */,
|
||||
7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */,
|
||||
7A4955812D58CCF900912E66 /* HistoryFilter.swift */,
|
||||
@ -691,7 +673,7 @@
|
||||
7A1441632C297E9800E79018 /* Screens */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC44B802DB390A500ADC026 /* MainTabScreen */,
|
||||
7AC44B802DB390A500ADC026 /* MainScreen */,
|
||||
7ADFC9552DAD026C001A43E3 /* GoogleAuthScreen */,
|
||||
7A386A3E2DABDBFF0051676A /* MapScreen */,
|
||||
7AF231912DA1C26C00AE5EB3 /* AuthScreen */,
|
||||
@ -758,11 +740,8 @@
|
||||
7AE24C5E251F1B4E00758E39 /* Buttons.swift */,
|
||||
7A3F07AA24360DC800E59687 /* Dated.swift */,
|
||||
7ADF6CA02512244400F237B2 /* MapExt.swift */,
|
||||
7ADF6C92250B954900F237B2 /* Navigation.swift */,
|
||||
7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */,
|
||||
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */,
|
||||
7A761C0A267E8FF90005F28F /* Error.swift */,
|
||||
7AC76D7A270083AE0084DB27 /* TextView.swift */,
|
||||
7AE8CBB22DBA1475005EF1AB /* UIDevice.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -1085,12 +1064,14 @@
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7AC44B802DB390A500ADC026 /* MainTabScreen */ = {
|
||||
7AC44B802DB390A500ADC026 /* MainScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC44B812DB390B900ADC026 /* MainTabScreen.swift */,
|
||||
7AE8CBB62DBA3E4E005EF1AB /* MainSplitScreen.swift */,
|
||||
7AFFF79F2DBAAFF300EE2DEE /* SideBarItem.swift */,
|
||||
);
|
||||
path = MainTabScreen;
|
||||
path = MainScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7ADFC9552DAD026C001A43E3 /* GoogleAuthScreen */ = {
|
||||
@ -1108,7 +1089,6 @@
|
||||
children = (
|
||||
7AF231922DA1C28100AE5EB3 /* AuthScreen.swift */,
|
||||
7AF231942DA1C29300AE5EB3 /* AuthViewModel.swift */,
|
||||
7AF231962DA1C30000AE5EB3 /* AuthCoordinator.swift */,
|
||||
);
|
||||
path = AuthScreen;
|
||||
sourceTree = "<group>";
|
||||
@ -1174,7 +1154,7 @@
|
||||
7AB4902A2D6B1446002F39C6 /* ACKeyboardButton.swift */,
|
||||
7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */,
|
||||
7AF231982DA27C1B00AE5EB3 /* ACButtonView.swift */,
|
||||
7AC44B852DB40A5C00ADC026 /* HideTabBarModifier.swift */,
|
||||
7AD176AC2DC110830023049D /* Environment.swift */,
|
||||
);
|
||||
path = SwiftUI;
|
||||
sourceTree = "<group>";
|
||||
@ -1371,11 +1351,9 @@
|
||||
7A61FF912575A5B300D905D5 /* InfoPlist.strings in Resources */,
|
||||
7A61FFA0257D3CFC00D905D5 /* Localizable.stringsdict in Resources */,
|
||||
7ADF6C99250F872C00F237B2 /* RoadNumbers.otf in Resources */,
|
||||
7A11470D23FDE7E600B424AF /* LaunchScreen.storyboard in Resources */,
|
||||
7A61FF8B2575A2CD00D905D5 /* Localizable.strings in Resources */,
|
||||
7A6DD90A24329541009DE740 /* RoadNumbers2.0.otf in Resources */,
|
||||
7A11470A23FDE7E600B424AF /* Assets.xcassets in Resources */,
|
||||
7A11470823FDE7E500B424AF /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1408,13 +1386,12 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7A961C6C2C4C3C8600CE2211 /* TextRowView.swift in Sources */,
|
||||
7AD176AD2DC110830023049D /* Environment.swift in Sources */,
|
||||
7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */,
|
||||
7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */,
|
||||
7A5D7E0C2C71EB25002C17E7 /* ToggleRowView.swift in Sources */,
|
||||
7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */,
|
||||
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
|
||||
7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */,
|
||||
7A14416E2C297F7C00E79018 /* Coordinator.swift in Sources */,
|
||||
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
|
||||
7A1441682C297EFD00E79018 /* NotesViewModel.swift in Sources */,
|
||||
7AFBE8C02C3024E5003C491D /* ACHud.swift in Sources */,
|
||||
@ -1424,10 +1401,12 @@
|
||||
7AC8B2762D6A01C700190706 /* UISearchTextField+Dumb.swift in Sources */,
|
||||
7A6DD90C24335A6D009DE740 /* FlagLayer.swift in Sources */,
|
||||
7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */,
|
||||
7A761C0B267E8FF90005F28F /* Error.swift in Sources */,
|
||||
7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */,
|
||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */,
|
||||
7AF860702CBAA24500954D2F /* NavigationLink.swift in Sources */,
|
||||
7ADCBC572DB51739002522C0 /* AutoCatApp.swift in Sources */,
|
||||
7AFFF7A02DBAAFF300EE2DEE /* SideBarItem.swift in Sources */,
|
||||
7AE8CBB32DBA1475005EF1AB /* UIDevice.swift in Sources */,
|
||||
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */,
|
||||
7A386A402DABDC190051676A /* MapScreen.swift in Sources */,
|
||||
7AB9FE282D08C2F4005DE374 /* EventsViewModel.swift in Sources */,
|
||||
@ -1435,15 +1414,12 @@
|
||||
7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */,
|
||||
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */,
|
||||
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
|
||||
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */,
|
||||
7AC44B882DB438F200ADC026 /* UIView+layout.swift in Sources */,
|
||||
7A6C65222D999325001240C2 /* AudioRecordViewModel.swift in Sources */,
|
||||
7A06E0AE2C7065C7005731AC /* SettingsViewModel.swift in Sources */,
|
||||
7AB4E42C2D397D8E0006D052 /* VehicleCellView.swift in Sources */,
|
||||
7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */,
|
||||
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */,
|
||||
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */,
|
||||
7AC44B842DB3EF7A00ADC026 /* HistorySplitScreen.swift in Sources */,
|
||||
7AC44B822DB390B900ADC026 /* MainTabScreen.swift in Sources */,
|
||||
7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */,
|
||||
7AF231952DA1C29300AE5EB3 /* AuthViewModel.swift in Sources */,
|
||||
@ -1462,7 +1438,6 @@
|
||||
7AC76D7B270083AE0084DB27 /* TextView.swift in Sources */,
|
||||
7A2C96122C3B155B00AE46B5 /* NoteAlertModifier.swift in Sources */,
|
||||
7AE24C5F251F1B4E00758E39 /* Buttons.swift in Sources */,
|
||||
7AF231972DA1C30000AE5EB3 /* AuthCoordinator.swift in Sources */,
|
||||
7A7DADAC2D99738300F52F6C /* AudioRecordView.swift in Sources */,
|
||||
7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */,
|
||||
7AB9FE2A2D08CF35005DE374 /* EventsScreenMode.swift in Sources */,
|
||||
@ -1470,12 +1445,12 @@
|
||||
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */,
|
||||
7A1022722C554A1300B84627 /* CustomHostingController.swift in Sources */,
|
||||
7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */,
|
||||
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */,
|
||||
7AF231932DA1C28100AE5EB3 /* AuthScreen.swift in Sources */,
|
||||
7A1E78F82CE900440004B740 /* ReportViewModel.swift in Sources */,
|
||||
7A10226E2C551EE000B84627 /* LocationEditViewModel.swift in Sources */,
|
||||
7ADFC95B2DAD1F45001A43E3 /* WebView.swift in Sources */,
|
||||
7AB4902B2D6B1446002F39C6 /* ACKeyboardButton.swift in Sources */,
|
||||
7AE8CBB72DBA3E4E005EF1AB /* MainSplitScreen.swift in Sources */,
|
||||
7AFBE8CE2C308B53003C491D /* ACMessageView.swift in Sources */,
|
||||
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */,
|
||||
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */,
|
||||
@ -1493,16 +1468,15 @@
|
||||
7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */,
|
||||
7ABD1B472D044A3200B43213 /* GalleryScreen.swift in Sources */,
|
||||
7A71580C2C44453200852088 /* AdsScreen.swift in Sources */,
|
||||
7AE8CBB52DBA3B55005EF1AB /* Router.swift in Sources */,
|
||||
7AADD4452DB2D4D60027FD7B /* MapInput.swift in Sources */,
|
||||
7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */,
|
||||
7AFBE8CC2C3085C6003C491D /* ACProgressView.swift in Sources */,
|
||||
7A131FD32D37B75500DC7755 /* HistoryScreen.swift in Sources */,
|
||||
7AB9FE222D08C2A5005DE374 /* EventsScreen.swift in Sources */,
|
||||
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */,
|
||||
7A5911F02D63266B00EC51BA /* SearchViewModel.swift in Sources */,
|
||||
7A589E0F2D6B6E8E00EF3FBE /* NumberEditView.swift in Sources */,
|
||||
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */,
|
||||
7AC44B862DB40A5C00ADC026 /* HideTabBarModifier.swift in Sources */,
|
||||
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
|
||||
7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */,
|
||||
7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */,
|
||||
@ -1630,24 +1604,6 @@
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
7A11470623FDE7E500B424AF /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
7A11470723FDE7E500B424AF /* Base */,
|
||||
7A61FF962576C16400D905D5 /* ru */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A11470B23FDE7E600B424AF /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
7A11470C23FDE7E600B424AF /* Base */,
|
||||
7A61FF8325759DE700D905D5 /* ru */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A61FF892575A2CD00D905D5 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
@ -1801,7 +1757,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 157;
|
||||
CURRENT_PROJECT_VERSION = 158;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||
@ -1828,7 +1784,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 157;
|
||||
CURRENT_PROJECT_VERSION = 158;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
import UIKit
|
||||
import PKHUD
|
||||
import AutoCatCore
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
|
||||
HUD.dimsBackground = true
|
||||
HUD.allowsInteraction = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: UISceneSession Lifecycle
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
// Called when a new scene session is being created.
|
||||
// Use this method to select a configuration to create the new scene with.
|
||||
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||
// Called when the user discards a scene session.
|
||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||
}
|
||||
}
|
||||
|
||||
86
AutoCat/AutoCatApp.swift
Normal file
86
AutoCat/AutoCatApp.swift
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// AutoCatApp.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 20.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
import SwiftLocation
|
||||
import CoreLocation
|
||||
|
||||
@main
|
||||
struct AutoCatApp: App {
|
||||
|
||||
@State var diReady: Bool = false
|
||||
@State var settingsService: SettingsServiceProtocol?
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
if diReady {
|
||||
if settingsService?.user.token.isEmpty == false {
|
||||
mainScreen
|
||||
} else {
|
||||
AuthScreen()
|
||||
}
|
||||
} else {
|
||||
Text("")
|
||||
.task {
|
||||
try! await registerServices()
|
||||
diReady = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var mainScreen: some View {
|
||||
if UIDevice.isIPhone {
|
||||
MainTabScreen()
|
||||
} else {
|
||||
MainSplitScreen()
|
||||
}
|
||||
}
|
||||
|
||||
func registerServices() async throws {
|
||||
|
||||
let container = ServiceContainer.shared
|
||||
|
||||
let settingsService = await SettingsService()
|
||||
self.settingsService = settingsService
|
||||
|
||||
container.register(SettingsServiceProtocol.self, instance: settingsService)
|
||||
|
||||
let apiService = ApiService(settingsService: settingsService)
|
||||
container.register(ApiServiceProtocol.self, instance: apiService)
|
||||
|
||||
let locationService = LocationService(
|
||||
geocoder: CLGeocoder(),
|
||||
locationManager: Location(),
|
||||
settingsService: settingsService
|
||||
)
|
||||
|
||||
container.register(LocationServiceProtocol.self, instance: locationService)
|
||||
|
||||
let storageService = try await StorageService(settingsService: settingsService)
|
||||
container.register(StorageServiceProtocol.self, instance: storageService)
|
||||
|
||||
let vehicleService = VehicleService(apiService: apiService,
|
||||
storageService: storageService,
|
||||
locationService: locationService)
|
||||
container.register(VehicleServiceProtocol.self, instance: vehicleService)
|
||||
|
||||
let audioRecordService = AudioRecordService()
|
||||
container.register(AudioRecordServiceProtocol.self, instance: audioRecordService)
|
||||
|
||||
let vehicleRecordService = VehicleRecordService(
|
||||
recordService: audioRecordService,
|
||||
locationService: locationService,
|
||||
settingsService: settingsService
|
||||
)
|
||||
container.register(VehicleRecordServiceProtocol.self, instance: vehicleRecordService)
|
||||
container.register(RecordPlayerServiceProtocol.self, instance: RecordPlayerService())
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="dark"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||
</dependencies>
|
||||
<scenes/>
|
||||
</document>
|
||||
@ -1,32 +0,0 @@
|
||||
import UIKit
|
||||
import PKHUD
|
||||
import AutoCatCore
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
func show(error: Error) {
|
||||
self.show(error: error, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func show(error: Error, animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
let msg = (error as NSError).displayMessage
|
||||
let alert = UIAlertController(title: msg.title, message: msg.body, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
|
||||
completion?()
|
||||
}))
|
||||
self.present(alert, animated: animated)
|
||||
}
|
||||
|
||||
func showAlert(title: String, message: String) {
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension HUD {
|
||||
static func show(error: Error) {
|
||||
let msg = (error as NSError).displayMessage
|
||||
self.flash(.labeledError(title: msg.title, subtitle: msg.body), delay: 2.0)
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
extension UINavigationController {
|
||||
public func pushViewController(
|
||||
_ viewController: UIViewController,
|
||||
animated: Bool,
|
||||
completion: @escaping () -> Void)
|
||||
{
|
||||
pushViewController(viewController, animated: animated)
|
||||
|
||||
guard animated, let coordinator = transitionCoordinator else {
|
||||
DispatchQueue.main.async { completion() }
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: nil) { _ in completion() }
|
||||
}
|
||||
|
||||
func popViewController(
|
||||
animated: Bool,
|
||||
completion: @escaping () -> Void)
|
||||
{
|
||||
popViewController(animated: animated)
|
||||
|
||||
guard animated, let coordinator = transitionCoordinator else {
|
||||
DispatchQueue.main.async { completion() }
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: nil) { _ in completion() }
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIImage {
|
||||
class func resize(image: UIImage, targetSize: CGSize) -> UIImage {
|
||||
let size = image.size
|
||||
|
||||
let widthRatio = targetSize.width / image.size.width
|
||||
let heightRatio = targetSize.height / image.size.height
|
||||
|
||||
var newSize: CGSize
|
||||
if widthRatio > heightRatio {
|
||||
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
|
||||
} else {
|
||||
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
|
||||
}
|
||||
|
||||
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
|
||||
image.draw(in: rect)
|
||||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return newImage!
|
||||
}
|
||||
|
||||
class func scale(image: UIImage, by scale: CGFloat) -> UIImage? {
|
||||
let size = image.size
|
||||
let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
|
||||
return UIImage.resize(image: image, targetSize: scaledSize)
|
||||
}
|
||||
|
||||
func cutHeight(to newHeight: CGFloat) -> UIImage {
|
||||
let newSize = CGSize(width: self.size.width, height: newHeight)
|
||||
let rect = CGRect(origin: .zero, size: newSize)
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(bounds: rect)
|
||||
return renderer.image { _ in
|
||||
self.draw(in: CGRect(origin: .zero, size: self.size))
|
||||
}
|
||||
}
|
||||
}
|
||||
17
AutoCat/Extensions/UIDevice.swift
Normal file
17
AutoCat/Extensions/UIDevice.swift
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// UIDevice.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 24.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIDevice {
|
||||
|
||||
static var isIPhone: Bool {
|
||||
|
||||
current.userInterfaceIdiom == .phone
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
extension UIViewController {
|
||||
func hideKeyboardWhenTappedAround() {
|
||||
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
|
||||
tap.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tap)
|
||||
}
|
||||
|
||||
@objc func dismissKeyboard() {
|
||||
view.endEditing(true)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
var parentViewController: UIViewController? {
|
||||
var parentResponder: UIResponder? = self
|
||||
while parentResponder != nil {
|
||||
parentResponder = parentResponder!.next
|
||||
if let viewController = parentResponder as? UIViewController {
|
||||
return viewController
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -2,25 +2,6 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>pro.aliencat.vehicle.event</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>yandexmaps</string>
|
||||
</array>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@ -37,8 +18,14 @@
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>yandexmaps</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
@ -93,14 +80,15 @@
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict>
|
||||
<key>UIImageName</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
@ -118,5 +106,18 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>pro.aliencat.vehicle.event</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
import UIKit
|
||||
import os.log
|
||||
import AVFoundation
|
||||
import PKHUD
|
||||
import AutoCatCore
|
||||
import SwiftLocation
|
||||
import CoreLocation
|
||||
import SwiftUI
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
|
||||
var number: String?
|
||||
|
||||
if let activity = connectionOptions.userActivities.first {
|
||||
|
||||
if let url = activity.webpageURL {
|
||||
if let param = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first, let token = param.value {
|
||||
if let jwt = JWT<NumberPayload>(string: token) {
|
||||
number = jwt.payload.plateNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task {
|
||||
try? await registerServices()
|
||||
setupRootController(scene: scene, openReport: number)
|
||||
}
|
||||
}
|
||||
|
||||
func registerServices() async throws {
|
||||
|
||||
let container = ServiceContainer.shared
|
||||
|
||||
let settingsService = await SettingsService()
|
||||
|
||||
container.register(SettingsServiceProtocol.self, instance: settingsService)
|
||||
|
||||
let apiService = ApiService(settingsService: settingsService)
|
||||
container.register(ApiServiceProtocol.self, instance: apiService)
|
||||
|
||||
let locationService = LocationService(
|
||||
geocoder: CLGeocoder(),
|
||||
locationManager: Location(),
|
||||
settingsService: settingsService
|
||||
)
|
||||
|
||||
container.register(LocationServiceProtocol.self, instance: locationService)
|
||||
|
||||
let storageService = try await StorageService(settingsService: settingsService)
|
||||
container.register(StorageServiceProtocol.self, instance: storageService)
|
||||
|
||||
let vehicleService = VehicleService(apiService: apiService,
|
||||
storageService: storageService,
|
||||
locationService: locationService)
|
||||
container.register(VehicleServiceProtocol.self, instance: vehicleService)
|
||||
|
||||
let audioRecordService = AudioRecordService()
|
||||
container.register(AudioRecordServiceProtocol.self, instance: audioRecordService)
|
||||
|
||||
let vehicleRecordService = VehicleRecordService(
|
||||
recordService: audioRecordService,
|
||||
locationService: locationService,
|
||||
settingsService: settingsService
|
||||
)
|
||||
container.register(VehicleRecordServiceProtocol.self, instance: vehicleRecordService)
|
||||
container.register(RecordPlayerServiceProtocol.self, instance: RecordPlayerService())
|
||||
}
|
||||
|
||||
func setupRootController(scene: UIScene, openReport number: String?) {
|
||||
guard let windowScene = (scene as? UIWindowScene) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.window = UIWindow(windowScene: windowScene)
|
||||
let storyboard = UIStoryboard(name: "Main", bundle: nil)
|
||||
|
||||
let settingsService = ServiceContainer.shared.resolve(SettingsServiceProtocol.self)
|
||||
|
||||
if settingsService.user.token.isEmpty {
|
||||
let coordinator = AuthCoordinator(window: self.window)
|
||||
self.window?.rootViewController = coordinator.start()
|
||||
} else {
|
||||
//self.window?.rootViewController = storyboard.instantiateViewController(identifier: "MainSplitController")
|
||||
|
||||
self.window?.rootViewController = UIHostingController(rootView: MainTabScreen())
|
||||
|
||||
if let number {
|
||||
Task { await openReport(with: number) }
|
||||
}
|
||||
}
|
||||
|
||||
self.window?.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene) {
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
|
||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
|
||||
}
|
||||
|
||||
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
||||
|
||||
if let url = userActivity.webpageURL {
|
||||
if let param = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first, let token = param.value {
|
||||
if let jwt = JWT<NumberPayload>(string: token) {
|
||||
Task { await self.openReport(with: jwt.payload.plateNumber) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openReport(with number: String) async {
|
||||
guard let rootController = self.window?.rootViewController else { return }
|
||||
|
||||
let apiService: ApiServiceProtocol = ServiceContainer.shared.resolve(ApiServiceProtocol.self)
|
||||
|
||||
do {
|
||||
HUD.show(.progress)
|
||||
let vehicle = try await apiService.getReport(for: number)
|
||||
|
||||
Task {
|
||||
let screen = ReportScreen(vehicle: vehicle, isPersistent: false, onUpdate: { _ in })
|
||||
let controller = UIHostingController(rootView: screen)
|
||||
let navController = UINavigationController(rootViewController: controller)
|
||||
rootController.present(navController, animated: true)
|
||||
}
|
||||
|
||||
HUD.hide()
|
||||
} catch {
|
||||
HUD.show(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
//
|
||||
// AuthCoordinator.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 05.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
@MainActor
|
||||
final class AuthCoordinator {
|
||||
|
||||
let window: UIWindow?
|
||||
|
||||
init(window: UIWindow?) {
|
||||
|
||||
self.window = window
|
||||
}
|
||||
|
||||
func start() -> UIViewController {
|
||||
|
||||
let resolver = ServiceContainer.shared
|
||||
let viewModel = AuthViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
settingsService: resolver.resolve(SettingsServiceProtocol.self)
|
||||
)
|
||||
|
||||
viewModel.coordinator = self
|
||||
|
||||
let view = AuthScreen(viewModel: viewModel)
|
||||
return UIHostingController(rootView: view)
|
||||
}
|
||||
|
||||
func openMainScreen() {
|
||||
|
||||
window?.rootViewController = UIHostingController(rootView: MainTabScreen())
|
||||
}
|
||||
}
|
||||
@ -7,11 +7,22 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
struct AuthScreen: View {
|
||||
|
||||
@State var viewModel: AuthViewModel
|
||||
|
||||
init() {
|
||||
|
||||
let resolver = ServiceContainer.shared
|
||||
self.viewModel = AuthViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
settingsService: resolver.resolve(SettingsServiceProtocol.self)
|
||||
)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
VStack(spacing: 16) {
|
||||
@ -20,6 +31,8 @@ struct AuthScreen: View {
|
||||
.keyboardType(.emailAddress)
|
||||
SecureField("Password", text: $viewModel.password, prompt: Text("Password"))
|
||||
}
|
||||
.disableAutocorrection(true)
|
||||
.autocapitalization(.none)
|
||||
.padding(8)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
|
||||
@ -16,7 +16,6 @@ final class AuthViewModel: ACHudContainer {
|
||||
let apiService: ApiServiceProtocol
|
||||
let storageService: StorageServiceProtocol
|
||||
var settingsService: SettingsServiceProtocol
|
||||
var coordinator: AuthCoordinator?
|
||||
|
||||
var hud: ACHud?
|
||||
|
||||
@ -49,7 +48,6 @@ final class AuthViewModel: ACHudContainer {
|
||||
guard let self else { return }
|
||||
let user = try await apiService.login(email: email, password: password)
|
||||
try await saveUser(user)
|
||||
coordinator?.openMainScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +56,6 @@ final class AuthViewModel: ACHudContainer {
|
||||
guard let self else { return }
|
||||
let user = try await apiService.signUp(email: email, password: password)
|
||||
try await saveUser(user)
|
||||
coordinator?.openMainScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -42,6 +42,7 @@ struct EventsScreen: View {
|
||||
list
|
||||
.zIndex(viewModel.mode == .list ? 1 : 0)
|
||||
}
|
||||
.onAppear(perform: viewModel.onAppear)
|
||||
.hud($viewModel.hud)
|
||||
.navigationTitle(viewModel.vehicle.number)
|
||||
.toolbar {
|
||||
|
||||
@ -61,11 +61,14 @@ class EventsViewModel: ACHudContainer {
|
||||
self.settingsService = settingsService
|
||||
self.vehicle = vehicle
|
||||
self.onUpdate = onUpdate
|
||||
|
||||
updateEvents()
|
||||
}
|
||||
|
||||
func updateEvents() {
|
||||
func onAppear() {
|
||||
|
||||
updateEvents(initialLoad: true)
|
||||
}
|
||||
|
||||
func updateEvents(initialLoad: Bool = false) {
|
||||
|
||||
let email = settingsService.user.email
|
||||
|
||||
@ -85,7 +88,9 @@ class EventsViewModel: ACHudContainer {
|
||||
)
|
||||
}
|
||||
|
||||
onUpdate(vehicle)
|
||||
if !initialLoad {
|
||||
onUpdate(vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
func onEventUpdated(_ event: VehicleEventDto) {
|
||||
|
||||
@ -134,6 +134,6 @@ struct FiltersScreen: View {
|
||||
.task {
|
||||
await viewModel.loadData()
|
||||
}
|
||||
.hideTabBar()
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,23 +9,28 @@
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
enum HistoryRoute: Hashable {
|
||||
|
||||
case localReport(VehicleDto)
|
||||
case sharedReport(VehicleDto)
|
||||
}
|
||||
|
||||
struct HistoryScreen: View {
|
||||
|
||||
@State var viewModel: HistoryViewModel
|
||||
@State var filterSheetPresented = false
|
||||
@State var exportSheetPresented = false
|
||||
|
||||
init(viewModel: HistoryViewModel) {
|
||||
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List(selection: $viewModel.selectedVehicleId) {
|
||||
ForEach(viewModel.vehicleSections) { section in
|
||||
Section(header: Text(section.header)) {
|
||||
ForEach(section.elements) { vehicle in
|
||||
NavigationLink(value: vehicle.id) {
|
||||
if UIDevice.isIPhone {
|
||||
NavigationLink(value: HistoryRoute.localReport(vehicle)) {
|
||||
vehicleCell(vehicle)
|
||||
}
|
||||
} else {
|
||||
vehicleCell(vehicle)
|
||||
}
|
||||
}
|
||||
@ -76,6 +81,22 @@ struct HistoryScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: HistoryRoute.self) { route in
|
||||
switch route {
|
||||
case .localReport(let vehicle):
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: true,
|
||||
onUpdate: viewModel.onVehicleChanged
|
||||
)
|
||||
case .sharedReport(let vehicle):
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: false,
|
||||
onUpdate: { _ in }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func vehicleCell(_ vehicle: VehicleDto) -> some View {
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
//
|
||||
// HistorySplitScreen.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 19.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
struct HistorySplitScreen: View {
|
||||
|
||||
var viewModel: HistoryViewModel
|
||||
|
||||
var body: some View {
|
||||
NavigationSplitView {
|
||||
HistoryScreen(viewModel: viewModel)
|
||||
} detail: {
|
||||
if let vehicle = viewModel.selectedVehicle {
|
||||
ReportScreen(vehicle: vehicle, isPersistent: true, onUpdate: viewModel.onVehicleChanged)
|
||||
.id(vehicle.id)
|
||||
} else {
|
||||
Text("No vehicle selected")
|
||||
}
|
||||
}
|
||||
.navigationSplitViewStyle(.balanced)
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,7 @@ final class HistoryViewModel: ACHudContainer {
|
||||
var vehiclesFiltered: [VehicleDto] = []
|
||||
var vehicleSections: [DateSection<VehicleDto>] = []
|
||||
|
||||
var vehicleToOpen: VehicleDto?
|
||||
var selectedVehicleId: VehicleDto.ID?
|
||||
|
||||
var selectedVehicle: VehicleDto? {
|
||||
@ -74,6 +75,9 @@ final class HistoryViewModel: ACHudContainer {
|
||||
}
|
||||
|
||||
func onAppear() async {
|
||||
guard vehicles.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
async let loadTask: () = loadVehicles()
|
||||
async let dbUrlTask = storageService.dbFileURL
|
||||
@ -141,8 +145,11 @@ final class HistoryViewModel: ACHudContainer {
|
||||
if errors.isEmpty {
|
||||
hud = nil
|
||||
if !vehicle.unrecognized {
|
||||
// TODO: Fix programmatic navigation
|
||||
//selectedVehicleId = vehicle.id
|
||||
if UIDevice.isIPhone {
|
||||
Router.shared.openLocalReport(vehicle: vehicle)
|
||||
} else {
|
||||
selectedVehicleId = vehicle.id
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showErrors(errors)
|
||||
@ -178,4 +185,30 @@ final class HistoryViewModel: ACHudContainer {
|
||||
await loadVehicles()
|
||||
}
|
||||
}
|
||||
|
||||
func onOpenUrl(_ url: URL) {
|
||||
guard let param = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first,
|
||||
let token = param.value,
|
||||
let jwt = JWT<NumberPayload>(string: token)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
await openReport(number: jwt.payload.plateNumber)
|
||||
}
|
||||
}
|
||||
|
||||
func openReport(number: String) async {
|
||||
await wrapWithToast { [weak self] in
|
||||
guard let self else { return }
|
||||
let vehicle = try await apiService.getReport(for: number)
|
||||
|
||||
if UIDevice.isIPhone {
|
||||
Router.shared.openSharedReport(vehicle: vehicle)
|
||||
} else {
|
||||
vehicleToOpen = vehicle
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
179
AutoCat/Screens/MainScreen/MainSplitScreen.swift
Normal file
179
AutoCat/Screens/MainScreen/MainSplitScreen.swift
Normal file
@ -0,0 +1,179 @@
|
||||
//
|
||||
// MainSplitScreen.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 24.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
struct MainSplitScreen: View {
|
||||
|
||||
@State private var selection: SideBarItem? = .history
|
||||
@State private var columnVisibility: NavigationSplitViewVisibility = .all
|
||||
|
||||
@State var historyViewModel: HistoryViewModel
|
||||
@State var recordsViewModel: RecordsViewModel
|
||||
@State var searchViewModel: SearchViewModel
|
||||
|
||||
@State var checkNumberSheetOpened = false
|
||||
|
||||
init() {
|
||||
let resolver = ServiceContainer.shared
|
||||
|
||||
historyViewModel = HistoryViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
|
||||
recordsViewModel = RecordsViewModel(
|
||||
recordService: resolver.resolve(VehicleRecordServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
recordPlayer: resolver.resolve(RecordPlayerServiceProtocol.self)
|
||||
)
|
||||
|
||||
searchViewModel = SearchViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
|
||||
recordsViewModel.onCheckRecord = checkRecord
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
threeColumnSplitView
|
||||
.opacity(selection?.needThreeColumns == true ? 1 : 0)
|
||||
twoColumnSplitView
|
||||
.opacity(selection?.needThreeColumns == true ? 0 : 1)
|
||||
}
|
||||
.navigationSplitViewStyle(.balanced)
|
||||
.sheet(isPresented: $checkNumberSheetOpened) {
|
||||
numberEditSheet
|
||||
}
|
||||
.sheet(item: $historyViewModel.vehicleToOpen) { vehicle in
|
||||
NavigationStack {
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: false,
|
||||
onUpdate: { _ in }
|
||||
)
|
||||
}
|
||||
}
|
||||
.onOpenURL { url in
|
||||
selection = .history
|
||||
historyViewModel.onOpenUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
var numberEditSheet: some View {
|
||||
NumberEditView { number in
|
||||
checkNumberSheetOpened = false
|
||||
selection = .history
|
||||
Task { await historyViewModel.checkNewNumber(number) }
|
||||
}
|
||||
.presentationDetents([.height(350)])
|
||||
}
|
||||
|
||||
var twoColumnSplitView: some View {
|
||||
|
||||
NavigationSplitView(columnVisibility: $columnVisibility) {
|
||||
sidebar
|
||||
} detail: {
|
||||
twoColumnDetail
|
||||
}
|
||||
}
|
||||
|
||||
var threeColumnSplitView: some View {
|
||||
|
||||
NavigationSplitView(columnVisibility: $columnVisibility) {
|
||||
sidebar
|
||||
} content: {
|
||||
content
|
||||
} detail: {
|
||||
threeColumnDetail
|
||||
}
|
||||
}
|
||||
|
||||
var sidebar: some View {
|
||||
List(SideBarItem.allSideBarItems, selection: $selection) { tab in
|
||||
NavigationLink(value: tab) {
|
||||
Label(tab.title, systemImage: tab.imageName)
|
||||
}
|
||||
}
|
||||
.navigationSplitViewColumnWidth(250)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("Check new number", systemImage: "plus") {
|
||||
checkNumberSheetOpened = true
|
||||
}
|
||||
}
|
||||
}
|
||||
.toolbar(.visible, for: .navigationBar)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var content: some View {
|
||||
Group {
|
||||
switch selection {
|
||||
case .history:
|
||||
HistoryScreen(viewModel: historyViewModel)
|
||||
case .search:
|
||||
SearchScreen(viewModel: searchViewModel)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.navigationSplitViewColumnWidth(350)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var twoColumnDetail: some View {
|
||||
switch selection {
|
||||
case .records:
|
||||
RecordsScreen(selectedTab: $selection, viewModel: recordsViewModel)
|
||||
case .settings:
|
||||
SettingsScreen()
|
||||
case .history, .search, .plus, .none:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var threeColumnDetail: some View {
|
||||
switch selection {
|
||||
case .history:
|
||||
if let vehicle = historyViewModel.selectedVehicle {
|
||||
NavigationStack {
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: true,
|
||||
onUpdate: historyViewModel.onVehicleChanged
|
||||
)
|
||||
.id(vehicle.id)
|
||||
}
|
||||
}
|
||||
case .search:
|
||||
if let vehicle = searchViewModel.selectedVehicle {
|
||||
NavigationStack {
|
||||
ReportScreen(
|
||||
vehicle: vehicle,
|
||||
isPersistent: false,
|
||||
onUpdate: searchViewModel.onVehicleChanged
|
||||
)
|
||||
.id(vehicle.id)
|
||||
}
|
||||
}
|
||||
case .settings, .records, .plus, .none:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
func checkRecord(number: String, event: VehicleEventDto?) {
|
||||
|
||||
Task { await historyViewModel.checkRecord(number: number, event: event) }
|
||||
}
|
||||
}
|
||||
121
AutoCat/Screens/MainScreen/MainTabScreen.swift
Normal file
121
AutoCat/Screens/MainScreen/MainTabScreen.swift
Normal file
@ -0,0 +1,121 @@
|
||||
//
|
||||
// MainTabScreen.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 19.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import SwiftEntryKit
|
||||
import AutoCatCore
|
||||
|
||||
struct MainTabScreen: View {
|
||||
|
||||
@State private var selection: SideBarItem? = .history
|
||||
@State private var oldSelection: SideBarItem? = .history
|
||||
|
||||
@State var historyViewModel: HistoryViewModel
|
||||
@State var recordsViewModel: RecordsViewModel
|
||||
@State var searchViewModel: SearchViewModel
|
||||
|
||||
@State var router = Router.shared
|
||||
@State var checkNumberSheetOpened = false
|
||||
|
||||
init() {
|
||||
let resolver = ServiceContainer.shared
|
||||
|
||||
historyViewModel = HistoryViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
|
||||
recordsViewModel = RecordsViewModel(
|
||||
recordService: resolver.resolve(VehicleRecordServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
recordPlayer: resolver.resolve(RecordPlayerServiceProtocol.self)
|
||||
)
|
||||
|
||||
searchViewModel = SearchViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
|
||||
recordsViewModel.onCheckRecord = checkRecord
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
nativeTabView
|
||||
.sheet(isPresented: $checkNumberSheetOpened) {
|
||||
numberEditSheet
|
||||
}
|
||||
.onOpenURL { url in
|
||||
selection = .history
|
||||
historyViewModel.onOpenUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
var nativeTabView: some View {
|
||||
TabView(selection: $selection) {
|
||||
NavigationStack(path: $router.historyPath) {
|
||||
HistoryScreen(viewModel: historyViewModel)
|
||||
}
|
||||
.tabItem {
|
||||
Label("History", systemImage: "clock.arrow.circlepath")
|
||||
}
|
||||
.tag(SideBarItem.history)
|
||||
.onAppear { oldSelection = selection }
|
||||
|
||||
RecordsScreen(selectedTab: $selection, viewModel: recordsViewModel)
|
||||
.tabItem {
|
||||
Label("Records", systemImage: "waveform")
|
||||
}
|
||||
.tag(SideBarItem.records)
|
||||
.onAppear { oldSelection = selection }
|
||||
|
||||
Text("")
|
||||
.tabItem {
|
||||
Label("", systemImage: "plus")
|
||||
}
|
||||
.tag(SideBarItem.plus)
|
||||
.onAppear {
|
||||
DispatchQueue.main.async {
|
||||
checkNumberSheetOpened = true
|
||||
selection = oldSelection
|
||||
}
|
||||
}
|
||||
|
||||
NavigationStack {
|
||||
SearchScreen(viewModel: searchViewModel)
|
||||
}
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
}
|
||||
.tag(SideBarItem.search)
|
||||
.onAppear { oldSelection = selection }
|
||||
|
||||
SettingsScreen()
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
.tag(SideBarItem.settings)
|
||||
.onAppear { oldSelection = selection }
|
||||
}
|
||||
}
|
||||
|
||||
var numberEditSheet: some View {
|
||||
NumberEditView { number in
|
||||
checkNumberSheetOpened = false
|
||||
selection = .history
|
||||
Task { await historyViewModel.checkNewNumber(number) }
|
||||
}
|
||||
.presentationDetents([.height(350)])
|
||||
}
|
||||
|
||||
func checkRecord(number: String, event: VehicleEventDto?) {
|
||||
|
||||
Task { await historyViewModel.checkRecord(number: number, event: event) }
|
||||
}
|
||||
}
|
||||
56
AutoCat/Screens/MainScreen/SideBarItem.swift
Normal file
56
AutoCat/Screens/MainScreen/SideBarItem.swift
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// SideBarItem.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 24.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum SideBarItem: Int, CaseIterable, Identifiable, Hashable, Equatable {
|
||||
|
||||
case history
|
||||
case records
|
||||
case plus
|
||||
case search
|
||||
case settings
|
||||
|
||||
var id: Int {
|
||||
rawValue
|
||||
}
|
||||
|
||||
var title: LocalizedStringKey {
|
||||
switch self {
|
||||
case .history: "History"
|
||||
case .records: "Records"
|
||||
case .plus: ""
|
||||
case .search: "Search"
|
||||
case .settings: "Settings"
|
||||
}
|
||||
}
|
||||
|
||||
var imageName: String {
|
||||
switch self {
|
||||
case .history: "clock.arrow.circlepath"
|
||||
case .records: "waveform"
|
||||
case .plus: "plus"
|
||||
case .search: "magnifyingglass"
|
||||
case .settings: "gear"
|
||||
}
|
||||
}
|
||||
|
||||
var needThreeColumns: Bool {
|
||||
switch self {
|
||||
case .history, .search:
|
||||
true
|
||||
case .records, .settings, .plus:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
static var allSideBarItems: [SideBarItem] {
|
||||
|
||||
SideBarItem.allCases.filter { $0 != .plus }
|
||||
}
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
//
|
||||
// MainTabScreen.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 19.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import SwiftEntryKit
|
||||
import AutoCatCore
|
||||
|
||||
struct MainTabScreen: View {
|
||||
|
||||
@State private var selection = 0
|
||||
|
||||
let historyViewModel: HistoryViewModel
|
||||
let recordsViewModel: RecordsViewModel
|
||||
|
||||
init() {
|
||||
let resolver = ServiceContainer.shared
|
||||
|
||||
historyViewModel = HistoryViewModel(
|
||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||
)
|
||||
|
||||
recordsViewModel = RecordsViewModel(
|
||||
recordService: resolver.resolve(VehicleRecordServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
recordPlayer: resolver.resolve(RecordPlayerServiceProtocol.self)
|
||||
)
|
||||
|
||||
recordsViewModel.onCheckRecord = checkRecord
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $selection) {
|
||||
HistorySplitScreen(viewModel: historyViewModel)
|
||||
.tabItem {
|
||||
Label("History", systemImage: "clock.arrow.circlepath")
|
||||
}
|
||||
.tag(0)
|
||||
|
||||
RecordsScreen(viewModel: recordsViewModel)
|
||||
.tabItem {
|
||||
Label("Records", image: "record")
|
||||
}
|
||||
.tag(1)
|
||||
|
||||
#if os(iOS)
|
||||
Text("")
|
||||
.tabItem {
|
||||
Label("", systemImage: "plus")
|
||||
}
|
||||
.tag(2)
|
||||
#endif
|
||||
|
||||
SearchSplitScreen()
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
}
|
||||
.tag(3)
|
||||
|
||||
SettingsScreen()
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
.tag(4)
|
||||
}
|
||||
#if os(iOS)
|
||||
.onChange(of: selection) { oldValue, newValue in
|
||||
if newValue == 2 {
|
||||
self.selection = oldValue
|
||||
showCheckPuller()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func showCheckPuller() {
|
||||
|
||||
var attributes = EKAttributes.bottomToast
|
||||
attributes.displayDuration = .infinity
|
||||
attributes.entryBackground = .color(color: .init(.secondarySystemBackground))
|
||||
attributes.screenBackground = .color(color: EKColor(UIColor(white: 0, alpha: 0.7)))
|
||||
attributes.roundCorners = .top(radius: 24)
|
||||
attributes.screenInteraction = .dismiss
|
||||
attributes.scroll = .disabled
|
||||
attributes.entryInteraction = .absorbTouches
|
||||
attributes.entranceAnimation = .init(translate: .init(duration: 0.2))
|
||||
attributes.exitAnimation = .init(translate: .init(duration: 0.2))
|
||||
|
||||
let newNumberController = NewNumberController()
|
||||
newNumberController.onCheck = { number in
|
||||
SwiftEntryKit.dismiss {
|
||||
selection = 0
|
||||
Task { await historyViewModel.checkNewNumber(number) }
|
||||
}
|
||||
}
|
||||
|
||||
SwiftEntryKit.display(entry: newNumberController, using: attributes)
|
||||
|
||||
// User probably just saw a vehicle and is about to start entering plate number
|
||||
// Requesting current location ASAP while we still close to initial location
|
||||
let locationService = ServiceContainer.shared.resolve(LocationServiceProtocol.self)
|
||||
Task { try? await locationService.requestCurrentLocation() }
|
||||
}
|
||||
|
||||
func checkRecord(number: String, event: VehicleEventDto?) {
|
||||
|
||||
selection = 0
|
||||
Task { await historyViewModel.checkRecord(number: number, event: event) }
|
||||
}
|
||||
}
|
||||
@ -61,6 +61,6 @@ struct MapScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.hideTabBar()
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,27 +11,13 @@ import AutoCatCore
|
||||
|
||||
struct RecordsScreen: View {
|
||||
|
||||
@Binding var selectedTab: SideBarItem?
|
||||
@State var viewModel: RecordsViewModel
|
||||
|
||||
@State var showEditAlert = false
|
||||
@State var numberText = ""
|
||||
@State var selectedRecordId: String = ""
|
||||
|
||||
init() {
|
||||
|
||||
let resolver = ServiceContainer.shared
|
||||
self.viewModel = RecordsViewModel(
|
||||
recordService: resolver.resolve(VehicleRecordServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
recordPlayer: resolver.resolve(RecordPlayerServiceProtocol.self)
|
||||
)
|
||||
}
|
||||
|
||||
init(viewModel: RecordsViewModel) {
|
||||
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
@ -138,6 +124,7 @@ struct RecordsScreen: View {
|
||||
|
||||
if record.number != nil {
|
||||
Button {
|
||||
selectedTab = .history
|
||||
viewModel.check(record)
|
||||
} label: {
|
||||
Label("Check", systemImage: "eye")
|
||||
|
||||
@ -36,119 +36,119 @@ struct ReportScreen: View {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
LabeledContent {
|
||||
Text(viewModel.vehicle.brand?.name?.original ?? "")
|
||||
} label: {
|
||||
AsyncImage(url: URL(string: viewModel.vehicle.brand?.logo ?? "")) { phase in
|
||||
phase.image?
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(height: 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section("General") {
|
||||
LabeledContent("Year", value: String(viewModel.vehicle.year))
|
||||
LabeledContent("Color", value: viewModel.vehicle.color ?? "")
|
||||
LabeledContent("Category", value: viewModel.vehicle.category ?? "")
|
||||
LabeledContent("Steering wheel position", value: viewModel.steerignWheelPosition)
|
||||
LabeledContent("Japanese", value: viewModel.isJapanese)
|
||||
}
|
||||
|
||||
Section("Identifiers") {
|
||||
LabeledContent("Plate number", value: viewModel.plateNumber)
|
||||
LabeledContent("VIN", value: viewModel.vehicle.vin1 ?? "")
|
||||
LabeledContent("STS", value: viewModel.vehicle.sts ?? "")
|
||||
LabeledContent("PTS", value: viewModel.vehicle.pts ?? "")
|
||||
}
|
||||
|
||||
Section("Engine") {
|
||||
LabeledContent("Number", value: viewModel.vehicle.engine?.number ?? "")
|
||||
LabeledContent("Fuel type", value: viewModel.vehicle.engine?.fuelType ?? "")
|
||||
LabeledContent("Volume (cm³)", value: String(viewModel.vehicle.engine?.volume ?? 0))
|
||||
LabeledContent("Power (HP)", value: String(viewModel.vehicle.engine?.powerHp ?? 0))
|
||||
LabeledContent("Power (kw)", value: String(viewModel.vehicle.engine?.powerKw ?? 0))
|
||||
}
|
||||
|
||||
Section("History") {
|
||||
NavigationLink(value: Screen.events) {
|
||||
LabeledContent("Events", value: String(viewModel.vehicle.events.count))
|
||||
}
|
||||
|
||||
NavigationLink(value: Screen.osago) {
|
||||
LabeledContent("OSAGO", value: String(viewModel.vehicle.osagoContracts.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.osagoContracts.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.owners) {
|
||||
LabeledContent("Owners", value: String(viewModel.vehicle.ownershipPeriods.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ownershipPeriods.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.photos) {
|
||||
LabeledContent("Photos", value: String(viewModel.vehicle.photos.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.photos.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.ads) {
|
||||
LabeledContent("Ads", value: String(viewModel.vehicle.ads.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ads.isEmpty)
|
||||
|
||||
|
||||
NavigationLink(value: Screen.notes) {
|
||||
LabeledContent("Notes", value: String(viewModel.vehicle.notes.count))
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Button("Check GB") {
|
||||
Task { await viewModel.checkGB() }
|
||||
Form {
|
||||
Section {
|
||||
LabeledContent {
|
||||
Text(viewModel.vehicle.brand?.name?.original ?? "")
|
||||
} label: {
|
||||
AsyncImage(url: URL(string: viewModel.vehicle.brand?.logo ?? "")) { phase in
|
||||
phase.image?
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(height: 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
|
||||
Section("General") {
|
||||
LabeledContent("Year", value: String(viewModel.vehicle.year))
|
||||
LabeledContent("Color", value: viewModel.vehicle.color ?? "")
|
||||
LabeledContent("Category", value: viewModel.vehicle.category ?? "")
|
||||
LabeledContent("Steering wheel position", value: viewModel.steerignWheelPosition)
|
||||
LabeledContent("Japanese", value: viewModel.isJapanese)
|
||||
}
|
||||
.hud($viewModel.hud)
|
||||
.toolbar {
|
||||
if let link = viewModel.shareLink {
|
||||
ShareLink(item: link)
|
||||
|
||||
Section("Identifiers") {
|
||||
LabeledContent("Plate number", value: viewModel.plateNumber)
|
||||
LabeledContent("VIN", value: viewModel.vehicle.vin1 ?? "")
|
||||
LabeledContent("STS", value: viewModel.vehicle.sts ?? "")
|
||||
LabeledContent("PTS", value: viewModel.vehicle.pts ?? "")
|
||||
}
|
||||
|
||||
Section("Engine") {
|
||||
LabeledContent("Number", value: viewModel.vehicle.engine?.number ?? "")
|
||||
LabeledContent("Fuel type", value: viewModel.vehicle.engine?.fuelType ?? "")
|
||||
LabeledContent("Volume (cm³)", value: String(viewModel.vehicle.engine?.volume ?? 0))
|
||||
LabeledContent("Power (HP)", value: String(viewModel.vehicle.engine?.powerHp ?? 0))
|
||||
LabeledContent("Power (kw)", value: String(viewModel.vehicle.engine?.powerKw ?? 0))
|
||||
}
|
||||
|
||||
Section("History") {
|
||||
NavigationLink(value: Screen.events) {
|
||||
LabeledContent("Events", value: String(viewModel.vehicle.events.count))
|
||||
}
|
||||
|
||||
NavigationLink(value: Screen.osago) {
|
||||
LabeledContent("OSAGO", value: String(viewModel.vehicle.osagoContracts.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.osagoContracts.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.owners) {
|
||||
LabeledContent("Owners", value: String(viewModel.vehicle.ownershipPeriods.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ownershipPeriods.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.photos) {
|
||||
LabeledContent("Photos", value: String(viewModel.vehicle.photos.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.photos.isEmpty)
|
||||
|
||||
NavigationLink(value: Screen.ads) {
|
||||
LabeledContent("Ads", value: String(viewModel.vehicle.ads.count))
|
||||
}
|
||||
.disabled(viewModel.vehicle.ads.isEmpty)
|
||||
|
||||
|
||||
NavigationLink(value: Screen.notes) {
|
||||
LabeledContent("Notes", value: String(viewModel.vehicle.notes.count))
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: Screen.self) { screen in
|
||||
switch screen {
|
||||
case .events:
|
||||
EventsScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .osago:
|
||||
OsagoScreen(contracts: viewModel.vehicle.osagoContracts)
|
||||
case .owners:
|
||||
OwnersScreen(ownerships: viewModel.vehicle.ownershipPeriods)
|
||||
case .notes:
|
||||
NotesScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .ads:
|
||||
AdsScreen(ads: viewModel.vehicle.ads)
|
||||
case .photos:
|
||||
GalleryScreen(photos: viewModel.vehicle.photos)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Button("Check GB") {
|
||||
Task { await viewModel.checkGB() }
|
||||
}
|
||||
}
|
||||
.hideTabBar()
|
||||
}
|
||||
.onAppear {
|
||||
Task { await viewModel.onAppear() }
|
||||
}
|
||||
.hud($viewModel.hud)
|
||||
.toolbar {
|
||||
if let link = viewModel.shareLink {
|
||||
ShareLink(item: link)
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: Screen.self) { screen in
|
||||
switch screen {
|
||||
case .events:
|
||||
EventsScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .osago:
|
||||
OsagoScreen(contracts: viewModel.vehicle.osagoContracts)
|
||||
case .owners:
|
||||
OwnersScreen(ownerships: viewModel.vehicle.ownershipPeriods)
|
||||
case .notes:
|
||||
NotesScreen(vehicle: viewModel.vehicle, onUpdate: viewModel.onVehicleChanged)
|
||||
case .ads:
|
||||
AdsScreen(ads: viewModel.vehicle.ads)
|
||||
case .photos:
|
||||
GalleryScreen(photos: viewModel.vehicle.photos)
|
||||
}
|
||||
}
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
@ -59,13 +59,20 @@ struct SearchScreen: View {
|
||||
MapScreen(mapInput: .filter(filter))
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: VehicleDto.self) { vehicle in
|
||||
return ReportScreen(vehicle: vehicle, isPersistent: false, onUpdate: viewModel.onVehicleChanged)
|
||||
}
|
||||
}
|
||||
|
||||
var vehicles: some View {
|
||||
ForEach(viewModel.vehicleSections) { section in
|
||||
Section(header: Text(section.header)) {
|
||||
ForEach(section.elements) { vehicle in
|
||||
NavigationLink(value: vehicle.id) {
|
||||
if UIDevice.isIPhone {
|
||||
NavigationLink(value: vehicle) {
|
||||
vehicleCell(vehicle)
|
||||
}
|
||||
} else {
|
||||
vehicleCell(vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
@ -49,19 +48,6 @@ class SettingsViewModel {
|
||||
|
||||
func signOut() {
|
||||
settingService.user.token = ""
|
||||
|
||||
let window = UIApplication.shared
|
||||
.connectedScenes
|
||||
.filter({$0.activationState == .foregroundActive})
|
||||
.map({$0 as? UIWindowScene})
|
||||
.compactMap({$0})
|
||||
.first?.windows
|
||||
.filter({$0.isKeyWindow}).first
|
||||
|
||||
if let window {
|
||||
let coordinator = AuthCoordinator(window: window)
|
||||
window.rootViewController = coordinator.start()
|
||||
}
|
||||
}
|
||||
|
||||
func googleSignout() {
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
struct ACKeyboardButton: View {
|
||||
|
||||
@ -37,7 +38,7 @@ struct ACKeyboardButton: View {
|
||||
var title: some View {
|
||||
switch type {
|
||||
case .symbol(let s):
|
||||
Text(s)
|
||||
Text(String(Constants.pnLettersMap[s] ?? s))
|
||||
.font(.custom("RoadNumbers", size: 36 - 2*sizeDelta))
|
||||
.offset(y: sizeDelta/2)
|
||||
case .backspace:
|
||||
@ -60,11 +61,7 @@ struct ACKeyboardButton: View {
|
||||
return 0
|
||||
}
|
||||
|
||||
if let character = title.first {
|
||||
return character.isNumber ? 3 : 0
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
return title.isNumber ? 3 : 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,24 +16,24 @@ struct ACKeyboardView: View {
|
||||
HStack(spacing: 16) {
|
||||
Grid(horizontalSpacing: 0, verticalSpacing: 0) {
|
||||
GridRow {
|
||||
ACKeyboardButton(type: .symbol("A"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("B"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("E"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("А"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("В"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("Е"), onTap: buttonPressed)
|
||||
}
|
||||
GridRow {
|
||||
ACKeyboardButton(type: .symbol("K"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("M"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("H"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("К"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("М"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("Н"), onTap: buttonPressed)
|
||||
}
|
||||
GridRow {
|
||||
ACKeyboardButton(type: .symbol("O"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("P"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("C"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("О"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("Р"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("С"), onTap: buttonPressed)
|
||||
}
|
||||
GridRow {
|
||||
ACKeyboardButton(type: .symbol("T"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("Y"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("X"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("Т"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("У"), onTap: buttonPressed)
|
||||
ACKeyboardButton(type: .symbol("Х"), onTap: buttonPressed)
|
||||
}
|
||||
}
|
||||
Grid(horizontalSpacing: 0, verticalSpacing: 0) {
|
||||
|
||||
@ -13,19 +13,24 @@ struct ACProgressHudModifier: ViewModifier {
|
||||
@Binding var item: ACHud?
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
|
||||
if let item {
|
||||
ZStack {
|
||||
content
|
||||
makeHud(for: item)
|
||||
}
|
||||
} else {
|
||||
content
|
||||
}
|
||||
|
||||
content
|
||||
.fullScreenCover(item: $item) { item in
|
||||
if #available(iOS 16.4, *) {
|
||||
makeHud(for: item)
|
||||
.presentationBackground(.clear)
|
||||
} else {
|
||||
makeHud(for: item)
|
||||
}
|
||||
}
|
||||
.transaction { transaction in
|
||||
transaction.disablesAnimations = true
|
||||
}
|
||||
// content
|
||||
// .fullScreenCover(item: $item) { item in
|
||||
// makeHud(for: item)
|
||||
// .presentationBackground(.clear)
|
||||
// }
|
||||
// .transaction { transaction in
|
||||
// transaction.disablesAnimations = true
|
||||
// }
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
14
AutoCat/SwiftUI/Environment.swift
Normal file
14
AutoCat/SwiftUI/Environment.swift
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Environment.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 29.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension EnvironmentValues {
|
||||
|
||||
@Entry var selectedTab: SideBarItem = .history
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
//
|
||||
// HideTabBarModifier.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 19.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct HideTabBarModifier: ViewModifier {
|
||||
|
||||
@Environment(\.horizontalSizeClass) var horizontalSizeClass
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
|
||||
if horizontalSizeClass == .compact {
|
||||
content
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
public func hideTabBar() -> some View {
|
||||
|
||||
modifier(HideTabBarModifier())
|
||||
}
|
||||
}
|
||||
@ -17,13 +17,17 @@ struct NumberEditView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 16) {
|
||||
Text("Check number")
|
||||
.font(.headline)
|
||||
//.padding(.top, 24)
|
||||
|
||||
HStack(spacing: 16) {
|
||||
LicensePlateView(number: number, foreground: .primary)
|
||||
.layoutPriority(1)
|
||||
.frame(height: 50)
|
||||
|
||||
Button {
|
||||
|
||||
onCheck(number.asString())
|
||||
} label: {
|
||||
Text("Check")
|
||||
.foregroundColor(.primary)
|
||||
@ -35,6 +39,7 @@ struct NumberEditView: View {
|
||||
}
|
||||
.layoutPriority(0)
|
||||
.frame(height: 50)
|
||||
.opacity(number.isValid ? 1 : 0.5)
|
||||
}
|
||||
|
||||
ACKeyboardView(buttonPressed: buttonPressed)
|
||||
@ -46,7 +51,7 @@ struct NumberEditView: View {
|
||||
func buttonPressed(type: PNButtonType) {
|
||||
switch type {
|
||||
case .symbol(let s):
|
||||
number.insertText(s)
|
||||
number.insertText(String(s))
|
||||
case .backspace:
|
||||
number.deleteBackward()
|
||||
case .done:
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Coordinator.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 24.06.2024.
|
||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol Coordinator {
|
||||
|
||||
associatedtype ResultType
|
||||
//associatedtype Controller: UIViewController
|
||||
|
||||
var viewController: UIViewController? { get }
|
||||
|
||||
@discardableResult
|
||||
func start() async throws -> ResultType
|
||||
}
|
||||
|
||||
extension Coordinator {
|
||||
|
||||
var viewController: UIViewController? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
29
AutoCat/Utils/Router.swift
Normal file
29
AutoCat/Utils/Router.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Router.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 24.04.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class Router {
|
||||
|
||||
static let shared = Router()
|
||||
|
||||
var historyPath = NavigationPath()
|
||||
|
||||
func openLocalReport(vehicle: VehicleDto) {
|
||||
|
||||
historyPath.append(HistoryRoute.localReport(vehicle))
|
||||
}
|
||||
|
||||
func openSharedReport(vehicle: VehicleDto) {
|
||||
|
||||
historyPath.append(HistoryRoute.sharedReport(vehicle))
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ protocol PNButtonDelegate: AnyObject {
|
||||
}
|
||||
|
||||
enum PNButtonType {
|
||||
case symbol(String)
|
||||
case symbol(Character)
|
||||
case backspace
|
||||
case done
|
||||
}
|
||||
@ -52,7 +52,7 @@ class PNButton: UIButton {
|
||||
}
|
||||
|
||||
init(letter: Character, keyboardType: PNKeyboardType = .plateNumber) {
|
||||
self.type = .symbol(String(letter))
|
||||
self.type = .symbol(letter)
|
||||
self.keyboardType = keyboardType
|
||||
super.init(frame: .zero)
|
||||
self.setup()
|
||||
@ -63,7 +63,7 @@ class PNButton: UIButton {
|
||||
}
|
||||
|
||||
init(digit: Int, keyboardType: PNKeyboardType = .plateNumber) {
|
||||
self.type = .symbol(String(digit))
|
||||
self.type = .symbol(String(digit).first!)
|
||||
self.keyboardType = keyboardType
|
||||
super.init(frame: .zero)
|
||||
self.setup()
|
||||
@ -284,7 +284,7 @@ class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate {
|
||||
|
||||
switch button.type {
|
||||
case .symbol(let s):
|
||||
self.target?.insertText(s)
|
||||
self.target?.insertText(String(s))
|
||||
case .backspace:
|
||||
self.target?.deleteBackward()
|
||||
case .done:
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -427,3 +427,9 @@
|
||||
"Password" = "Пароль";
|
||||
|
||||
"Sign up" = "Регистрация";
|
||||
|
||||
"Records" = "Аудио записи";
|
||||
|
||||
"Search" = "Поиск";
|
||||
|
||||
"Settings" = "Настройки";
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
/* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "7QQ-Qi-qEw"; */
|
||||
"7QQ-Qi-qEw.title" = "Назад";
|
||||
|
||||
/* Class = "UILabel"; text = "00:00"; ObjectID = "bFQ-eU-5YJ"; */
|
||||
"bFQ-eU-5YJ.text" = "00:00";
|
||||
|
||||
/* Class = "UIViewController"; title = "Events"; ObjectID = "DmF-8j-ae3"; */
|
||||
"DmF-8j-ae3.title" = "События";
|
||||
|
||||
/* Class = "UITextField"; placeholder = "Password"; ObjectID = "G1p-Hz-8yn"; */
|
||||
"G1p-Hz-8yn.placeholder" = "Пароль";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "Search"; ObjectID = "gDG-z8-R0t"; */
|
||||
"gDG-z8-R0t.title" = "Поиск";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Close"; ObjectID = "hCi-F4-z7b"; */
|
||||
"hCi-F4-z7b.title" = "Закрыть";
|
||||
|
||||
/* Class = "UIButton"; normalTitle = "Sign up"; ObjectID = "hRD-Ha-MrP"; */
|
||||
"hRD-Ha-MrP.normalTitle" = "Регистрация";
|
||||
|
||||
/* Class = "UIButton"; normalTitle = "Log in"; ObjectID = "ltG-B1-UBj"; */
|
||||
"ltG-B1-UBj.normalTitle" = "Вход";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Voice records"; ObjectID = "lu2-xz-pMr"; */
|
||||
"lu2-xz-pMr.title" = "Голосовые записи";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "Records"; ObjectID = "lxF-EY-z8V"; */
|
||||
"lxF-EY-z8V.title" = "Аудио";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "History"; ObjectID = "QJd-35-4OB"; */
|
||||
"QJd-35-4OB.title" = "История";
|
||||
|
||||
/* Class = "UILabel"; text = "or"; ObjectID = "SXb-1Q-TxY"; */
|
||||
"SXb-1Q-TxY.text" = "или";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Filters"; ObjectID = "U4X-4i-ZJm"; */
|
||||
"U4X-4i-ZJm.title" = "Фильтры";
|
||||
|
||||
/* Class = "UITextField"; placeholder = "Email"; ObjectID = "Uey-88-eZL"; */
|
||||
"Uey-88-eZL.placeholder" = "Email";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "Add"; ObjectID = "X0w-PF-Dn8"; */
|
||||
"X0w-PF-Dn8.title" = "Добавить";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "Settings"; ObjectID = "zEL-ph-E2f"; */
|
||||
"zEL-ph-E2f.title" = "Настройки";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Close"; ObjectID = "ZHH-OZ-vHc"; */
|
||||
"ZHH-OZ-vHc.title" = "Закрыть";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
public class PlateNumber {
|
||||
public struct PlateNumber {
|
||||
private var number: String = ""
|
||||
private var numberEnglish: String = ""
|
||||
|
||||
@ -13,7 +13,7 @@ public class PlateNumber {
|
||||
}
|
||||
|
||||
public func asString() -> String {
|
||||
return self.number
|
||||
return self.number.filter { !$0.isWhitespace }.uppercased()
|
||||
}
|
||||
|
||||
public func mainPart() -> String {
|
||||
@ -32,7 +32,7 @@ public class PlateNumber {
|
||||
number.count >= 8
|
||||
}
|
||||
|
||||
public func setNumber(_ number: String) {
|
||||
public mutating func setNumber(_ number: String) {
|
||||
self.number = number
|
||||
self.numberEnglish = String(self.number.map { Constants.pnLettersMap[$0] ?? $0 })
|
||||
}
|
||||
@ -45,7 +45,7 @@ public class PlateNumber {
|
||||
|
||||
// Returns true if number was changed
|
||||
@discardableResult
|
||||
public func insertText(_ text: String) -> Bool {
|
||||
public mutating func insertText(_ text: String) -> Bool {
|
||||
guard number.count < maxLength, text.count == 1 else {
|
||||
return false
|
||||
}
|
||||
@ -63,7 +63,7 @@ public class PlateNumber {
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func deleteBackward() -> Bool {
|
||||
public mutating func deleteBackward() -> Bool {
|
||||
guard !number.isEmpty else {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -19,7 +19,8 @@ public enum Constants {
|
||||
public var baseUrl: String {
|
||||
|
||||
#if DEBUG
|
||||
"http://127.0.0.1:3000/"
|
||||
//"http://127.0.0.1:3000/"
|
||||
"https://charon.aliencat.pro:8444/"
|
||||
#else
|
||||
rawValue
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user