This is required to be accepted by the appstore Suggested-by: Apple store reviewer Signed-off-by: William Casarin <jb55@jb55.com>
189 lines
7.6 KiB
Swift
189 lines
7.6 KiB
Swift
//
|
||
// PurchasedProductView.swift
|
||
// damus
|
||
//
|
||
// Created by Daniel D’Aquino on 2024-02-09.
|
||
//
|
||
|
||
import SwiftUI
|
||
import StoreKit
|
||
|
||
// MARK: - IAPProductStateView
|
||
|
||
extension DamusPurpleView {
|
||
typealias PurchasedProduct = DamusPurple.StoreKitManager.PurchasedProduct
|
||
static let SHOW_IAP_DEBUG_INFO = false
|
||
|
||
struct IAPProductStateView: View {
|
||
var products: ProductState
|
||
var purchased: PurchasedProduct?
|
||
let account_uuid: UUID
|
||
let subscribe: (Product) async throws -> Void
|
||
|
||
@State var show_manage_subscriptions = false
|
||
@State var subscription_purchase_loading = false
|
||
|
||
var body: some View {
|
||
if subscription_purchase_loading {
|
||
HStack(spacing: 10) {
|
||
Text(NSLocalizedString("Purchasing", comment: "Loading label indicating the purchase action is in progress"))
|
||
.foregroundStyle(.white)
|
||
ProgressView()
|
||
.progressViewStyle(.circular)
|
||
.tint(.white)
|
||
}
|
||
}
|
||
else {
|
||
switch self.products {
|
||
case .failed:
|
||
PurpleViewPrimitives.ProductLoadErrorView()
|
||
case .loaded(let products):
|
||
if let purchased {
|
||
PurchasedView(purchased)
|
||
} else {
|
||
ProductsView(products)
|
||
}
|
||
case .loading:
|
||
ProgressView()
|
||
.progressViewStyle(.circular)
|
||
}
|
||
}
|
||
}
|
||
|
||
func PurchasedView(_ purchased: PurchasedProduct) -> some View {
|
||
return Group {
|
||
if self.account_uuid == purchased.tx.appAccountToken {
|
||
// If the In-app purchase is registered to this account, show options to manage the subscription
|
||
PurchasedManageView(purchased)
|
||
}
|
||
else {
|
||
// If the In-app purchase is registered to a different account, we cannot manage the subscription
|
||
// This seems to be a limitation of StoreKit where we can only have one renewable subscription for a product at a time.
|
||
// Therefore, instruct the user about this limitation, or to contact us if they believe this is a mistake.
|
||
PurchasedUnmanageableView(purchased)
|
||
}
|
||
}
|
||
}
|
||
|
||
func PurchasedUnmanageableView(_ purchased: PurchasedProduct) -> some View {
|
||
Text(NSLocalizedString("This device's in-app purchase is registered to a different Nostr account. Unable to manage this Purple account. If you believe this was a mistake, please contact us via support@damus.io.", comment: "Notice label that user cannot manage their In-App purchases"))
|
||
.font(.caption)
|
||
.foregroundColor(.white.opacity(0.6))
|
||
.multilineTextAlignment(.center)
|
||
.padding(.horizontal)
|
||
}
|
||
|
||
func PurchasedManageView(_ purchased: PurchasedProduct) -> some View {
|
||
VStack(spacing: 10) {
|
||
if SHOW_IAP_DEBUG_INFO == true {
|
||
Text(NSLocalizedString("Purchased!", comment: "User purchased a subscription"))
|
||
.font(.title2)
|
||
.foregroundColor(.white)
|
||
price_description(product: purchased.product)
|
||
.foregroundColor(.white)
|
||
.opacity(0.65)
|
||
.frame(width: 200)
|
||
Text(NSLocalizedString("Purchased on", comment: "Indicating when the user purchased the subscription"))
|
||
.font(.title2)
|
||
.foregroundColor(.white)
|
||
Text(format_date(date: purchased.tx.purchaseDate))
|
||
.foregroundColor(.white)
|
||
.opacity(0.65)
|
||
if let expiry = purchased.tx.expirationDate {
|
||
Text(NSLocalizedString("Renews on", comment: "Indicating when the subscription will renew"))
|
||
.font(.title2)
|
||
.foregroundColor(.white)
|
||
Text(format_date(date: expiry))
|
||
.foregroundColor(.white)
|
||
.opacity(0.65)
|
||
}
|
||
}
|
||
Button(action: {
|
||
show_manage_subscriptions = true
|
||
}, label: {
|
||
Text(NSLocalizedString("Manage", comment: "Manage the damus subscription"))
|
||
.padding(.horizontal, 20)
|
||
})
|
||
.buttonStyle(GradientButtonStyle())
|
||
}
|
||
.manageSubscriptionsSheet(isPresented: $show_manage_subscriptions)
|
||
.padding()
|
||
}
|
||
|
||
func ProductsView(_ products: [Product]) -> some View {
|
||
VStack(spacing: 10) {
|
||
Text(NSLocalizedString("Save 20% off on an annual subscription", comment: "Savings for purchasing an annual subscription"))
|
||
.font(.callout.bold())
|
||
.foregroundColor(.white)
|
||
ForEach(products) { product in
|
||
Button(action: {
|
||
Task { @MainActor in
|
||
do {
|
||
subscription_purchase_loading = true
|
||
try await subscribe(product)
|
||
subscription_purchase_loading = false
|
||
} catch {
|
||
print(error.localizedDescription)
|
||
}
|
||
}
|
||
}, label: {
|
||
price_description(product: product)
|
||
})
|
||
.buttonStyle(GradientButtonStyle())
|
||
}
|
||
|
||
Text("By subscribing to Damus Purple you are accepting our [privacy policy](https://damus.io/privacy-policy.txt) and Apple's Standard [EULA](https://www.apple.com/legal/internet-services/itunes/dev/stdeula/)")
|
||
.foregroundColor(.white.opacity(0.6))
|
||
.font(.caption)
|
||
.padding()
|
||
|
||
}
|
||
.padding()
|
||
}
|
||
|
||
func price_description(product: Product) -> some View {
|
||
let purple_type = DamusPurple.StoreKitManager.DamusPurpleType(rawValue: product.id)
|
||
return (
|
||
HStack(spacing: 10) {
|
||
Text(purple_type?.label() ?? product.displayName)
|
||
Spacer()
|
||
if let non_discounted_price = purple_type?.non_discounted_price(product: product) {
|
||
Text(verbatim: non_discounted_price)
|
||
.strikethrough()
|
||
.foregroundColor(DamusColors.white.opacity(0.5))
|
||
}
|
||
Text(verbatim: product.displayPrice)
|
||
.fontWeight(.bold)
|
||
}
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Helper structures
|
||
|
||
extension DamusPurpleView {
|
||
enum ProductState {
|
||
case loading
|
||
case loaded([Product])
|
||
case failed
|
||
|
||
var products: [Product]? {
|
||
switch self {
|
||
case .loading:
|
||
return nil
|
||
case .loaded(let ps):
|
||
return ps
|
||
case .failed:
|
||
return nil
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#Preview {
|
||
PurpleBackdrop {
|
||
DamusPurpleView.IAPProductStateView(products: .loaded([]), purchased: nil, account_uuid: UUID(), subscribe: {_ in })
|
||
}
|
||
}
|