270 lines
10 KiB
Swift
270 lines
10 KiB
Swift
//
|
|
// PurchaseButtonView.swift
|
|
// Feels
|
|
//
|
|
// Created by Trey Tartt on 7/7/22.
|
|
//
|
|
|
|
import SwiftUI
|
|
import StoreKit
|
|
|
|
struct PurchaseButtonView: View {
|
|
enum ViewStatus: String {
|
|
case needToBuy
|
|
case error
|
|
case success
|
|
case subscribed
|
|
}
|
|
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
|
|
@AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor
|
|
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
|
|
|
|
var iapManager: IAPManager
|
|
|
|
private let height: Float
|
|
private let showCountdownTimer: Bool
|
|
private let showManageSubClosure: (() -> Void)?
|
|
|
|
@State var isPurchasing: Bool = false
|
|
|
|
@State private var viewStatus: ViewStatus = .needToBuy {
|
|
didSet {
|
|
isPurchasing = false
|
|
}
|
|
}
|
|
|
|
public init(height: Float, iapManager: IAPManager, showManageSubClosure: (() -> Void)? = nil, showCountdownTimer: Bool = false) {
|
|
self.height = height
|
|
self.showManageSubClosure = showManageSubClosure
|
|
self.iapManager = iapManager
|
|
self.showCountdownTimer = showCountdownTimer
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
switch viewStatus {
|
|
case .needToBuy, .error:
|
|
buyOptionsView
|
|
.frame(height: CGFloat(height))
|
|
.background(theme.currentTheme.secondaryBGColor)
|
|
case .success:
|
|
subscribedView
|
|
.frame(height: CGFloat(height))
|
|
.background(theme.currentTheme.secondaryBGColor)
|
|
case .subscribed:
|
|
subscribedView
|
|
.frame(height: CGFloat(height))
|
|
.background(theme.currentTheme.secondaryBGColor)
|
|
}
|
|
}
|
|
.onAppear{
|
|
if let _ = iapManager.currentSubscription {
|
|
viewStatus = .subscribed
|
|
}
|
|
}
|
|
}
|
|
|
|
private var buyOptionsSetingsView: some View {
|
|
GeometryReader { metrics in
|
|
VStack(spacing: 20) {
|
|
Text(String(localized: "purchase_view_title"))
|
|
.foregroundColor(textColor)
|
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
|
.frame(height: 50)
|
|
.padding([.leading, .trailing])
|
|
VStack(alignment: .leading) {
|
|
ForEach(iapManager.sortedSubscriptionKeysByPriceOptions, id: \.self) { product in
|
|
HStack {
|
|
Button(action: {
|
|
purchase(product: product)
|
|
}, label: {
|
|
Text("\(product.displayPrice)\n\(product.displayName)")
|
|
.foregroundColor(.white)
|
|
.bold()
|
|
.frame(maxWidth: .infinity)
|
|
.contentShape(Rectangle())
|
|
})
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 50)
|
|
.padding()
|
|
.background(RoundedRectangle(cornerRadius: 10).fill(iapManager.colorForIAPButton(iapIdentifier: product.id)))
|
|
}
|
|
}
|
|
}
|
|
.padding([.leading, .leading])
|
|
}
|
|
}
|
|
}
|
|
|
|
private var buyOptionsView: some View {
|
|
VStack {
|
|
ZStack {
|
|
theme.currentTheme.secondaryBGColor
|
|
VStack(spacing: 20) {
|
|
Text(String(localized: "purchase_view_title"))
|
|
.font(.body)
|
|
.bold()
|
|
.foregroundColor(textColor)
|
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
|
|
|
if showCountdownTimer {
|
|
if let date = Calendar.current.date(byAdding: .day, value: 30, to: firstLaunchDate) {
|
|
HStack {
|
|
if iapManager.daysLeftBeforeIAP > 0 {
|
|
Text(String(localized: "purchase_view_current_subscription_expires_in"))
|
|
.font(.body)
|
|
.bold()
|
|
.foregroundColor(textColor)
|
|
|
|
Text(date, style: .relative)
|
|
.font(.body)
|
|
.bold()
|
|
.foregroundColor(textColor)
|
|
} else {
|
|
Text(String(localized: "purchase_view_current_subscription_expired_on"))
|
|
.font(.body)
|
|
.bold()
|
|
.foregroundColor(textColor)
|
|
|
|
Text(date, style: .date)
|
|
.font(.body)
|
|
.bold()
|
|
.foregroundColor(textColor)
|
|
}
|
|
}
|
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
|
}
|
|
}
|
|
HStack {
|
|
ForEach(iapManager.sortedSubscriptionKeysByPriceOptions) { product in
|
|
Button(action: {
|
|
purchase(product: product)
|
|
}, label: {
|
|
Text("\(product.displayPrice)\n\(product.displayName)")
|
|
.foregroundColor(.white)
|
|
.bold()
|
|
.frame(maxWidth: .infinity)
|
|
.contentShape(Rectangle())
|
|
})
|
|
.padding()
|
|
.frame(maxWidth: .infinity)
|
|
.background(RoundedRectangle(cornerRadius: 10).fill(iapManager.colorForIAPButton(iapIdentifier: product.id)))
|
|
}
|
|
}
|
|
}
|
|
.padding([.leading, .trailing])
|
|
.frame(minWidth: 0, maxWidth: .infinity)
|
|
|
|
}
|
|
.background(.ultraThinMaterial)
|
|
.frame(minWidth: 0, maxWidth: .infinity)
|
|
}
|
|
.background(.clear)
|
|
}
|
|
|
|
private var successView: some View {
|
|
HStack {
|
|
Text("it worked")
|
|
}
|
|
.background(.green)
|
|
}
|
|
|
|
private var errorView: some View {
|
|
HStack {
|
|
Text("something broke")
|
|
}
|
|
.background(.red)
|
|
}
|
|
|
|
private var subscribedView: some View {
|
|
VStack(alignment: .leading) {
|
|
Text(String(localized: "purchase_view_current_subscription"))
|
|
.font(.title3)
|
|
.padding(.leading)
|
|
|
|
Divider()
|
|
|
|
if let currentProduct = iapManager.currentSubscription,
|
|
let value = iapManager.subscriptions[currentProduct] {
|
|
HStack {
|
|
VStack (alignment: .leading, spacing: 10) {
|
|
Text(currentProduct.displayName)
|
|
.font(.title3)
|
|
Text(currentProduct.displayPrice)
|
|
.font(.title3)
|
|
}.padding([.leading, .trailing])
|
|
|
|
ForEach(value!.status, id: \.self) { singleStatus in
|
|
StatusInfoView(product: currentProduct, status: singleStatus)
|
|
.padding([.leading])
|
|
.font(.body)
|
|
}
|
|
}
|
|
}
|
|
|
|
Button(action: {
|
|
showManageSubClosure?()
|
|
}, label: {
|
|
Text(String(localized: "purchase_view_cancel"))
|
|
.foregroundColor(.red)
|
|
})
|
|
.frame(maxWidth: .infinity)
|
|
.padding([.bottom])
|
|
.multilineTextAlignment(.center)
|
|
|
|
Divider()
|
|
|
|
showOtherSubOptions
|
|
}
|
|
}
|
|
|
|
private var showOtherSubOptions: some View {
|
|
VStack (spacing: 10) {
|
|
HStack {
|
|
ForEach(iapManager.sortedSubscriptionKeysByPriceOptions, id: \.self) { product in
|
|
if product.id != iapManager.nextRenewllOption?.id {
|
|
Button(action: {
|
|
purchase(product: product)
|
|
}, label: {
|
|
Text("\(product.displayPrice)\n\(product.displayName)")
|
|
.foregroundColor(.white)
|
|
.font(.headline)
|
|
})
|
|
.contentShape(Rectangle())
|
|
.padding()
|
|
.frame(maxWidth: .infinity)
|
|
.background(RoundedRectangle(cornerRadius: 10).fill(iapManager.colorForIAPButton(iapIdentifier: product.id)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding([.leading, .trailing])
|
|
}
|
|
|
|
private var purchasingView: some View {
|
|
HStack {
|
|
Text("purcasing")
|
|
}
|
|
.background(.yellow)
|
|
}
|
|
|
|
private func purchase(product: Product) {
|
|
isPurchasing = true
|
|
Task {
|
|
do {
|
|
if let _ = try await iapManager.purchase(product) {
|
|
viewStatus = .success
|
|
}
|
|
} catch {
|
|
viewStatus = .error
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct PurchaseButtonView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
PurchaseButtonView(height: 175, iapManager: IAPManager())
|
|
}
|
|
}
|