-
Notifications
You must be signed in to change notification settings - Fork 160
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CP] filterable attributions (#2375)
- Loading branch information
Showing
20 changed files
with
532 additions
and
319 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import Foundation | ||
import UIKit | ||
@_implementationOnly import MapboxCommon_Private | ||
|
||
/// API for attribution menu configuration | ||
/// Restricted API. Please contact Mapbox to discuss your use case if you intend to use this property. | ||
@_spi(Restricted) | ||
public class AttributionMenu { | ||
private let urlOpener: AttributionURLOpener | ||
private let feedbackURLRef: Ref<URL?> | ||
|
||
/// Filters attribution menu items based on the provided closure. | ||
public var filter: ((AttributionMenuItem) -> Bool)? | ||
|
||
init( | ||
urlOpener: AttributionURLOpener, | ||
feedbackURLRef: Ref<URL?>, | ||
filter: ((AttributionMenuItem) -> Bool)? = nil | ||
) { | ||
self.urlOpener = urlOpener | ||
self.filter = filter | ||
self.feedbackURLRef = feedbackURLRef | ||
} | ||
} | ||
|
||
extension AttributionMenu { | ||
var isMetricsEnabled: Bool { | ||
get { UserDefaults.standard.MGLMapboxMetricsEnabled } | ||
set { UserDefaults.standard.MGLMapboxMetricsEnabled = newValue } | ||
} | ||
|
||
internal func menu(from attributions: [Attribution]) -> AttributionMenuSection { | ||
var elements = [AttributionMenuElement]() | ||
let items = attributions.compactMap { attribution in | ||
switch attribution.kind { | ||
case .actionable(let url): | ||
return AttributionMenuItem(title: attribution.localizedTitle, id: .copyright, category: .main) { [weak self] in | ||
self?.urlOpener.openAttributionURL(url) | ||
} | ||
case .nonActionable: | ||
return AttributionMenuItem(title: attribution.localizedTitle, id: .copyright, category: .main) | ||
case .feedback: | ||
guard let feedbackURL = feedbackURLRef.value else { return nil } | ||
return AttributionMenuItem(title: attribution.localizedTitle, id: .contribute, category: .main) { [weak self] in | ||
self?.urlOpener.openAttributionURL(feedbackURL) | ||
} | ||
} | ||
} | ||
let menuSubtitle: String? | ||
if items.count == 1, let item = items.first, item.action == nil { | ||
menuSubtitle = item.title | ||
} else { | ||
menuSubtitle = nil | ||
elements.append(contentsOf: items.map(AttributionMenuElement.item)) | ||
} | ||
|
||
elements.append(.section(telemetryMenu)) | ||
|
||
elements.append(.item(privacyPolicyItem)) | ||
elements.append(.item(cancelItem)) | ||
|
||
let mainTitle = Bundle.mapboxMaps.localizedString( | ||
forKey: "SDK_NAME", | ||
value: "Powered by Mapbox", | ||
table: Ornaments.localizableTableName | ||
) | ||
|
||
return AttributionMenuSection(title: mainTitle, subtitle: menuSubtitle, category: .main, elements: elements) | ||
} | ||
|
||
private var cancelItem: AttributionMenuItem { | ||
let cancelTitle = NSLocalizedString("ATTRIBUTION_CANCEL", | ||
tableName: Ornaments.localizableTableName, | ||
bundle: .mapboxMaps, | ||
value: "Cancel", | ||
comment: "Title of button for dismissing attribution action sheet") | ||
|
||
return AttributionMenuItem(title: cancelTitle, style: .cancel, id: .cancel, category: .main) { } | ||
} | ||
|
||
private var privacyPolicyItem: AttributionMenuItem { | ||
let privacyPolicyTitle = NSLocalizedString("ATTRIBUTION_PRIVACY_POLICY", | ||
tableName: Ornaments.localizableTableName, | ||
bundle: .mapboxMaps, | ||
value: "Mapbox Privacy Policy", | ||
comment: "Privacy policy action in attribution sheet") | ||
|
||
return AttributionMenuItem(title: privacyPolicyTitle, id: .privacyPolicy, category: .main) { [weak self] in | ||
self?.urlOpener.openAttributionURL(Attribution.privacyPolicyURL) | ||
} | ||
} | ||
|
||
private var telemetryMenu: AttributionMenuSection { | ||
let telemetryTitle = TelemetryStrings.telemetryTitle | ||
let telemetryURL = URL(string: Ornaments.telemetryURL)! | ||
let message: String | ||
let participateTitle: String | ||
let declineTitle: String | ||
|
||
if isMetricsEnabled { | ||
message = TelemetryStrings.telemetryEnabledMessage | ||
participateTitle = TelemetryStrings.telemetryEnabledOnMessage | ||
declineTitle = TelemetryStrings.telemetryEnabledOffMessage | ||
} else { | ||
message = TelemetryStrings.telemetryDisabledMessage | ||
participateTitle = TelemetryStrings.telemetryDisabledOnMessage | ||
declineTitle = TelemetryStrings.telemetryDisabledOffMessage | ||
} | ||
|
||
return AttributionMenuSection(title: telemetryTitle, actionTitle: TelemetryStrings.telemetryName, subtitle: message, category: .telemetry, elements: [ | ||
AttributionMenuItem(title: TelemetryStrings.telemetryMore, id: .telemetryInfo, category: .telemetry) { [weak self] in | ||
self?.urlOpener.openAttributionURL(telemetryURL) | ||
}, | ||
AttributionMenuItem(title: declineTitle, id: .disable, category: .telemetry) { [weak self] in | ||
self?.isMetricsEnabled = false | ||
}, | ||
AttributionMenuItem(title: participateTitle, style: .cancel, id: .enable, category: .telemetry) { [weak self] in | ||
self?.isMetricsEnabled = true | ||
} | ||
].map(AttributionMenuElement.item)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import Foundation | ||
import UIKit | ||
|
||
/// A menu item entry in the attribution list. | ||
@_spi(Restricted) | ||
public struct AttributionMenuItem { | ||
|
||
/// Denotes a category(section) that item belongs to. | ||
public struct Category: RawRepresentable { | ||
public let rawValue: String | ||
|
||
public init(rawValue: String) { | ||
self.rawValue = rawValue | ||
} | ||
|
||
/// Main(root) category | ||
public static let main = Category(rawValue: "com.mapbox.maps.attribution.main") | ||
|
||
/// Category for opting in/out of telemetry | ||
public static let telemetry = Category(rawValue: "com.mapbox.maps.attribution.telemetry") | ||
|
||
/// Category for opting in/out of geofencing | ||
public static let geofencing = Category(rawValue: "com.mapbox.maps.attribution.geofencing") | ||
} | ||
|
||
/// Denotes an identifier of an item | ||
public struct ID: RawRepresentable { | ||
public let rawValue: String | ||
|
||
public init(rawValue: String) { | ||
self.rawValue = rawValue | ||
} | ||
|
||
/// Item attributing a copyright | ||
public static let copyright = ID(rawValue: "com.mapbox.maps.attribution.copyright") | ||
|
||
/// Represents an item opening a contribution form | ||
public static let contribute = ID(rawValue: "com.mapbox.maps.attribution.contribute") | ||
|
||
/// Opens privacy policy page | ||
public static let privacyPolicy = ID(rawValue: "com.mapbox.maps.attribution.privacyPolicy") | ||
|
||
/// Opens page with the info about Mapbox telemetry | ||
public static let telemetryInfo = ID(rawValue: "com.mapbox.maps.attribution.telemetryInfo") | ||
|
||
/// Item that enables a certain option, typically associated with a category | ||
/// e.g. `category: .telemetry, id: .enable` | ||
public static let enable = ID(rawValue: "com.mapbox.maps.attribution.enable") | ||
|
||
/// Item that disables a certain option, typically associated with a category | ||
/// e.g. `category: .telemetry, id: .disable` | ||
public static let disable = ID(rawValue: "com.mapbox.maps.attribution.disable") | ||
|
||
/// Item that dismisses the attribution menu | ||
public static let cancel = ID(rawValue: "com.mapbox.maps.attribution.cancel") | ||
} | ||
|
||
/// Title of the attribution menu item | ||
public let title: String | ||
|
||
/// Identifier of the item | ||
public let id: ID | ||
|
||
/// Category of the item | ||
public let category: Category | ||
|
||
let action: (() -> Void)? | ||
let style: Style | ||
|
||
init(title: String, style: Style = .default, id: ID, category: Category, action: (() -> Void)? = nil) { | ||
self.title = title | ||
self.id = id | ||
self.category = category | ||
self.action = action | ||
self.style = style | ||
} | ||
} | ||
|
||
extension AttributionMenuItem { | ||
enum Style { | ||
case `default` | ||
case cancel | ||
|
||
var uiActionStyle: UIAlertAction.Style { | ||
switch self { | ||
case .default: return .default | ||
case .cancel: return .cancel | ||
} | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
Sources/MapboxMaps/Attribution/AttributionMenuSection.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import Foundation | ||
import UIKit | ||
|
||
indirect enum AttributionMenuElement { | ||
case section(AttributionMenuSection) | ||
case item(AttributionMenuItem) | ||
} | ||
|
||
internal struct AttributionMenuSection { | ||
var title: String | ||
var actionTitle: String? | ||
var subtitle: String? | ||
var category: AttributionMenuItem.Category | ||
var elements: [AttributionMenuElement] | ||
|
||
init(title: String, actionTitle: String? = nil, subtitle: String? = nil, category: AttributionMenuItem.Category, elements: [AttributionMenuElement]) { | ||
self.title = title | ||
self.actionTitle = actionTitle | ||
self.subtitle = subtitle | ||
self.category = category | ||
self.elements = elements | ||
} | ||
|
||
mutating func filter(_ filter: (AttributionMenuItem) -> Bool) { | ||
elements = elements.compactMap { element in | ||
switch element { | ||
case .item(let item): | ||
return filter(item) ? .item(item) : nil | ||
case .section(var section): | ||
section.filter(filter) | ||
return .section(section) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.