mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 20:39:24 +00:00
Refactor and redesign Location Sharing Screen (#5155)
* refactored the static location screen to the location sharing screen # Conflicts: # UnitTests/Sources/LocationSharingScreenViewModelTests.swift # Conflicts: # ElementX.xcodeproj/project.pbxproj * implemented a custom pin with an overlayable view * implemented the render of the user when the location is sender instead of the pin type * removed description and body they are not used at all. * reimplemented single button for sharing this or user location + implemented an experimental way to update annotations * removed unnecessary @Suite description * implemented a way to display the alert on top of the sheet and added a spinner to the center user location button * fixed alerts strings * fixed a failing test * improved preview tests
This commit is contained in:
@@ -20,10 +20,10 @@ struct MapLibreMapView: UIViewRepresentable {
|
||||
/// The initial map center
|
||||
let mapCenter: CLLocationCoordinate2D
|
||||
|
||||
/// Map annotations
|
||||
let annotations: [LocationAnnotation]
|
||||
/// Map annotations keyed by a stable identifier (e.g. sender userID for user pins, UUID string for generic pins)
|
||||
let annotations: [String: LocationAnnotation]
|
||||
|
||||
init(zoomLevel: Double, initialZoomLevel: Double, mapCenter: CLLocationCoordinate2D, annotations: [LocationAnnotation] = []) {
|
||||
init(zoomLevel: Double, initialZoomLevel: Double, mapCenter: CLLocationCoordinate2D, annotations: [String: LocationAnnotation] = [:]) {
|
||||
self.zoomLevel = zoomLevel
|
||||
self.initialZoomLevel = initialZoomLevel
|
||||
self.mapCenter = mapCenter
|
||||
@@ -45,6 +45,7 @@ struct MapLibreMapView: UIViewRepresentable {
|
||||
@Binding var error: MapLibreError?
|
||||
/// Coordinate of the center of the map
|
||||
@Binding var mapCenterCoordinate: CLLocationCoordinate2D?
|
||||
@Binding var hasLoadedUserLocation: Bool
|
||||
@Binding var isLocationAuthorized: Bool?
|
||||
/// The radius of uncertainty for the location, measured in meters.
|
||||
@Binding var geolocationUncertainty: CLLocationAccuracy?
|
||||
@@ -70,6 +71,11 @@ struct MapLibreMapView: UIViewRepresentable {
|
||||
mapView.styleURL = dynamicMapURL
|
||||
}
|
||||
|
||||
// Update existing annotation views with fresh SwiftUI content.
|
||||
// This handles the case where the annotation's view data changes after
|
||||
// the annotation was initially placed (e.g. user avatar loads asynchronously).
|
||||
updateAnnotations(in: mapView)
|
||||
|
||||
showUserLocation(in: mapView)
|
||||
}
|
||||
|
||||
@@ -80,17 +86,52 @@ struct MapLibreMapView: UIViewRepresentable {
|
||||
// MARK: - Private
|
||||
|
||||
private func setupMap(mapView: MLNMapView, with options: Options) {
|
||||
mapView.addAnnotations(options.annotations)
|
||||
mapView.addAnnotations(Array(options.annotations.values))
|
||||
mapView.zoomLevel = options.annotations.isEmpty ? options.initialZoomLevel : options.zoomLevel
|
||||
mapView.centerCoordinate = options.mapCenter
|
||||
}
|
||||
|
||||
private func updateAnnotations(in mapView: MLNMapView) {
|
||||
let existingByID = Dictionary(uniqueKeysWithValues:
|
||||
(mapView.annotations ?? []).compactMap { $0 as? LocationAnnotation }.map { ($0.id, $0) })
|
||||
|
||||
let existingIDs = Set(existingByID.keys)
|
||||
let updatedIDs = Set(options.annotations.keys)
|
||||
|
||||
// Remove annotations that are no longer present
|
||||
let removedIDs = existingIDs.subtracting(updatedIDs)
|
||||
if !removedIDs.isEmpty {
|
||||
let toRemove = removedIDs.compactMap { existingByID[$0] }
|
||||
mapView.removeAnnotations(toRemove)
|
||||
}
|
||||
|
||||
// Add new annotations
|
||||
let addedIDs = updatedIDs.subtracting(existingIDs)
|
||||
if !addedIDs.isEmpty {
|
||||
let toAdd = addedIDs.compactMap { options.annotations[$0] }
|
||||
mapView.addAnnotations(toAdd)
|
||||
}
|
||||
|
||||
// Update existing annotations that are still present
|
||||
let keptIDs = existingIDs.intersection(updatedIDs)
|
||||
for id in keptIDs {
|
||||
guard let existingAnnotation = existingByID[id],
|
||||
let updatedAnnotation = options.annotations[id] else {
|
||||
continue
|
||||
}
|
||||
existingAnnotation.coordinate = updatedAnnotation.coordinate
|
||||
if let annotationView = mapView.view(for: existingAnnotation) as? LocationAnnotationView {
|
||||
annotationView.updateContent(with: updatedAnnotation.view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeMapView() -> MLNMapView {
|
||||
let mapView = MLNMapView(frame: .zero, styleURL: mapURLBuilder.interactiveMapURL(for: colorScheme == .dark ? .dark : .light))
|
||||
mapView.logoViewPosition = .topLeft
|
||||
mapView.attributionButtonPosition = .topLeft
|
||||
mapView.attributionButtonMargins = .init(x: mapView.logoView.frame.maxX + 8, y: mapView.logoView.center.y / 2)
|
||||
mapView.tintColor = .black
|
||||
mapView.tintColor = .compound.iconAccentPrimary
|
||||
mapView.allowsRotating = false
|
||||
mapView.allowsTilting = false
|
||||
return mapView
|
||||
@@ -149,7 +190,8 @@ extension MapLibreMapView {
|
||||
|
||||
func mapView(_ mapView: MLNMapView, didUpdate userLocation: MLNUserLocation?) {
|
||||
guard let userLocation else { return }
|
||||
|
||||
mapLibreView.hasLoadedUserLocation = true
|
||||
|
||||
if previousUserLocation == nil, mapLibreView.options.annotations.isEmpty {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
mapView.setCenter(userLocation.coordinate, zoomLevel: self.mapLibreView.options.zoomLevel, animated: true)
|
||||
|
||||
Reference in New Issue
Block a user