Replacing UINavigationController with SwiftUI's NavigationStack
This commit is contained in:
parent
0ca4c232db
commit
180866c31b
@ -14,11 +14,9 @@
|
|||||||
7A06E0B52C707E2B005731AC /* SettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06E0B42C707E2B005731AC /* SettingsService.swift */; };
|
7A06E0B52C707E2B005731AC /* SettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06E0B42C707E2B005731AC /* SettingsService.swift */; };
|
||||||
7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */; };
|
7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */; };
|
||||||
7A10226E2C551EE000B84627 /* LocationEditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */; };
|
7A10226E2C551EE000B84627 /* LocationEditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */; };
|
||||||
7A1022702C551EFD00B84627 /* LocationEditCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */; };
|
|
||||||
7A1022722C554A1300B84627 /* CustomHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022712C554A1300B84627 /* CustomHostingController.swift */; };
|
7A1022722C554A1300B84627 /* CustomHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022712C554A1300B84627 /* CustomHostingController.swift */; };
|
||||||
7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022762C557EC400B84627 /* LocationPickerScreen.swift */; };
|
7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022762C557EC400B84627 /* LocationPickerScreen.swift */; };
|
||||||
7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */; };
|
7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */; };
|
||||||
7A10227B2C557EE900B84627 /* LocationPickerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10227A2C557EE900B84627 /* LocationPickerCoordinator.swift */; };
|
|
||||||
7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */; };
|
7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */; };
|
||||||
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470023FDE7E500B424AF /* AppDelegate.swift */; };
|
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470023FDE7E500B424AF /* AppDelegate.swift */; };
|
||||||
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470223FDE7E500B424AF /* SceneDelegate.swift */; };
|
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470223FDE7E500B424AF /* SceneDelegate.swift */; };
|
||||||
@ -32,7 +30,6 @@
|
|||||||
7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */; };
|
7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */; };
|
||||||
7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441652C297EDE00E79018 /* NotesScreen.swift */; };
|
7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441652C297EDE00E79018 /* NotesScreen.swift */; };
|
||||||
7A1441682C297EFD00E79018 /* NotesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441672C297EFD00E79018 /* NotesViewModel.swift */; };
|
7A1441682C297EFD00E79018 /* NotesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441672C297EFD00E79018 /* NotesViewModel.swift */; };
|
||||||
7A14416C2C297F2100E79018 /* NotesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A14416B2C297F2100E79018 /* NotesCoordinator.swift */; };
|
|
||||||
7A14416E2C297F7C00E79018 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A14416D2C297F7C00E79018 /* Coordinator.swift */; };
|
7A14416E2C297F7C00E79018 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A14416D2C297F7C00E79018 /* Coordinator.swift */; };
|
||||||
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE492A2E820300626A6E /* UIStackView.swift */; };
|
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE492A2E820300626A6E /* UIStackView.swift */; };
|
||||||
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */; };
|
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */; };
|
||||||
@ -40,7 +37,6 @@
|
|||||||
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */; };
|
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */; };
|
||||||
7A1E78F62CE900330004B740 /* ReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F52CE900330004B740 /* ReportScreen.swift */; };
|
7A1E78F62CE900330004B740 /* ReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F52CE900330004B740 /* ReportScreen.swift */; };
|
||||||
7A1E78F82CE900440004B740 /* ReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F72CE900440004B740 /* ReportViewModel.swift */; };
|
7A1E78F82CE900440004B740 /* ReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F72CE900440004B740 /* ReportViewModel.swift */; };
|
||||||
7A1E78FA2CE9005C0004B740 /* ReportCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F92CE9005C0004B740 /* ReportCoordinator.swift */; };
|
|
||||||
7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78FE2CE91A740004B740 /* Vehicle.swift */; };
|
7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78FE2CE91A740004B740 /* Vehicle.swift */; };
|
||||||
7A2C96122C3B155B00AE46B5 /* NoteAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */; };
|
7A2C96122C3B155B00AE46B5 /* NoteAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */; };
|
||||||
7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */; };
|
7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */; };
|
||||||
@ -48,14 +44,12 @@
|
|||||||
7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; };
|
7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; };
|
||||||
7A386A402DABDC190051676A /* MapScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A3F2DABDC190051676A /* MapScreen.swift */; };
|
7A386A402DABDC190051676A /* MapScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A3F2DABDC190051676A /* MapScreen.swift */; };
|
||||||
7A386A442DABDC360051676A /* MapViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A432DABDC360051676A /* MapViewModel.swift */; };
|
7A386A442DABDC360051676A /* MapViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A432DABDC360051676A /* MapViewModel.swift */; };
|
||||||
7A386A462DABDC660051676A /* MapCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A452DABDC660051676A /* MapCoordinator.swift */; };
|
|
||||||
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A472DABE0D00051676A /* MapMarkerModel.swift */; };
|
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A386A472DABE0D00051676A /* MapMarkerModel.swift */; };
|
||||||
7A386A4B2DAC35F10051676A /* ClusterMap in Frameworks */ = {isa = PBXBuildFile; productRef = 7A386A4A2DAC35F10051676A /* ClusterMap */; };
|
7A386A4B2DAC35F10051676A /* ClusterMap in Frameworks */ = {isa = PBXBuildFile; productRef = 7A386A4A2DAC35F10051676A /* ClusterMap */; };
|
||||||
7A386A4D2DAC35F10051676A /* ClusterMapSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 7A386A4C2DAC35F10051676A /* ClusterMapSwiftUI */; };
|
7A386A4D2DAC35F10051676A /* ClusterMapSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 7A386A4C2DAC35F10051676A /* ClusterMapSwiftUI */; };
|
||||||
7A3F07AB24360DC800E59687 /* Dated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AA24360DC800E59687 /* Dated.swift */; };
|
7A3F07AB24360DC800E59687 /* Dated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AA24360DC800E59687 /* Dated.swift */; };
|
||||||
7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */; };
|
7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */; };
|
||||||
7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */; };
|
7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */; };
|
||||||
7A4322952CB2CD0F00085CF6 /* FiltersCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */; };
|
|
||||||
7A45FB382C27073700618694 /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A45FB372C27073700618694 /* StorageService.swift */; };
|
7A45FB382C27073700618694 /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A45FB372C27073700618694 /* StorageService.swift */; };
|
||||||
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4927D42CCE438600851C01 /* OptionalBinding.swift */; };
|
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4927D42CCE438600851C01 /* OptionalBinding.swift */; };
|
||||||
7A4955822D58CCF900912E66 /* HistoryFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4955812D58CCF900912E66 /* HistoryFilter.swift */; };
|
7A4955822D58CCF900912E66 /* HistoryFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4955812D58CCF900912E66 /* HistoryFilter.swift */; };
|
||||||
@ -105,11 +99,8 @@
|
|||||||
7A6F096026DBF588003A965D /* VehicleNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F095F26DBF588003A965D /* VehicleNote.swift */; };
|
7A6F096026DBF588003A965D /* VehicleNote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F095F26DBF588003A965D /* VehicleNote.swift */; };
|
||||||
7A7097C22C9EC139007CFDCA /* ServiceContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */; };
|
7A7097C22C9EC139007CFDCA /* ServiceContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */; };
|
||||||
7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7157FF2C43EA6900852088 /* OwnersScreen.swift */; };
|
7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7157FF2C43EA6900852088 /* OwnersScreen.swift */; };
|
||||||
7A7158042C43EAA200852088 /* OwnersCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158032C43EAA200852088 /* OwnersCoordinator.swift */; };
|
|
||||||
7A7158072C44085600852088 /* OsagoScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158062C44085600852088 /* OsagoScreen.swift */; };
|
7A7158072C44085600852088 /* OsagoScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158062C44085600852088 /* OsagoScreen.swift */; };
|
||||||
7A7158092C44087E00852088 /* OsagoCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158082C44087E00852088 /* OsagoCoordinator.swift */; };
|
|
||||||
7A71580C2C44453200852088 /* AdsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71580B2C44453200852088 /* AdsScreen.swift */; };
|
7A71580C2C44453200852088 /* AdsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71580B2C44453200852088 /* AdsScreen.swift */; };
|
||||||
7A71580E2C4445A200852088 /* AdsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71580D2C4445A200852088 /* AdsCoordinator.swift */; };
|
|
||||||
7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158112C444A6400852088 /* AdsViewModel.swift */; };
|
7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158112C444A6400852088 /* AdsViewModel.swift */; };
|
||||||
7A71EF572D0A26B200943129 /* EventModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71EF562D0A26B200943129 /* EventModel.swift */; };
|
7A71EF572D0A26B200943129 /* EventModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71EF562D0A26B200943129 /* EventModel.swift */; };
|
||||||
7A761C042677F18E0005F28F /* ApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11474323FF06CA00B424AF /* ApiService.swift */; };
|
7A761C042677F18E0005F28F /* ApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11474323FF06CA00B424AF /* ApiService.swift */; };
|
||||||
@ -149,6 +140,7 @@
|
|||||||
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */; };
|
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */; };
|
||||||
7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABB1F1267E9CC800D7AB32 /* SwiftDate */; };
|
7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABB1F1267E9CC800D7AB32 /* SwiftDate */; };
|
||||||
7AABBE3B2CF9F85600346588 /* Binding+Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABBE3A2CF9F85600346588 /* Binding+Map.swift */; };
|
7AABBE3B2CF9F85600346588 /* Binding+Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABBE3A2CF9F85600346588 /* Binding+Map.swift */; };
|
||||||
|
7AADD4452DB2D4D60027FD7B /* MapInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AADD4442DB2D4D60027FD7B /* MapInput.swift */; };
|
||||||
7AB0EF812C5CC0FE00291EE6 /* SwiftLocationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */; };
|
7AB0EF812C5CC0FE00291EE6 /* SwiftLocationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */; };
|
||||||
7AB490292D6B1217002F39C6 /* ACKeyboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB490282D6B1217002F39C6 /* ACKeyboardView.swift */; };
|
7AB490292D6B1217002F39C6 /* ACKeyboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB490282D6B1217002F39C6 /* ACKeyboardView.swift */; };
|
||||||
7AB4902B2D6B1446002F39C6 /* ACKeyboardButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB4902A2D6B1446002F39C6 /* ACKeyboardButton.swift */; };
|
7AB4902B2D6B1446002F39C6 /* ACKeyboardButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB4902A2D6B1446002F39C6 /* ACKeyboardButton.swift */; };
|
||||||
@ -165,12 +157,9 @@
|
|||||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
||||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; };
|
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; };
|
||||||
7AB9FE222D08C2A5005DE374 /* EventsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB9FE212D08C2A5005DE374 /* EventsScreen.swift */; };
|
7AB9FE222D08C2A5005DE374 /* EventsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB9FE212D08C2A5005DE374 /* EventsScreen.swift */; };
|
||||||
7AB9FE262D08C2D7005DE374 /* EventsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB9FE252D08C2D7005DE374 /* EventsCoordinator.swift */; };
|
|
||||||
7AB9FE282D08C2F4005DE374 /* EventsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB9FE272D08C2F4005DE374 /* EventsViewModel.swift */; };
|
7AB9FE282D08C2F4005DE374 /* EventsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB9FE272D08C2F4005DE374 /* EventsViewModel.swift */; };
|
||||||
7AB9FE2A2D08CF35005DE374 /* EventsScreenMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB9FE292D08CF35005DE374 /* EventsScreenMode.swift */; };
|
7AB9FE2A2D08CF35005DE374 /* EventsScreenMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB9FE292D08CF35005DE374 /* EventsScreenMode.swift */; };
|
||||||
7ABD1B472D044A3200B43213 /* GalleryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD1B462D044A3200B43213 /* GalleryScreen.swift */; };
|
7ABD1B472D044A3200B43213 /* GalleryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD1B462D044A3200B43213 /* GalleryScreen.swift */; };
|
||||||
7ABD1B492D044A4700B43213 /* GalleryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD1B482D044A4700B43213 /* GalleryViewModel.swift */; };
|
|
||||||
7ABD1B4B2D044A7D00B43213 /* GalleryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD1B4A2D044A7D00B43213 /* GalleryCoordinator.swift */; };
|
|
||||||
7ABDA8032D8704F70083C715 /* VehicleRecordService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABDA8022D8704F70083C715 /* VehicleRecordService.swift */; };
|
7ABDA8032D8704F70083C715 /* VehicleRecordService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABDA8022D8704F70083C715 /* VehicleRecordService.swift */; };
|
||||||
7ABDA8052D8705210083C715 /* VehicleRecordServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABDA8042D8705210083C715 /* VehicleRecordServiceProtocol.swift */; };
|
7ABDA8052D8705210083C715 /* VehicleRecordServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABDA8042D8705210083C715 /* VehicleRecordServiceProtocol.swift */; };
|
||||||
7ABDA8092D8710F80083C715 /* AutoCancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABDA8082D8710F80083C715 /* AutoCancellable.swift */; };
|
7ABDA8092D8710F80083C715 /* AutoCancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABDA8082D8710F80083C715 /* AutoCancellable.swift */; };
|
||||||
@ -290,11 +279,9 @@
|
|||||||
7A06E0B42C707E2B005731AC /* SettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsService.swift; sourceTree = "<group>"; };
|
7A06E0B42C707E2B005731AC /* SettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsService.swift; sourceTree = "<group>"; };
|
||||||
7A10226B2C551EC500B84627 /* LocationEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditScreen.swift; sourceTree = "<group>"; };
|
7A10226B2C551EC500B84627 /* LocationEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditScreen.swift; sourceTree = "<group>"; };
|
||||||
7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditViewModel.swift; sourceTree = "<group>"; };
|
7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditViewModel.swift; sourceTree = "<group>"; };
|
||||||
7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A1022712C554A1300B84627 /* CustomHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomHostingController.swift; sourceTree = "<group>"; };
|
7A1022712C554A1300B84627 /* CustomHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomHostingController.swift; sourceTree = "<group>"; };
|
||||||
7A1022762C557EC400B84627 /* LocationPickerScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerScreen.swift; sourceTree = "<group>"; };
|
7A1022762C557EC400B84627 /* LocationPickerScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerScreen.swift; sourceTree = "<group>"; };
|
||||||
7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerViewModel.swift; sourceTree = "<group>"; };
|
7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerViewModel.swift; sourceTree = "<group>"; };
|
||||||
7A10227A2C557EE900B84627 /* LocationPickerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellProgressView.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; };
|
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>"; };
|
7A11470023FDE7E500B424AF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
@ -314,7 +301,6 @@
|
|||||||
7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCoordinator.swift; sourceTree = "<group>"; };
|
7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCoordinator.swift; sourceTree = "<group>"; };
|
||||||
7A1441652C297EDE00E79018 /* NotesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesScreen.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>"; };
|
7A1441672C297EFD00E79018 /* NotesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesViewModel.swift; sourceTree = "<group>"; };
|
||||||
7A14416B2C297F2100E79018 /* NotesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A14416D2C297F7C00E79018 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.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>"; };
|
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>"; };
|
7A17CE492A2E820300626A6E /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = "<group>"; };
|
||||||
@ -323,7 +309,6 @@
|
|||||||
7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockBarButtonItem.swift; sourceTree = "<group>"; };
|
7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockBarButtonItem.swift; sourceTree = "<group>"; };
|
||||||
7A1E78F52CE900330004B740 /* ReportScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportScreen.swift; sourceTree = "<group>"; };
|
7A1E78F52CE900330004B740 /* ReportScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportScreen.swift; sourceTree = "<group>"; };
|
||||||
7A1E78F72CE900440004B740 /* ReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = "<group>"; };
|
7A1E78F72CE900440004B740 /* ReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = "<group>"; };
|
||||||
7A1E78F92CE9005C0004B740 /* ReportCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A1E78FE2CE91A740004B740 /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; };
|
7A1E78FE2CE91A740004B740 /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; };
|
||||||
7A27ADF824A09CAD0035F39E /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = "<group>"; };
|
7A27ADF824A09CAD0035F39E /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = "<group>"; };
|
||||||
7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteAlertModifier.swift; sourceTree = "<group>"; };
|
7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteAlertModifier.swift; sourceTree = "<group>"; };
|
||||||
@ -334,12 +319,10 @@
|
|||||||
7A3399AA299063370087DF98 /* SearchControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchControllerExt.swift; sourceTree = "<group>"; };
|
7A3399AA299063370087DF98 /* SearchControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchControllerExt.swift; sourceTree = "<group>"; };
|
||||||
7A386A3F2DABDC190051676A /* MapScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapScreen.swift; sourceTree = "<group>"; };
|
7A386A3F2DABDC190051676A /* MapScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapScreen.swift; sourceTree = "<group>"; };
|
||||||
7A386A432DABDC360051676A /* MapViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewModel.swift; sourceTree = "<group>"; };
|
7A386A432DABDC360051676A /* MapViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewModel.swift; sourceTree = "<group>"; };
|
||||||
7A386A452DABDC660051676A /* MapCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A386A472DABE0D00051676A /* MapMarkerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapMarkerModel.swift; sourceTree = "<group>"; };
|
7A386A472DABE0D00051676A /* MapMarkerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapMarkerModel.swift; sourceTree = "<group>"; };
|
||||||
7A3F07AA24360DC800E59687 /* Dated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dated.swift; sourceTree = "<group>"; };
|
7A3F07AA24360DC800E59687 /* Dated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dated.swift; sourceTree = "<group>"; };
|
||||||
7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersScreen.swift; sourceTree = "<group>"; };
|
7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersScreen.swift; sourceTree = "<group>"; };
|
||||||
7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = "<group>"; };
|
7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = "<group>"; };
|
||||||
7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A43F9F7246C8A6200BA5B49 /* JWT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWT.swift; sourceTree = "<group>"; };
|
7A43F9F7246C8A6200BA5B49 /* JWT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWT.swift; sourceTree = "<group>"; };
|
||||||
7A45FB372C27073700618694 /* StorageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = "<group>"; };
|
7A45FB372C27073700618694 /* StorageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = "<group>"; };
|
||||||
7A4927D42CCE438600851C01 /* OptionalBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalBinding.swift; sourceTree = "<group>"; };
|
7A4927D42CCE438600851C01 /* OptionalBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalBinding.swift; sourceTree = "<group>"; };
|
||||||
@ -399,11 +382,8 @@
|
|||||||
7A6F095F26DBF588003A965D /* VehicleNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleNote.swift; sourceTree = "<group>"; };
|
7A6F095F26DBF588003A965D /* VehicleNote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleNote.swift; sourceTree = "<group>"; };
|
||||||
7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceContainer.swift; sourceTree = "<group>"; };
|
7A7097C12C9EC139007CFDCA /* ServiceContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceContainer.swift; sourceTree = "<group>"; };
|
||||||
7A7157FF2C43EA6900852088 /* OwnersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OwnersScreen.swift; sourceTree = "<group>"; };
|
7A7157FF2C43EA6900852088 /* OwnersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OwnersScreen.swift; sourceTree = "<group>"; };
|
||||||
7A7158032C43EAA200852088 /* OwnersCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OwnersCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A7158062C44085600852088 /* OsagoScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OsagoScreen.swift; sourceTree = "<group>"; };
|
7A7158062C44085600852088 /* OsagoScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OsagoScreen.swift; sourceTree = "<group>"; };
|
||||||
7A7158082C44087E00852088 /* OsagoCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OsagoCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A71580B2C44453200852088 /* AdsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsScreen.swift; sourceTree = "<group>"; };
|
7A71580B2C44453200852088 /* AdsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsScreen.swift; sourceTree = "<group>"; };
|
||||||
7A71580D2C4445A200852088 /* AdsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7A7158112C444A6400852088 /* AdsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsViewModel.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>"; };
|
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>"; };
|
7A761C0A267E8FF90005F28F /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
|
||||||
@ -437,6 +417,7 @@
|
|||||||
7AAAFADB2C4D1E130050410D /* ACImageSliderView+Modifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ACImageSliderView+Modifier.swift"; sourceTree = "<group>"; };
|
7AAAFADB2C4D1E130050410D /* ACImageSliderView+Modifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ACImageSliderView+Modifier.swift"; sourceTree = "<group>"; };
|
||||||
7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACImageSliderModel.swift; sourceTree = "<group>"; };
|
7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACImageSliderModel.swift; sourceTree = "<group>"; };
|
||||||
7AABBE3A2CF9F85600346588 /* Binding+Map.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Map.swift"; sourceTree = "<group>"; };
|
7AABBE3A2CF9F85600346588 /* Binding+Map.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Map.swift"; sourceTree = "<group>"; };
|
||||||
|
7AADD4442DB2D4D60027FD7B /* MapInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapInput.swift; sourceTree = "<group>"; };
|
||||||
7AAE6AD224CDDF950023860B /* VehicleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleEvent.swift; sourceTree = "<group>"; };
|
7AAE6AD224CDDF950023860B /* VehicleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleEvent.swift; sourceTree = "<group>"; };
|
||||||
7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLocationProtocol.swift; sourceTree = "<group>"; };
|
7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLocationProtocol.swift; sourceTree = "<group>"; };
|
||||||
7AB490282D6B1217002F39C6 /* ACKeyboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACKeyboardView.swift; sourceTree = "<group>"; };
|
7AB490282D6B1217002F39C6 /* ACKeyboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACKeyboardView.swift; sourceTree = "<group>"; };
|
||||||
@ -455,12 +436,9 @@
|
|||||||
7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
|
7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
|
||||||
7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = "<group>"; };
|
7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = "<group>"; };
|
||||||
7AB9FE212D08C2A5005DE374 /* EventsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsScreen.swift; sourceTree = "<group>"; };
|
7AB9FE212D08C2A5005DE374 /* EventsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsScreen.swift; sourceTree = "<group>"; };
|
||||||
7AB9FE252D08C2D7005DE374 /* EventsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7AB9FE272D08C2F4005DE374 /* EventsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsViewModel.swift; sourceTree = "<group>"; };
|
7AB9FE272D08C2F4005DE374 /* EventsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsViewModel.swift; sourceTree = "<group>"; };
|
||||||
7AB9FE292D08CF35005DE374 /* EventsScreenMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsScreenMode.swift; sourceTree = "<group>"; };
|
7AB9FE292D08CF35005DE374 /* EventsScreenMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsScreenMode.swift; sourceTree = "<group>"; };
|
||||||
7ABD1B462D044A3200B43213 /* GalleryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryScreen.swift; sourceTree = "<group>"; };
|
7ABD1B462D044A3200B43213 /* GalleryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryScreen.swift; sourceTree = "<group>"; };
|
||||||
7ABD1B482D044A4700B43213 /* GalleryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
7ABD1B4A2D044A7D00B43213 /* GalleryCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
7ABDA8022D8704F70083C715 /* VehicleRecordService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleRecordService.swift; sourceTree = "<group>"; };
|
7ABDA8022D8704F70083C715 /* VehicleRecordService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleRecordService.swift; sourceTree = "<group>"; };
|
||||||
7ABDA8042D8705210083C715 /* VehicleRecordServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleRecordServiceProtocol.swift; sourceTree = "<group>"; };
|
7ABDA8042D8705210083C715 /* VehicleRecordServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleRecordServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
7ABDA8082D8710F80083C715 /* AutoCancellable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCancellable.swift; sourceTree = "<group>"; };
|
7ABDA8082D8710F80083C715 /* AutoCancellable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCancellable.swift; sourceTree = "<group>"; };
|
||||||
@ -593,7 +571,6 @@
|
|||||||
children = (
|
children = (
|
||||||
7A10226B2C551EC500B84627 /* LocationEditScreen.swift */,
|
7A10226B2C551EC500B84627 /* LocationEditScreen.swift */,
|
||||||
7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */,
|
7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */,
|
||||||
7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */,
|
|
||||||
);
|
);
|
||||||
path = LocationEditScreen;
|
path = LocationEditScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -603,7 +580,6 @@
|
|||||||
children = (
|
children = (
|
||||||
7A1022762C557EC400B84627 /* LocationPickerScreen.swift */,
|
7A1022762C557EC400B84627 /* LocationPickerScreen.swift */,
|
||||||
7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */,
|
7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */,
|
||||||
7A10227A2C557EE900B84627 /* LocationPickerCoordinator.swift */,
|
|
||||||
);
|
);
|
||||||
path = LocationPickerScreen;
|
path = LocationPickerScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -755,7 +731,6 @@
|
|||||||
children = (
|
children = (
|
||||||
7A1441652C297EDE00E79018 /* NotesScreen.swift */,
|
7A1441652C297EDE00E79018 /* NotesScreen.swift */,
|
||||||
7A1441672C297EFD00E79018 /* NotesViewModel.swift */,
|
7A1441672C297EFD00E79018 /* NotesViewModel.swift */,
|
||||||
7A14416B2C297F2100E79018 /* NotesCoordinator.swift */,
|
|
||||||
7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */,
|
7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */,
|
||||||
);
|
);
|
||||||
path = NotesScreen;
|
path = NotesScreen;
|
||||||
@ -766,7 +741,6 @@
|
|||||||
children = (
|
children = (
|
||||||
7A1E78F52CE900330004B740 /* ReportScreen.swift */,
|
7A1E78F52CE900330004B740 /* ReportScreen.swift */,
|
||||||
7A1E78F72CE900440004B740 /* ReportViewModel.swift */,
|
7A1E78F72CE900440004B740 /* ReportViewModel.swift */,
|
||||||
7A1E78F92CE9005C0004B740 /* ReportCoordinator.swift */,
|
|
||||||
);
|
);
|
||||||
path = ReportScreen;
|
path = ReportScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -784,8 +758,8 @@
|
|||||||
children = (
|
children = (
|
||||||
7A386A3F2DABDC190051676A /* MapScreen.swift */,
|
7A386A3F2DABDC190051676A /* MapScreen.swift */,
|
||||||
7A386A432DABDC360051676A /* MapViewModel.swift */,
|
7A386A432DABDC360051676A /* MapViewModel.swift */,
|
||||||
7A386A452DABDC660051676A /* MapCoordinator.swift */,
|
|
||||||
7A386A472DABE0D00051676A /* MapMarkerModel.swift */,
|
7A386A472DABE0D00051676A /* MapMarkerModel.swift */,
|
||||||
|
7AADD4442DB2D4D60027FD7B /* MapInput.swift */,
|
||||||
);
|
);
|
||||||
path = MapScreen;
|
path = MapScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -811,7 +785,6 @@
|
|||||||
children = (
|
children = (
|
||||||
7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */,
|
7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */,
|
||||||
7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */,
|
7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */,
|
||||||
7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */,
|
|
||||||
);
|
);
|
||||||
path = FiltersScreen;
|
path = FiltersScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -954,7 +927,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7A7157FF2C43EA6900852088 /* OwnersScreen.swift */,
|
7A7157FF2C43EA6900852088 /* OwnersScreen.swift */,
|
||||||
7A7158032C43EAA200852088 /* OwnersCoordinator.swift */,
|
|
||||||
);
|
);
|
||||||
path = OwnersScreen;
|
path = OwnersScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -963,7 +935,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7A7158062C44085600852088 /* OsagoScreen.swift */,
|
7A7158062C44085600852088 /* OsagoScreen.swift */,
|
||||||
7A7158082C44087E00852088 /* OsagoCoordinator.swift */,
|
|
||||||
);
|
);
|
||||||
path = OsagoScreen;
|
path = OsagoScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -972,7 +943,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7A71580B2C44453200852088 /* AdsScreen.swift */,
|
7A71580B2C44453200852088 /* AdsScreen.swift */,
|
||||||
7A71580D2C4445A200852088 /* AdsCoordinator.swift */,
|
|
||||||
7A7158112C444A6400852088 /* AdsViewModel.swift */,
|
7A7158112C444A6400852088 /* AdsViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = AdsScreen;
|
path = AdsScreen;
|
||||||
@ -1075,7 +1045,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7AB9FE212D08C2A5005DE374 /* EventsScreen.swift */,
|
7AB9FE212D08C2A5005DE374 /* EventsScreen.swift */,
|
||||||
7AB9FE252D08C2D7005DE374 /* EventsCoordinator.swift */,
|
|
||||||
7AB9FE272D08C2F4005DE374 /* EventsViewModel.swift */,
|
7AB9FE272D08C2F4005DE374 /* EventsViewModel.swift */,
|
||||||
7AB9FE292D08CF35005DE374 /* EventsScreenMode.swift */,
|
7AB9FE292D08CF35005DE374 /* EventsScreenMode.swift */,
|
||||||
7A71EF562D0A26B200943129 /* EventModel.swift */,
|
7A71EF562D0A26B200943129 /* EventModel.swift */,
|
||||||
@ -1087,8 +1056,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7ABD1B462D044A3200B43213 /* GalleryScreen.swift */,
|
7ABD1B462D044A3200B43213 /* GalleryScreen.swift */,
|
||||||
7ABD1B482D044A4700B43213 /* GalleryViewModel.swift */,
|
|
||||||
7ABD1B4A2D044A7D00B43213 /* GalleryCoordinator.swift */,
|
|
||||||
);
|
);
|
||||||
path = GalleryScreen;
|
path = GalleryScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1451,7 +1418,6 @@
|
|||||||
7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */,
|
7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */,
|
||||||
7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */,
|
7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */,
|
||||||
7A5D7E0C2C71EB25002C17E7 /* ToggleRowView.swift in Sources */,
|
7A5D7E0C2C71EB25002C17E7 /* ToggleRowView.swift in Sources */,
|
||||||
7A7158092C44087E00852088 /* OsagoCoordinator.swift in Sources */,
|
|
||||||
7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */,
|
7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */,
|
||||||
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
|
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
|
||||||
7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */,
|
7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */,
|
||||||
@ -1472,11 +1438,9 @@
|
|||||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */,
|
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */,
|
||||||
7AF860702CBAA24500954D2F /* NavigationLink.swift in Sources */,
|
7AF860702CBAA24500954D2F /* NavigationLink.swift in Sources */,
|
||||||
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */,
|
7A386A482DABE0D00051676A /* MapMarkerModel.swift in Sources */,
|
||||||
7AB9FE262D08C2D7005DE374 /* EventsCoordinator.swift in Sources */,
|
|
||||||
7A386A402DABDC190051676A /* MapScreen.swift in Sources */,
|
7A386A402DABDC190051676A /* MapScreen.swift in Sources */,
|
||||||
7AB9FE282D08C2F4005DE374 /* EventsViewModel.swift in Sources */,
|
7AB9FE282D08C2F4005DE374 /* EventsViewModel.swift in Sources */,
|
||||||
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */,
|
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */,
|
||||||
7A386A462DABDC660051676A /* MapCoordinator.swift in Sources */,
|
|
||||||
7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */,
|
7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */,
|
||||||
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */,
|
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */,
|
||||||
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
|
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
|
||||||
@ -1489,8 +1453,6 @@
|
|||||||
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */,
|
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */,
|
||||||
7AC3554E29696C4500889457 /* DummyNewController.swift in Sources */,
|
7AC3554E29696C4500889457 /* DummyNewController.swift in Sources */,
|
||||||
7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */,
|
7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */,
|
||||||
7A1E78FA2CE9005C0004B740 /* ReportCoordinator.swift in Sources */,
|
|
||||||
7A71580E2C4445A200852088 /* AdsCoordinator.swift in Sources */,
|
|
||||||
7AF231952DA1C29300AE5EB3 /* AuthViewModel.swift in Sources */,
|
7AF231952DA1C29300AE5EB3 /* AuthViewModel.swift in Sources */,
|
||||||
7AB4E4662D58A16C0006D052 /* GenericError.swift in Sources */,
|
7AB4E4662D58A16C0006D052 /* GenericError.swift in Sources */,
|
||||||
7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */,
|
7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */,
|
||||||
@ -1499,7 +1461,6 @@
|
|||||||
7A1E78F62CE900330004B740 /* ReportScreen.swift in Sources */,
|
7A1E78F62CE900330004B740 /* ReportScreen.swift in Sources */,
|
||||||
7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */,
|
7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */,
|
||||||
7A7158072C44085600852088 /* OsagoScreen.swift in Sources */,
|
7A7158072C44085600852088 /* OsagoScreen.swift in Sources */,
|
||||||
7ABD1B492D044A4700B43213 /* GalleryViewModel.swift in Sources */,
|
|
||||||
7A386A442DABDC360051676A /* MapViewModel.swift in Sources */,
|
7A386A442DABDC360051676A /* MapViewModel.swift in Sources */,
|
||||||
7ADFC9592DAD1C3D001A43E3 /* GoogleAuthViewModel.swift in Sources */,
|
7ADFC9592DAD1C3D001A43E3 /* GoogleAuthViewModel.swift in Sources */,
|
||||||
7AAAFAD32C4D0FD00050410D /* ACImageSliderView.swift in Sources */,
|
7AAAFAD32C4D0FD00050410D /* ACImageSliderView.swift in Sources */,
|
||||||
@ -1513,7 +1474,6 @@
|
|||||||
7A7DADAC2D99738300F52F6C /* AudioRecordView.swift in Sources */,
|
7A7DADAC2D99738300F52F6C /* AudioRecordView.swift in Sources */,
|
||||||
7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */,
|
7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */,
|
||||||
7AB9FE2A2D08CF35005DE374 /* EventsScreenMode.swift in Sources */,
|
7AB9FE2A2D08CF35005DE374 /* EventsScreenMode.swift in Sources */,
|
||||||
7A10227B2C557EE900B84627 /* LocationPickerCoordinator.swift in Sources */,
|
|
||||||
7AB490292D6B1217002F39C6 /* ACKeyboardView.swift in Sources */,
|
7AB490292D6B1217002F39C6 /* ACKeyboardView.swift in Sources */,
|
||||||
7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */,
|
7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */,
|
||||||
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */,
|
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */,
|
||||||
@ -1526,7 +1486,6 @@
|
|||||||
7ADFC95B2DAD1F45001A43E3 /* WebView.swift in Sources */,
|
7ADFC95B2DAD1F45001A43E3 /* WebView.swift in Sources */,
|
||||||
7AB4902B2D6B1446002F39C6 /* ACKeyboardButton.swift in Sources */,
|
7AB4902B2D6B1446002F39C6 /* ACKeyboardButton.swift in Sources */,
|
||||||
7AFBE8CE2C308B53003C491D /* ACMessageView.swift in Sources */,
|
7AFBE8CE2C308B53003C491D /* ACMessageView.swift in Sources */,
|
||||||
7A14416C2C297F2100E79018 /* NotesCoordinator.swift in Sources */,
|
|
||||||
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */,
|
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */,
|
||||||
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */,
|
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */,
|
||||||
7AC3555229696E3F00889457 /* UIView+layout.swift in Sources */,
|
7AC3555229696E3F00889457 /* UIView+layout.swift in Sources */,
|
||||||
@ -1538,7 +1497,6 @@
|
|||||||
7AFBE8C42C302561003C491D /* ACHudContainer.swift in Sources */,
|
7AFBE8C42C302561003C491D /* ACHudContainer.swift in Sources */,
|
||||||
7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */,
|
7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */,
|
||||||
7A06E0AC2C7065AC005731AC /* SettingsScreen.swift in Sources */,
|
7A06E0AC2C7065AC005731AC /* SettingsScreen.swift in Sources */,
|
||||||
7A4322952CB2CD0F00085CF6 /* FiltersCoordinator.swift in Sources */,
|
|
||||||
7AF231992DA27C1B00AE5EB3 /* ACButtonView.swift in Sources */,
|
7AF231992DA27C1B00AE5EB3 /* ACButtonView.swift in Sources */,
|
||||||
7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */,
|
7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */,
|
||||||
7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */,
|
7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */,
|
||||||
@ -1546,6 +1504,7 @@
|
|||||||
7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */,
|
7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */,
|
||||||
7ABD1B472D044A3200B43213 /* GalleryScreen.swift in Sources */,
|
7ABD1B472D044A3200B43213 /* GalleryScreen.swift in Sources */,
|
||||||
7A71580C2C44453200852088 /* AdsScreen.swift in Sources */,
|
7A71580C2C44453200852088 /* AdsScreen.swift in Sources */,
|
||||||
|
7AADD4452DB2D4D60027FD7B /* MapInput.swift in Sources */,
|
||||||
7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */,
|
7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */,
|
||||||
7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */,
|
7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */,
|
||||||
7AFBE8CC2C3085C6003C491D /* ACProgressView.swift in Sources */,
|
7AFBE8CC2C3085C6003C491D /* ACProgressView.swift in Sources */,
|
||||||
@ -1553,11 +1512,8 @@
|
|||||||
7AB9FE222D08C2A5005DE374 /* EventsScreen.swift in Sources */,
|
7AB9FE222D08C2A5005DE374 /* EventsScreen.swift in Sources */,
|
||||||
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */,
|
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */,
|
||||||
7A5911F02D63266B00EC51BA /* SearchViewModel.swift in Sources */,
|
7A5911F02D63266B00EC51BA /* SearchViewModel.swift in Sources */,
|
||||||
7ABD1B4B2D044A7D00B43213 /* GalleryCoordinator.swift in Sources */,
|
|
||||||
7A589E0F2D6B6E8E00EF3FBE /* NumberEditView.swift in Sources */,
|
7A589E0F2D6B6E8E00EF3FBE /* NumberEditView.swift in Sources */,
|
||||||
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */,
|
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */,
|
||||||
7A1022702C551EFD00B84627 /* LocationEditCoordinator.swift in Sources */,
|
|
||||||
7A7158042C43EAA200852088 /* OwnersCoordinator.swift in Sources */,
|
|
||||||
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
|
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
|
||||||
7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */,
|
7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */,
|
||||||
7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */,
|
7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import PKHUD
|
|||||||
import AutoCatCore
|
import AutoCatCore
|
||||||
import SwiftLocation
|
import SwiftLocation
|
||||||
import CoreLocation
|
import CoreLocation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
@ -164,9 +165,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
HUD.show(.progress)
|
HUD.show(.progress)
|
||||||
let vehicle = try await apiService.getReport(for: number)
|
let vehicle = try await apiService.getReport(for: number)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
let coordinator = ReportCoordinator(controller: rootController, vehicle: vehicle, isPersistent: false)
|
let screen = ReportScreen(vehicle: vehicle, isPersistent: false, onUpdate: { _ in })
|
||||||
_ = try? await coordinator.start()
|
let controller = UIHostingController(rootView: screen)
|
||||||
|
let navController = UINavigationController(rootViewController: controller)
|
||||||
|
rootController.present(navController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
HUD.hide()
|
HUD.hide()
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
//
|
|
||||||
// AdsCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 14.07.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
class AdsCoordinator: Coordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController?
|
|
||||||
let ads: [VehicleAdDto]
|
|
||||||
|
|
||||||
init(navController: UINavigationController, ads: [VehicleAdDto]) {
|
|
||||||
|
|
||||||
self.viewController = navController
|
|
||||||
self.ads = ads
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async throws {
|
|
||||||
|
|
||||||
let viewModel = await AdsViewModel(ads: ads)
|
|
||||||
let controller = await UIHostingController(rootView: AdsScreen(viewModel: viewModel))
|
|
||||||
await viewController?.pushViewController(controller, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import AutoCatCore
|
||||||
|
|
||||||
struct AdsScreen: View {
|
struct AdsScreen: View {
|
||||||
|
|
||||||
@ -14,6 +15,10 @@ struct AdsScreen: View {
|
|||||||
|
|
||||||
@State var galleryModel: ACImageSliderModel?
|
@State var galleryModel: ACImageSliderModel?
|
||||||
|
|
||||||
|
init(ads: [VehicleAdDto]) {
|
||||||
|
self.viewModel = AdsViewModel(ads: ads)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List(viewModel.ads, id: \.self) { ad in
|
List(viewModel.ads, id: \.self) { ad in
|
||||||
Section(viewModel.dateStringFrom(ad.date)) {
|
Section(viewModel.dateStringFrom(ad.date)) {
|
||||||
@ -68,7 +73,3 @@ struct AdsScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
|
||||||
AdsScreen(viewModel: AdsViewModel(ads: []))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
//
|
|
||||||
// EventsCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 10.12.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class EventsCoordinator {
|
|
||||||
|
|
||||||
let navController: UINavigationController
|
|
||||||
|
|
||||||
init(navController: UINavigationController) {
|
|
||||||
|
|
||||||
self.navController = navController
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(vehicle: VehicleDto) async -> VehicleDto {
|
|
||||||
|
|
||||||
let resolver = ServiceContainer.shared
|
|
||||||
let viewModel = EventsViewModel(
|
|
||||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
|
||||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
|
||||||
settingsService: resolver.resolve(SettingsServiceProtocol.self),
|
|
||||||
vehicle: vehicle
|
|
||||||
)
|
|
||||||
viewModel.coordinator = self
|
|
||||||
let controller = CustomHostingController(rootView: EventsScreen(viewModel: viewModel))
|
|
||||||
navController.pushViewController(controller, animated: true)
|
|
||||||
await controller.waitForDisappear()
|
|
||||||
return viewModel.vehicle
|
|
||||||
}
|
|
||||||
|
|
||||||
func editEvent(event: VehicleEventDto) async -> VehicleEventDto? {
|
|
||||||
|
|
||||||
let coordinator = LocationEditCoordinator(navController: navController)
|
|
||||||
return await coordinator.start(event: event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,13 +12,27 @@ import MapKit
|
|||||||
|
|
||||||
struct EventsScreen: View {
|
struct EventsScreen: View {
|
||||||
|
|
||||||
|
enum Screen: Hashable {
|
||||||
|
|
||||||
|
case newEvent(VehicleEventDto)
|
||||||
|
case editEvent(VehicleEventDto)
|
||||||
|
}
|
||||||
|
|
||||||
@State var viewModel: EventsViewModel
|
@State var viewModel: EventsViewModel
|
||||||
@State var selectedEvent: EventModel?
|
@State var selectedEvent: EventModel?
|
||||||
@State var deleteConfirmationPresented: Bool = false
|
@State var deleteConfirmationPresented: Bool = false
|
||||||
@State var eventToDelete: EventModel?
|
@State var eventToDelete: EventModel?
|
||||||
|
|
||||||
init(viewModel: EventsViewModel) {
|
init(vehicle: VehicleDto, onUpdate: @escaping (VehicleDto) -> Void) {
|
||||||
self.viewModel = viewModel
|
|
||||||
|
let resolver = ServiceContainer.shared
|
||||||
|
self.viewModel = EventsViewModel(
|
||||||
|
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||||
|
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||||
|
settingsService: resolver.resolve(SettingsServiceProtocol.self),
|
||||||
|
vehicle: vehicle,
|
||||||
|
onUpdate: onUpdate
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -39,8 +53,14 @@ struct EventsScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button("", systemImage: "plus") {
|
NavigationLink(
|
||||||
Task { await viewModel.addNewEvent() }
|
value: Screen.newEvent(VehicleEventDto(
|
||||||
|
lat: 0,
|
||||||
|
lon: 0,
|
||||||
|
addedBy: viewModel.settingsService.user.email
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
Image(systemName: "plus")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
@ -72,7 +92,14 @@ struct EventsScreen: View {
|
|||||||
} message: {
|
} message: {
|
||||||
Text("Are you sure you want to delete this event?")
|
Text("Are you sure you want to delete this event?")
|
||||||
}
|
}
|
||||||
|
.navigationDestination(for: Screen.self) { screen in
|
||||||
|
switch screen {
|
||||||
|
case .newEvent(let event):
|
||||||
|
LocationEditScreen( event: event, onUpdate: viewModel.onNewEvent)
|
||||||
|
case .editEvent(let event):
|
||||||
|
LocationEditScreen(event: event, onUpdate: viewModel.onEventUpdated)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var map: some View {
|
var map: some View {
|
||||||
@ -125,10 +152,10 @@ struct EventsScreen: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func makeActions(for event: EventModel, useLabels: Bool = false) -> some View {
|
func makeActions(for event: EventModel, useLabels: Bool = false) -> some View {
|
||||||
|
|
||||||
Button() {
|
if let dto = viewModel.vehicle.events.first(where: { $0.id == event.id }) {
|
||||||
Task { await viewModel.editEvent(event) }
|
NavigationLink(value: Screen.editEvent(dto)) {
|
||||||
} label: {
|
Label(useLabels ? "Edit" : "", systemImage: "pencil")
|
||||||
Label(useLabels ? "Edit" : "", systemImage: "pencil")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button() {
|
Button() {
|
||||||
@ -157,14 +184,3 @@ struct EventsScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
EventsScreen(viewModel: .init(apiService: MockApiServiceProtocol(),
|
|
||||||
storageService: MockStorageServiceProtocol(),
|
|
||||||
settingsService: MockSettingsServiceProtocol(),
|
|
||||||
vehicle: .preview))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -30,8 +30,6 @@ class EventsViewModel: ACHudContainer {
|
|||||||
let storageService: StorageServiceProtocol
|
let storageService: StorageServiceProtocol
|
||||||
let settingsService: SettingsServiceProtocol
|
let settingsService: SettingsServiceProtocol
|
||||||
|
|
||||||
weak var coordinator: EventsCoordinator?
|
|
||||||
|
|
||||||
var hud: ACHud?
|
var hud: ACHud?
|
||||||
|
|
||||||
var vehicle: VehicleDto
|
var vehicle: VehicleDto
|
||||||
@ -41,6 +39,8 @@ class EventsViewModel: ACHudContainer {
|
|||||||
var showPasteAlert: Bool = false
|
var showPasteAlert: Bool = false
|
||||||
var pastedEvent: VehicleEventDto?
|
var pastedEvent: VehicleEventDto?
|
||||||
|
|
||||||
|
var onUpdate: (VehicleDto) -> Void
|
||||||
|
|
||||||
var isPasteAvailable: Bool {
|
var isPasteAvailable: Bool {
|
||||||
UIPasteboard.general.data(forPasteboardType: UTType.vehicleEvent.identifier) != nil
|
UIPasteboard.general.data(forPasteboardType: UTType.vehicleEvent.identifier) != nil
|
||||||
}
|
}
|
||||||
@ -53,12 +53,14 @@ class EventsViewModel: ACHudContainer {
|
|||||||
init(apiService: ApiServiceProtocol,
|
init(apiService: ApiServiceProtocol,
|
||||||
storageService: StorageServiceProtocol,
|
storageService: StorageServiceProtocol,
|
||||||
settingsService: SettingsServiceProtocol,
|
settingsService: SettingsServiceProtocol,
|
||||||
vehicle: VehicleDto) {
|
vehicle: VehicleDto,
|
||||||
|
onUpdate: @escaping (VehicleDto) -> Void) {
|
||||||
|
|
||||||
self.apiService = apiService
|
self.apiService = apiService
|
||||||
self.storageService = storageService
|
self.storageService = storageService
|
||||||
self.settingsService = settingsService
|
self.settingsService = settingsService
|
||||||
self.vehicle = vehicle
|
self.vehicle = vehicle
|
||||||
|
self.onUpdate = onUpdate
|
||||||
|
|
||||||
updateEvents()
|
updateEvents()
|
||||||
}
|
}
|
||||||
@ -82,6 +84,28 @@ class EventsViewModel: ACHudContainer {
|
|||||||
user: event.addedBy
|
user: event.addedBy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onUpdate(vehicle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func onEventUpdated(_ event: VehicleEventDto) {
|
||||||
|
|
||||||
|
Task {
|
||||||
|
await eventOperation {
|
||||||
|
try await self.storageService.edit(event: event, for: self.vehicle.getNumber())
|
||||||
|
} apiOperation: {
|
||||||
|
try await self.apiService.edit(event: event)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onNewEvent(_ event: VehicleEventDto) {
|
||||||
|
|
||||||
|
Task {
|
||||||
|
await addEvent(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addEvent(_ event: VehicleEventDto) async {
|
func addEvent(_ event: VehicleEventDto) async {
|
||||||
@ -95,14 +119,6 @@ class EventsViewModel: ACHudContainer {
|
|||||||
updateEvents()
|
updateEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
func addNewEvent() async {
|
|
||||||
|
|
||||||
let emptyEvent = VehicleEventDto(lat: 0, lon: 0, addedBy: settingsService.user.email)
|
|
||||||
if let newEvent = await coordinator?.editEvent(event: emptyEvent) {
|
|
||||||
await addEvent(newEvent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteEvent(_ event: EventModel) async {
|
func deleteEvent(_ event: EventModel) async {
|
||||||
|
|
||||||
await eventOperation {
|
await eventOperation {
|
||||||
@ -114,22 +130,6 @@ class EventsViewModel: ACHudContainer {
|
|||||||
updateEvents()
|
updateEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
func editEvent(_ event: EventModel) async {
|
|
||||||
guard let eventDto = vehicle.events.first(where: { $0.id == event.id }) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if let updatedEvent = await coordinator?.editEvent(event: eventDto) {
|
|
||||||
await eventOperation {
|
|
||||||
try await self.storageService.edit(event: updatedEvent, for: self.vehicle.getNumber())
|
|
||||||
} apiOperation: {
|
|
||||||
try await self.apiService.edit(event: updatedEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateEvents()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eventOperation(_ storageOperation: @escaping VehicleOperation, apiOperation: @escaping VehicleOperation) async {
|
func eventOperation(_ storageOperation: @escaping VehicleOperation, apiOperation: @escaping VehicleOperation) async {
|
||||||
|
|
||||||
if vehicle.unrecognized {
|
if vehicle.unrecognized {
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
//
|
|
||||||
// FiltersCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 06.10.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AutoCatCore
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class FiltersCoordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController
|
|
||||||
let filter: Filter
|
|
||||||
|
|
||||||
init(navController: UINavigationController, filter: Filter) {
|
|
||||||
|
|
||||||
self.viewController = navController
|
|
||||||
self.filter = filter
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async -> Filter? {
|
|
||||||
let viewModel = FiltersViewModel(
|
|
||||||
apiService: ServiceContainer.shared.resolve(ApiServiceProtocol.self),
|
|
||||||
filter: filter
|
|
||||||
)
|
|
||||||
let controller = CustomHostingController(rootView: FiltersScreen(viewModel: viewModel))
|
|
||||||
viewController.pushViewController(controller, animated: true)
|
|
||||||
await controller.waitForDisappear()
|
|
||||||
return viewModel.filterResult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -23,6 +23,14 @@ struct FiltersScreen: View {
|
|||||||
|
|
||||||
@State var viewModel: FiltersViewModel
|
@State var viewModel: FiltersViewModel
|
||||||
|
|
||||||
|
init(filter: Filter, onUpdate: @escaping (Filter) -> Void) {
|
||||||
|
self.viewModel = FiltersViewModel(
|
||||||
|
apiService: ServiceContainer.shared.resolve(ApiServiceProtocol.self),
|
||||||
|
filter: filter,
|
||||||
|
onUpdate: onUpdate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section("Main filters") {
|
Section("Main filters") {
|
||||||
@ -118,14 +126,3 @@ struct FiltersScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
FiltersScreen(viewModel: .init(
|
|
||||||
apiService: MockApiServiceProtocol(),
|
|
||||||
filter: Filter()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -26,8 +26,6 @@ class FiltersViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ObservationIgnored var filterResult: Filter?
|
|
||||||
|
|
||||||
var models: [StringOption] = [.any]
|
var models: [StringOption] = [.any]
|
||||||
var brands: [StringOption] = [.any]
|
var brands: [StringOption] = [.any]
|
||||||
var colors: [StringOption] = [.any]
|
var colors: [StringOption] = [.any]
|
||||||
@ -35,10 +33,16 @@ class FiltersViewModel {
|
|||||||
|
|
||||||
@ObservationIgnored var currentBrand: StringOption = .any
|
@ObservationIgnored var currentBrand: StringOption = .any
|
||||||
|
|
||||||
init(apiService: ApiServiceProtocol, filter: Filter) {
|
let onUpdate: (Filter) -> Void
|
||||||
|
|
||||||
|
init(
|
||||||
|
apiService: ApiServiceProtocol,
|
||||||
|
filter: Filter,
|
||||||
|
onUpdate: @escaping (Filter) -> Void
|
||||||
|
) {
|
||||||
self.apiService = apiService
|
self.apiService = apiService
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
|
self.onUpdate = onUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadData() async {
|
func loadData() async {
|
||||||
@ -61,7 +65,7 @@ class FiltersViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applyFilters() {
|
func applyFilters() {
|
||||||
filterResult = filter
|
onUpdate(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func nullifyTime(of date: Date?) -> Date? {
|
func nullifyTime(of date: Date?) -> Date? {
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// GalleryCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 07.12.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class GalleryCoordinator: Coordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController
|
|
||||||
let photos: [VehiclePhotoDto]
|
|
||||||
|
|
||||||
init(navController: UINavigationController, photos: [VehiclePhotoDto]) {
|
|
||||||
|
|
||||||
self.viewController = navController
|
|
||||||
self.photos = photos
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async throws {
|
|
||||||
|
|
||||||
let viewModel = GalleryViewModel(photos: photos)
|
|
||||||
let controller = UIHostingController(rootView: GalleryScreen(viewModel: viewModel))
|
|
||||||
viewController.pushViewController(controller, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,14 +11,14 @@ import AutoCatCore
|
|||||||
|
|
||||||
struct GalleryScreen: View {
|
struct GalleryScreen: View {
|
||||||
|
|
||||||
@State var viewModel: GalleryViewModel
|
let photos: [VehiclePhotoDto]
|
||||||
|
|
||||||
@State var galleryModel: ACImageSliderModel?
|
@State var galleryModel: ACImageSliderModel?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
LazyVGrid(columns: columns, spacing: 2) {
|
LazyVGrid(columns: columns, spacing: 2) {
|
||||||
ForEach(viewModel.photos) { photo in
|
ForEach(photos) { photo in
|
||||||
ZStack {
|
ZStack {
|
||||||
AsyncImage(url: URL(string: photo.url)) { phase in
|
AsyncImage(url: URL(string: photo.url)) { phase in
|
||||||
switch phase {
|
switch phase {
|
||||||
@ -43,7 +43,7 @@ struct GalleryScreen: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
galleryModel = ACImageSliderModel(
|
galleryModel = ACImageSliderModel(
|
||||||
urls: viewModel.photos.compactMap { URL(string: $0.url) },
|
urls: photos.compactMap { URL(string: $0.url) },
|
||||||
selected: url
|
selected: url
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -66,7 +66,3 @@ struct GalleryScreen: View {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
|
||||||
GalleryScreen(viewModel: .init(photos: []))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// GalleryViewModel.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 07.12.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
@Observable
|
|
||||||
class GalleryViewModel {
|
|
||||||
|
|
||||||
let photos: [VehiclePhotoDto]
|
|
||||||
|
|
||||||
init(photos: [VehiclePhotoDto]) {
|
|
||||||
self.photos = photos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,8 +13,6 @@ import AutoCatCore
|
|||||||
@MainActor
|
@MainActor
|
||||||
final class HistoryCoordinator {
|
final class HistoryCoordinator {
|
||||||
|
|
||||||
var navController: UINavigationController?
|
|
||||||
|
|
||||||
func start() -> (UIViewController, HistoryViewModel) {
|
func start() -> (UIViewController, HistoryViewModel) {
|
||||||
|
|
||||||
let resolver = ServiceContainer.shared
|
let resolver = ServiceContainer.shared
|
||||||
@ -24,22 +22,9 @@ final class HistoryCoordinator {
|
|||||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||||
)
|
)
|
||||||
|
|
||||||
viewModel.coordinator = self
|
|
||||||
|
|
||||||
let view = HistoryScreen(viewModel: viewModel)
|
let view = HistoryScreen(viewModel: viewModel)
|
||||||
let controller = UIHostingController(rootView: view)
|
let controller = UIHostingController(rootView: view)
|
||||||
|
|
||||||
let navController = UINavigationController(rootViewController: controller)
|
return (controller, viewModel)
|
||||||
self.navController = navController
|
|
||||||
|
|
||||||
return (navController, viewModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openReport(vehicle: VehicleDto) async {
|
|
||||||
|
|
||||||
let coordinator = ReportCoordinator(controller: navController,
|
|
||||||
vehicle: vehicle,
|
|
||||||
isPersistent: true)
|
|
||||||
_ = try? await coordinator.start()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,69 +16,89 @@ struct HistoryScreen: View {
|
|||||||
@State var exportSheetPresented = false
|
@State var exportSheetPresented = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
NavigationStack {
|
||||||
ForEach(viewModel.vehicleSections) { section in
|
List {
|
||||||
Section(header: Text(section.header)) {
|
ForEach(viewModel.vehicleSections) { section in
|
||||||
ForEach(section.elements) { vehicle in
|
Section(header: Text(section.header)) {
|
||||||
VehicleCellView(vehicle: vehicle)
|
ForEach(section.elements) { vehicle in
|
||||||
.onTapGesture {
|
NavigationLink(value: vehicle) {
|
||||||
Task { await viewModel.openReport(vehicle: vehicle) }
|
vehicleCell(vehicle)
|
||||||
}
|
|
||||||
.swipeActions(allowsFullSwipe: false) {
|
|
||||||
makeActions(for: vehicle)
|
|
||||||
}
|
|
||||||
.contextMenu {
|
|
||||||
makeActions(for: vehicle, useLabels: true)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.onAppear {
|
||||||
.onAppear {
|
Task { await viewModel.onAppear() }
|
||||||
Task { await viewModel.onAppear() }
|
}
|
||||||
}
|
.hud($viewModel.hud)
|
||||||
.hud($viewModel.hud)
|
.listStyle(.plain)
|
||||||
.listStyle(.plain)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
||||||
viewModel.vehiclesCount))
|
viewModel.vehiclesCount))
|
||||||
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
||||||
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
||||||
.autocorrectionDisabled()
|
.autocorrectionDisabled()
|
||||||
.textInputAutocapitalization(.never)
|
.textInputAutocapitalization(.never)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button("", systemImage: "square.and.arrow.up") {
|
Button("", systemImage: "square.and.arrow.up") {
|
||||||
exportSheetPresented = true
|
exportSheetPresented = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
Button("", systemImage: "line.horizontal.3.decrease") {
|
||||||
|
filterSheetPresented = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .primaryAction) {
|
.confirmationDialog("Filter check history", isPresented: $filterSheetPresented, titleVisibility: .visible) {
|
||||||
Button("", systemImage: "line.horizontal.3.decrease") {
|
ForEach(HistoryFilter.allCases) { filter in
|
||||||
filterSheetPresented = true
|
Button(filter.title) {
|
||||||
|
viewModel.filter = filter
|
||||||
|
viewModel.applyFilters()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.confirmationDialog("Export history as", isPresented: $exportSheetPresented, titleVisibility: .visible) {
|
||||||
.confirmationDialog("Filter check history", isPresented: $filterSheetPresented, titleVisibility: .visible) {
|
|
||||||
ForEach(HistoryFilter.allCases) { filter in
|
ShareLink(item: viewModel.vehiclesArchive, preview: SharePreview(VehiclesArchive.fileName)) {
|
||||||
Button(filter.title) {
|
Text("CSV table")
|
||||||
viewModel.filter = filter
|
}
|
||||||
viewModel.applyFilters()
|
|
||||||
|
if let dbUrl = viewModel.dbFileURL {
|
||||||
|
ShareLink(item: dbUrl) {
|
||||||
|
Text("Database file")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.navigationDestination(for: VehicleDto.self) { vehicle in
|
||||||
.confirmationDialog("Export history as", isPresented: $exportSheetPresented, titleVisibility: .visible) {
|
ReportScreen(
|
||||||
|
vehicle: vehicle,
|
||||||
ShareLink(item: viewModel.vehiclesArchive, preview: SharePreview(VehiclesArchive.fileName)) {
|
isPersistent: true,
|
||||||
Text("CSV table")
|
onUpdate: viewModel.onVehicleChanged
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
.navigationDestination(item: $viewModel.vehicleToOpen) { vehicle in
|
||||||
if let dbUrl = viewModel.dbFileURL {
|
ReportScreen(
|
||||||
ShareLink(item: dbUrl) {
|
vehicle: vehicle,
|
||||||
Text("Database file")
|
isPersistent: true,
|
||||||
}
|
onUpdate: viewModel.onVehicleChanged
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vehicleCell(_ vehicle: VehicleDto) -> some View {
|
||||||
|
VehicleCellView(vehicle: vehicle)
|
||||||
|
.swipeActions(allowsFullSwipe: false) {
|
||||||
|
makeActions(for: vehicle)
|
||||||
|
}
|
||||||
|
.contextMenu {
|
||||||
|
makeActions(for: vehicle, useLabels: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func makeActions(for vehicle: VehicleDto, useLabels: Bool = false) -> some View {
|
func makeActions(for vehicle: VehicleDto, useLabels: Bool = false) -> some View {
|
||||||
|
|
||||||
@ -95,7 +115,3 @@ struct HistoryScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//#Preview {
|
|
||||||
// HistoryScreen(viewModel: .init())
|
|
||||||
//}
|
|
||||||
|
|||||||
@ -23,7 +23,6 @@ final class HistoryViewModel: ACHudContainer {
|
|||||||
let apiService: ApiServiceProtocol
|
let apiService: ApiServiceProtocol
|
||||||
let storageService: StorageServiceProtocol
|
let storageService: StorageServiceProtocol
|
||||||
let vehicleService: VehicleServiceProtocol
|
let vehicleService: VehicleServiceProtocol
|
||||||
var coordinator: HistoryCoordinator?
|
|
||||||
|
|
||||||
var hud: ACHud?
|
var hud: ACHud?
|
||||||
|
|
||||||
@ -31,6 +30,8 @@ final class HistoryViewModel: ACHudContainer {
|
|||||||
var vehiclesFiltered: [VehicleDto] = []
|
var vehiclesFiltered: [VehicleDto] = []
|
||||||
var vehicleSections: [DateSection<VehicleDto>] = []
|
var vehicleSections: [DateSection<VehicleDto>] = []
|
||||||
|
|
||||||
|
var vehicleToOpen: VehicleDto?
|
||||||
|
|
||||||
var searchText: String = "" {
|
var searchText: String = "" {
|
||||||
didSet {
|
didSet {
|
||||||
if searchText != oldValue {
|
if searchText != oldValue {
|
||||||
@ -75,11 +76,6 @@ final class HistoryViewModel: ACHudContainer {
|
|||||||
applyFilters()
|
applyFilters()
|
||||||
}
|
}
|
||||||
|
|
||||||
func openReport(vehicle: VehicleDto) async {
|
|
||||||
await coordinator?.openReport(vehicle: vehicle)
|
|
||||||
await loadVehicles()
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyFilters() {
|
func applyFilters() {
|
||||||
vehiclesFiltered = filterType(vehicles)
|
vehiclesFiltered = filterType(vehicles)
|
||||||
vehiclesFiltered = filterSearch(vehiclesFiltered)
|
vehiclesFiltered = filterSearch(vehiclesFiltered)
|
||||||
@ -133,7 +129,7 @@ final class HistoryViewModel: ACHudContainer {
|
|||||||
if errors.isEmpty {
|
if errors.isEmpty {
|
||||||
hud = nil
|
hud = nil
|
||||||
if !vehicle.unrecognized {
|
if !vehicle.unrecognized {
|
||||||
await openReport(vehicle: vehicle)
|
vehicleToOpen = vehicle
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showErrors(errors)
|
showErrors(errors)
|
||||||
@ -162,4 +158,11 @@ final class HistoryViewModel: ACHudContainer {
|
|||||||
func checkRecord(number: String, event: VehicleEventDto?) async {
|
func checkRecord(number: String, event: VehicleEventDto?) async {
|
||||||
await checkVehicle(number: number, type: .record(event))
|
await checkVehicle(number: number, type: .record(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onVehicleChanged(_ vehicle: VehicleDto) {
|
||||||
|
|
||||||
|
Task {
|
||||||
|
await loadVehicles()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
//
|
|
||||||
// LocationEditCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 27.07.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
final class LocationEditCoordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController
|
|
||||||
|
|
||||||
init(navController: UINavigationController) {
|
|
||||||
self.viewController = navController
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(event: VehicleEventDto) async -> VehicleEventDto? {
|
|
||||||
|
|
||||||
let viewModel = LocationEditViewModel(event: event)
|
|
||||||
viewModel.coordinator = self
|
|
||||||
|
|
||||||
let screen = LocationEditScreen(viewModel: viewModel)
|
|
||||||
let controller = CustomHostingController(rootView: screen)
|
|
||||||
viewController.pushViewController(controller, animated: true)
|
|
||||||
await controller.waitForDisappear()
|
|
||||||
return viewModel.result
|
|
||||||
}
|
|
||||||
|
|
||||||
func openLocationPicker(event: VehicleEventDto) async -> VehicleEventDto? {
|
|
||||||
|
|
||||||
let coordinator = LocationPickerCoordinator(navController: viewController,
|
|
||||||
event: event)
|
|
||||||
return try? await coordinator.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,18 +11,30 @@ import AutoCatCore
|
|||||||
|
|
||||||
struct LocationEditScreen: View {
|
struct LocationEditScreen: View {
|
||||||
|
|
||||||
|
enum Screen: String {
|
||||||
|
|
||||||
|
case locationPicker
|
||||||
|
}
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
@State var viewModel: LocationEditViewModel
|
@State var viewModel: LocationEditViewModel
|
||||||
|
|
||||||
|
init(event: VehicleEventDto, onUpdate: @escaping (VehicleEventDto) -> Void) {
|
||||||
|
|
||||||
|
self.viewModel = LocationEditViewModel(
|
||||||
|
event: event,
|
||||||
|
onUpdate: onUpdate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
DatePicker("Date", selection: $viewModel.date)
|
DatePicker("Date", selection: $viewModel.date)
|
||||||
.datePickerStyle(.compact)
|
.datePickerStyle(.compact)
|
||||||
TextRowView(title: "Location", value: viewModel.event.location)
|
NavigationLink(value: Screen.locationPicker) {
|
||||||
.onTapGesture {
|
TextRowView(title: "Location", value: viewModel.event.location)
|
||||||
Task { await viewModel.pickLocation() }
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationTitle("Edit event")
|
.navigationTitle("Edit event")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
@ -33,13 +45,8 @@ struct LocationEditScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.navigationDestination(for: Screen.self) { _ in
|
||||||
|
LocationPickerScreen(event: viewModel.event, onUpdate: viewModel.onEventUpdated)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
|
||||||
|
|
||||||
var event = VehicleEventDto(lat: 25.54984, lon: 36.34857, addedBy: "")
|
|
||||||
event.address = "Ул. Ленина, 123"
|
|
||||||
|
|
||||||
return LocationEditScreen(viewModel: .init(event: event))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -13,26 +13,23 @@ import SwiftUI
|
|||||||
@Observable
|
@Observable
|
||||||
final class LocationEditViewModel {
|
final class LocationEditViewModel {
|
||||||
|
|
||||||
weak var coordinator: LocationEditCoordinator?
|
|
||||||
|
|
||||||
var event: VehicleEventDto
|
var event: VehicleEventDto
|
||||||
var date: Date
|
var date: Date
|
||||||
|
|
||||||
var result: VehicleEventDto?
|
var onUpdate: (VehicleEventDto) -> Void
|
||||||
|
|
||||||
init(event: VehicleEventDto) {
|
init(event: VehicleEventDto, onUpdate: @escaping (VehicleEventDto) -> Void) {
|
||||||
self.event = event
|
self.event = event
|
||||||
self.date = Date(timeIntervalSince1970: event.date)
|
self.date = Date(timeIntervalSince1970: event.date)
|
||||||
|
self.onUpdate = onUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
func done() {
|
func done() {
|
||||||
event.date = date.timeIntervalSince1970
|
event.date = date.timeIntervalSince1970
|
||||||
result = event
|
onUpdate(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickLocation() async {
|
func onEventUpdated(_ event: VehicleEventDto) {
|
||||||
if let newEvent = await coordinator?.openLocationPicker(event: event) {
|
self.event = event
|
||||||
event = newEvent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// LocationPickerCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 27.07.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
final class LocationPickerCoordinator: Coordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController?
|
|
||||||
let event: VehicleEventDto
|
|
||||||
|
|
||||||
init(navController: UINavigationController?, event: VehicleEventDto) {
|
|
||||||
self.viewController = navController
|
|
||||||
self.event = event
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async throws -> VehicleEventDto? {
|
|
||||||
|
|
||||||
let viewModel = LocationPickerViewModel(
|
|
||||||
locationService: ServiceContainer.shared.resolve(LocationServiceProtocol.self),
|
|
||||||
event: event
|
|
||||||
)
|
|
||||||
let screen = LocationPickerScreen(viewModel: viewModel)
|
|
||||||
let controller = CustomHostingController(rootView: screen)
|
|
||||||
viewController?.pushViewController(controller, animated: true)
|
|
||||||
await controller.waitForDisappear()
|
|
||||||
return viewModel.result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -16,6 +16,15 @@ struct LocationPickerScreen: View {
|
|||||||
|
|
||||||
@State var viewModel: LocationPickerViewModel
|
@State var viewModel: LocationPickerViewModel
|
||||||
|
|
||||||
|
init(event: VehicleEventDto, onUpdate: @escaping (VehicleEventDto) -> Void) {
|
||||||
|
|
||||||
|
self.viewModel = LocationPickerViewModel(
|
||||||
|
locationService: ServiceContainer.shared.resolve(LocationServiceProtocol.self),
|
||||||
|
event: event,
|
||||||
|
onUpdate: onUpdate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Map(initialPosition: viewModel.position)
|
Map(initialPosition: viewModel.position)
|
||||||
@ -45,16 +54,3 @@ struct LocationPickerScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
|
||||||
|
|
||||||
var event = VehicleEventDto(lat: 47.250049, lon: 39.711821, addedBy: nil)
|
|
||||||
event.address = "Ул. Ленина, 123"
|
|
||||||
|
|
||||||
let viewModel = LocationPickerViewModel(
|
|
||||||
locationService: ServiceContainer.shared.resolve(LocationServiceProtocol.self),
|
|
||||||
event: event
|
|
||||||
)
|
|
||||||
|
|
||||||
return LocationPickerScreen(viewModel: viewModel)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -20,12 +20,17 @@ final class LocationPickerViewModel {
|
|||||||
var event: VehicleEventDto
|
var event: VehicleEventDto
|
||||||
var position: MapCameraPosition
|
var position: MapCameraPosition
|
||||||
|
|
||||||
var result: VehicleEventDto?
|
var onUpdate: (VehicleEventDto) -> Void
|
||||||
|
|
||||||
init(locationService: LocationServiceProtocol, event: VehicleEventDto) {
|
init(
|
||||||
|
locationService: LocationServiceProtocol,
|
||||||
|
event: VehicleEventDto,
|
||||||
|
onUpdate: @escaping (VehicleEventDto) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.locationService = locationService
|
self.locationService = locationService
|
||||||
self.event = event
|
self.event = event
|
||||||
|
self.onUpdate = onUpdate
|
||||||
|
|
||||||
if event.latitude == 0 && event.longitude == 0 {
|
if event.latitude == 0 && event.longitude == 0 {
|
||||||
self.position = .userLocation(fallback: .automatic)
|
self.position = .userLocation(fallback: .automatic)
|
||||||
@ -43,6 +48,6 @@ final class LocationPickerViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func done() {
|
func done() {
|
||||||
result = event
|
onUpdate(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,45 +0,0 @@
|
|||||||
//
|
|
||||||
// MapCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 13.04.2025.
|
|
||||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
enum MapInput {
|
|
||||||
|
|
||||||
case event(VehicleEventDto)
|
|
||||||
case filter(Filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
final class MapCoordinator {
|
|
||||||
|
|
||||||
let navController: UINavigationController
|
|
||||||
|
|
||||||
init(navController: UINavigationController) {
|
|
||||||
|
|
||||||
self.navController = navController
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(mapInput: MapInput) {
|
|
||||||
let resolver = ServiceContainer.shared
|
|
||||||
|
|
||||||
let viewModel = MapViewModel(
|
|
||||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
|
||||||
mapInput: mapInput
|
|
||||||
)
|
|
||||||
|
|
||||||
let controller = UIHostingController(
|
|
||||||
rootView: MapScreen(viewModel: viewModel)
|
|
||||||
)
|
|
||||||
|
|
||||||
//controller.modalPresentationStyle = .fullScreen
|
|
||||||
controller.hidesBottomBarWhenPushed = true
|
|
||||||
navController.pushViewController(controller, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
AutoCat/Screens/MapScreen/MapInput.swift
Normal file
15
AutoCat/Screens/MapScreen/MapInput.swift
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// MapInput.swift
|
||||||
|
// AutoCat
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 18.04.2025.
|
||||||
|
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AutoCatCore
|
||||||
|
|
||||||
|
enum MapInput {
|
||||||
|
|
||||||
|
case event(VehicleEventDto)
|
||||||
|
case filter(Filter)
|
||||||
|
}
|
||||||
@ -9,11 +9,20 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MapKit
|
import MapKit
|
||||||
import ClusterMapSwiftUI
|
import ClusterMapSwiftUI
|
||||||
|
import AutoCatCore
|
||||||
|
|
||||||
struct MapScreen: View {
|
struct MapScreen: View {
|
||||||
|
|
||||||
@State var viewModel: MapViewModel
|
@State var viewModel: MapViewModel
|
||||||
|
|
||||||
|
init(mapInput: MapInput) {
|
||||||
|
|
||||||
|
self.viewModel = MapViewModel(
|
||||||
|
apiService: ServiceContainer.shared.resolve(ApiServiceProtocol.self),
|
||||||
|
mapInput: mapInput
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Map {
|
Map {
|
||||||
ForEach(viewModel.markers) {
|
ForEach(viewModel.markers) {
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
//
|
|
||||||
// NotesCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 24.06.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class NotesCoordinator: Coordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController?
|
|
||||||
let vehicle: VehicleDto
|
|
||||||
|
|
||||||
init(navController: UINavigationController, vehicle: VehicleDto) {
|
|
||||||
|
|
||||||
self.viewController = navController
|
|
||||||
self.vehicle = vehicle
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async throws -> VehicleDto {
|
|
||||||
|
|
||||||
let resolver = ServiceContainer.shared
|
|
||||||
let viewModel = NotesViewModel(
|
|
||||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
|
||||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
|
||||||
vehicle: vehicle
|
|
||||||
)
|
|
||||||
let controller = CustomHostingController(rootView: NotesScreen(viewModel: viewModel))
|
|
||||||
viewController?.pushViewController(controller, animated: true)
|
|
||||||
await controller.waitForDisappear()
|
|
||||||
return viewModel.vehicle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -18,6 +18,17 @@ struct NotesScreen: View {
|
|||||||
@State var selectedNoteId = ""
|
@State var selectedNoteId = ""
|
||||||
@State var noteText = ""
|
@State var noteText = ""
|
||||||
|
|
||||||
|
init(vehicle: VehicleDto, onUpdate: @escaping (VehicleDto) -> Void) {
|
||||||
|
|
||||||
|
let resolver = ServiceContainer.shared
|
||||||
|
self.viewModel = NotesViewModel(
|
||||||
|
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||||
|
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||||
|
vehicle: vehicle,
|
||||||
|
onUpdate: onUpdate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
List(viewModel.vehicle.notes) { note in
|
List(viewModel.vehicle.notes) { note in
|
||||||
@ -85,26 +96,3 @@ struct NotesScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
|
|
||||||
var vehicle = VehicleDto()
|
|
||||||
|
|
||||||
vehicle.notes = [
|
|
||||||
.init(text: "qwe", user: ""),
|
|
||||||
.init(text: "asdf", user: ""),
|
|
||||||
.init(text: "zxcv", user: "")
|
|
||||||
]
|
|
||||||
|
|
||||||
let vm = NotesViewModel(
|
|
||||||
storageService: MockStorageServiceProtocol(),
|
|
||||||
apiService: MockApiServiceProtocol(),
|
|
||||||
vehicle: vehicle
|
|
||||||
)
|
|
||||||
|
|
||||||
return NotesScreen(viewModel: vm)
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -21,13 +21,19 @@ class NotesViewModel: ACHudContainer {
|
|||||||
var vehicle: VehicleDto
|
var vehicle: VehicleDto
|
||||||
var hud: ACHud?
|
var hud: ACHud?
|
||||||
|
|
||||||
init(storageService: StorageServiceProtocol,
|
var onUpdate: (VehicleDto) -> Void
|
||||||
apiService: ApiServiceProtocol,
|
|
||||||
vehicle: VehicleDto) {
|
init(
|
||||||
|
storageService: StorageServiceProtocol,
|
||||||
|
apiService: ApiServiceProtocol,
|
||||||
|
vehicle: VehicleDto,
|
||||||
|
onUpdate: @escaping (VehicleDto) -> Void
|
||||||
|
) {
|
||||||
|
|
||||||
self.storageService = storageService
|
self.storageService = storageService
|
||||||
self.apiService = apiService
|
self.apiService = apiService
|
||||||
self.vehicle = vehicle
|
self.vehicle = vehicle
|
||||||
|
self.onUpdate = onUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
func addNote(text: String) async {
|
func addNote(text: String) async {
|
||||||
@ -67,6 +73,7 @@ class NotesViewModel: ACHudContainer {
|
|||||||
await wrapWithToast(showProgress: false) { [weak self] in
|
await wrapWithToast(showProgress: false) { [weak self] in
|
||||||
guard let self else { throw GenericError.somethingWentWrong }
|
guard let self else { throw GenericError.somethingWentWrong }
|
||||||
vehicle = try await storageOp()
|
vehicle = try await storageOp()
|
||||||
|
onUpdate(vehicle)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -76,6 +83,7 @@ class NotesViewModel: ACHudContainer {
|
|||||||
let vehicle = try await apiOp()
|
let vehicle = try await apiOp()
|
||||||
try await storageService.updateVehicle(dto: vehicle, policy: .ifExists)
|
try await storageService.updateVehicle(dto: vehicle, policy: .ifExists)
|
||||||
self.vehicle = vehicle
|
self.vehicle = vehicle
|
||||||
|
onUpdate(vehicle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// OsagoCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 14.07.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
class OsagoCoordinator: Coordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController?
|
|
||||||
let contracts: [OsagoDto]
|
|
||||||
|
|
||||||
init(navController: UINavigationController, contracts: [OsagoDto]) {
|
|
||||||
|
|
||||||
self.viewController = navController
|
|
||||||
self.contracts = contracts
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async throws {
|
|
||||||
|
|
||||||
let controller = await UIHostingController(rootView: OsagoScreen(contracts: contracts))
|
|
||||||
await viewController?.pushViewController(controller, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// OwnersCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 14.07.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
class OwnersCoordinator: Coordinator {
|
|
||||||
|
|
||||||
let viewController: UINavigationController?
|
|
||||||
let ownerships: [VehicleOwnershipPeriodDto]
|
|
||||||
|
|
||||||
init(navController: UINavigationController, ownerships: [VehicleOwnershipPeriodDto]) {
|
|
||||||
|
|
||||||
self.viewController = navController
|
|
||||||
self.ownerships = ownerships
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async throws {
|
|
||||||
|
|
||||||
let controller = await UIHostingController(rootView: OwnersScreen(ownerships: ownerships))
|
|
||||||
await viewController?.pushViewController(controller, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,8 +13,6 @@ import AutoCatCore
|
|||||||
@MainActor
|
@MainActor
|
||||||
final class RecordsCoordinator {
|
final class RecordsCoordinator {
|
||||||
|
|
||||||
var navController = UINavigationController()
|
|
||||||
|
|
||||||
func start(output: RecordScreenOutput?) -> UIViewController {
|
func start(output: RecordScreenOutput?) -> UIViewController {
|
||||||
|
|
||||||
let resolver = ServiceContainer.shared
|
let resolver = ServiceContainer.shared
|
||||||
@ -24,20 +22,9 @@ final class RecordsCoordinator {
|
|||||||
recordPlayer: resolver.resolve(RecordPlayerServiceProtocol.self)
|
recordPlayer: resolver.resolve(RecordPlayerServiceProtocol.self)
|
||||||
)
|
)
|
||||||
|
|
||||||
viewModel.coordinator = self
|
|
||||||
viewModel.output = output
|
viewModel.output = output
|
||||||
|
|
||||||
let view = RecordsScreen(viewModel: viewModel)
|
let view = RecordsScreen(viewModel: viewModel)
|
||||||
let controller = UIHostingController(rootView: view)
|
return UIHostingController(rootView: view)
|
||||||
|
|
||||||
let navController = UINavigationController(rootViewController: controller)
|
|
||||||
self.navController = navController
|
|
||||||
return navController
|
|
||||||
}
|
|
||||||
|
|
||||||
func showOnMap(event: VehicleEventDto) {
|
|
||||||
|
|
||||||
let coordinator = MapCoordinator(navController: navController)
|
|
||||||
coordinator.start(mapInput: .event(event))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,59 +18,65 @@ struct RecordsScreen: View {
|
|||||||
@State var selectedRecordId: String = ""
|
@State var selectedRecordId: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
NavigationStack {
|
||||||
ForEach(viewModel.recordSections) { section in
|
List {
|
||||||
Section(header: Text(section.header)) {
|
ForEach(viewModel.recordSections) { section in
|
||||||
ForEach(section.elements) { model in
|
Section(header: Text(section.header)) {
|
||||||
AudioRecordView(
|
ForEach(section.elements) { model in
|
||||||
record: .init(
|
AudioRecordView(
|
||||||
dto: model,
|
record: .init(
|
||||||
isPlaying: model.id == viewModel.playingRecord?.id
|
dto: model,
|
||||||
),
|
isPlaying: model.id == viewModel.playingRecord?.id
|
||||||
progress: viewModel.progress
|
),
|
||||||
) {
|
progress: viewModel.progress
|
||||||
viewModel.onPlayTapped(record: model)
|
) {
|
||||||
}
|
viewModel.onPlayTapped(record: model)
|
||||||
.listRowInsets(EdgeInsets())
|
}
|
||||||
.swipeActions(allowsFullSwipe: false) {
|
.listRowInsets(EdgeInsets())
|
||||||
makeActions(for: model)
|
.swipeActions(allowsFullSwipe: false) {
|
||||||
}
|
makeActions(for: model)
|
||||||
.contextMenu {
|
}
|
||||||
makeActions(for: model, useLabels: true)
|
.contextMenu {
|
||||||
|
makeActions(for: model, useLabels: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.listStyle(.inset)
|
||||||
.listStyle(.inset)
|
.hud($viewModel.hud)
|
||||||
.hud($viewModel.hud)
|
.navigationTitle("Voice records")
|
||||||
.navigationTitle("Voice records")
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task { await viewModel.onAppear() }
|
Task { await viewModel.onAppear() }
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button {
|
Button {
|
||||||
Task { await viewModel.startRecording() }
|
Task { await viewModel.startRecording() }
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
}
|
|
||||||
.alert("Recording...", isPresented: $viewModel.showRecordingAlert) {
|
|
||||||
Button("Cancel", role: .cancel) {
|
|
||||||
Task { await viewModel.cancelRecording() }
|
|
||||||
}
|
}
|
||||||
Button("Done") {
|
.alert("Recording...", isPresented: $viewModel.showRecordingAlert) {
|
||||||
Task { await viewModel.stopRecording() }
|
Button("Cancel", role: .cancel) {
|
||||||
|
Task { await viewModel.cancelRecording() }
|
||||||
|
}
|
||||||
|
Button("Done") {
|
||||||
|
Task { await viewModel.stopRecording() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.noteAlert(
|
||||||
.noteAlert(
|
title: String(localized: "Edit plate number"),
|
||||||
title: String(localized: "Edit plate number"),
|
body: $numberText,
|
||||||
body: $numberText,
|
isPresented: $showEditAlert
|
||||||
isPresented: $showEditAlert
|
) { text in
|
||||||
) { text in
|
Task { await viewModel.editRecord(id: selectedRecordId, number: numberText) }
|
||||||
Task { await viewModel.editRecord(id: selectedRecordId, number: numberText) }
|
}
|
||||||
|
.navigationDestination(for: VehicleEventDto.self) { event in
|
||||||
|
MapScreen(mapInput: .event(event))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,10 +115,8 @@ struct RecordsScreen: View {
|
|||||||
Label("Show recognized text", systemImage: "textformat")
|
Label("Show recognized text", systemImage: "textformat")
|
||||||
}
|
}
|
||||||
|
|
||||||
if record.event != nil {
|
if let event = record.event {
|
||||||
Button {
|
NavigationLink(value: event) {
|
||||||
viewModel.showOnMap(record)
|
|
||||||
} label: {
|
|
||||||
Label("Show on map", systemImage: "map")
|
Label("Show on map", systemImage: "map")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,6 @@ final class RecordsViewModel: ACHudContainer {
|
|||||||
let recordService: VehicleRecordServiceProtocol
|
let recordService: VehicleRecordServiceProtocol
|
||||||
let storageService: StorageServiceProtocol
|
let storageService: StorageServiceProtocol
|
||||||
let recordPlayer: RecordPlayerServiceProtocol
|
let recordPlayer: RecordPlayerServiceProtocol
|
||||||
var coordinator: RecordsCoordinator?
|
|
||||||
weak var output: RecordScreenOutput?
|
weak var output: RecordScreenOutput?
|
||||||
|
|
||||||
var hud: ACHud?
|
var hud: ACHud?
|
||||||
@ -112,14 +111,6 @@ final class RecordsViewModel: ACHudContainer {
|
|||||||
hud = .message(record.rawText)
|
hud = .message(record.rawText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showOnMap(_ record: AudioRecordDto) {
|
|
||||||
guard let event = record.event else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
coordinator?.showOnMap(event: event)
|
|
||||||
}
|
|
||||||
|
|
||||||
func check(_ record: AudioRecordDto) {
|
func check(_ record: AudioRecordDto) {
|
||||||
guard let number = record.number else {
|
guard let number = record.number else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// ReportCoordinator.swift
|
|
||||||
// AutoCat
|
|
||||||
//
|
|
||||||
// Created by Selim Mustafaev on 16.11.2024.
|
|
||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SwiftUI
|
|
||||||
import AutoCatCore
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class ReportCoordinator {
|
|
||||||
|
|
||||||
let viewController: UIViewController?
|
|
||||||
let vehicle: VehicleDto
|
|
||||||
let isPersistent: Bool
|
|
||||||
|
|
||||||
weak var navController: UINavigationController?
|
|
||||||
|
|
||||||
init(controller: UIViewController?, vehicle: VehicleDto, isPersistent: Bool) {
|
|
||||||
|
|
||||||
self.viewController = controller
|
|
||||||
self.vehicle = vehicle
|
|
||||||
self.isPersistent = isPersistent
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() async -> VehicleDto {
|
|
||||||
|
|
||||||
let resolver = ServiceContainer.shared
|
|
||||||
let viewModel = ReportViewModel(
|
|
||||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
|
||||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
|
||||||
settingsService: resolver.resolve(SettingsServiceProtocol.self),
|
|
||||||
vehicle: vehicle,
|
|
||||||
isPersistent: isPersistent
|
|
||||||
)
|
|
||||||
viewModel.coordinator = self
|
|
||||||
let controller = CustomHostingController(rootView: ReportScreen(viewModel: viewModel))
|
|
||||||
let newNavController = UINavigationController(rootViewController: controller)
|
|
||||||
viewController?.showDetailViewController(newNavController, sender: self)
|
|
||||||
navController = controller.navigationController
|
|
||||||
await controller.waitForDisappear()
|
|
||||||
return viewModel.vehicle
|
|
||||||
}
|
|
||||||
|
|
||||||
func openEvents(vehicle: VehicleDto) async -> VehicleDto? {
|
|
||||||
guard let navController else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let coordinator = EventsCoordinator(navController: navController)
|
|
||||||
return await coordinator.start(vehicle: vehicle)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openOsago(contracts: [OsagoDto]) {
|
|
||||||
guard let navController else { return }
|
|
||||||
|
|
||||||
Task {
|
|
||||||
let coordinator = OsagoCoordinator(navController: navController, contracts: contracts)
|
|
||||||
try? await coordinator.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openOwners(ownerships: [VehicleOwnershipPeriodDto]) {
|
|
||||||
guard let navController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Task {
|
|
||||||
let coordiantor = OwnersCoordinator(navController: navController, ownerships: ownerships)
|
|
||||||
try? await coordiantor.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openNotes(vehicle: VehicleDto) async -> VehicleDto? {
|
|
||||||
guard let navController else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let coordinator = NotesCoordinator(navController: navController, vehicle: vehicle)
|
|
||||||
return try? await coordinator.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func openAds(_ ads: [VehicleAdDto]) {
|
|
||||||
guard let navController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Task {
|
|
||||||
let coordinator = AdsCoordinator(navController: navController, ads: ads)
|
|
||||||
try? await coordinator.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openPhotos(_ photos: [VehiclePhotoDto]) {
|
|
||||||
guard let navController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Task {
|
|
||||||
let coordinator = GalleryCoordinator(navController: navController, photos: photos)
|
|
||||||
try? await coordinator.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,8 +11,31 @@ import AutoCatCore
|
|||||||
|
|
||||||
struct ReportScreen: View {
|
struct ReportScreen: View {
|
||||||
|
|
||||||
|
enum Screen: String {
|
||||||
|
|
||||||
|
case events
|
||||||
|
case osago
|
||||||
|
case owners
|
||||||
|
case notes
|
||||||
|
case ads
|
||||||
|
case photos
|
||||||
|
}
|
||||||
|
|
||||||
@State var viewModel: ReportViewModel
|
@State var viewModel: ReportViewModel
|
||||||
|
|
||||||
|
init(vehicle: VehicleDto, isPersistent: Bool, onUpdate: @escaping (VehicleDto) -> Void) {
|
||||||
|
|
||||||
|
let resolver = ServiceContainer.shared
|
||||||
|
self.viewModel = ReportViewModel(
|
||||||
|
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||||
|
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||||
|
settingsService: resolver.resolve(SettingsServiceProtocol.self),
|
||||||
|
vehicle: vehicle,
|
||||||
|
isPersistent: isPersistent,
|
||||||
|
onUpdate: onUpdate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
@ -52,22 +75,34 @@ struct ReportScreen: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section("History") {
|
Section("History") {
|
||||||
LabeledContent("Events", value: String(viewModel.vehicle.events.count))
|
NavigationLink(value: Screen.events) {
|
||||||
.navigationLink(onTap: viewModel.openEvents)
|
LabeledContent("Events", value: String(viewModel.vehicle.events.count))
|
||||||
LabeledContent("OSAGO", value: String(viewModel.vehicle.osagoContracts.count))
|
}
|
||||||
.navigationLink(isActive: !viewModel.vehicle.osagoContracts.isEmpty,
|
|
||||||
onTap: viewModel.openOsago)
|
NavigationLink(value: Screen.osago) {
|
||||||
LabeledContent("Owners", value: String(viewModel.vehicle.ownershipPeriods.count))
|
LabeledContent("OSAGO", value: String(viewModel.vehicle.osagoContracts.count))
|
||||||
.navigationLink(isActive: !viewModel.vehicle.ownershipPeriods.isEmpty,
|
}
|
||||||
onTap: viewModel.openOwners)
|
.disabled(viewModel.vehicle.osagoContracts.isEmpty)
|
||||||
LabeledContent("Photos", value: String(viewModel.vehicle.photos.count))
|
|
||||||
.navigationLink(isActive: !viewModel.vehicle.photos.isEmpty,
|
NavigationLink(value: Screen.owners) {
|
||||||
onTap: viewModel.openPhotoGallery)
|
LabeledContent("Owners", value: String(viewModel.vehicle.ownershipPeriods.count))
|
||||||
LabeledContent("Ads", value: String(viewModel.vehicle.ads.count))
|
}
|
||||||
.navigationLink(isActive: !viewModel.vehicle.ads.isEmpty,
|
.disabled(viewModel.vehicle.ownershipPeriods.isEmpty)
|
||||||
onTap: viewModel.openAds)
|
|
||||||
LabeledContent("Notes", value: String(viewModel.vehicle.notes.count))
|
NavigationLink(value: Screen.photos) {
|
||||||
.navigationLink(onTap: viewModel.openNotes)
|
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 {
|
if viewModel.showDebugInfo {
|
||||||
@ -95,6 +130,22 @@ struct ReportScreen: View {
|
|||||||
ShareLink(item: link)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@ -122,17 +173,3 @@ struct ReportScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
ReportScreen(viewModel: .init(
|
|
||||||
apiService: MockApiServiceProtocol(),
|
|
||||||
storageService: MockStorageServiceProtocol(),
|
|
||||||
settingsService: MockSettingsServiceProtocol(),
|
|
||||||
vehicle: .preview,
|
|
||||||
isPersistent: false
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -17,12 +17,12 @@ class ReportViewModel: ACHudContainer {
|
|||||||
let storageService: StorageServiceProtocol
|
let storageService: StorageServiceProtocol
|
||||||
let settingsService: SettingsServiceProtocol
|
let settingsService: SettingsServiceProtocol
|
||||||
|
|
||||||
var coordinator: ReportCoordinator?
|
|
||||||
|
|
||||||
let isPersistent: Bool
|
let isPersistent: Bool
|
||||||
var vehicle: VehicleDto
|
var vehicle: VehicleDto
|
||||||
var hud: ACHud?
|
var hud: ACHud?
|
||||||
|
|
||||||
|
let onUpdate: (VehicleDto) -> Void
|
||||||
|
|
||||||
var plateNumber: String {
|
var plateNumber: String {
|
||||||
if vehicle.outdated, let current = vehicle.currentNumber {
|
if vehicle.outdated, let current = vehicle.currentNumber {
|
||||||
"\(vehicle.number) (\(current))"
|
"\(vehicle.number) (\(current))"
|
||||||
@ -59,13 +59,15 @@ class ReportViewModel: ACHudContainer {
|
|||||||
storageService: StorageServiceProtocol,
|
storageService: StorageServiceProtocol,
|
||||||
settingsService: SettingsServiceProtocol,
|
settingsService: SettingsServiceProtocol,
|
||||||
vehicle: VehicleDto,
|
vehicle: VehicleDto,
|
||||||
isPersistent: Bool) {
|
isPersistent: Bool,
|
||||||
|
onUpdate: @escaping (VehicleDto) -> Void) {
|
||||||
|
|
||||||
self.apiService = apiService
|
self.apiService = apiService
|
||||||
self.storageService = storageService
|
self.storageService = storageService
|
||||||
self.settingsService = settingsService
|
self.settingsService = settingsService
|
||||||
self.vehicle = vehicle
|
self.vehicle = vehicle
|
||||||
self.isPersistent = isPersistent
|
self.isPersistent = isPersistent
|
||||||
|
self.onUpdate = onUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
func onAppear() async {
|
func onAppear() async {
|
||||||
@ -95,40 +97,12 @@ class ReportViewModel: ACHudContainer {
|
|||||||
guard let self else { throw GenericError.somethingWentWrong }
|
guard let self else { throw GenericError.somethingWentWrong }
|
||||||
vehicle = try await apiService.checkVehicleGb(by: vehicle.getNumber())
|
vehicle = try await apiService.checkVehicleGb(by: vehicle.getNumber())
|
||||||
try await storageService.updateVehicle(dto: vehicle, policy: .ifExists)
|
try await storageService.updateVehicle(dto: vehicle, policy: .ifExists)
|
||||||
|
onUpdate(vehicle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Open detail screens
|
func onVehicleChanged(_ vehicle: VehicleDto) {
|
||||||
|
self.vehicle = vehicle
|
||||||
func openEvents() {
|
onUpdate(vehicle)
|
||||||
Task {
|
|
||||||
if let vehicle = await coordinator?.openEvents(vehicle: vehicle) {
|
|
||||||
self.vehicle = vehicle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openOsago() {
|
|
||||||
coordinator?.openOsago(contracts: vehicle.osagoContracts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openOwners() {
|
|
||||||
coordinator?.openOwners(ownerships: vehicle.ownershipPeriods)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openPhotoGallery() {
|
|
||||||
coordinator?.openPhotos(vehicle.photos)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openNotes() {
|
|
||||||
Task {
|
|
||||||
if let vehicle = await coordinator?.openNotes(vehicle: vehicle) {
|
|
||||||
self.vehicle = vehicle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openAds() {
|
|
||||||
coordinator?.openAds(vehicle.ads)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,8 +13,6 @@ import AutoCatCore
|
|||||||
@MainActor
|
@MainActor
|
||||||
final class SearchCoordinator {
|
final class SearchCoordinator {
|
||||||
|
|
||||||
var navController = UINavigationController()
|
|
||||||
|
|
||||||
func start() -> UIViewController {
|
func start() -> UIViewController {
|
||||||
|
|
||||||
let resolver = ServiceContainer.shared
|
let resolver = ServiceContainer.shared
|
||||||
@ -22,47 +20,8 @@ final class SearchCoordinator {
|
|||||||
apiService: resolver.resolve(ApiServiceProtocol.self),
|
apiService: resolver.resolve(ApiServiceProtocol.self),
|
||||||
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
vehicleService: resolver.resolve(VehicleServiceProtocol.self)
|
||||||
)
|
)
|
||||||
viewModel.coordinator = self
|
|
||||||
|
|
||||||
let view = SearchScreen(viewModel: viewModel)
|
let view = SearchScreen(viewModel: viewModel)
|
||||||
let controller = UIHostingController(rootView: view)
|
return UIHostingController(rootView: view)
|
||||||
|
|
||||||
let navController = UINavigationController(rootViewController: controller)
|
|
||||||
self.navController = navController
|
|
||||||
return navController
|
|
||||||
}
|
|
||||||
|
|
||||||
func openReport(vehicle: VehicleDto) async -> VehicleDto? {
|
|
||||||
|
|
||||||
let coordinator = ReportCoordinator(controller: navController,
|
|
||||||
vehicle: vehicle,
|
|
||||||
isPersistent: false)
|
|
||||||
return await coordinator.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func openFilterDetail(filter: Filter) async -> Filter? {
|
|
||||||
|
|
||||||
let coordinator = FiltersCoordinator(navController: navController, filter: filter)
|
|
||||||
return await coordinator.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func showOnMap(filter: Filter) {
|
|
||||||
|
|
||||||
let coordinator = MapCoordinator(navController: navController)
|
|
||||||
coordinator.start(mapInput: .filter(filter))
|
|
||||||
}
|
|
||||||
|
|
||||||
func export(url: URL) {
|
|
||||||
guard let currentController = navController.visibleViewController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
#if targetEnvironment(macCatalyst)
|
|
||||||
let controller = UIDocumentPickerViewController(forExporting: [url])
|
|
||||||
currentController.present(controller, animated: true)
|
|
||||||
#else
|
|
||||||
let activityController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
|
|
||||||
currentController.present(activityController, animated: true)
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,32 +11,56 @@ import AutoCatCore
|
|||||||
|
|
||||||
struct SearchScreen: View {
|
struct SearchScreen: View {
|
||||||
|
|
||||||
|
enum Screen: Hashable {
|
||||||
|
|
||||||
|
case report(VehicleDto)
|
||||||
|
case filter(Filter)
|
||||||
|
case map(Filter)
|
||||||
|
}
|
||||||
|
|
||||||
@State var viewModel: SearchViewModel
|
@State var viewModel: SearchViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
NavigationStack {
|
||||||
vehicles
|
List {
|
||||||
if viewModel.hasMoreData && !viewModel.vehicleSections.isEmpty {
|
vehicles
|
||||||
progressCell
|
if viewModel.hasMoreData && !viewModel.vehicleSections.isEmpty {
|
||||||
|
progressCell
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.listStyle(.plain)
|
||||||
.listStyle(.plain)
|
.hud($viewModel.hud)
|
||||||
.hud($viewModel.hud)
|
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
||||||
.searchable(text: $viewModel.searchText, prompt: "Search plate numbers")
|
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
||||||
.searchPresentationToolbarBehavior(.avoidHidingContent)
|
.disableAutocorrection(true)
|
||||||
.disableAutocorrection(true)
|
.autocapitalization(.none)
|
||||||
.autocapitalization(.none)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
.navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""),
|
||||||
viewModel.vehiclesCount))
|
viewModel.vehiclesCount))
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task { await viewModel.onAppear() }
|
Task { await viewModel.onAppear() }
|
||||||
}
|
}
|
||||||
.refreshable {
|
.refreshable {
|
||||||
Task { await viewModel.reloadData() }
|
Task { await viewModel.reloadData() }
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .topBarTrailing) {
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
toolbarMenu
|
toolbarMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationDestination(for: Screen.self) { screen in
|
||||||
|
switch screen {
|
||||||
|
case .report(let vehicle):
|
||||||
|
ReportScreen(
|
||||||
|
vehicle: vehicle,
|
||||||
|
isPersistent: false,
|
||||||
|
onUpdate: viewModel.onVehicleChanged
|
||||||
|
)
|
||||||
|
case .filter(let filter):
|
||||||
|
FiltersScreen(filter: filter, onUpdate: viewModel.onFilterChanged)
|
||||||
|
case .map(let filter):
|
||||||
|
MapScreen(mapInput: .filter(filter))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,21 +69,24 @@ struct SearchScreen: View {
|
|||||||
ForEach(viewModel.vehicleSections) { section in
|
ForEach(viewModel.vehicleSections) { section in
|
||||||
Section(header: Text(section.header)) {
|
Section(header: Text(section.header)) {
|
||||||
ForEach(section.elements) { vehicle in
|
ForEach(section.elements) { vehicle in
|
||||||
VehicleCellView(vehicle: vehicle)
|
NavigationLink(value: Screen.report(vehicle)) {
|
||||||
.onTapGesture {
|
vehicleCell(vehicle)
|
||||||
Task { await viewModel.openReport(vehicle: vehicle) }
|
}
|
||||||
}
|
|
||||||
.swipeActions(allowsFullSwipe: false) {
|
|
||||||
makeActions(for: vehicle)
|
|
||||||
}
|
|
||||||
.contextMenu {
|
|
||||||
makeActions(for: vehicle, useLabels: true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vehicleCell(_ vehicle: VehicleDto) -> some View {
|
||||||
|
VehicleCellView(vehicle: vehicle)
|
||||||
|
.swipeActions(allowsFullSwipe: false) {
|
||||||
|
makeActions(for: vehicle)
|
||||||
|
}
|
||||||
|
.contextMenu {
|
||||||
|
makeActions(for: vehicle, useLabels: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var progressCell: some View {
|
var progressCell: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -74,11 +101,11 @@ struct SearchScreen: View {
|
|||||||
|
|
||||||
var toolbarMenu: some View {
|
var toolbarMenu: some View {
|
||||||
Menu("", systemImage: "ellipsis") {
|
Menu("", systemImage: "ellipsis") {
|
||||||
Button("Filter results", systemImage: "line.horizontal.3.decrease") {
|
NavigationLink(value: Screen.filter(viewModel.filter)) {
|
||||||
Task { await viewModel.openFilterDetail() }
|
Label("Filter results", systemImage: "line.horizontal.3.decrease")
|
||||||
}
|
}
|
||||||
Button("Show on map", systemImage: "map") {
|
NavigationLink(value: Screen.map(viewModel.filter)) {
|
||||||
viewModel.showOnMap()
|
Label("Show on map", systemImage: "map")
|
||||||
}
|
}
|
||||||
ShareLink(item: viewModel.vehiclesArchive, preview: SharePreview(VehiclesArchive.fileName))
|
ShareLink(item: viewModel.vehiclesArchive, preview: SharePreview(VehiclesArchive.fileName))
|
||||||
//ShareLink(items: [viewModel.vehiclesArchive])
|
//ShareLink(items: [viewModel.vehiclesArchive])
|
||||||
@ -95,7 +122,3 @@ struct SearchScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//#Preview {
|
|
||||||
// SearchScreen(viewModel: .init())
|
|
||||||
//}
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ final class SearchViewModel: ACHudContainer {
|
|||||||
|
|
||||||
let apiService: ApiServiceProtocol
|
let apiService: ApiServiceProtocol
|
||||||
let vehicleService: VehicleServiceProtocol
|
let vehicleService: VehicleServiceProtocol
|
||||||
var coordinator: SearchCoordinator?
|
|
||||||
|
|
||||||
var hud: ACHud?
|
var hud: ACHud?
|
||||||
|
|
||||||
@ -119,57 +118,6 @@ final class SearchViewModel: ACHudContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openReport(vehicle: VehicleDto) async {
|
|
||||||
guard let updatedVehicle = await coordinator?.openReport(vehicle: vehicle) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if let index = vehicles.firstIndex(where: { $0.number == updatedVehicle.number }) {
|
|
||||||
vehicles[index] = updatedVehicle
|
|
||||||
vehicleSections = vehicles.groupedByDate(type: .updatedDate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openFilterDetail() async {
|
|
||||||
guard let updatedFilter = await coordinator?.openFilterDetail(filter: filter) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
filter = updatedFilter
|
|
||||||
resetData()
|
|
||||||
await wrapWithToast { [weak self] in
|
|
||||||
guard let self else { return }
|
|
||||||
try await loadSearchResults(filter: filter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func showOnMap() {
|
|
||||||
coordinator?.showOnMap(filter: filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportSearchResults() async {
|
|
||||||
await wrapWithToast { [weak self] in
|
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp = try await apiService.getVehicles(with: filter, pageToken: nil, pageSize: 0)
|
|
||||||
|
|
||||||
let newLine = "\r\n"
|
|
||||||
var csvString = VehicleDto.csvHeader + newLine
|
|
||||||
|
|
||||||
for vehicle in resp.items {
|
|
||||||
csvString.append(vehicle.csvLine)
|
|
||||||
csvString.append(newLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
let tmpUrl = FileManager.default.tmpUrl(name: "search", ext: "csv")
|
|
||||||
try csvString.write(to: tmpUrl, atomically: true, encoding: .utf8)
|
|
||||||
|
|
||||||
coordinator?.export(url: tmpUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateVehicle(_ vehicle: VehicleDto) async {
|
func updateVehicle(_ vehicle: VehicleDto) async {
|
||||||
do {
|
do {
|
||||||
hud = .progress
|
hud = .progress
|
||||||
@ -189,4 +137,28 @@ final class SearchViewModel: ACHudContainer {
|
|||||||
hud = .error(error)
|
hud = .error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onVehicleChanged(_ vehicle: VehicleDto) {
|
||||||
|
|
||||||
|
if let index = vehicles.firstIndex(where: { $0.number == vehicle.number }) {
|
||||||
|
vehicles[index] = vehicle
|
||||||
|
vehicleSections = vehicles.groupedByDate(type: .updatedDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onFilterChanged(_ filter: Filter) {
|
||||||
|
Task {
|
||||||
|
await updateWithFilter(filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateWithFilter(_ filter: Filter) async {
|
||||||
|
|
||||||
|
self.filter = filter
|
||||||
|
resetData()
|
||||||
|
await wrapWithToast { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
try await loadSearchResults(filter: filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,8 +24,7 @@ class SettingsCoordinator {
|
|||||||
|
|
||||||
let viewModel = SettingsViewModel(settingsService: ServiceContainer.shared.resolve(SettingsServiceProtocol.self))
|
let viewModel = SettingsViewModel(settingsService: ServiceContainer.shared.resolve(SettingsServiceProtocol.self))
|
||||||
viewModel.coordinator = self
|
viewModel.coordinator = self
|
||||||
let controller = UIHostingController(rootView: SettingsScreen(viewModel: viewModel))
|
return UIHostingController(rootView: SettingsScreen(viewModel: viewModel))
|
||||||
return UINavigationController(rootViewController: controller)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func openAuthScreen() {
|
func openAuthScreen() {
|
||||||
|
|||||||
@ -16,85 +16,79 @@ struct SettingsScreen: View {
|
|||||||
@State var googleLoginSheetOpened = false
|
@State var googleLoginSheetOpened = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
NavigationStack {
|
||||||
Section {
|
Form {
|
||||||
TextRowView(title: "Version", value: Bundle.main.fullVersion)
|
Section {
|
||||||
}
|
TextRowView(title: "Version", value: Bundle.main.fullVersion)
|
||||||
|
}
|
||||||
Section("Profile") {
|
|
||||||
SimpleTextRowView(title: "AutoCat Account", value: viewModel.autocatEmail)
|
Section("Profile") {
|
||||||
SimpleTextRowView(title: "Google",
|
SimpleTextRowView(title: "AutoCat Account", value: viewModel.autocatEmail)
|
||||||
value: viewModel.googleEmail ?? "Log In",
|
SimpleTextRowView(title: "Google",
|
||||||
showArrow: true)
|
value: viewModel.googleEmail ?? "Log In",
|
||||||
.onTapGesture {
|
showArrow: true)
|
||||||
if viewModel.googleAuthorized {
|
.onTapGesture {
|
||||||
googleSheetOpened = true
|
if viewModel.googleAuthorized {
|
||||||
} else {
|
googleSheetOpened = true
|
||||||
googleLoginSheetOpened = true
|
} else {
|
||||||
|
googleLoginSheetOpened = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if viewModel.canSignOut {
|
||||||
|
Button("Sign Out", action: viewModel.signOut)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if viewModel.canSignOut {
|
Section {
|
||||||
Button("Sign Out", action: viewModel.signOut)
|
Picker("Server", selection: $viewModel.settingService.backend) {
|
||||||
}
|
ForEach(Constants.Backend.allCases, id: \.self) { backend in
|
||||||
}
|
Text(backend.name)
|
||||||
|
}
|
||||||
Section {
|
|
||||||
Picker("Server", selection: $viewModel.settingService.backend) {
|
|
||||||
ForEach(Constants.Backend.allCases, id: \.self) { backend in
|
|
||||||
Text(backend.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Section("Plate number recognition") {
|
||||||
Section("Plate number recognition") {
|
ToggleRowView(title: "Alternative order",
|
||||||
ToggleRowView(title: "Alternative order",
|
description: "Recognize plate numbers in alternative form. For example 'ЕВА 123 777' instead of 'Е123ВА 777'",
|
||||||
description: "Recognize plate numbers in alternative form. For example 'ЕВА 123 777' instead of 'Е123ВА 777'",
|
toggle: $viewModel.settingService.recognizeAlternativeOrder)
|
||||||
toggle: $viewModel.settingService.recognizeAlternativeOrder)
|
ToggleRowView(title: "Shortened numbers",
|
||||||
ToggleRowView(title: "Shortened numbers",
|
description: "If enabled, app will try to recognize shortened plate numbers (without region) and add default region",
|
||||||
description: "If enabled, app will try to recognize shortened plate numbers (without region) and add default region",
|
toggle: $viewModel.settingService.recognizeShortenedNumbers)
|
||||||
toggle: $viewModel.settingService.recognizeShortenedNumbers)
|
if viewModel.settingService.recognizeShortenedNumbers {
|
||||||
if viewModel.settingService.recognizeShortenedNumbers {
|
LabeledContent("Default region") {
|
||||||
LabeledContent("Default region") {
|
TextField("", text: $viewModel.settingService.defaultRegion)
|
||||||
TextField("", text: $viewModel.settingService.defaultRegion)
|
.frame(width: 50)
|
||||||
.frame(width: 50)
|
.multilineTextAlignment(.trailing)
|
||||||
.multilineTextAlignment(.trailing)
|
}
|
||||||
}
|
}
|
||||||
|
ToggleRowView(title: "Beep before record",
|
||||||
|
description: "When enabled, you will hear short sound before starting audio recording. This will only work when audio record is started via Siri",
|
||||||
|
toggle: $viewModel.settingService.recordBeep)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section("Debug") {
|
||||||
|
ToggleRowView(title: "Show debug info",
|
||||||
|
description: nil,
|
||||||
|
toggle: $viewModel.settingService.showDebugInfo)
|
||||||
}
|
}
|
||||||
ToggleRowView(title: "Beep before record",
|
|
||||||
description: "When enabled, you will hear short sound before starting audio recording. This will only work when audio record is started via Siri",
|
|
||||||
toggle: $viewModel.settingService.recordBeep)
|
|
||||||
}
|
}
|
||||||
|
.navigationTitle("Settings")
|
||||||
Section("Debug") {
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
ToggleRowView(title: "Show debug info",
|
.confirmationDialog(viewModel.googleUsername ?? "",
|
||||||
description: nil,
|
isPresented: $googleSheetOpened,
|
||||||
toggle: $viewModel.settingService.showDebugInfo)
|
titleVisibility: .visible) {
|
||||||
|
Button("Sign Out", role: .destructive) {
|
||||||
|
viewModel.googleSignout()
|
||||||
|
}
|
||||||
|
} message: {
|
||||||
|
if let email = viewModel.googleEmail {
|
||||||
|
Text("You are currently signed in with email \(email). It will help to gather more data about vehicles.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $googleLoginSheetOpened) {
|
||||||
|
GoogleAuthScreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Settings")
|
|
||||||
.confirmationDialog(viewModel.googleUsername ?? "",
|
|
||||||
isPresented: $googleSheetOpened,
|
|
||||||
titleVisibility: .visible) {
|
|
||||||
Button("Sign Out", role: .destructive) {
|
|
||||||
viewModel.googleSignout()
|
|
||||||
}
|
|
||||||
} message: {
|
|
||||||
if let email = viewModel.googleEmail {
|
|
||||||
Text("You are currently signed in with email \(email). It will help to gather more data about vehicles.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $googleLoginSheetOpened) {
|
|
||||||
GoogleAuthScreen()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
SettingsScreen(viewModel: .init(settingsService: MockSettingsServiceProtocol()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ struct NavigationLinkModifier: ViewModifier {
|
|||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
|
|
||||||
func navigationLink(isActive: Bool = true, onTap: (() -> Void)?) -> some View {
|
func navigationLink(isActive: Bool = true, onTap: (() -> Void)? = nil) -> some View {
|
||||||
modifier(NavigationLinkModifier(onTap: onTap, isActive: isActive))
|
modifier(NavigationLinkModifier(onTap: onTap, isActive: isActive))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@propertyWrapper
|
@propertyWrapper
|
||||||
public struct NullifyDate: Sendable {
|
public struct NullifyDate: Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var wrappedValue: Date? {
|
public var wrappedValue: Date? {
|
||||||
didSet {
|
didSet {
|
||||||
|
|||||||
@ -8,13 +8,13 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum DebugInfoStatus: Int, Sendable, Decodable, Equatable {
|
public enum DebugInfoStatus: Int, Sendable, Decodable, Equatable, Hashable {
|
||||||
case success = 0
|
case success = 0
|
||||||
case error = 1
|
case error = 1
|
||||||
case warning = 2
|
case warning = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct DebugInfoDto: Decodable, Sendable, Equatable {
|
public struct DebugInfoDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var autocod: DebugInfoEntryDto?
|
public var autocod: DebugInfoEntryDto?
|
||||||
public var vin01vin: DebugInfoEntryDto?
|
public var vin01vin: DebugInfoEntryDto?
|
||||||
@ -23,7 +23,7 @@ public struct DebugInfoDto: Decodable, Sendable, Equatable {
|
|||||||
public var nomerogram: DebugInfoEntryDto?
|
public var nomerogram: DebugInfoEntryDto?
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct DebugInfoEntryDto: Decodable, Sendable, Equatable {
|
public struct DebugInfoEntryDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var fields: Int64 = 0
|
public var fields: Int64 = 0
|
||||||
public var error: String?
|
public var error: String?
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct OsagoDto: Decodable, Sendable, Equatable {
|
public struct OsagoDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var date: TimeInterval = 0
|
public var date: TimeInterval = 0
|
||||||
public var number: String = ""
|
public var number: String = ""
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleBrandDto: Decodable, Sendable, Equatable {
|
public struct VehicleBrandDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var name: VehicleNameDto?
|
public var name: VehicleNameDto?
|
||||||
public var logo: String?
|
public var logo: String?
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleDto: Sendable, Equatable {
|
public struct VehicleDto: Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var brand: VehicleBrandDto?
|
public var brand: VehicleBrandDto?
|
||||||
public var model: VehicleModelDto?
|
public var model: VehicleModelDto?
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleEngineDto: Decodable, Sendable, Equatable {
|
public struct VehicleEngineDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var number: String?
|
public var number: String?
|
||||||
public var volume: Int? = 0
|
public var volume: Int? = 0
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleEventDto: Codable, Sendable, Equatable {
|
public struct VehicleEventDto: Codable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var id: String = UUID().uuidString
|
public var id: String = UUID().uuidString
|
||||||
public var date: TimeInterval = Date().timeIntervalSince1970
|
public var date: TimeInterval = Date().timeIntervalSince1970
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleModelDto: Decodable, Sendable, Equatable {
|
public struct VehicleModelDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var name: VehicleNameDto?
|
public var name: VehicleNameDto?
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleNameDto: Decodable, Sendable, Equatable {
|
public struct VehicleNameDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var original: String?
|
public var original: String?
|
||||||
public var normalized: String?
|
public var normalized: String?
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleNoteDto: Codable, Sendable, Identifiable, Equatable {
|
public struct VehicleNoteDto: Codable, Sendable, Identifiable, Equatable, Hashable {
|
||||||
|
|
||||||
public var id: String = UUID().uuidString
|
public var id: String = UUID().uuidString
|
||||||
public var user: String = ""
|
public var user: String = ""
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehicleOwnershipPeriodDto: Decodable, Sendable, Equatable {
|
public struct VehicleOwnershipPeriodDto: Decodable, Sendable, Equatable, Hashable {
|
||||||
|
|
||||||
public var lastOperation: String = ""
|
public var lastOperation: String = ""
|
||||||
public var ownerType: String = ""
|
public var ownerType: String = ""
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct VehiclePhotoDto: Decodable, Sendable, Equatable, Identifiable {
|
public struct VehiclePhotoDto: Decodable, Sendable, Equatable, Identifiable, Hashable {
|
||||||
|
|
||||||
public let id = UUID()
|
public let id = UUID()
|
||||||
public var brand: String?
|
public var brand: String?
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public enum SortOrder: String, CustomStringConvertible, CaseIterable, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SearchScope: Int, CaseIterable, Sendable {
|
public enum SearchScope: Int, CaseIterable, Sendable, Hashable {
|
||||||
|
|
||||||
case plateNumber = 0
|
case plateNumber = 0
|
||||||
case vin = 1
|
case vin = 1
|
||||||
@ -88,7 +88,7 @@ public enum StringOption: Hashable, Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Filter: Sendable {
|
public struct Filter: Sendable, Hashable {
|
||||||
public var searchString = ""
|
public var searchString = ""
|
||||||
public var brand: StringOption = .any
|
public var brand: StringOption = .any
|
||||||
public var model: StringOption = .any
|
public var model: StringOption = .any
|
||||||
|
|||||||
@ -60,6 +60,18 @@ public final class VehiclesArchive {
|
|||||||
vehicles = result.items
|
vehicles = result.items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeCsvData() async throws -> Data {
|
||||||
|
|
||||||
|
try await loadVehiclesIfNeeded()
|
||||||
|
let csvString = try makeCsvString()
|
||||||
|
|
||||||
|
if let data = csvString.data(using: .utf8){
|
||||||
|
return data
|
||||||
|
} else {
|
||||||
|
throw VehiclesArchiveError.filedCreateCsv
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension VehiclesArchive: Transferable {
|
extension VehiclesArchive: Transferable {
|
||||||
@ -72,15 +84,16 @@ extension VehiclesArchive: Transferable {
|
|||||||
|
|
||||||
DataRepresentation(exportedContentType: .commaSeparatedText) { archive in
|
DataRepresentation(exportedContentType: .commaSeparatedText) { archive in
|
||||||
|
|
||||||
try await archive.loadVehiclesIfNeeded()
|
try await archive.makeCsvData()
|
||||||
let csvString = try archive.makeCsvString()
|
|
||||||
|
|
||||||
if let data = csvString.data(using: .utf8){
|
|
||||||
return data
|
|
||||||
} else {
|
|
||||||
throw VehiclesArchiveError.filedCreateCsv
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.suggestedFileName(fileName)
|
.suggestedFileName(fileName)
|
||||||
|
|
||||||
|
FileRepresentation(exportedContentType: .commaSeparatedText) { archive in
|
||||||
|
|
||||||
|
let data = try await archive.makeCsvData()
|
||||||
|
let url = FileManager.default.tmpUrl(name: "search", ext: "csv")
|
||||||
|
try data.write(to: url)
|
||||||
|
return SentTransferredFile(url, allowAccessingOriginalFile: false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user