Update paywall (#441)

Group features and drop platform purchases.
This commit is contained in:
Davide De Rosa 2023-12-23 12:10:34 +01:00 committed by GitHub
parent 239d3e6853
commit 7d7aaa8b0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 77 deletions

View File

@ -372,6 +372,14 @@ extension View {
.foregroundColor(themeSecondaryColor)
}
func themeCellTitleStyle() -> some View {
font(.headline)
}
func themeCellSubtitleStyle() -> some View {
font(.subheadline)
}
func themeDebugLogStyle() -> some View {
font(.system(size: 13, weight: .medium, design: .monospaced))
}

View File

@ -45,11 +45,11 @@ extension OrganizerView {
return HStack {
VStack(alignment: .leading, spacing: 5) {
Text(profile.header.name)
.font(.headline)
.themeCellTitleStyle()
.themeLongTextStyle()
VPNStatusText(isActiveProfile: isActiveProfile)
.font(.subheadline)
.themeCellSubtitleStyle()
.themeSecondaryTextStyle()
}
Spacer()

View File

@ -52,7 +52,10 @@ extension PaywallView {
var body: some View {
List {
productsSection
featuresSection
purchaseSection
.disabled(purchaseState != nil)
restoreSection
.disabled(purchaseState != nil)
}.navigationTitle(Unlocalized.appName)
@ -77,38 +80,41 @@ private struct PurchaseRow: View {
let title: String
let extra: String?
let action: () -> Void
let purchaseState: PaywallView.PurchaseView.PurchaseState?
var body: some View {
VStack(alignment: .leading) {
actionButton
.padding(.bottom, 5)
extra.map {
Text($0)
.frame(maxHeight: .infinity)
}
}.padding([.top, .bottom])
actionButton
}
}
private typealias RowModel = (product: InAppProduct, extra: String?)
// MARK: -
private extension PaywallView.PurchaseView {
var productsSection: some View {
var featuresSection: some View {
Section {
ForEach(features, id: \.productIdentifier) { product in
VStack(alignment: .leading) {
Text(product.localizedTitle)
.themeCellTitleStyle()
if product.localizedDescription != product.localizedTitle {
Text(product.localizedDescription)
.themeCellSubtitleStyle()
.themeSecondaryTextStyle()
}
}
}
}
}
var purchaseSection: some View {
Section {
if !productManager.isRefreshingProducts {
ForEach(productRowModels, id: \.product.productIdentifier, content: productRow)
ForEach(productRowModels, id: \.productIdentifier, content: productRow)
} else {
ProgressView()
}
restoreRow
} header: {
Text(L10n.Paywall.title)
} footer: {
@ -116,13 +122,20 @@ private extension PaywallView.PurchaseView {
}
}
func productRow(_ model: RowModel) -> some View {
var restoreSection: some View {
Section {
restoreRow
} footer: {
Text(L10n.Paywall.Items.Restore.description)
}
}
func productRow(_ product: InAppProduct) -> some View {
PurchaseRow(
product: model.product,
title: model.product.localizedTitle,
extra: model.extra,
product: product,
title: product.localizedTitle,
action: {
purchaseProduct(model.product)
purchaseProduct(product)
},
purchaseState: purchaseState
)
@ -131,7 +144,6 @@ private extension PaywallView.PurchaseView {
var restoreRow: some View {
PurchaseRow(
title: L10n.Paywall.Items.Restore.title,
extra: L10n.Paywall.Items.Restore.description,
action: restorePurchases,
purchaseState: purchaseState
)
@ -139,20 +151,6 @@ private extension PaywallView.PurchaseView {
}
private extension PaywallView.PurchaseView {
var skFeature: InAppProduct? {
guard let feature = feature else {
return nil
}
return productManager.product(withIdentifier: feature)
}
var skPlatformVersion: InAppProduct? {
#if targetEnvironment(macCatalyst)
productManager.product(withIdentifier: .fullVersion_macOS)
#else
productManager.product(withIdentifier: .fullVersion_iOS)
#endif
}
// hide full version if already bought the other platform version
var skFullVersion: InAppProduct? {
@ -168,43 +166,18 @@ private extension PaywallView.PurchaseView {
return productManager.product(withIdentifier: .fullVersion)
}
var platformVersionExtra: [String] {
productManager.featureProducts(excluding: [
.fullVersion,
.fullVersion_iOS,
.fullVersion_macOS
]).map {
$0.localizedTitle
}.sorted {
$0.lowercased() < $1.lowercased()
var features: [InAppProduct] {
productManager.featureProducts(excluding: {
$0 == .fullVersion || $0.isPlatformVersion
})
.sorted {
$0.localizedTitle.lowercased() < $1.localizedTitle.lowercased()
}
}
var fullVersionExtra: [String] {
productManager.featureProducts(including: [
.fullVersion_iOS,
.fullVersion_macOS
]).map {
$0.localizedTitle
}.sorted {
$0.lowercased() < $1.lowercased()
}
}
var productRowModels: [RowModel] {
var models: [RowModel] = []
skPlatformVersion.map {
let extra = platformVersionExtra.joined(separator: "\n")
models.append(($0, extra))
}
skFullVersion.map {
let extra = fullVersionExtra.joined(separator: "\n")
models.append(($0, extra))
}
skFeature.map {
models.append(($0, nil))
}
return models
var productRowModels: [InAppProduct] {
[skFullVersion]
.compactMap { $0 }
}
}

View File

@ -132,12 +132,12 @@ public final class ProductManager: NSObject, ObservableObject {
inApp.product(withIdentifier: identifier)
}
public func featureProducts(including: [LocalProduct]) -> [InAppProduct] {
public func featureProducts(including: (LocalProduct) -> Bool) -> [InAppProduct] {
inApp.products().filter {
guard let p = LocalProduct(rawValue: $0.productIdentifier) else {
return false
}
guard including.contains(p) else {
guard including(p) else {
return false
}
guard p.isFeature else {
@ -147,12 +147,12 @@ public final class ProductManager: NSObject, ObservableObject {
}
}
public func featureProducts(excluding: [LocalProduct]) -> [InAppProduct] {
public func featureProducts(excluding: (LocalProduct) -> Bool) -> [InAppProduct] {
inApp.products().filter {
guard let p = LocalProduct(rawValue: $0.productIdentifier) else {
return false
}
guard !excluding.contains(p) else {
guard !excluding(p) else {
return false
}
guard p.isFeature else {