From e9ec8111b8bed62fd22c590aebbfee45722d6d6a Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 3 May 2024 16:30:39 +0200 Subject: [PATCH] More work on subscriptions and fixes --- .../AccentColor.colorset/Contents.json | 12 ++++ PadelClub/Data/DataStore.swift | 52 ++++++++++++----- PadelClub/Data/User.swift | 6 +- PadelClub/Launch Screen.storyboard | 9 ++- PadelClub/Views/Match/MatchDetailView.swift | 4 +- PadelClub/Views/Subscription/Guard.swift | 7 +-- .../Views/Subscription/PurchaseListView.swift | 2 +- .../Views/Subscription/SubscriptionView.swift | 16 +++-- .../Tournament/Screen/BroadcastView.swift | 2 +- .../Components/TournamentStatusView.swift | 2 +- .../Views/Tournament/TournamentView.swift | 3 + PadelClub/Views/User/AccountView.swift | 2 +- PadelClub/Views/User/LoginView.swift | 2 +- PadelClubTests/PaymentTests.swift | 58 ++++++++++++++----- 14 files changed, 127 insertions(+), 50 deletions(-) diff --git a/PadelClub/Assets.xcassets/AccentColor.colorset/Contents.json b/PadelClub/Assets.xcassets/AccentColor.colorset/Contents.json index eb87897..b6c3d54 100644 --- a/PadelClub/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/PadelClub/Assets.xcassets/AccentColor.colorset/Contents.json @@ -1,11 +1,23 @@ { "colors" : [ { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.573", + "red" : "0.953" + } + }, "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "localizable" : true } } diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index fc87ab7..4109db9 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -28,7 +28,9 @@ class DataStore: ObservableObject { fileprivate(set) var monthData: StoredCollection fileprivate(set) var dateIntervals: StoredCollection - fileprivate var _userStorage: OptionalStorage = OptionalStorage(fileName: "user.json") + fileprivate(set) var userStorage: StoredObject + +// fileprivate var _userStorage: OptionalStorage = OptionalStorage(fileName: "user.json") fileprivate var _appSettingsStorage: MicroStorage = MicroStorage() var appSettings: AppSettings { @@ -51,11 +53,18 @@ class DataStore: ObservableObject { } var user: User? { - return self._userStorage.item + return self.userStorage.item() +// return self._userStorage.item } - func setUser(_ user: User?) { - self._userStorage.item = user + func setUser(_ user: User) { + do { + try self.userStorage.setItem(user) + self._loadCollections() + } catch { + Logger.error(error) + } +// self._userStorage.item = user } init() { @@ -68,18 +77,21 @@ class DataStore: ObservableObject { // store.addMigration(Migration(version: 3)) let indexed : Bool = true - self.clubs = store.registerCollection(synchronized: false, indexed: indexed) - self.courts = store.registerCollection(synchronized: false, indexed: indexed) - self.tournaments = store.registerCollection(synchronized: false, indexed: indexed) - self.events = store.registerCollection(synchronized: false, indexed: indexed) - self.groupStages = store.registerCollection(synchronized: false, indexed: indexed) - self.teamScores = store.registerCollection(synchronized: false, indexed: indexed) - self.teamRegistrations = store.registerCollection(synchronized: false, indexed: indexed) - self.playerRegistrations = store.registerCollection(synchronized: false, indexed: indexed) - self.rounds = store.registerCollection(synchronized: false, indexed: indexed) - self.matches = store.registerCollection(synchronized: false, indexed: indexed) - self.monthData = store.registerCollection(synchronized: false, indexed: indexed) - self.dateIntervals = store.registerCollection(synchronized: false, indexed: indexed) + let synchronized : Bool = false + self.clubs = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.courts = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.tournaments = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.events = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.groupStages = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.teamScores = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.teamRegistrations = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.playerRegistrations = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.rounds = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.matches = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.monthData = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.dateIntervals = store.registerCollection(synchronized: synchronized, indexed: indexed) + + self.userStorage = store.registerObject(synchronized: synchronized) NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidLoad, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidChange, object: nil) @@ -108,4 +120,12 @@ class DataStore: ObservableObject { return .none } + func disconnect() { + Store.main.disconnect(resetAll: true) + } + + fileprivate func _loadCollections() { + Store.main.loadCollections() + } + } diff --git a/PadelClub/Data/User.swift b/PadelClub/Data/User.swift index 1626594..34f6d95 100644 --- a/PadelClub/Data/User.swift +++ b/PadelClub/Data/User.swift @@ -15,7 +15,11 @@ enum UserRight: Int, Codable { } @Observable -class User: UserBase { +class User: UserBase, Storable { + + static func resourceName() -> String { "users" } + + func deleteDependencies() throws { } public var id: String = Store.randomId() public var username: String diff --git a/PadelClub/Launch Screen.storyboard b/PadelClub/Launch Screen.storyboard index 75ac2fe..02a97bc 100644 --- a/PadelClub/Launch Screen.storyboard +++ b/PadelClub/Launch Screen.storyboard @@ -1,9 +1,8 @@ - + - - + @@ -18,7 +17,7 @@ - + @@ -26,7 +25,7 @@ - + diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 5f61db8..67cfb4f 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -181,7 +181,9 @@ struct MatchDetailView: View { MatchTeamDetailView(match: match).tint(.master) } .sheet(isPresented: self.$showSubscriptionView, content: { - SubscriptionView(showLackOfPlanMessage: true) + NavigationStack { + SubscriptionView(showLackOfPlanMessage: true) + } }) .sheet(item: $scoreType, onDismiss: { if match.hasEnded() { diff --git a/PadelClub/Views/Subscription/Guard.swift b/PadelClub/Views/Subscription/Guard.swift index cd3ad60..7888277 100644 --- a/PadelClub/Views/Subscription/Guard.swift +++ b/PadelClub/Views/Subscription/Guard.swift @@ -185,13 +185,12 @@ import LeStorage } return nil default: -// let subscriptionPayed = DataStore.shared.tournaments.filter { $0.payment?.isSubscription == true } - - let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == .unit && $0.isCanceled == false }.count - if unitlyPayed == 0 { + let freelyPayed = DataStore.shared.tournaments.filter { $0.payment == .free && $0.isCanceled == false }.count + if freelyPayed < 1 { return Tournament.TournamentPayment.free } let tournamentCreditCount = self._purchasedTournamentCount() + let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == .unit && $0.isCanceled == false }.count if tournamentCreditCount > unitlyPayed { return Tournament.TournamentPayment.unit } diff --git a/PadelClub/Views/Subscription/PurchaseListView.swift b/PadelClub/Views/Subscription/PurchaseListView.swift index 073de88..82ffe94 100644 --- a/PadelClub/Views/Subscription/PurchaseListView.swift +++ b/PadelClub/Views/Subscription/PurchaseListView.swift @@ -115,7 +115,7 @@ struct PurchaseView: View { .foregroundColor(.accentColor) Text(self.purchaseRow.name) Spacer() - if let quantity = purchaseRow.quantity { + if let _ = purchaseRow.quantity { let remaining = Guard.main.remainingTournaments Text("\(remaining)") } diff --git a/PadelClub/Views/Subscription/SubscriptionView.swift b/PadelClub/Views/Subscription/SubscriptionView.swift index f6457dc..9e4b7f0 100644 --- a/PadelClub/Views/Subscription/SubscriptionView.swift +++ b/PadelClub/Views/Subscription/SubscriptionView.swift @@ -51,6 +51,7 @@ class SubscriptionModel: ObservableObject, StoreDelegate { } @Published var products: [Product] = [] @Published var totalPrice: String = "" + @State var showSuccessfulPurchaseView: Bool = false func load() { self.isLoading = true @@ -77,7 +78,9 @@ class SubscriptionModel: ObservableObject, StoreDelegate { } Task { if product.item.isConsumable { - let _ = try await self.storeManager?.purchase(product, quantity: self.quantity) + if let result = try await self.storeManager?.purchase(product, quantity: self.quantity) { + self.showSuccessfulPurchaseView = true + } } else { let _ = try await self.storeManager?.purchase(product) } @@ -121,7 +124,12 @@ struct SubscriptionView: View { Form { if self.showLackOfPlanMessage { - Text("Vous ne disposez malheureusement pas d'offre pour continuer votre tournoi. Voici ce que nous proposons:") + HStack { + Image(systemName: "exclamationmark.bubble.fill").foregroundStyle(Color.accentColor) + .font(.title) + Text("Vous ne disposez malheureusement plus d'offre pour continuer votre tournoi. Voici ce que nous proposons:") + .fontWeight(.semibold) + } } if self.model.products.count > 0 { @@ -196,7 +204,6 @@ struct SubscriptionView: View { fileprivate func _restore() { Task { - do { self.isRestoring = true try await Guard.main.refreshPurchasedAppleProducts() @@ -205,7 +212,6 @@ struct SubscriptionView: View { self.isRestoring = false Logger.error(error) } - } } @@ -292,6 +298,6 @@ struct SubscriptionFooterView: View { #Preview { NavigationStack { - SubscriptionView() + SubscriptionView(showLackOfPlanMessage: true) } } diff --git a/PadelClub/Views/Tournament/Screen/BroadcastView.swift b/PadelClub/Views/Tournament/Screen/BroadcastView.swift index 6351a46..286b90b 100644 --- a/PadelClub/Views/Tournament/Screen/BroadcastView.swift +++ b/PadelClub/Views/Tournament/Screen/BroadcastView.swift @@ -26,7 +26,7 @@ struct BroadcastView: View { List { Section { Toggle(isOn: $tournament.isPrivate) { - Text("Tournoi privée") + Text("Tournoi privé") } } footer: { Text("Le tournoi sera masqué sur le site \(URLs.main.rawValue)") diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift index 2c89d0d..bc17610 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift @@ -68,7 +68,7 @@ struct TournamentStatusView: View { Section { Toggle(isOn: $tournament.isPrivate) { - Text("Tournoi privée") + Text("Tournoi privé") } } footer: { Text("Le tournoi sera masqué sur le site padelclub.app") diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 273fe63..d1299c7 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -157,6 +157,9 @@ struct TournamentView: View { } } } + .onAppear { + Logger.log("Payment = \(String(describing: self.tournament.payment)), canceled = \(self.tournament.isCanceled)") + } } private func _save() { diff --git a/PadelClub/Views/User/AccountView.swift b/PadelClub/Views/User/AccountView.swift index d524d2f..d3ad144 100644 --- a/PadelClub/Views/User/AccountView.swift +++ b/PadelClub/Views/User/AccountView.swift @@ -19,7 +19,7 @@ struct AccountView: View { ChangePasswordView() } Button("Disconnect") { - Store.main.disconnect() + DataStore.shared.disconnect() handler() } }.navigationTitle(user.username) diff --git a/PadelClub/Views/User/LoginView.swift b/PadelClub/Views/User/LoginView.swift index 580fc76..af815d7 100644 --- a/PadelClub/Views/User/LoginView.swift +++ b/PadelClub/Views/User/LoginView.swift @@ -13,7 +13,7 @@ struct LoginView: View { @EnvironmentObject var dataStore: DataStore @State var username: String = "laurent" - @State var password: String = "staxstax" + @State var password: String = "StaxKikoo12" @State var isLoading: Bool = false @State var showEmailPopup: Bool = false diff --git a/PadelClubTests/PaymentTests.swift b/PadelClubTests/PaymentTests.swift index 3f0b455..34c6260 100644 --- a/PadelClubTests/PaymentTests.swift +++ b/PadelClubTests/PaymentTests.swift @@ -21,25 +21,57 @@ final class PaymentTests: XCTestCase { func testPayments() throws { let tournament = Tournament.fake() - tournament.setPayment(.free) - assert(tournament.decryptPayment() == .free) - tournament.setPayment(.subscriptionUnit) - assert(tournament.decryptPayment() == .subscriptionUnit) - tournament.setPayment(.unit) - assert(tournament.decryptPayment() == .unit) - tournament.setPayment(.unlimited) - assert(tournament.decryptPayment() == .unlimited) - + do { + tournament.payment = .free + var encoded = try JSONEncoder().encode(tournament) + var decoded = try JSONDecoder().decode(Tournament.self, from: encoded) + assert(decoded.payment == .free) + + tournament.payment = .subscriptionUnit + encoded = try JSONEncoder().encode(tournament) + decoded = try JSONDecoder().decode(Tournament.self, from: encoded) + assert(decoded.payment == .subscriptionUnit) + + tournament.payment = .unit + encoded = try JSONEncoder().encode(tournament) + decoded = try JSONDecoder().decode(Tournament.self, from: encoded) + assert(decoded.payment == .unit) + + tournament.payment = .unlimited + encoded = try JSONEncoder().encode(tournament) + decoded = try JSONDecoder().decode(Tournament.self, from: encoded) + assert(decoded.payment == .unlimited) + + } catch { + assertionFailure(error.localizedDescription) + } + } func testCanceled() throws { let tournament = Tournament.fake() - tournament.setCanceled(true) - assert(tournament.decryptCanceled() == true) - tournament.setCanceled(false) - assert(tournament.decryptCanceled() == false) + do { + tournament.isCanceled = false + var encoded = try JSONEncoder().encode(tournament) + var decoded = try JSONDecoder().decode(Tournament.self, from: encoded) + assert(decoded.isCanceled == false) + + tournament.isCanceled = true + encoded = try JSONEncoder().encode(tournament) + decoded = try JSONDecoder().decode(Tournament.self, from: encoded) + assert(decoded.isCanceled == true) + + } catch { + assertionFailure(error.localizedDescription) + } + + +// tournament.setCanceled(true) +// assert(tournament.decryptCanceled() == true) +// tournament.setCanceled(false) +// assert(tournament.decryptCanceled() == false) }