From d7749bb5da54e6815040f46f376a5b62acd6483a Mon Sep 17 00:00:00 2001 From: Frank Rotermund Date: Tue, 9 Jan 2024 11:02:02 +0100 Subject: [PATCH] MESSENGER-5379 dynamic location attribution from style.json --- .../RoomTimelineLocationView.swift | 12 +++++++ .../View/LiveLocationSharingViewer.swift | 11 +++++-- .../MapCredits/MapCreditsActionSheet.swift | 30 ++++++++++++----- .../MapCredits/View/MapCreditsView.swift | 22 +++++++++++-- .../MapView/View/LocationSharingMapView.swift | 20 +++++++++++ .../LocationSharingModels.swift | 3 ++ .../LocationSharingViewModel.swift | 2 ++ .../View/LocationSharingView.swift | 12 +++++-- .../StaticLocationViewingModels.swift | 3 ++ .../StaticLocationViewingViewModel.swift | 2 ++ .../View/StaticLocationView.swift | 15 ++++++--- .../LocationSharingAttribution.swift | 33 +++++++++++++++++++ 12 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 bwi/LocationSharingAttribution/LocationSharingAttribution.swift diff --git a/Riot/Modules/Room/TimelineCells/LocationView/RoomTimelineLocationView.swift b/Riot/Modules/Room/TimelineCells/LocationView/RoomTimelineLocationView.swift index 25a90260e..e156ad922 100644 --- a/Riot/Modules/Room/TimelineCells/LocationView/RoomTimelineLocationView.swift +++ b/Riot/Modules/Room/TimelineCells/LocationView/RoomTimelineLocationView.swift @@ -410,6 +410,18 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat self.isMapViewLoadingFailed = false } + func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) { + // bwi #5379: set copyright to info from style json if available. Needs reevaluation if there is more than one source + + for source in style.sources { + if let tileSource = source as? MGLRasterTileSource { + for attributionInfo in tileSource.attributionInfos { + attributionLabel.text = attributionLabel.text?.appending(attributionInfo.title.plainTextString()) + } + } + } + } + // MARK: - Action @IBAction private func didTapTightButton(_ sender: Any) { diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift index 2e51df3ce..c9a75768b 100644 --- a/RiotSwiftUI/Modules/LocationSharing/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift @@ -68,7 +68,9 @@ struct LiveLocationSharingViewer: View { if viewModel.viewState.isBottomSheetVisible { VStack(alignment: .center) { Spacer() - MapCreditsView(action: { + // bwi #5379: we don't use live location sharing, if this changes we need to use attribution from viewmodel here + MapCreditsView(attributions: LocationSharingAttribution(), + action: { viewModel.send(viewAction: .mapCreditsDidTap) }) .offset(y: -bottomSheetCollapsedHeight) // Put the copyright action above the collapsed bottom sheet @@ -88,7 +90,8 @@ struct LiveLocationSharingViewer: View { // Show map credits only if map is visible if !viewModel.viewState.showMapLoadingError { - MapCreditsView(action: { + MapCreditsView(attributions:LocationSharingAttribution(), + action: { viewModel.send(viewAction: .mapCreditsDidTap) }) .padding(.bottom, 5) @@ -122,7 +125,9 @@ struct LiveLocationSharingViewer: View { .background(theme.colors.system.ignoresSafeArea()) .bottomSheet(sheet, if: viewModel.viewState.isBottomSheetVisible) .actionSheet(isPresented: $viewModel.showMapCreditsSheet) { - MapCreditsActionSheet(openURL: { url in + MapCreditsActionSheet( + attribution: LocationSharingAttribution(), + openURL: { url in UIApplication.shared.vc_open(url, completionHandler: nil) }).sheet } diff --git a/RiotSwiftUI/Modules/LocationSharing/MapCredits/MapCreditsActionSheet.swift b/RiotSwiftUI/Modules/LocationSharing/MapCredits/MapCreditsActionSheet.swift index 31fa36cdb..854a5d08e 100644 --- a/RiotSwiftUI/Modules/LocationSharing/MapCredits/MapCreditsActionSheet.swift +++ b/RiotSwiftUI/Modules/LocationSharing/MapCredits/MapCreditsActionSheet.swift @@ -17,20 +17,32 @@ import SwiftUI struct MapCreditsActionSheet { + // bwi #5379 dynamic attribution from style.json + let attribution: LocationSharingAttribution + // Open URL action let openURL: (URL) -> Void // Map credits action sheet var sheet: ActionSheet { ActionSheet(title: Text(BWIL10n.locationSharingMapCreditsTitle), - buttons: [ - .default(Text("© MapTiler")) { - openURL(URL(string: "https://www.maptiler.com/copyright/")!) - }, - .default(Text("© OpenStreetMap")) { - openURL(URL(string: "https://www.openstreetmap.org/copyright")!) - }, - .cancel() - ]) + buttons: self.creditButtons(attribution: attribution)) + } + + func creditButtons( attribution: LocationSharingAttribution) -> [ActionSheet.Button] { + + var buttons = [ActionSheet.Button]() + + // bwi #5379 a bit scetchy but you can asume that url and text have the same index + for (index, copyright) in attribution.copyrightTexts.enumerated() { + buttons.append(.default(Text(copyright)) { + if let url = attribution.copyrightLinks[index] { + openURL(url) + } + }) + } + buttons.append(.cancel()) + + return buttons } } diff --git a/RiotSwiftUI/Modules/LocationSharing/MapCredits/View/MapCreditsView.swift b/RiotSwiftUI/Modules/LocationSharing/MapCredits/View/MapCreditsView.swift index 8add1c00e..a53466f53 100644 --- a/RiotSwiftUI/Modules/LocationSharing/MapCredits/View/MapCreditsView.swift +++ b/RiotSwiftUI/Modules/LocationSharing/MapCredits/View/MapCreditsView.swift @@ -25,25 +25,41 @@ struct MapCreditsView: View { // MARK: Public + // MARK: Public + + // bwi #5379 use dynamic attributions from style.json + @ObservedObject var attributions:LocationSharingAttribution + var action: (() -> Void)? var body: some View { HStack { - Spacer() + Spacer() Button { action?() } label: { - Text(BWIL10n.locationSharingCopyrightLabel) + Text(copyrightText(attribution:attributions)) .font(theme.fonts.footnote) .foregroundColor(theme.colors.accent) } .padding(.horizontal) } } + + func copyrightText( attribution: LocationSharingAttribution) -> String { + var copyright = "" + + for copyrightText in attribution.copyrightTexts { + copyright.append(copyrightText) + } + + return copyright + } } struct MapCreditsView_Previews: PreviewProvider { static var previews: some View { - MapCreditsView() + + MapCreditsView(attributions: LocationSharingAttribution(copyrightTexts: [""], copyrightLinks: [URL(string: "www.someurl.org")])) } } diff --git a/RiotSwiftUI/Modules/LocationSharing/MapView/View/LocationSharingMapView.swift b/RiotSwiftUI/Modules/LocationSharing/MapView/View/LocationSharingMapView.swift index 7a357b911..6375c8959 100644 --- a/RiotSwiftUI/Modules/LocationSharing/MapView/View/LocationSharingMapView.swift +++ b/RiotSwiftUI/Modules/LocationSharing/MapView/View/LocationSharingMapView.swift @@ -68,6 +68,9 @@ struct LocationSharingMapView: UIViewRepresentable { /// Called when the user pan on the map var userDidPan: (() -> Void)? + + // bwi #5379: for copyright attribution + var attributionsChanged: ((LocationSharingAttribution) -> Void)? // MARK: - UIViewRepresentable @@ -154,6 +157,23 @@ extension LocationSharingMapView { return nil } + func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) { + // bwi #5379: set copyright to info from style json if available. Needs reevaluation if there is more than one source + + var attribution = LocationSharingAttribution() + + for source in style.sources { + if let tileSource = source as? MGLRasterTileSource { + for attributionInfo in tileSource.attributionInfos { + attribution.copyrightTexts.append(attributionInfo.title.plainTextString()) + attribution.copyrightLinks.append(attributionInfo.url) + } + } + } + + locationSharingMapView.attributionsChanged?(attribution) + } + func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) { locationSharingMapView.errorSubject.send(.failedLoadingMap) } diff --git a/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingModels.swift b/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingModels.swift index 0c623230c..913673f0c 100644 --- a/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingModels.swift +++ b/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingModels.swift @@ -41,6 +41,7 @@ enum LocationSharingViewAction { case shareLiveLocation(timeout: LiveLocationSharingTimeout) case userDidPan case mapCreditsDidTap + case showMapCredit(mapAttribution: LocationSharingAttribution) } enum LocationSharingViewModelResult { @@ -95,6 +96,8 @@ struct LocationSharingViewState: BindableState { let errorSubject = PassthroughSubject() var bindings = LocationSharingViewStateBindings() + + var attribution = LocationSharingAttribution() } struct LocationSharingViewStateBindings { diff --git a/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingViewModel.swift b/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingViewModel.swift index 8c8d7a078..57c432580 100644 --- a/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingViewModel.swift +++ b/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/LocationSharingViewModel.swift @@ -91,6 +91,8 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie state.isPinDropSharing = true case .mapCreditsDidTap: state.bindings.showMapCreditsSheet.toggle() + case .showMapCredit(mapAttribution: let mapAttribution): + state.attribution = mapAttribution } } diff --git a/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/View/LocationSharingView.swift b/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/View/LocationSharingView.swift index ef6e33604..33c4ab4ee 100644 --- a/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/View/LocationSharingView.swift +++ b/RiotSwiftUI/Modules/LocationSharing/StartLocationSharing/View/LocationSharingView.swift @@ -43,12 +43,15 @@ struct LocationSharingView: View { } else { // Show map credits only if map is visible - MapCreditsView(action: { + MapCreditsView( + attributions:context.viewState.attribution, + action: { context.send(viewAction: .mapCreditsDidTap) }) .padding(.bottom, 10.0) .actionSheet(isPresented: $context.showMapCreditsSheet) { - MapCreditsActionSheet(openURL: { url in + MapCreditsActionSheet(attribution:context.viewState.attribution, + openURL: { url in UIApplication.shared.vc_open(url, completionHandler: nil) }).sheet } @@ -101,7 +104,10 @@ struct LocationSharingView: View { errorSubject: context.viewState.errorSubject, userDidPan: { context.send(viewAction: .userDidPan) - }) + }, + attributionsChanged: { attribution in + context.send(viewAction: .showMapCredit(mapAttribution: attribution)) + }) if context.viewState.isPinDropSharing { LocationSharingMarkerView(backgroundColor: theme.colors.accent) { if BWIBuildSettings.shared.bwiEnableBuMUI { diff --git a/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingModels.swift b/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingModels.swift index c91487950..ded12042a 100644 --- a/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingModels.swift +++ b/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingModels.swift @@ -25,6 +25,7 @@ enum StaticLocationViewingViewAction { case share case mapCreditsDidTap case showUserLocation + case showMapCredit(mapAttribution: LocationSharingAttribution) } enum StaticLocationViewingViewModelResult { @@ -60,6 +61,8 @@ struct StaticLocationViewingViewState: BindableState { let errorSubject = PassthroughSubject() var bindings = StaticLocationViewingViewBindings() + + var attribution = LocationSharingAttribution() } struct StaticLocationViewingViewBindings { diff --git a/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingViewModel.swift b/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingViewModel.swift index cd998665b..fdcf484b0 100644 --- a/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingViewModel.swift +++ b/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/StaticLocationViewingViewModel.swift @@ -71,6 +71,8 @@ class StaticLocationViewingViewModel: StaticLocationViewingViewModelType, Static state.bindings.showMapCreditsSheet.toggle() case .showUserLocation: showsCurrentUserLocation() + case .showMapCredit(mapAttribution: let mapAttribution): + state.attribution = mapAttribution } } diff --git a/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/View/StaticLocationView.swift b/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/View/StaticLocationView.swift index 9c94a6f32..7e89148a7 100644 --- a/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/View/StaticLocationView.swift +++ b/RiotSwiftUI/Modules/LocationSharing/StaticLocationSharingViewer/View/StaticLocationView.swift @@ -39,7 +39,10 @@ struct StaticLocationView: View { showsUserLocationMode: viewModel.viewState.showsUserLocationMode, userLocation: Binding.constant(nil), mapCenterCoordinate: Binding.constant(nil), - errorSubject: viewModel.viewState.errorSubject) + errorSubject: viewModel.viewState.errorSubject, + attributionsChanged: { attribution in + viewModel.send(viewAction: .showMapCredit(mapAttribution: attribution)) + }) } var body: some View { @@ -52,13 +55,17 @@ struct StaticLocationView: View { showsUserLocationMode: ShowUserLocationMode.hide, userLocation: Binding.constant(nil), mapCenterCoordinate: Binding.constant(nil), - errorSubject: viewModel.viewState.errorSubject) - MapCreditsView(action: { + errorSubject: viewModel.viewState.errorSubject, + attributionsChanged: { attribution in + viewModel.send(viewAction: .showMapCredit(mapAttribution: attribution)) + }) + MapCreditsView(attributions: viewModel.viewState.attribution, action: { viewModel.send(viewAction: .mapCreditsDidTap) }) .padding(.bottom, 10.0 + safeAreaInsets.bottom) .actionSheet(isPresented: $viewModel.showMapCreditsSheet) { - MapCreditsActionSheet(openURL: { url in + MapCreditsActionSheet(attribution:viewModel.viewState.attribution, + openURL: { url in UIApplication.shared.vc_open(url, completionHandler: nil) }).sheet } diff --git a/bwi/LocationSharingAttribution/LocationSharingAttribution.swift b/bwi/LocationSharingAttribution/LocationSharingAttribution.swift new file mode 100644 index 000000000..d300eef0c --- /dev/null +++ b/bwi/LocationSharingAttribution/LocationSharingAttribution.swift @@ -0,0 +1,33 @@ +// +/* + * Copyright (c) 2022 BWI GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Foundation + +class LocationSharingAttribution: ObservableObject { + var copyrightTexts: [String] + var copyrightLinks: [URL?] + + init() { + copyrightTexts = [] + copyrightLinks = [] + } + + init( copyrightTexts: [String], copyrightLinks: [URL?]) { + self.copyrightLinks = copyrightLinks + self.copyrightTexts = copyrightTexts + } +}