From 5f71e62be8591a9b67ef288f210ab2dc2d373c6b Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Fri, 13 Sep 2024 11:41:39 +0300 Subject: [PATCH] Set location issue (#147) * Fixed rerender issue * Fixed reverse geocoded address * Fixed wrong coordinates resolved from mapbox * Propagate map pin coordinate * Fixed device info refresh * Fixed delegate implementation * Temp fix for CI --------- Co-authored-by: Pantelis Giazitsis --- .../SelectLocationMapView.swift | 3 +- .../SelectLocationMapViewModel.swift | 32 +++++++++++++++---- .../MapBox/MapBoxClaimDevice.swift | 21 +++++++----- .../ClaimDeviceLocationViewModel.swift | 2 +- .../DeviceInfo/DeviceInfoRowView.swift | 1 + .../SelectStationLocationViewModel.swift | 10 +++--- ci_scripts/ci_post_clone.sh | 2 +- .../LocationRepositoryImpl.swift | 21 +++++++----- .../DomainModels/DeviceLocation.swift | 4 +-- 9 files changed, 63 insertions(+), 33 deletions(-) diff --git a/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapView.swift b/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapView.swift index 09a740c40..865917959 100644 --- a/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapView.swift +++ b/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapView.swift @@ -19,7 +19,8 @@ struct SelectLocationMapView: View { MapBoxClaimDeviceView(location: $viewModel.selectedCoordinate, annotationTitle: Binding(get: { viewModel.selectedDeviceLocation?.name }, set: { _ in }), - geometryProxyForFrameOfMapView: proxy.frame(in: .local)) + geometryProxyForFrameOfMapView: proxy.frame(in: .local), + mapControls: viewModel.mapControls) searchArea } diff --git a/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift b/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift index 586cea494..3cf651ac8 100644 --- a/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift +++ b/PresentationLayer/UIComponents/BaseComponents/SelectLocationMap/SelectLocationMapViewModel.swift @@ -11,18 +11,30 @@ import Combine import DomainLayer protocol SelectLocationMapViewModelDelegate: AnyObject { - func updatedSelectedLocation(location: DeviceLocation?) + func updatedResolvedLocation(location: DeviceLocation?) + func updatedSelectedCoordinate(coordinate: CLLocationCoordinate2D?) +} + +// Make protocol functions optional +extension SelectLocationMapViewModelDelegate { + func updatedResolvedLocation(location: DeviceLocation?) {} + func updatedSelectedCoordinate(coordinate: CLLocationCoordinate2D?) {} } class SelectLocationMapViewModel: ObservableObject { - @Published var selectedCoordinate: CLLocationCoordinate2D + @Published var selectedCoordinate: CLLocationCoordinate2D { + didSet { + delegate?.updatedSelectedCoordinate(coordinate: selectedCoordinate) + } + } @Published private(set) var selectedDeviceLocation: DeviceLocation? { didSet { - delegate?.updatedSelectedLocation(location: selectedDeviceLocation) + delegate?.updatedResolvedLocation(location: selectedDeviceLocation) } } @Published var searchTerm: String = "" @Published private(set) var searchResults: [DeviceLocationSearchResult] = [] + var mapControls: MapControls = .init() private var cancellableSet: Set = .init() private var latestTask: Cancellable? let useCase: DeviceLocationUseCase @@ -54,17 +66,21 @@ class SelectLocationMapViewModel: ObservableObject { func handleSearchResultTap(result: DeviceLocationSearchResult) { latestTask?.cancel() latestTask = useCase.locationFromSearchResult(result).sink { [weak self] location in - self?.selectedCoordinate = location.coordinates.toCLLocationCoordinate2D() + self?.mapControls.setMapCenter?(location.coordinates.toCLLocationCoordinate2D()) } } func moveToUserLocation() { - Task { - let result = await useCase.getUserLocation() + Task { [weak self] in + guard let self else { + return + } + + let result = await self.useCase.getUserLocation() DispatchQueue.main.async { switch result { case .success(let coordinates): - self.selectedCoordinate = coordinates + self.mapControls.setMapCenter?(coordinates) case .failure(let error): switch error { case .locationNotFound: @@ -85,7 +101,9 @@ class SelectLocationMapViewModel: ObservableObject { private extension SelectLocationMapViewModel { func getLocationFromCoordinate() { latestTask?.cancel() + print("selectedCoordinate: \(selectedCoordinate)" ) latestTask = useCase.locationFromCoordinates(LocationCoordinates.fromCLLocationCoordinate2D(selectedCoordinate)).sink { [weak self] location in + print("selectedDeviceLocation: \(location?.coordinates)" ) self?.selectedDeviceLocation = location } } diff --git a/PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift b/PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift index 9d27d4e15..8165d1025 100644 --- a/PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift +++ b/PresentationLayer/UIComponents/MapBox/MapBoxClaimDevice.swift @@ -21,14 +21,16 @@ struct MapBoxClaimDeviceView: View { private let markerSize: CGSize = CGSize(width: 44.0, height: 44.0) @State private var locationPoint: CGPoint = .zero @State private var markerViewSize: CGSize = .zero - @StateObject private var controls: MapBoxClaimDevice.MapControls = .init() + @StateObject private var controls: MapControls init(location: Binding, annotationTitle: Binding, - geometryProxyForFrameOfMapView: CGRect) { + geometryProxyForFrameOfMapView: CGRect, + mapControls: MapControls) { _location = location _annotationTitle = annotationTitle self.geometryProxyForFrameOfMapView = geometryProxyForFrameOfMapView + _controls = StateObject(wrappedValue: mapControls) } var body: some View { @@ -103,19 +105,21 @@ private struct MapBoxClaimDevice: UIViewControllerRepresentable { controller?.zoomOut() } + controls.setMapCenter = { [weak controller] coordinate in + controller?.setCenter(coordinate) + } + return controller } func updateUIViewController(_ controller: MapViewLocationController, context _: Context) { - controller.setCenter(location) } } -extension MapBoxClaimDevice { - class MapControls: ObservableObject { - var zoomInAction: VoidCallback? - var zoomOutAction: VoidCallback? - } +class MapControls: ObservableObject { + var zoomInAction: VoidCallback? + var zoomOutAction: VoidCallback? + var setMapCenter: GenericCallback? } class MapViewLocationController: UIViewController { @@ -182,6 +186,7 @@ class MapViewLocationController: UIViewController { } }.store(in: &cancelablesSet) + setCenter(location) } func setCenter(_ center: CLLocationCoordinate2D) { diff --git a/PresentationLayer/UIComponents/Screens/ClaimDevice/Location/ClaimDeviceLocationViewModel.swift b/PresentationLayer/UIComponents/Screens/ClaimDevice/Location/ClaimDeviceLocationViewModel.swift index 74cb737b9..c214f208a 100644 --- a/PresentationLayer/UIComponents/Screens/ClaimDevice/Location/ClaimDeviceLocationViewModel.swift +++ b/PresentationLayer/UIComponents/Screens/ClaimDevice/Location/ClaimDeviceLocationViewModel.swift @@ -33,7 +33,7 @@ class ClaimDeviceLocationViewModel: ObservableObject { } extension ClaimDeviceLocationViewModel: SelectLocationMapViewModelDelegate { - func updatedSelectedLocation(location: DeviceLocation?) { + func updatedResolvedLocation(location: DeviceLocation?) { self.selectedLocation = location } } diff --git a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoRowView.swift b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoRowView.swift index c3b1f4413..31643dc91 100644 --- a/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoRowView.swift +++ b/PresentationLayer/UIComponents/Screens/DeviceInfo/DeviceInfoRowView.swift @@ -83,6 +83,7 @@ extension DeviceInfoRowView { static func == (lhs: DeviceInfoRowView.Row, rhs: DeviceInfoRowView.Row) -> Bool { lhs.title == rhs.title && lhs.description == rhs.description && + lhs.imageUrl == rhs.imageUrl && lhs.buttonInfo == rhs.buttonInfo && lhs.warning == rhs.warning } diff --git a/PresentationLayer/UIComponents/Screens/SelectStationLocation/SelectStationLocationViewModel.swift b/PresentationLayer/UIComponents/Screens/SelectStationLocation/SelectStationLocationViewModel.swift index 05d69f826..d690c1b7d 100644 --- a/PresentationLayer/UIComponents/Screens/SelectStationLocation/SelectStationLocationViewModel.swift +++ b/PresentationLayer/UIComponents/Screens/SelectStationLocation/SelectStationLocationViewModel.swift @@ -20,7 +20,7 @@ class SelectStationLocationViewModel: ObservableObject { let deviceLocationUseCase: DeviceLocationUseCase let meUseCase: MeUseCase @Published var termsAccepted: Bool = false - @Published private(set) var selectedDeviceLocation: DeviceLocation? + @Published private(set) var selectedCoordinate: CLLocationCoordinate2D? @Published var isSuccessful: Bool = false private(set) var successObj: FailSuccessStateObject = .emptyObj let locationViewModel: SelectLocationMapViewModel @@ -42,7 +42,7 @@ class SelectStationLocationViewModel: ObservableObject { } func handleConfirmTap() { - guard let selectedCoordinate = selectedDeviceLocation?.coordinates.toCLLocationCoordinate2D(), + guard let selectedCoordinate, deviceLocationUseCase.areLocationCoordinatesValid(LocationCoordinates.fromCLLocationCoordinate2D(selectedCoordinate)) else { Toast.shared.show(text: LocalizableString.invalidLocationErrorText.localized.attributedMarkdown ?? "") return @@ -53,14 +53,14 @@ class SelectStationLocationViewModel: ObservableObject { } extension SelectStationLocationViewModel: SelectLocationMapViewModelDelegate { - func updatedSelectedLocation(location: DeviceLocation?) { - self.selectedDeviceLocation = location + func updatedSelectedCoordinate(coordinate: CLLocationCoordinate2D?) { + self.selectedCoordinate = coordinate } } private extension SelectStationLocationViewModel { func setLocation() { - guard let deviceId = device.id, let selectedCoordinate = selectedDeviceLocation?.coordinates.toCLLocationCoordinate2D() else { + guard let deviceId = device.id, let selectedCoordinate else { return } diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh index d78f11435..98d064755 100644 --- a/ci_scripts/ci_post_clone.sh +++ b/ci_scripts/ci_post_clone.sh @@ -34,7 +34,7 @@ echo "password ${MAPBOX_TOKEN}" >> ~/.netrc if ([ "$CI_WORKFLOW" = "QA Production" ]) || ([ "$CI_WORKFLOW" = "QA Dev" ]) || ([ "$CI_WORKFLOW" = "Dev Build" ]); then echo "Install Firebase CLI" -curl -Lo ./firebase-tools-macos https://firebase.tools/bin/macos/latest +curl -Lo ./firebase-tools-macos https://github.com/firebase/firebase-tools/releases/download/v13.16.0/firebase-tools-macos chmod +x ./firebase-tools-macos DEBUG_CONFIGURATION_PATH=${CI_PRIMARY_REPOSITORY_PATH}/Configuration/Debug/ConfigDebug.xcconfig diff --git a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift index 86d7c0946..a3a8c921f 100644 --- a/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift +++ b/wxm-ios/DataLayer/DataLayer/RepositoryImplementations/LocationRepositoryImpl.swift @@ -114,7 +114,7 @@ public class DeviceLocationRepositoryImpl: DeviceLocationRepository { return } - self.reverseGeocodedAddressSubject.send(actualAddress.toLocation()) + self.reverseGeocodedAddressSubject.send(actualAddress.toLocation(with: coordinates.toCLLocationCoordinate2D())) case let .failure(error): if let locationError = error.toDeviceLocationError() { self.errorSubject.send(locationError) @@ -190,21 +190,26 @@ extension SearchSuggestion { } extension SearchResult { - func toLocation() -> DeviceLocation { - var parsedAddress = name - - if let houseNumber = address?.houseNumber { - parsedAddress += " \(houseNumber)" - } + func toLocation(with locationCoordinate: CLLocationCoordinate2D? = nil) -> DeviceLocation { + let parsedAddress = isAccurate ? address?.formattedAddress(style: .medium) : nil return DeviceLocation( id: id, name: parsedAddress, country: address?.country, countryCode: metadata?["iso_3166_1"], - coordinates: LocationCoordinates.fromCLLocationCoordinate2D(coordinate) + coordinates: LocationCoordinates.fromCLLocationCoordinate2D(locationCoordinate ?? coordinate) ) } + + var isAccurate: Bool { + switch accuracy { + case .point, .rooftop, .street: + true + default: + false + } + } } public extension LocationCoordinates { diff --git a/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceLocation.swift b/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceLocation.swift index ba69524e9..ef71a64bc 100644 --- a/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceLocation.swift +++ b/wxm-ios/DomainLayer/DomainLayer/Entities/DomainModels/DeviceLocation.swift @@ -30,12 +30,12 @@ public struct DeviceLocationSearchResult: Identifiable, Equatable, Hashable { public struct DeviceLocation: Identifiable, Equatable { public let id: String - public let name: String + public let name: String? public let country: String? public let countryCode: String? public let coordinates: LocationCoordinates - public init(id: String, name: String, country: String?, countryCode: String?, coordinates: LocationCoordinates) { + public init(id: String, name: String?, country: String?, countryCode: String?, coordinates: LocationCoordinates) { self.id = id self.name = name self.country = country