damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

IAPProductStateView.swift (7800B)


      1 //
      2 //  PurchasedProductView.swift
      3 //  damus
      4 //
      5 //  Created by Daniel D’Aquino on 2024-02-09.
      6 //
      7 
      8 import SwiftUI
      9 import StoreKit
     10 
     11 // MARK: - IAPProductStateView
     12 
     13 extension DamusPurpleView {
     14     typealias PurchasedProduct = DamusPurple.StoreKitManager.PurchasedProduct
     15     static let SHOW_IAP_DEBUG_INFO = false
     16     
     17     struct IAPProductStateView: View {
     18         var products: ProductState
     19         var purchased: PurchasedProduct?
     20         let account_uuid: UUID
     21         let subscribe: (Product) async throws -> Void
     22         
     23         @State var show_manage_subscriptions = false
     24         @State var subscription_purchase_loading = false
     25         
     26         var body: some View {
     27             if subscription_purchase_loading {
     28                 HStack(spacing: 10) {
     29                     Text("Purchasing", comment: "Loading label indicating the purchase action is in progress")
     30                         .foregroundStyle(.white)
     31                     ProgressView()
     32                         .progressViewStyle(.circular)
     33                         .tint(.white)
     34                 }
     35             }
     36             else {
     37                 switch self.products {
     38                     case .failed:
     39                         PurpleViewPrimitives.ProductLoadErrorView()
     40                     case .loaded(let products):
     41                         if let purchased {
     42                             PurchasedView(purchased)
     43                         } else {
     44                             ProductsView(products)
     45                         }
     46                     case .loading:
     47                         ProgressView()
     48                             .progressViewStyle(.circular)
     49                 }
     50             }
     51         }
     52         
     53         func PurchasedView(_ purchased: PurchasedProduct) -> some View {
     54             return Group {
     55                 if self.account_uuid == purchased.tx.appAccountToken {
     56                     // If the In-app purchase is registered to this account, show options to manage the subscription
     57                     PurchasedManageView(purchased)
     58                 }
     59                 else {
     60                     // If the In-app purchase is registered to a different account, we cannot manage the subscription
     61                     // This seems to be a limitation of StoreKit where we can only have one renewable subscription for a product at a time.
     62                     // Therefore, instruct the user about this limitation, or to contact us if they believe this is a mistake.
     63                     PurchasedUnmanageableView(purchased)
     64                 }
     65             }
     66         }
     67         
     68         func PurchasedUnmanageableView(_ purchased: PurchasedProduct) -> some View {
     69             Text("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")
     70                 .font(.caption)
     71                 .foregroundColor(.white.opacity(0.6))
     72                 .multilineTextAlignment(.center)
     73                 .padding(.horizontal)
     74         }
     75         
     76         func PurchasedManageView(_ purchased: PurchasedProduct) -> some View {
     77             VStack(spacing: 10) {
     78                 if SHOW_IAP_DEBUG_INFO == true {
     79                     Text("Purchased!", comment: "User purchased a subscription")
     80                         .font(.title2)
     81                         .foregroundColor(.white)
     82                     price_description(product: purchased.product)
     83                         .foregroundColor(.white)
     84                         .opacity(0.65)
     85                         .frame(width: 200)
     86                     Text("Purchased on", comment: "Indicating when the user purchased the subscription")
     87                         .font(.title2)
     88                         .foregroundColor(.white)
     89                     Text(format_date(date: purchased.tx.purchaseDate))
     90                         .foregroundColor(.white)
     91                         .opacity(0.65)
     92                     if let expiry = purchased.tx.expirationDate {
     93                         Text("Renews on", comment: "Indicating when the subscription will renew")
     94                             .font(.title2)
     95                             .foregroundColor(.white)
     96                         Text(format_date(date: expiry))
     97                             .foregroundColor(.white)
     98                             .opacity(0.65)
     99                     }
    100                 }
    101                 Button(action: {
    102                     show_manage_subscriptions = true
    103                 }, label: {
    104                     Text("Manage", comment: "Manage the damus subscription")
    105                         .padding(.horizontal, 20)
    106                 })
    107                 .buttonStyle(GradientButtonStyle())
    108             }
    109             .manageSubscriptionsSheet(isPresented: $show_manage_subscriptions)
    110             .padding()
    111         }
    112         
    113         func ProductsView(_ products: [Product]) -> some View {
    114             VStack(spacing: 10) {
    115                 Text("Save 20% off on an annual subscription", comment: "Savings for purchasing an annual subscription")
    116                     .font(.callout.bold())
    117                     .foregroundColor(.white)
    118                 ForEach(products) { product in
    119                     Button(action: {
    120                         Task { @MainActor in
    121                             do {
    122                                 subscription_purchase_loading = true
    123                                 try await subscribe(product)
    124                                 subscription_purchase_loading = false
    125                             } catch {
    126                                 print(error.localizedDescription)
    127                             }
    128                         }
    129                     }, label: {
    130                         price_description(product: product)
    131                     })
    132                     .buttonStyle(GradientButtonStyle())
    133                 }
    134 
    135                 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/)", comment: "Text explaining the terms and conditions of subscribing to Damus Purple. EULA stands for End User License Agreement.")
    136                 .foregroundColor(.white.opacity(0.6))
    137                 .font(.caption)
    138                 .padding()
    139 
    140             }
    141             .padding()
    142         }
    143         
    144         func price_description(product: Product) -> some View {
    145             let purple_type = DamusPurple.StoreKitManager.DamusPurpleType(rawValue: product.id)
    146             return (
    147                 HStack(spacing: 10) {
    148                     Text(purple_type?.label() ?? product.displayName)
    149                     Spacer()
    150                     if let non_discounted_price = purple_type?.non_discounted_price(product: product) {
    151                         Text(non_discounted_price)
    152                             .strikethrough()
    153                             .foregroundColor(DamusColors.white.opacity(0.5))
    154                     }
    155                     Text(product.displayPrice)
    156                         .fontWeight(.bold)
    157                 }
    158             )
    159         }
    160     }
    161 }
    162 
    163 // MARK: - Helper structures
    164 
    165 extension DamusPurpleView {
    166     enum ProductState {
    167         case loading
    168         case loaded([Product])
    169         case failed
    170         
    171         var products: [Product]? {
    172             switch self {
    173                 case .loading:
    174                     return nil
    175                 case .loaded(let ps):
    176                     return ps
    177                 case .failed:
    178                     return nil
    179             }
    180         }
    181     }
    182 }
    183 
    184 #Preview {
    185     PurpleBackdrop {
    186         DamusPurpleView.IAPProductStateView(products: .loaded([]), purchased: nil, account_uuid: UUID(), subscribe: {_ in })
    187     }
    188 }