You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
LeCountdown/LeCountdown/Subscription/StoreView.swift

221 lines
6.1 KiB

//
// StoreView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 17/02/2023.
//
import SwiftUI
import StoreKit
fileprivate enum Feature: Int, Identifiable, CaseIterable {
case unlimitedTimers
case allSounds
case longSounds
case allStats
var id: Int { self.rawValue }
var localizedString: String {
switch self {
case .unlimitedTimers: return NSLocalizedString("Unlimited timers and stopwatches", comment: "")
case .allSounds: return NSLocalizedString("Access all the sound library", comment: "")
case .longSounds: return NSLocalizedString("Access long version of sounds", comment: "")
case .allStats: return NSLocalizedString("See all your activities in detail", comment: "")
}
}
}
struct StoreView: View, StoreDelegate {
@StateObject private var store: Store = Store()
@State private var errorMessage: String? = nil
@Binding var isPresented: Bool
var body: some View {
Group {
if !self.store.products.isEmpty {
PlanView(isPresented: self.$isPresented)
.environmentObject(self.store)
} else {
ProgressView()
.progressViewStyle(.circular)
}
}.onAppear {
self._configure()
}
}
fileprivate func _configure() {
self.store.delegate = self
}
// MARK: - StoreDelegate
func productsReceived() {
}
func errorDidOccur(error: Error) {
}
}
struct PlanView: View {
@EnvironmentObject var store: Store
@State var _loadingProduct: String? = nil
@State var _purchased: Bool = false
@Binding var isPresented: Bool
var body: some View {
VStack {
Text("Permanent enchantment")
.font(.title)
.fontWeight(.bold)
.padding()
Group {
ForEach(Feature.allCases) { feature in
HStack {
Text(feature.localizedString)
Spacer()
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.accentColor)
}.fontWeight(.medium)
}
}
.padding(.horizontal, 24.0)
.padding(.vertical, 2.0)
Spacer()
PlayerWrapperView()
Text("Purchase").font(.title)
ForEach(self.store.products) { product in
Button {
self._purchase(product: product)
} label: {
HStack {
Spacer()
if product.id == self._loadingProduct {
if self._purchased {
Image(systemName: "checkmark.circle.fill")
} else {
ProgressView()
.progressViewStyle(.circular).tint(.white)
}
} else {
VStack {
if let plan = StorePlan(rawValue: product.id) {
Text("\(product.displayPrice) / \(plan.formattedPeriod)").font(.title3)
.foregroundColor(.white)
} else {
Text("Plan not found")
}
}
}
Spacer()
}.frame(height: 44.0)
}
.buttonStyle(.borderedProminent)
.fontWeight(.medium)
}
}.padding()
.foregroundColor(.black)
.background(Color(red: 1.0, green: 0.8, blue: 0.9))
}
fileprivate func _purchase(product: Product) {
Task {
self._loadingProduct = product.id
let result = try await store.purchase(product)
switch result {
case .none:
self._loadingProduct = nil
case .some:
self._purchased = true
self.isPresented = false
}
}
}
}
struct PlayerWrapperView: View {
@StateObject var player: StatePlayer = StatePlayer()
var body: some View {
let state = self.player.state
Group {
switch state {
case .none, .paused:
Button {
self._pause()
} label: {
HStack {
Image(systemName: state.systemImage)
Spacer()
ProgressView(value: self.player.completion)
}.font(.title)
}.buttonStyle(.borderedProminent)
.fontWeight(.medium)
case .playing:
Button {
self._pause()
} label: {
HStack {
Image(systemName: state.systemImage)
Spacer()
ProgressView(value: self.player.completion)
// Slider(value: self.$player.completion)
}.font(.title)
}.buttonStyle(.borderedProminent)
.fontWeight(.medium)
case .noResource:
EmptyView()
}
}
.onAppear {
self.player.load(resource: "QP01 0118 Riparian Zone thrush", ext: "wav")
}
}
fileprivate func _play() {
self.player.play()
}
fileprivate func _pause() {
self.player.pause()
}
}
struct StoreView_Previews: PreviewProvider {
static var previews: some View {
PlanView(isPresented: .constant(false)).environmentObject(Store())
}
}