|
@@ -18,32 +18,24 @@ struct PaywallView: View {
|
|
|
subscriptions.isPremiumActive
|
|
subscriptions.isPremiumActive
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private var isSelectedPlanLifetime: Bool {
|
|
|
|
|
- viewModel.selectedPlan == .lifetime
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
private var canPurchaseSelectedPlan: Bool {
|
|
private var canPurchaseSelectedPlan: Bool {
|
|
|
guard isPremium else { return true }
|
|
guard isPremium else { return true }
|
|
|
guard subscriptions.hasManageableSubscription else { return false }
|
|
guard subscriptions.hasManageableSubscription else { return false }
|
|
|
guard let activePlan = subscriptions.activeRecurringPlan ?? subscriptions.activePlan else {
|
|
guard let activePlan = subscriptions.activeRecurringPlan ?? subscriptions.activePlan else {
|
|
|
- return viewModel.selectedPlan == .lifetime
|
|
|
|
|
|
|
+ return false
|
|
|
}
|
|
}
|
|
|
return viewModel.selectedPlan != activePlan
|
|
return viewModel.selectedPlan != activePlan
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private var ctaDescription: String {
|
|
private var ctaDescription: String {
|
|
|
if isPremium, !subscriptions.hasManageableSubscription {
|
|
if isPremium, !subscriptions.hasManageableSubscription {
|
|
|
- return "You already have lifetime access."
|
|
|
|
|
|
|
+ return "You already have premium access."
|
|
|
}
|
|
}
|
|
|
if subscriptions.hasManageableSubscription,
|
|
if subscriptions.hasManageableSubscription,
|
|
|
let activePlan = subscriptions.activeRecurringPlan ?? subscriptions.activePlan,
|
|
let activePlan = subscriptions.activeRecurringPlan ?? subscriptions.activePlan,
|
|
|
viewModel.selectedPlan == activePlan {
|
|
viewModel.selectedPlan == activePlan {
|
|
|
return "You're currently on the \(subscriptions.displayTitle(for: activePlan)) plan."
|
|
return "You're currently on the \(subscriptions.displayTitle(for: activePlan)) plan."
|
|
|
}
|
|
}
|
|
|
- if subscriptions.hasManageableSubscription,
|
|
|
|
|
- viewModel.selectedPlan == .lifetime {
|
|
|
|
|
- return "Upgrade to lifetime with a one-time payment. Your current subscription won't cancel automatically — cancel it after purchasing to avoid further charges."
|
|
|
|
|
- }
|
|
|
|
|
return subscriptions.billingDescription(for: viewModel.selectedPlan)
|
|
return subscriptions.billingDescription(for: viewModel.selectedPlan)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -107,9 +99,6 @@ struct PaywallView: View {
|
|
|
.clearHostingBackground()
|
|
.clearHostingBackground()
|
|
|
.task {
|
|
.task {
|
|
|
await subscriptions.loadProducts(presentLoadingUI: true)
|
|
await subscriptions.loadProducts(presentLoadingUI: true)
|
|
|
- if subscriptions.hasManageableSubscription {
|
|
|
|
|
- viewModel.selectPlan(.lifetime)
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
.alert(
|
|
.alert(
|
|
|
"Subscriptions",
|
|
"Subscriptions",
|
|
@@ -137,27 +126,6 @@ struct PaywallView: View {
|
|
|
} message: {
|
|
} message: {
|
|
|
Text(subscriptions.subscriptionSuccessMessage ?? "")
|
|
Text(subscriptions.subscriptionSuccessMessage ?? "")
|
|
|
}
|
|
}
|
|
|
- .alert(
|
|
|
|
|
- "Cancel your subscription",
|
|
|
|
|
- isPresented: Binding(
|
|
|
|
|
- get: { subscriptions.showsCancelSubscriptionReminder },
|
|
|
|
|
- set: { presented in
|
|
|
|
|
- if !presented { subscriptions.clearCancelSubscriptionReminder() }
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- ) {
|
|
|
|
|
- Button("Manage Subscription") {
|
|
|
|
|
- subscriptions.openSubscriptionManagement()
|
|
|
|
|
- subscriptions.clearCancelSubscriptionReminder()
|
|
|
|
|
- onClose()
|
|
|
|
|
- }
|
|
|
|
|
- Button("Not Now", role: .cancel) {
|
|
|
|
|
- subscriptions.clearCancelSubscriptionReminder()
|
|
|
|
|
- onClose()
|
|
|
|
|
- }
|
|
|
|
|
- } message: {
|
|
|
|
|
- Text(subscriptions.cancelSubscriptionReminderMessage)
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private var premiumTitleSection: some View {
|
|
private var premiumTitleSection: some View {
|
|
@@ -181,12 +149,12 @@ struct PaywallView: View {
|
|
|
|
|
|
|
|
private var premiumStatusSubtitle: String {
|
|
private var premiumStatusSubtitle: String {
|
|
|
if subscriptions.hasManageableSubscription, let plan = subscriptions.activeRecurringPlan ?? subscriptions.activePlan {
|
|
if subscriptions.hasManageableSubscription, let plan = subscriptions.activeRecurringPlan ?? subscriptions.activePlan {
|
|
|
- return "Your \(subscriptions.displayTitle(for: plan)) plan is active. Upgrade to lifetime or switch plans below."
|
|
|
|
|
|
|
+ return "Your \(subscriptions.displayTitle(for: plan)) plan is active. Switch plans below."
|
|
|
}
|
|
}
|
|
|
if subscriptions.hasManageableSubscription {
|
|
if subscriptions.hasManageableSubscription {
|
|
|
- return "Your subscription is active. Upgrade to lifetime or switch plans below."
|
|
|
|
|
|
|
+ return "Your subscription is active. Switch plans below."
|
|
|
}
|
|
}
|
|
|
- return "You have lifetime access to all premium features."
|
|
|
|
|
|
|
+ return "You have premium access to all features."
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private var closeButton: some View {
|
|
private var closeButton: some View {
|
|
@@ -257,13 +225,8 @@ struct PaywallView: View {
|
|
|
Button {
|
|
Button {
|
|
|
Task {
|
|
Task {
|
|
|
let purchased = await subscriptions.purchase(viewModel.selectedPlan)
|
|
let purchased = await subscriptions.purchase(viewModel.selectedPlan)
|
|
|
- if purchased {
|
|
|
|
|
- if subscriptions.showsCancelSubscriptionReminder {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if subscriptions.isPremiumActive {
|
|
|
|
|
- onClose()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if purchased, subscriptions.isPremiumActive {
|
|
|
|
|
+ onClose()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
} label: {
|
|
} label: {
|
|
@@ -306,7 +269,7 @@ struct PaywallView: View {
|
|
|
.buttonStyle(.plain)
|
|
.buttonStyle(.plain)
|
|
|
.onHover { isCTAHovered = $0 }
|
|
.onHover { isCTAHovered = $0 }
|
|
|
|
|
|
|
|
- Text(isSelectedPlanLifetime ? "One-time purchase, yours forever." : "No commitment, cancel anytime.")
|
|
|
|
|
|
|
+ Text("No commitment, cancel anytime.")
|
|
|
.font(.system(size: 13, weight: .semibold))
|
|
.font(.system(size: 13, weight: .semibold))
|
|
|
.foregroundStyle(Color(red: 0.58, green: 0.61, blue: 0.66))
|
|
.foregroundStyle(Color(red: 0.58, green: 0.61, blue: 0.66))
|
|
|
}
|
|
}
|
|
@@ -314,16 +277,14 @@ struct PaywallView: View {
|
|
|
|
|
|
|
|
private var legalSection: some View {
|
|
private var legalSection: some View {
|
|
|
VStack(spacing: 12) {
|
|
VStack(spacing: 12) {
|
|
|
- if !isSelectedPlanLifetime {
|
|
|
|
|
- VStack(spacing: 2) {
|
|
|
|
|
- Text("Your subscription will automatically renew unless auto-renew is turned off at least 24-hours before the end of the current subscription period.")
|
|
|
|
|
- Text("Payment will be charged to your Apple ID at confirmation of purchase.")
|
|
|
|
|
- }
|
|
|
|
|
- .padding(.top, -8)
|
|
|
|
|
- .font(.system(size: 12, weight: .medium))
|
|
|
|
|
- .foregroundStyle(Color(red: 0.59, green: 0.62, blue: 0.68))
|
|
|
|
|
- .multilineTextAlignment(.center)
|
|
|
|
|
|
|
+ VStack(spacing: 2) {
|
|
|
|
|
+ Text("Your subscription will automatically renew unless auto-renew is turned off at least 24-hours before the end of the current subscription period.")
|
|
|
|
|
+ Text("Payment will be charged to your Apple ID at confirmation of purchase.")
|
|
|
}
|
|
}
|
|
|
|
|
+ .padding(.top, -8)
|
|
|
|
|
+ .font(.system(size: 12, weight: .medium))
|
|
|
|
|
+ .foregroundStyle(Color(red: 0.59, green: 0.62, blue: 0.68))
|
|
|
|
|
+ .multilineTextAlignment(.center)
|
|
|
|
|
|
|
|
HStack {
|
|
HStack {
|
|
|
PaywallFooterLinkView(
|
|
PaywallFooterLinkView(
|
|
@@ -469,10 +430,10 @@ private struct PaywallPlanCardView: View {
|
|
|
if let tagText {
|
|
if let tagText {
|
|
|
Text(tagText)
|
|
Text(tagText)
|
|
|
.font(.system(size: 12, weight: .semibold))
|
|
.font(.system(size: 12, weight: .semibold))
|
|
|
- .foregroundStyle(tagText == "Pay Once" ? Color(red: 0.53, green: 0.39, blue: 0.90) : Color(red: 0.07, green: 0.60, blue: 0.46))
|
|
|
|
|
|
|
+ .foregroundStyle(Color(red: 0.07, green: 0.60, blue: 0.46))
|
|
|
.padding(.horizontal, 12)
|
|
.padding(.horizontal, 12)
|
|
|
.padding(.vertical, 5)
|
|
.padding(.vertical, 5)
|
|
|
- .background(tagText == "Pay Once" ? Color(red: 0.94, green: 0.91, blue: 0.99) : Color(red: 0.91, green: 0.98, blue: 0.95))
|
|
|
|
|
|
|
+ .background(Color(red: 0.91, green: 0.98, blue: 0.95))
|
|
|
.clipShape(Capsule())
|
|
.clipShape(Capsule())
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|