diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 2b9cc37..7486b9a 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ C425D41C2B6D249E002A7B48 /* PadelClubUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41B2B6D249E002A7B48 /* PadelClubUITests.swift */; }; C425D41E2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */; }; C44B79112BBDA63A00906534 /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B79102BBDA63A00906534 /* Locale+Extensions.swift */; }; + C45BAE3B2BC6DF10002EEC8A /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; }; C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; }; C4A47D5E2B6D38EC00ADC637 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; }; C4A47D632B6D3D6500ADC637 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; }; @@ -263,6 +264,7 @@ C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITestsLaunchTests.swift; sourceTree = ""; }; C425D44E2B6D24E1002A7B48 /* LeStorage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LeStorage.xcodeproj; path = ../../LeStorage/LeStorage.xcodeproj; sourceTree = ""; }; C44B79102BBDA63A00906534 /* Locale+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Locale+Extensions.swift"; sourceTree = ""; }; + C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = SyncedProducts.storekit; sourceTree = ""; }; C4A47D592B6D383C00ADC637 /* Tournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tournament.swift; sourceTree = ""; }; C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = ""; }; C4A47D622B6D3D6500ADC637 /* Club.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Club.swift; sourceTree = ""; }; @@ -506,6 +508,7 @@ FFA6D78A2BB0BEB3003A31F3 /* Info.plist */, C425D44E2B6D24E1002A7B48 /* LeStorage.xcodeproj */, C425D4002B6D249D002A7B48 /* PadelClubApp.swift */, + C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */, FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */, C4A47D722B72881500ADC637 /* Views */, FF3F74FD2B91A087004CFE0E /* ViewModel */, @@ -1152,6 +1155,7 @@ FF0EC5572BB195E20056B6D1 /* CLASSEMENT-PADEL-DAMES-10-2022.csv in Resources */, FF0EC5582BB195E20056B6D1 /* CLASSEMENT-PADEL-MESSIEURS-3-05-2023.csv in Resources */, FF0EC5592BB195E20056B6D1 /* CLASSEMENT-PADEL-DAMES-03-2023.csv in Resources */, + C45BAE3B2BC6DF10002EEC8A /* SyncedProducts.storekit in Resources */, FF0EC55A2BB195E20056B6D1 /* CLASSEMENT PADEL MESSIEURS_2-07-2023.csv in Resources */, FF0EC55B2BB195E20056B6D1 /* CLASSEMENT-PADEL-DAMES-01-2023.csv in Resources */, FF0EC55C2BB195E20056B6D1 /* CLASSEMENT PADEL DAMES-08-2023.csv in Resources */, @@ -1516,7 +1520,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; - DEVELOPMENT_TEAM = 526E96RFNP; + DEVELOPMENT_TEAM = BQ3Y44M3Q6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PadelClub/Info.plist; @@ -1531,7 +1535,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.PadelClub; + PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -1547,7 +1551,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; - DEVELOPMENT_TEAM = 526E96RFNP; + DEVELOPMENT_TEAM = BQ3Y44M3Q6; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PadelClub/Info.plist; @@ -1562,7 +1566,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.PadelClub; + PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/PadelClub.xcodeproj/xcshareddata/xcschemes/PadelClub.xcscheme b/PadelClub.xcodeproj/xcshareddata/xcschemes/PadelClub.xcscheme new file mode 100644 index 0000000..79259be --- /dev/null +++ b/PadelClub.xcodeproj/xcshareddata/xcschemes/PadelClub.xcscheme @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PadelClub/Assets.xcassets/MasterColor.colorset/Contents.json b/PadelClub/Assets.xcassets/MasterColor.colorset/Contents.json new file mode 100644 index 0000000..210d225 --- /dev/null +++ b/PadelClub/Assets.xcassets/MasterColor.colorset/Contents.json @@ -0,0 +1,56 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.573", + "red" : "0.953" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.573", + "red" : "0.953" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.827", + "red" : "0.996" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index b21eab3..f89dd00 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -37,8 +37,8 @@ class DataStore: ObservableObject { init() { let store = Store.main -// store.synchronizationApiURL = "http://127.0.0.1:8000/api/" - store.synchronizationApiURL = "https://xlr.alwaysdata.net/api/" + store.synchronizationApiURL = "http://127.0.0.1:8000/api/" +// store.synchronizationApiURL = "https://xlr.alwaysdata.net/api/" // store.addMigration(Migration(version: 2)) // store.addMigration(Migration(version: 2)) diff --git a/PadelClub/Info.plist b/PadelClub/Info.plist index 5f5eebc..4562fb0 100644 --- a/PadelClub/Info.plist +++ b/PadelClub/Info.plist @@ -5,10 +5,10 @@ CFBundleDocumentTypes - CFBundleTypeRole - Editor CFBundleTypeName CSV,XLS + CFBundleTypeRole + Editor LSHandlerRank Alternate LSItemContentTypes diff --git a/PadelClub/PadelClubApp.swift b/PadelClub/PadelClubApp.swift index bc1f33d..fae6ecb 100644 --- a/PadelClub/PadelClubApp.swift +++ b/PadelClub/PadelClubApp.swift @@ -18,7 +18,7 @@ struct PadelClubApp: App { WindowGroup { MainView() .environment(navigationViewModel) - .accentColor(.launchScreenBackground) + .accentColor(.master) .onAppear { self._onAppear() } diff --git a/PadelClub/SyncedProducts.storekit b/PadelClub/SyncedProducts.storekit new file mode 100644 index 0000000..8b91825 --- /dev/null +++ b/PadelClub/SyncedProducts.storekit @@ -0,0 +1,118 @@ +{ + "identifier" : "2055C391", + "nonRenewingSubscriptions" : [ + + ], + "products" : [ + { + "displayPrice" : "14.0", + "familyShareable" : false, + "internalID" : "6484163993", + "localizations" : [ + { + "description" : "Achetez votre tournoi à l'unité", + "displayName" : "Tournoi à l'unité", + "locale" : "fr" + } + ], + "productID" : "app.padelclub.tournament.unit", + "referenceName" : "Tournoi à l'unité", + "type" : "Consumable" + } + ], + "settings" : { + "_applicationInternalID" : "6484163558", + "_developerTeamID" : "BQ3Y44M3Q6", + "_failTransactionsEnabled" : false, + "_lastSynchronizedDate" : 734533081.06639695, + "_locale" : "en_US", + "_storefront" : "USA", + "_storeKitErrors" : [ + { + "current" : null, + "enabled" : false, + "name" : "Load Products" + }, + { + "current" : null, + "enabled" : false, + "name" : "Purchase" + }, + { + "current" : null, + "enabled" : false, + "name" : "Verification" + }, + { + "current" : null, + "enabled" : false, + "name" : "App Store Sync" + }, + { + "current" : null, + "enabled" : false, + "name" : "Subscription Status" + }, + { + "current" : null, + "enabled" : false, + "name" : "App Transaction" + }, + { + "current" : null, + "enabled" : false, + "name" : "Manage Subscriptions Sheet" + }, + { + "current" : null, + "enabled" : false, + "name" : "Refund Request Sheet" + }, + { + "current" : null, + "enabled" : false, + "name" : "Offer Code Redeem Sheet" + } + ] + }, + "subscriptionGroups" : [ + { + "id" : "21474782", + "localizations" : [ + + ], + "name" : "Main", + "subscriptions" : [ + { + "adHocOffers" : [ + + ], + "codeOffers" : [ + + ], + "displayPrice" : "89.0", + "familyShareable" : false, + "groupNumber" : 1, + "internalID" : "6484163670", + "introductoryOffer" : null, + "localizations" : [ + { + "description" : "Créez autant de tournois que vous souhaitez", + "displayName" : "Abonnement illimité", + "locale" : "fr" + } + ], + "productID" : "app.padelclub.unlimited", + "recurringSubscriptionPeriod" : "P1M", + "referenceName" : "Unlimited", + "subscriptionGroupID" : "21474782", + "type" : "RecurringSubscription" + } + ] + } + ], + "version" : { + "major" : 3, + "minor" : 0 + } +} diff --git a/PadelClub/Views/Components/StepperView.swift b/PadelClub/Views/Components/StepperView.swift index 6505393..2df2604 100644 --- a/PadelClub/Views/Components/StepperView.swift +++ b/PadelClub/Views/Components/StepperView.swift @@ -19,7 +19,7 @@ struct StepperView: View { var body: some View { VStack(spacing: 0) { - HStack(spacing: 16) { + HStack(spacing: 8) { Button(action: { self._subtract() }, label: { @@ -34,8 +34,9 @@ struct StepperView: View { TextField("00", value: $count, format: .number) .keyboardType(.numberPad) .fixedSize() - .font(.title2) +// .font(.title2) .monospacedDigit() + .multilineTextAlignment(.center) .onSubmit { if let minimum, count < minimum { count = minimum diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index e2fd508..e795259 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -31,7 +31,7 @@ struct SelectablePlayerListView: View { init(allowSelection: Int = 0, searchField: String? = nil, user: User? = nil, dataSet: DataSet = .national, filterOption: PlayerFilterOption = .all, hideAssimilation: Bool = false, ascending: Bool = true, sortOption: SortOption = .rank, fromPlayer: FederalPlayer? = nil, codeClub: String? = nil, ligue: String? = nil, playerSelectionAction: PlayerSelectionAction? = nil, contentUnavailableAction: ContentUnavailableAction? = nil) { self.allowSelection = allowSelection - self.searchText = searchField ?? "" +// self.searchText = searchField ?? "" self.playerSelectionAction = playerSelectionAction self.contentUnavailableAction = contentUnavailableAction let searchViewModel = SearchViewModel() diff --git a/PadelClub/Views/Subscription/Guard.swift b/PadelClub/Views/Subscription/Guard.swift index 67a4576..39b203a 100644 --- a/PadelClub/Views/Subscription/Guard.swift +++ b/PadelClub/Views/Subscription/Guard.swift @@ -106,7 +106,7 @@ import LeStorage var currentPlan: StoreItem? { #if DEBUG - return .monthly + return .unlimited #else if let currentBestPlan = self.currentBestPlan, let plan = StorePlan(rawValue: currentBestPlan.productID) { return plan @@ -122,7 +122,7 @@ import LeStorage fileprivate func _updateBestPlan() { - if let monthly = self.purchasedTransactions.first(where: { $0.productID == StoreItem.monthly.rawValue }) { + if let monthly = self.purchasedTransactions.first(where: { $0.productID == StoreItem.unlimited.rawValue }) { self.currentBestPlan = monthly } else { self.currentBestPlan = nil diff --git a/PadelClub/Views/Subscription/StoreItem.swift b/PadelClub/Views/Subscription/StoreItem.swift index 80a2188..8fa641d 100644 --- a/PadelClub/Views/Subscription/StoreItem.swift +++ b/PadelClub/Views/Subscription/StoreItem.swift @@ -18,5 +18,19 @@ enum StoreItem: String, Identifiable, CaseIterable { // var formattedPrice: String { return "119.99€ / an" } // // var price: Double { return 19.99 } + + var systemImage: String { + switch self { + case .unlimited: return "star.circle.fill" + case .unit: return "tennisball.circle.fill" + } + } + + var isConsumable: Bool { + switch self { + case .unlimited: return false + case .unit: return true + } + } } diff --git a/PadelClub/Views/Subscription/StoreManager.swift b/PadelClub/Views/Subscription/StoreManager.swift index 377154e..2de98dd 100644 --- a/PadelClub/Views/Subscription/StoreManager.swift +++ b/PadelClub/Views/Subscription/StoreManager.swift @@ -56,6 +56,8 @@ class StoreManager { func requestProducts() async { do { let identifiers: [String] = StoreItem.allCases.map { $0.rawValue } + Logger.log("Request products: \(identifiers)") + var products = try await Product.products(for: identifiers) products = products.sorted { p1, p2 in return p2.price > p1.price diff --git a/PadelClub/Views/Subscription/SubscriptionView.swift b/PadelClub/Views/Subscription/SubscriptionView.swift index 9fa182a..f9776f8 100644 --- a/PadelClub/Views/Subscription/SubscriptionView.swift +++ b/PadelClub/Views/Subscription/SubscriptionView.swift @@ -7,6 +7,30 @@ import SwiftUI import StoreKit +import LeStorage + +extension Product.SubscriptionPeriod.Unit { + var label: String { + switch self { + case .day: return "jour" + case .week: return "semaine" + case .month: return "mois" + case .year: return "année" + @unknown default: return "inconnu" + } + } +} +extension Product { + var item: StoreItem { + return StoreItem(rawValue: self.id)! + } + var formattedPrice: String { + if let period = self.subscription?.subscriptionPeriod { + return self.displayPrice + " / " + period.unit.label + } + return self.displayPrice + } +} class SubscriptionModel: ObservableObject, StoreDelegate { @@ -14,9 +38,18 @@ class SubscriptionModel: ObservableObject, StoreDelegate { @Published var error: Error? = nil @Published var isLoading: Bool = false - @Published var selectedProduct: Product? = nil - @Published var quantity: Int = 1 + @Published var selectedProduct: Product? = nil { + didSet { + self._computePrice() + } + } + @Published var quantity: Int = 1 { + didSet { + self._computePrice() + } + } @Published var products: [Product] = [] + @Published var totalPrice: String = "" func load() { self.isLoading = true @@ -34,46 +67,110 @@ class SubscriptionModel: ObservableObject, StoreDelegate { } func purchase() { - if let product = self.selectedProduct { - Task { + guard let product: Product = self.selectedProduct else { + return + } + Task { + if product.item.isConsumable { + let _ = try await self.storeManager?.purchase(product, quantity: self.quantity) + } else { let _ = try await self.storeManager?.purchase(product) } } } + fileprivate func _computePrice() { + + if let product = self.selectedProduct, let item = StoreItem(rawValue: product.id) { + if item.isConsumable { + let price = NSDecimalNumber(decimal: product.price).multiplying(by: NSDecimalNumber(integerLiteral: self.quantity)) + self.totalPrice = product.priceFormatStyle.format(price.decimalValue) + } else { + self.totalPrice = product.displayPrice + } + } else { + self.totalPrice = "" + } + } + } struct SubscriptionView: View { @ObservedObject var model: SubscriptionModel = SubscriptionModel() + @State var isRestoring: Bool = false + var body: some View { - Form { - - List { - ForEach(self.model.products) { product in - ProductView(product: product, - quantity: self.$model.quantity) - .onTapGesture { - self.model.selectedProduct = product + + VStack { + if self.model.products.count > 0 { + + Form { + + List { + ForEach(self.model.products) { product in + ProductView(product: product, + quantity: self.$model.quantity, selected: self.model.selectedProduct == product) + .onTapGesture { + self.model.selectedProduct = product + } + } + } + Section { + Button { + self._purchase() + } label: { + HStack { + Text("Purchase") + if let _ = self.model.selectedProduct { + Spacer() + Text(self.model.totalPrice) + } + } + } + } footer : { + if self.model.selectedProduct?.item.isConsumable == false { + + Text("Conditions d’utilisations concernant l’abonnement:\n- Le paiement sera facturé sur votre compte Apple.\n- L’abonnement est renouvelé automatiquement chaque mois, à moins d’avoir été désactivé au moins 24 heures avant la fin de la période de l’abonnement.\n- L’abonnement peut être géré par l’utilisateur et désactivé en allant dans les réglages de son compte après s’être abonné.\n- Le compte sera facturé pour le renouvellement de l'abonnement dans les 24 heures précédent la fin de la période d’abonnement.\n- Un abonnement en cours ne peut être annulé.\n- Toute partie inutilisée de l'offre gratuite, si souscrite, sera abandonnée lorsque l'utilisateur s'abonnera, dans les cas applicables.") } + } + + + } + } else { + + VStack(alignment: .center) { + + if let error = self.model.error { + Text(error.localizedDescription) + } else { + Text("Aucun produit disponible") + } + + if self.model.isLoading { + ProgressView() + } else { + Button(action: { + self._load() + }, label: { + Image(systemName: "arrow.clockwise.circle.fill") + .font(.system(size: 64.0)) + }) + } } } - - Section { - Button { - self._purchase() - } label: { - VStack { - Text("Purchase") - if let product = self.model.selectedProduct { - Text(product.displayName) - Text(product.displayPrice) - } + } + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + if self.isRestoring { + ProgressView() + } else { + Button("Restore") { + self._restore() } } } - } .navigationTitle("Subscriptions") .onAppear { @@ -86,7 +183,23 @@ struct SubscriptionView: View { } fileprivate func _load() { + self.model.load() + } + + fileprivate func _restore() { + Task { + + do { + self.isRestoring = true + try await Guard.main.refreshPurchasedProducts() + self.isRestoring = false + } catch { + self.isRestoring = false + Logger.error(error) + } + + } } } @@ -95,18 +208,44 @@ struct ProductView: View { var product: Product @Binding var quantity: Int + var selected: Bool var body: some View { HStack { - Image(systemName: "star.circle.fill") + Image(systemName: self._image) .font(.title) - .foregroundColor(.blue) + .foregroundColor(.accentColor) VStack(alignment: .leading) { Text(product.displayName) - Text(product.displayPrice).foregroundColor(.blue) + Text(product.formattedPrice) + .foregroundColor(.accentColor) + if self._isConsumable { + StepperView(count: self.$quantity, minimum: 1).font(.callout) +// Stepper(value: self.$quantity) { +// Text("") +// } + } } Spacer() - Image(systemName: "checkmark").foregroundColor(.blue) + if self.selected { + Image(systemName: "checkmark").foregroundColor(.accentColor) + } + }.contentShape(.rect) + } + + fileprivate var _item: StoreItem? { + return StoreItem(rawValue: self.product.id) + } + + fileprivate var _isConsumable: Bool { + return self._item?.isConsumable ?? false + } + + fileprivate var _image: String { + if let item = self._item { + return item.systemImage + } else { + return "gift.circle.fill" } } diff --git a/PadelClub/Views/User/LoginView.swift b/PadelClub/Views/User/LoginView.swift index 0478b77..58343de 100644 --- a/PadelClub/Views/User/LoginView.swift +++ b/PadelClub/Views/User/LoginView.swift @@ -19,16 +19,25 @@ struct LoginView: View { @State var error: Error? = nil + var showEmailValidationMessage: Bool = false + var handler: (User) -> () var body: some View { Form { - TextField("Username", text: self.$username) - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - SecureField("Password", text: self.$password) + Section { + TextField("Username", text: self.$username) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + SecureField("Password", text: self.$password) + + } header: { + if self.showEmailValidationMessage { + Text("Vous pouvez maintenant ouvrir votre boîte mail pour valider votre compte. Vous pourrez ensuite vous connecter ici.") + } + } Section { Button(action: { @@ -39,22 +48,24 @@ struct LoginView: View { .frame(maxWidth: .infinity) } - Section { - NavigationLink("Sign up") { - UserCreationView() - } - Button(action: { - self.showEmailPopup = true - }, label: { - Text("Forgotten password") - }) - .alert( - Text("Password reset"), - isPresented: self.$showEmailPopup - ) { - EmailConfirmationView() - } message: { - Text("Please enter your email") + if !self.showEmailValidationMessage { + Section { + NavigationLink("Sign up") { + UserCreationView() + } + Button(action: { + self.showEmailPopup = true + }, label: { + Text("Forgotten password") + }) + .alert( + Text("Password reset"), + isPresented: self.$showEmailPopup + ) { + EmailConfirmationView() + } message: { + Text("Please enter your email") + } } } diff --git a/PadelClub/Views/User/UserCreationView.swift b/PadelClub/Views/User/UserCreationView.swift index 8fda541..4c86b3f 100644 --- a/PadelClub/Views/User/UserCreationView.swift +++ b/PadelClub/Views/User/UserCreationView.swift @@ -8,8 +8,10 @@ import SwiftUI import LeStorage -struct UserCreationView: View { +struct UserCreationFormView: View { + @Binding var showLoginScreen: Bool + @State var username: String = "" @State var password1: String = "" @State var password2: String = "" @@ -21,11 +23,12 @@ struct UserCreationView: View { @State var showUnmatchingPasswordView = false @State var selectedCountryIndex = 0 + + @State var dataCollectAuthorized: Bool = false let countries: [String] = Locale.countries() @State var isLoading = false - @State var showLoginScreen: Bool = false var body: some View { @@ -62,6 +65,12 @@ struct UserCreationView: View { .padding() } + Section { + Toggle(isOn: self.$dataCollectAuthorized) { + Text("J'autorise XLR Sport, éditeur de Padel Club, à sauvegarder les données ci-dessus. XLR Sport s'engage à ne pas partager ces données.").font(.footnote) + } + } + Section { Button(action: { self._create() @@ -71,7 +80,7 @@ struct UserCreationView: View { } else { Text("Create") } - }) + }).disabled(!self.dataCollectAuthorized) .frame(maxWidth: .infinity) } } @@ -81,9 +90,9 @@ struct UserCreationView: View { .alert("Password do not match", isPresented: self.$showUnmatchingPasswordView, actions: { Button("Ok", action: {}) } ) - .navigationTitle("Create user") } + fileprivate func _selectCountry() { guard let regionCode = Locale.current.region?.identifier, let country = Locale.current.localizedString(forRegionCode: regionCode) else { return @@ -115,17 +124,35 @@ struct UserCreationView: View { email: self.email, phone: self.phone, country: self.countries[self.selectedCountryIndex]) - + let _: User = try await service.createAccount(user: userCreationForm) - + self.isLoading = false + self.showLoginScreen = true } catch { self.isLoading = false Logger.error(error) } } + } + +} + +struct UserCreationView: View { + + @State var showLoginScreen: Bool = false + + var body: some View { + Group { + if self.showLoginScreen { + LoginView(showEmailValidationMessage: true) { _ in } + } else { + UserCreationFormView(showLoginScreen: self.$showLoginScreen) + } + } + .navigationTitle("Create user") } }