Move providers paywall to picker (#764)

Paywall on module creation suggests that OpenVPN modules are a paid
feature.
This commit is contained in:
Davide 2024-10-28 19:55:42 +01:00 committed by GitHub
parent 639dee55ee
commit ecb0348b90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 55 additions and 12 deletions

View File

@ -151,12 +151,12 @@ extension IAPManager {
features.allSatisfy(eligibleFeatures.contains)
}
// public func isEligible(forProvider providerName: ProviderName) -> Bool {
// guard providerName != .oeck else {
// return true
// }
// return isEligible(forFeature: providerName.product)
// }
public func isEligible(forProvider providerId: ProviderID) -> Bool {
if providerId == .oeck {
return true
}
return isEligible(for: .providers)
}
public func isEligibleForFeedback() -> Bool {
#if os(tvOS)

View File

@ -473,6 +473,10 @@ public enum Strings {
/// Loading...
public static let loading = Strings.tr("Localizable", "providers.last_updated.loading", fallback: "Loading...")
}
public enum Picker {
/// Add more providers
public static let purchase = Strings.tr("Localizable", "providers.picker.purchase", fallback: "Add more providers")
}
public enum Vpn {
/// No servers
public static let noServers = Strings.tr("Localizable", "providers.vpn.no_servers", fallback: "No servers")

View File

@ -175,7 +175,6 @@
"modules.general.sections.storage.footer" = "Profiles are stored to iCloud encrypted.";
"modules.general.rows.icloud_sharing" = "Shared on iCloud";
"modules.general.rows.icloud_sharing.purchase" = "Share on iCloud";
"modules.dns.servers.add" = "Add address";
"modules.dns.search_domains.add" = "Add domain";
@ -187,7 +186,6 @@
"modules.ip.routes.excluded" = "Excluded routes";
"modules.ip.routes.add_family" = "Add %@";
"modules.on_demand.purchase" = "Add on-demand rules";
"modules.on_demand.policy" = "Policy";
"modules.on_demand.policy.footer" = "Activate the VPN %@.";
"modules.on_demand.policy.footer.any" = "in any network";
@ -213,7 +211,6 @@
"modules.openvpn.randomize_endpoint" = "Randomize endpoint";
"modules.openvpn.randomize_hostname" = "Randomize hostname";
"modules.openvpn.credentials.interactive" = "Interactive";
"modules.openvpn.credentials.interactive.purchase" = "Log in interactively";
"modules.openvpn.credentials.interactive.footer" = "On-demand will be disabled.";
"modules.openvpn.credentials.otp_method.approach.append" = "The OTP will be appended to the password.";
"modules.openvpn.credentials.otp_method.approach.encode" = "The OTP will be encoded in Base64 with the password.";
@ -242,6 +239,13 @@
"ui.connection_status.on_demand_suffix" = " (on-demand)";
"ui.profile_context.connect_to" = "Connect to...";
// MARK: - Paywalls
"modules.general.rows.icloud_sharing.purchase" = "Share on iCloud";
"modules.on_demand.purchase" = "Add on-demand rules";
"modules.openvpn.credentials.interactive.purchase" = "Log in interactively";
"providers.picker.purchase" = "Add more providers";
// MARK: - Alerts
"alerts.iap.restricted.title" = "Restricted";

View File

@ -38,6 +38,9 @@ struct OpenVPNView: View, ModuleDraftEditing {
private let isServerPushed: Bool
@State
private var paywallReason: PaywallReason?
init(serverConfiguration: OpenVPN.Configuration) {
let module = OpenVPNModule.Builder(configurationBuilder: serverConfiguration.builder())
let editor = ProfileEditor(modules: [module])
@ -56,6 +59,7 @@ struct OpenVPNView: View, ModuleDraftEditing {
var body: some View {
contentView
.moduleView(editor: editor, draft: draft.wrappedValue, withName: !isServerPushed)
.modifier(PaywallModifier(reason: $paywallReason))
.navigationDestination(for: Subroute.self, destination: destination)
}
}
@ -82,6 +86,7 @@ private extension OpenVPNView {
providerId: providerId,
selectedEntity: providerEntity,
isRequired: true,
paywallReason: $paywallReason,
entityDestination: Subroute.providerServer,
providerRows: {
moduleGroup(for: providerAccountRows)

View File

@ -113,7 +113,7 @@ private extension ProfileCoordinator {
paywallReason = iapManager.paywallReason(forFeature: .routing)
case .openVPN, .wireGuard:
paywallReason = iapManager.paywallReason(forFeature: .providers)
break
case .onDemand:
break

View File

@ -31,6 +31,9 @@ struct ProviderContentModifier<Entity, ProviderRows>: ViewModifier where Entity:
@EnvironmentObject
private var providerManager: ProviderManager
@EnvironmentObject
private var iapManager: IAPManager
let apis: [APIMapper]
@Binding
@ -40,6 +43,9 @@ struct ProviderContentModifier<Entity, ProviderRows>: ViewModifier where Entity:
let isRequired: Bool
@Binding
var paywallReason: PaywallReason?
@ViewBuilder
let providerRows: ProviderRows
@ -71,9 +77,14 @@ struct ProviderContentModifier<Entity, ProviderRows>: ViewModifier where Entity:
private extension ProviderContentModifier {
#if os(iOS)
@ViewBuilder
var providerView: some View {
Group {
providerPicker
purchaseButton
}
.themeSection()
Group {
if providerId != nil {
providerRows
refreshButton {
@ -90,9 +101,13 @@ private extension ProviderContentModifier {
.themeSection(footer: lastUpdatedString)
}
#else
@ViewBuilder
var providerView: some View {
Group {
Section {
providerPicker
purchaseButton
}
Section {
if providerId != nil {
providerRows
HStack {
@ -119,6 +134,15 @@ private extension ProviderContentModifier {
)
}
@ViewBuilder
var purchaseButton: some View {
if let reason = iapManager.paywallReason(forFeature: .providers) {
Button(Strings.Providers.Picker.purchase) {
paywallReason = reason
}
}
}
func refreshButton<Label>(label: () -> Label) -> some View where Label: View {
Button(action: onRefreshInfrastructure, label: label)
}
@ -127,7 +151,7 @@ private extension ProviderContentModifier {
providerManager
.providers
.filter {
$0.supports(Entity.Configuration.self)
iapManager.isEligible(forProvider: $0.id) && $0.supports(Entity.Configuration.self)
}
}
@ -198,6 +222,7 @@ private extension ProviderContentModifier {
providerId: .constant(.hideme),
entityType: VPNEntity<OpenVPN.Configuration>.self,
isRequired: false,
paywallReason: .constant(nil),
providerRows: {},
onSelectProvider: { _, _, _ in }
))

View File

@ -40,6 +40,9 @@ struct VPNProviderContentModifier<Configuration, Destination, ProviderRows>: Vie
let isRequired: Bool
@Binding
var paywallReason: PaywallReason?
let entityDestination: Destination
@ViewBuilder
@ -53,6 +56,7 @@ struct VPNProviderContentModifier<Configuration, Destination, ProviderRows>: Vie
providerId: $providerId,
entityType: VPNEntity<Configuration>.self,
isRequired: isRequired,
paywallReason: $paywallReason,
providerRows: {
providerEntityRow
providerRows
@ -96,6 +100,7 @@ private extension VPNProviderContentModifier {
providerId: .constant(.hideme),
selectedEntity: .constant(nil as VPNEntity<OpenVPN.Configuration>?),
isRequired: false,
paywallReason: .constant(nil),
entityDestination: "Destination",
providerRows: {
Text("Other")