fixes stuff for subscriptions

multistore
Laurent 2 years ago
parent 7996e4b1d6
commit a3c2db3823
  1. 1
      PadelClub/Utils/SourceFileManager.swift
  2. 15
      PadelClub/Views/Calling/CallView.swift
  3. 13
      PadelClub/Views/Calling/SendToAllView.swift
  4. 6
      PadelClub/Views/Match/MatchDetailView.swift
  5. 2
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  6. 27
      PadelClub/Views/Subscription/Guard.swift
  7. 87
      PadelClub/Views/Subscription/SubscriptionView.swift

@ -34,7 +34,6 @@ class SourceFileManager {
print("Error: \(error)") print("Error: \(error)")
} }
} }
var lastDataSource: String? { var lastDataSource: String? {
DataStore.shared.appSettings.lastDataSource DataStore.shared.appSettings.lastDataSource

@ -59,6 +59,8 @@ struct CallView: View {
@State private var contactType: ContactType? = nil @State private var contactType: ContactType? = nil
@State private var sentError: ContactManagerError? = nil @State private var sentError: ContactManagerError? = nil
@State var cannotPayForTournament: Bool = false
var messageSentFailed: Binding<Bool> { var messageSentFailed: Binding<Bool> {
Binding { Binding {
sentError != nil sentError != nil
@ -82,8 +84,6 @@ struct CallView: View {
ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat) ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat)
} }
// TODO: Guard
var body: some View { var body: some View {
let callWord = teams.allSatisfy({ $0.called() }) ? "Reconvoquer" : "Convoquer" let callWord = teams.allSatisfy({ $0.called() }) ? "Reconvoquer" : "Convoquer"
HStack { HStack {
@ -110,6 +110,9 @@ struct CallView: View {
.underline() .underline()
} }
} }
.onAppear {
self.cannotPayForTournament = Guard.main.paymentForNewTournament() == nil
}
.font(.subheadline) .font(.subheadline)
.buttonStyle(.borderless) .buttonStyle(.borderless)
.alert("Un problème est survenu", isPresented: messageSentFailed) { .alert("Un problème est survenu", isPresented: messageSentFailed) {
@ -123,7 +126,7 @@ struct CallView: View {
Group { Group {
switch contactType { switch contactType {
case .message(_, let recipients, let body, _): case .message(_, let recipients, let body, _):
if Guard.main.paymentForNewTournament() != nil { if !self.cannotPayForTournament {
MessageComposeView(recipients: recipients, body: body) { result in MessageComposeView(recipients: recipients, body: body) { result in
switch result { switch result {
case .cancelled: case .cancelled:
@ -142,10 +145,10 @@ struct CallView: View {
} }
} }
} else { } else {
SubscriptionView(showLackOfPlanMessage: true) SubscriptionView(isPresented: self.$cannotPayForTournament, showLackOfPlanMessage: true)
} }
case .mail(_, let recipients, let bccRecipients, let body, let subject, _): case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
if Guard.main.paymentForNewTournament() != nil { if !self.cannotPayForTournament {
MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in
switch result { switch result {
case .cancelled, .saved: case .cancelled, .saved:
@ -166,7 +169,7 @@ struct CallView: View {
} }
} }
} else { } else {
SubscriptionView(showLackOfPlanMessage: true) SubscriptionView(isPresented: self.$cannotPayForTournament, showLackOfPlanMessage: true)
} }
} }
} }

@ -17,6 +17,8 @@ struct SendToAllView: View {
@State private var contactRecipients: Set<String> = Set() @State private var contactRecipients: Set<String> = Set()
@State private var sentError: ContactManagerError? = nil @State private var sentError: ContactManagerError? = nil
@State var cannotPayForTournament: Bool = false
var messageSentFailed: Binding<Bool> { var messageSentFailed: Binding<Bool> {
Binding { Binding {
sentError != nil sentError != nil
@ -93,7 +95,7 @@ struct SendToAllView: View {
Group { Group {
switch contactType { switch contactType {
case .message(_, let recipients, let body, _): case .message(_, let recipients, let body, _):
if Guard.main.paymentForNewTournament() != nil { if !self.cannotPayForTournament {
MessageComposeView(recipients: recipients, body: body) { result in MessageComposeView(recipients: recipients, body: body) { result in
switch result { switch result {
case .cancelled: case .cancelled:
@ -109,10 +111,10 @@ struct SendToAllView: View {
} }
} }
} else { } else {
SubscriptionView(showLackOfPlanMessage: true) SubscriptionView(isPresented: self.$cannotPayForTournament, showLackOfPlanMessage: true)
} }
case .mail(_, let recipients, let bccRecipients, let body, let subject, _): case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
if Guard.main.paymentForNewTournament() != nil { if !self.cannotPayForTournament {
MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in
switch result { switch result {
case .cancelled, .saved: case .cancelled, .saved:
@ -130,13 +132,16 @@ struct SendToAllView: View {
} }
} }
} else { } else {
SubscriptionView(showLackOfPlanMessage: true) SubscriptionView(isPresented: self.$cannotPayForTournament, showLackOfPlanMessage: true)
} }
} }
} }
.tint(.master) .tint(.master)
} }
} }
.onAppear {
self.cannotPayForTournament = Guard.main.paymentForNewTournament() == nil
}
} }
func _teams() -> [TeamRegistration] { func _teams() -> [TeamRegistration] {

@ -182,7 +182,7 @@ struct MatchDetailView: View {
} }
.sheet(isPresented: self.$showSubscriptionView, content: { .sheet(isPresented: self.$showSubscriptionView, content: {
NavigationStack { NavigationStack {
SubscriptionView(showLackOfPlanMessage: true) SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
} }
}) })
.sheet(item: $scoreType, onDismiss: { .sheet(item: $scoreType, onDismiss: {
@ -254,7 +254,7 @@ struct MatchDetailView: View {
} }
} }
} else { } else {
SubscriptionView(showLackOfPlanMessage: true) SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
} }
case .mail(_, let recipients, let bccRecipients, let body, let subject, _): case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
if Guard.main.paymentForNewTournament() != nil { if Guard.main.paymentForNewTournament() != nil {
@ -275,7 +275,7 @@ struct MatchDetailView: View {
} }
} }
} else { } else {
SubscriptionView(showLackOfPlanMessage: true) SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
} }
} }
} }

@ -167,7 +167,7 @@ struct UmpireView: View {
} }
.sheet(isPresented: self.$showSubscriptions, content: { .sheet(isPresented: self.$showSubscriptions, content: {
NavigationStack { NavigationStack {
SubscriptionView() SubscriptionView(isPresented: self.$showSubscriptions)
} }
}) })
.sheet(isPresented: $presentSearchView) { .sheet(isPresented: $presentSearchView) {

@ -87,6 +87,7 @@ import LeStorage
@MainActor @MainActor
func updatePurchasedIdentifiers(_ transaction: StoreKit.Transaction) async { func updatePurchasedIdentifiers(_ transaction: StoreKit.Transaction) async {
Logger.log("\(transaction.productID) > purchase = \(transaction.originalPurchaseDate), exp date= \(transaction.expirationDate), rev date = \(transaction.revocationDate)")
do { do {
if transaction.revocationDate == nil { if transaction.revocationDate == nil {
// If the App Store has not revoked the transaction, add it to the list of `purchasedIdentifiers`. // If the App Store has not revoked the transaction, add it to the list of `purchasedIdentifiers`.
@ -205,22 +206,26 @@ import LeStorage
return Tournament.TournamentPayment.subscriptionUnit return Tournament.TournamentPayment.subscriptionUnit
} }
} }
return nil return self._paymentWithoutSubscription()
default: default:
let freelyPayed: Int = DataStore.shared.tournaments.filter { $0.payment == .free && $0.isCanceled == false }.count return self._paymentWithoutSubscription()
if freelyPayed < 1 {
return Tournament.TournamentPayment.free
}
let tournamentCreditCount: Int = self._purchasedTournamentCount()
let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == .unit && $0.isCanceled == false }.count
if tournamentCreditCount > unitlyPayed {
return Tournament.TournamentPayment.unit
}
return nil
} }
} }
fileprivate func _paymentWithoutSubscription() -> Tournament.TournamentPayment? {
let freelyPayed: Int = DataStore.shared.tournaments.filter { $0.payment == .free && $0.isCanceled == false }.count
if freelyPayed < 1 {
return Tournament.TournamentPayment.free
}
let tournamentCreditCount: Int = self._purchasedTournamentCount()
let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == .unit && $0.isCanceled == false }.count
if tournamentCreditCount > unitlyPayed {
return Tournament.TournamentPayment.unit
}
return nil
}
var remainingTournaments: Int { var remainingTournaments: Int {
let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == Tournament.TournamentPayment.unit }.count let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == Tournament.TournamentPayment.unit }.count
let tournamentCreditCount = self._purchasedTournamentCount() let tournamentCreditCount = self._purchasedTournamentCount()

@ -38,7 +38,6 @@ class SubscriptionModel: ObservableObject, StoreDelegate {
@Published var error: Error? = nil @Published var error: Error? = nil
@Published var isLoading: Bool = false @Published var isLoading: Bool = false
@Published var isPurchasing: Bool = false
@Published var selectedProduct: Product? = nil { @Published var selectedProduct: Product? = nil {
didSet { didSet {
self._computePrice() self._computePrice()
@ -52,7 +51,6 @@ class SubscriptionModel: ObservableObject, StoreDelegate {
} }
@Published var products: [Product] = [] @Published var products: [Product] = []
@Published var totalPrice: String = "" @Published var totalPrice: String = ""
@State var showSuccessfulPurchaseView: Bool = false
func load() { func load() {
self.isLoading = true self.isLoading = true
@ -72,30 +70,22 @@ class SubscriptionModel: ObservableObject, StoreDelegate {
self.error = error self.error = error
} }
func purchase() { func purchase() async throws -> Bool {
Logger.log("start purchase...") Logger.log("start purchase...")
guard let product: Product = self.selectedProduct, let storeManager = self.storeManager else { guard let product: Product = self.selectedProduct, let storeManager = self.storeManager else {
Logger.w("missing product or store manager") Logger.w("missing product or store manager")
return return false
} }
self.isPurchasing = true
Task { if product.item.isConsumable {
do { if let _ = try await storeManager.purchase(product, quantity: self.quantity) {
if product.item.isConsumable { return true
if let _ = try await storeManager.purchase(product, quantity: self.quantity) {
self.isPurchasing = false
self.showSuccessfulPurchaseView = true
}
} else {
let _ = try await storeManager.purchase(product)
self.isPurchasing = false
} }
} catch { } else {
Logger.error(error) let _ = try await storeManager.purchase(product)
self.isPurchasing = false return true
} }
} return false
} }
fileprivate func _computePrice() { fileprivate func _computePrice() {
@ -118,10 +108,14 @@ struct SubscriptionView: View {
@ObservedObject var model: SubscriptionModel = SubscriptionModel() @ObservedObject var model: SubscriptionModel = SubscriptionModel()
@Binding var isPresented: Bool
var showLackOfPlanMessage: Bool = false var showLackOfPlanMessage: Bool = false
@State var isRestoring: Bool = false @State var isRestoring: Bool = false
@State var showLoginView: Bool = false @State var showLoginView: Bool = false
@State var isPurchasing: Bool = false
@State var showSuccessfulPurchaseView: Bool = false
var body: some View { var body: some View {
Group { Group {
@ -147,21 +141,21 @@ struct SubscriptionView: View {
Section { Section {
ForEach(self.model.products) { product in ForEach(self.model.products) { product in
let isSelected = self.model.selectedProduct == product let isSelected = self.model.selectedProduct == product
ProductView(product: product, ProductView(product: product,
quantity: self.$model.quantity, quantity: self.$model.quantity,
selected: isSelected) selected: isSelected)
.foregroundStyle(.white) .foregroundStyle(.white)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
.tint(Color.master) .tint(Color.master)
.listRowBackground(Color.clear) .listRowBackground(Color.clear)
.onTapGesture { .onTapGesture {
self.model.selectedProduct = product self.model.selectedProduct = product
}
} }
}
} header: { } header: {
Text("Sélectionnez une offre") Text("Sélectionnez une offre")
} }
@ -178,7 +172,7 @@ struct SubscriptionView: View {
} }
} label: { } label: {
HStack { HStack {
if self.model.isPurchasing { if self.isPurchasing {
Spacer() Spacer()
ProgressView().tint(.white) ProgressView().tint(.white)
Spacer() Spacer()
@ -230,7 +224,26 @@ struct SubscriptionView: View {
} }
fileprivate func _purchase() { fileprivate func _purchase() {
self.model.purchase()
self.isPurchasing = true
Task {
do {
let success = try await self.model.purchase()
DispatchQueue.main.async {
self.isPurchasing = false
self.showSuccessfulPurchaseView = true
if success {
self.isPresented = false
}
}
} catch {
Logger.error(error)
DispatchQueue.main.async {
self.isPurchasing = false
}
}
}
} }
fileprivate func _load() { fileprivate func _load() {
@ -337,6 +350,6 @@ struct SubscriptionFooterView: View {
#Preview { #Preview {
NavigationStack { NavigationStack {
SubscriptionView(showLackOfPlanMessage: false) SubscriptionView(isPresented: .constant(true), showLackOfPlanMessage: false)
} }
} }

Loading…
Cancel
Save