From 671edd34128baf9ad9698d279aabd020994a088e Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 18 Oct 2024 10:22:57 +0200 Subject: [PATCH 01/43] first commit --- PadelClub.xcodeproj/project.pbxproj | 20 +- PadelClub/Data/Club.swift | 28 +- PadelClub/Data/Court.swift | 21 +- .../Data/{User.swift => CustomUser.swift} | 17 +- PadelClub/Data/DataStore.swift | 62 ++- PadelClub/Data/DateInterval.swift | 10 +- PadelClub/Data/Event.swift | 16 +- PadelClub/Data/GroupStage.swift | 62 +-- PadelClub/Data/Match.swift | 90 +--- PadelClub/Data/MonthData.swift | 4 +- PadelClub/Data/PlayerRegistration.swift | 22 +- PadelClub/Data/README.md | 1 - PadelClub/Data/Round.swift | 63 +-- PadelClub/Data/TeamRegistration.swift | 58 +-- PadelClub/Data/TeamScore.swift | 13 +- PadelClub/Data/Tournament.swift | 132 ++--- PadelClub/Data/TournamentStore.swift | 17 +- PadelClub/Utils/SourceFileManager.swift | 4 +- PadelClub/Views/Club/ClubDetailView.swift | 6 +- .../LoserBracketFromGroupStageView.swift | 26 +- .../Agenda/TournamentSubscriptionView.swift | 4 +- PadelClub/Views/Round/RoundSettingsView.swift | 53 +- .../Views/Tournament/Screen/AddTeamView.swift | 476 +++++++++++------- .../Views/Tournament/Subscription/Guard.swift | 1 - .../Tournament/Subscription/Purchase.swift | 10 +- PadelClub/Views/User/AccountView.swift | 2 +- PadelClub/Views/User/LoginView.swift | 4 +- PadelClub/Views/User/UserCreationView.swift | 2 +- PadelClubTests/ServerDataTests.swift | 32 +- PadelClubTests/SynchronizationTests.swift | 33 ++ PadelClubTests/UserDataTests.swift | 8 +- 31 files changed, 657 insertions(+), 640 deletions(-) rename PadelClub/Data/{User.swift => CustomUser.swift} (93%) create mode 100644 PadelClubTests/SynchronizationTests.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index b8a1649..8de2d45 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -42,12 +42,13 @@ C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; }; C4A47DA62B83948E00ADC637 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; }; C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; }; - C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; + C4A47DAD2B85FCCD00ADC637 /* CustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */; }; C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; }; C4B3A1552C2581DA0078EAA8 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; }; C4C01D982C481C0C0059087C /* CapsuleViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */; }; C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; + C4D477992CB6704C0077713D /* SynchronizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D477982CB6704C0077713D /* SynchronizationTests.swift */; }; C4EC6F572BE92CAC000CEAB4 /* local.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4EC6F562BE92CAC000CEAB4 /* local.plist */; }; C4EC6F592BE92D88000CEAB4 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; }; C4FC2E272C2AABC90021F3BF /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; }; @@ -308,7 +309,7 @@ FF4CBFFA2C996C0600151637 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; }; FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; }; FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; - FF4CBFFD2C996C0600151637 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; + FF4CBFFD2C996C0600151637 /* CustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */; }; FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; }; FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; }; FF4CC0002C996C0600151637 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; }; @@ -611,7 +612,7 @@ FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; }; FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; }; FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; - FF70FB7C2C90584900129CC2 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; + FF70FB7C2C90584900129CC2 /* CustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */; }; FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; }; FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; }; FF70FB7F2C90584900129CC2 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; }; @@ -897,11 +898,12 @@ C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepperView.swift; sourceTree = ""; }; C4A47DA52B83948E00ADC637 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePasswordView.swift; sourceTree = ""; }; - C4A47DAC2B85FCCD00ADC637 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; + C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomUser.swift; sourceTree = ""; }; C4A47DB22B86387500ADC637 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = ""; }; C4B3A1542C2581DA0078EAA8 /* Patcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Patcher.swift; sourceTree = ""; }; C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapsuleViewModifier.swift; sourceTree = ""; }; C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CodingContainer+Extensions.swift"; sourceTree = ""; }; + C4D477982CB6704C0077713D /* SynchronizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizationTests.swift; sourceTree = ""; }; C4EC6F562BE92CAC000CEAB4 /* local.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = local.plist; sourceTree = ""; }; C4EC6F582BE92D88000CEAB4 /* PListReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PListReader.swift; sourceTree = ""; }; C4FC2E262C2AABC90021F3BF /* PasswordField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordField.swift; sourceTree = ""; }; @@ -1293,6 +1295,7 @@ C411C9C22BEBA453003017AD /* ServerDataTests.swift */, C411C9C82BF219CB003017AD /* UserDataTests.swift */, C411C9CF2BF38F41003017AD /* TokenExemptionTests.swift */, + C4D477982CB6704C0077713D /* SynchronizationTests.swift */, ); path = PadelClubTests; sourceTree = ""; @@ -1320,7 +1323,7 @@ C411C9CC2BF21DAF003017AD /* README.md */, C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */, C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */, - C4A47DAC2B85FCCD00ADC637 /* User.swift */, + C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */, C4A47D592B6D383C00ADC637 /* Tournament.swift */, FF967CE72BAEC70100A9A3BD /* GroupStage.swift */, FF967CED2BAECBD700A9A3BD /* Round.swift */, @@ -2405,7 +2408,7 @@ FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */, FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */, FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */, - C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */, + C4A47DAD2B85FCCD00ADC637 /* CustomUser.swift in Sources */, C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */, FF967D012BAEF0B400A9A3BD /* MatchSummaryView.swift in Sources */, FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */, @@ -2470,6 +2473,7 @@ files = ( C411C9D02BF38F41003017AD /* TokenExemptionTests.swift in Sources */, C49EF0422BE23BF50077B5AA /* PaymentTests.swift in Sources */, + C4D477992CB6704C0077713D /* SynchronizationTests.swift in Sources */, C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */, C411C9C92BF219CB003017AD /* UserDataTests.swift in Sources */, C411C9C32BEBA453003017AD /* ServerDataTests.swift in Sources */, @@ -2675,7 +2679,7 @@ FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */, FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */, FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */, - FF4CBFFD2C996C0600151637 /* User.swift in Sources */, + FF4CBFFD2C996C0600151637 /* CustomUser.swift in Sources */, FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */, FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */, FF4CC0002C996C0600151637 /* MockData.swift in Sources */, @@ -2922,7 +2926,7 @@ FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */, FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */, FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */, - FF70FB7C2C90584900129CC2 /* User.swift in Sources */, + FF70FB7C2C90584900129CC2 /* CustomUser.swift in Sources */, C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */, FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */, FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */, diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index 58ef226..b7dc5ad 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -10,22 +10,15 @@ import SwiftUI import LeStorage @Observable -final class Club : ModelObject, Storable, Hashable { +final class Club: ModelObject, SyncedStorable { static func resourceName() -> String { return "clubs" } static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] } static func filterByStoreIdentifier() -> Bool { return false } static var relationshipNames: [String] = [] - static func == (lhs: Club, rhs: Club) -> Bool { - lhs.id == rhs.id - } - - func hash(into hasher: inout Hasher) { - return hasher.combine(id) - } - var id: String = Store.randomId() + var lastUpdate: Date var creator: String? var name: String var acronym: String @@ -41,7 +34,11 @@ final class Club : ModelObject, Storable, Hashable { var broadcastCode: String? // var alphabeticalName: Bool = false + var storeId: String? { return nil } + internal init(creator: String? = nil, name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil, courtCount: Int = 2, broadcastCode: String? = nil) { + self.lastUpdate = Date() + self.name = name self.creator = creator self.acronym = acronym ?? name.acronym() @@ -54,8 +51,14 @@ final class Club : ModelObject, Storable, Hashable { self.longitude = longitude self.courtCount = courtCount self.broadcastCode = broadcastCode + + super.init() } +// required init(from decoder: any Decoder) throws { +// fatalError("init(from:) has not been implemented") +// } + override func copyFromServerInstance(_ instance: any Storable) -> Bool { guard let copy = instance as? Club else { return false } self.broadcastCode = copy.broadcastCode @@ -80,16 +83,17 @@ final class Club : ModelObject, Storable, Hashable { DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index) } - override func deleteDependencies() throws { + override func deleteDependencies() { let customizedCourts = self.customizedCourts for customizedCourt in customizedCourts { - try customizedCourt.deleteDependencies() + customizedCourt.deleteDependencies() } DataStore.shared.courts.deleteDependencies(customizedCourts) } enum CodingKeys: String, CodingKey { case _id = "id" + case _lastUpdate = "lastUpdate" case _creator = "creator" case _name = "name" case _acronym = "acronym" @@ -106,9 +110,11 @@ final class Club : ModelObject, Storable, Hashable { } func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(creator, forKey: ._creator) try container.encode(name, forKey: ._name) try container.encode(acronym, forKey: ._acronym) diff --git a/PadelClub/Data/Court.swift b/PadelClub/Data/Court.swift index e7751bc..3853c3a 100644 --- a/PadelClub/Data/Court.swift +++ b/PadelClub/Data/Court.swift @@ -10,7 +10,8 @@ import SwiftUI import LeStorage @Observable -final class Court : ModelObject, Storable, Hashable { +final class Court : ModelObject, SyncedStorable { + static func resourceName() -> String { return "courts" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } @@ -20,31 +21,25 @@ final class Court : ModelObject, Storable, Hashable { lhs.id == rhs.id } - func hash(into hasher: inout Hasher) { - return hasher.combine(id) - } - var id: String = Store.randomId() + var lastUpdate: Date var index: Int var club: String var name: String? var exitAllowed: Bool = false var indoor: Bool = false + var storeId: String? { return nil } + init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) { self.index = index + self.lastUpdate = Date() self.club = club self.name = name self.exitAllowed = exitAllowed self.indoor = indoor } -// internal init(club: String, name: String? = nil, index: Int) { -// self.club = club -// self.name = name -// self.index = index -// } - func courtTitle() -> String { self.name ?? courtIndexTitle() } @@ -61,11 +56,12 @@ final class Court : ModelObject, Storable, Hashable { Store.main.findById(club) } - override func deleteDependencies() throws { + override func deleteDependencies() { } enum CodingKeys: String, CodingKey { case _id = "id" + case _lastUpdate = "lastUpdate" case _index = "index" case _club = "club" case _name = "name" @@ -77,6 +73,7 @@ final class Court : ModelObject, Storable, Hashable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(index, forKey: ._index) try container.encode(club, forKey: ._club) try container.encode(name, forKey: ._name) diff --git a/PadelClub/Data/User.swift b/PadelClub/Data/CustomUser.swift similarity index 93% rename from PadelClub/Data/User.swift rename to PadelClub/Data/CustomUser.swift index 06ff5bd..3d0b71d 100644 --- a/PadelClub/Data/User.swift +++ b/PadelClub/Data/CustomUser.swift @@ -15,7 +15,7 @@ enum UserRight: Int, Codable { } @Observable -class User: ModelObject, UserBase, Storable { +class CustomUser: ModelObject, UserBase, SyncedStorable { static func resourceName() -> String { "users" } static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } @@ -23,6 +23,7 @@ class User: ModelObject, UserBase, Storable { static var relationshipNames: [String] = [] public var id: String = Store.randomId() + var lastUpdate: Date public var username: String public var email: String var clubs: [String] = [] @@ -46,8 +47,11 @@ class User: ModelObject, UserBase, Storable { var loserBracketMode: LoserBracketMode = .automatic var deviceId: String? + + var storeId: String? { return nil } init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) { + self.lastUpdate = Date() self.username = username self.firstName = firstName self.lastName = lastName @@ -121,6 +125,7 @@ class User: ModelObject, UserBase, Storable { enum CodingKeys: String, CodingKey { case _id = "id" + case _lastUpdate = "lastUpdate" case _username = "username" case _email = "email" case _clubs = "clubs" @@ -149,6 +154,7 @@ class User: ModelObject, UserBase, Storable { // Required properties id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() username = try container.decode(String.self, forKey: ._username) email = try container.decode(String.self, forKey: ._email) firstName = try container.decode(String.self, forKey: ._firstName) @@ -181,6 +187,7 @@ class User: ModelObject, UserBase, Storable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(username, forKey: ._username) try container.encode(email, forKey: ._email) try container.encode(clubs, forKey: ._clubs) @@ -207,15 +214,15 @@ class User: ModelObject, UserBase, Storable { try container.encode(loserBracketMode, forKey: ._loserBracketMode) } - static func placeHolder() -> User { - return User(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil) + static func placeHolder() -> CustomUser { + return CustomUser(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil) } } -class UserCreationForm: User, UserPasswordBase { +class UserCreationForm: CustomUser, UserPasswordBase { - init(user: User, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) { + init(user: CustomUser, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) { self.password = password super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 2a2052e..8991440 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -13,7 +13,7 @@ class DataStore: ObservableObject { static let shared = DataStore() - @Published var user: User = User.placeHolder() { + @Published var user: CustomUser = CustomUser.placeHolder() { didSet { let loggedUser = StoreCenter.main.userId != nil StoreCenter.main.collectionsCanSynchronize = loggedUser @@ -22,7 +22,7 @@ class DataStore: ObservableObject { if self.user.id != self.userStorage.item()?.id { self.userStorage.setItemNoSync(self.user) if StoreCenter.main.collectionsCanSynchronize { - Store.main.loadCollectionsFromServer() + StoreCenter.main.initialSynchronization() self._fixMissingClubCreatorIfNecessary(self.clubs) self._fixMissingEventCreatorIfNecessary(self.events) } @@ -41,9 +41,9 @@ class DataStore: ObservableObject { fileprivate(set) var dateIntervals: StoredCollection fileprivate(set) var purchases: StoredCollection - fileprivate var userStorage: StoredSingleton + fileprivate var userStorage: StoredSingleton - fileprivate var _temporaryLocalUser: OptionalStorage = OptionalStorage(fileName: "tmp_local_user.json") + fileprivate var _temporaryLocalUser: OptionalStorage = OptionalStorage(fileName: "tmp_local_user.json") fileprivate(set) var appSettingsStorage: MicroStorage = MicroStorage(fileName: "appsettings.json") var appSettings: AppSettings { @@ -75,18 +75,20 @@ class DataStore: ObservableObject { } #endif + StoreCenter.main.forceNoSynchronization = !synchronized + Logger.log("Sync URL: \(StoreCenter.main.synchronizationApiURL ?? "none"), sync: \(synchronized) ") let indexed: Bool = true - 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.monthData = store.registerCollection(synchronized: false, indexed: indexed) - self.dateIntervals = store.registerCollection(synchronized: synchronized, indexed: indexed) - + self.clubs = store.registerSynchronizedCollection(indexed: indexed) + self.courts = store.registerSynchronizedCollection(indexed: indexed) + self.tournaments = store.registerSynchronizedCollection(indexed: indexed) + self.events = store.registerSynchronizedCollection(indexed: indexed) + self.dateIntervals = store.registerSynchronizedCollection(indexed: indexed) self.userStorage = store.registerObject(synchronized: synchronized) - self.purchases = Store.main.registerCollection(synchronized: true, inMemory: true) + self.purchases = Store.main.registerSynchronizedCollection(inMemory: true) + + self.monthData = store.registerCollection(indexed: indexed) // Load ApiCallCollection, making them restart at launch and deletable on disconnect StoreCenter.main.loadApiCallCollection(type: GroupStage.self) @@ -102,14 +104,10 @@ class DataStore: ObservableObject { } func saveUser() { - do { - if user.username.count > 0 { - try self.userStorage.update() - } else { - self._temporaryLocalUser.item = self.user - } - } catch { - Logger.error(error) + if user.username.count > 0 { + self.userStorage.update() + } else { + self._temporaryLocalUser.item = self.user } } @@ -119,8 +117,8 @@ class DataStore: ObservableObject { self.objectWillChange.send() } - if let userSingleton: StoredSingleton = notification.object as? StoredSingleton { - self.user = userSingleton.item() ?? self._temporaryLocalUser.item ?? User.placeHolder() + if let userSingleton: StoredSingleton = notification.object as? StoredSingleton { + self.user = userSingleton.item() ?? self._temporaryLocalUser.item ?? CustomUser.placeHolder() } else if let clubsCollection: StoredCollection = notification.object as? StoredCollection { self._fixMissingClubCreatorIfNecessary(clubsCollection) } else if let eventsCollection: StoredCollection = notification.object as? StoredCollection { @@ -134,17 +132,13 @@ class DataStore: ObservableObject { } fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: StoredCollection) { - do { - for club in clubsCollection { - if let userId = StoreCenter.main.userId, club.creator == nil { - club.creator = userId - self.userStorage.item()?.addClub(club) - try self.userStorage.update() - clubsCollection.writeChangeAndInsertOnServer(instance: club) - } + for club in clubsCollection { + if let userId = StoreCenter.main.userId, club.creator == nil { + club.creator = userId + self.userStorage.item()?.addClub(club) + self.userStorage.update() + clubsCollection.writeChangeAndInsertOnServer(instance: club) } - } catch { - Logger.error(error) } } @@ -221,7 +215,7 @@ class DataStore: ObservableObject { Guard.main.disconnect() StoreCenter.main.disconnect() - self.user = self._temporaryLocalUser.item ?? User.placeHolder() + self.user = self._temporaryLocalUser.item ?? CustomUser.placeHolder() self.user.clubs.removeAll() // done after because otherwise folders remain @@ -240,7 +234,7 @@ class DataStore: ObservableObject { let login = PListReader.readString(plist: "local", key: "username"), let pass = PListReader.readString(plist: "local", key: "password") { let service = Services(url: url) - let _: User = try await service.login(username: login, password: pass) + let _: CustomUser = try await service.login(username: login, password: pass) tournament.event = nil _ = try await service.post(tournament) diff --git a/PadelClub/Data/DateInterval.swift b/PadelClub/Data/DateInterval.swift index 23438bd..c6a7a9f 100644 --- a/PadelClub/Data/DateInterval.swift +++ b/PadelClub/Data/DateInterval.swift @@ -10,19 +10,22 @@ import SwiftUI import LeStorage @Observable -final class DateInterval: ModelObject, Storable { +final class DateInterval: ModelObject, SyncedStorable { + static func resourceName() -> String { return "date-intervals" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } static var relationshipNames: [String] = [] var id: String = Store.randomId() + var lastUpdate: Date var event: String var courtIndex: Int var startDate: Date var endDate: Date internal init(event: String, courtIndex: Int, startDate: Date, endDate: Date) { + self.lastUpdate = Date() self.event = event self.courtIndex = courtIndex self.startDate = startDate @@ -45,11 +48,12 @@ final class DateInterval: ModelObject, Storable { date <= startDate && date <= endDate && date >= startDate && date >= endDate } - override func deleteDependencies() throws { + override func deleteDependencies() { } enum CodingKeys: String, CodingKey { case _id = "id" + case _lastUpdate = "lastUpdate" case _event = "event" case _courtIndex = "courtIndex" case _startDate = "startDate" @@ -57,7 +61,7 @@ final class DateInterval: ModelObject, Storable { } func insertOnServer() throws { - try DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self) + DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self) } } diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index 0517c72..4b6a59f 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -10,7 +10,7 @@ import LeStorage import SwiftUI @Observable -final class Event: ModelObject, Storable { +final class Event: ModelObject, SyncedStorable { static func resourceName() -> String { return "events" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } @@ -18,30 +18,34 @@ final class Event: ModelObject, Storable { static var relationshipNames: [String] = [] var id: String = Store.randomId() + var lastUpdate: Date var creator: String? var club: String? var creationDate: Date = Date() var name: String? var tenupId: String? + var storeId: String? { return nil } + internal init(creator: String? = nil, club: String? = nil, name: String? = nil, tenupId: String? = nil) { + self.lastUpdate = Date() self.creator = creator self.club = club self.name = name self.tenupId = tenupId } - override func deleteDependencies() throws { + override func deleteDependencies() { let tournaments = self.tournaments for tournament in tournaments { - try tournament.deleteDependencies() + tournament.deleteDependencies() } DataStore.shared.tournaments.deleteDependencies(tournaments) let courtsUnavailabilities = self.courtsUnavailability for courtsUnavailability in courtsUnavailabilities { - try courtsUnavailability.deleteDependencies() + courtsUnavailability.deleteDependencies() } DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities) } @@ -94,7 +98,7 @@ final class Event: ModelObject, Storable { } func insertOnServer() throws { - try DataStore.shared.events.writeChangeAndInsertOnServer(instance: self) + DataStore.shared.events.writeChangeAndInsertOnServer(instance: self) for tournament in self.tournaments { try tournament.insertOnServer() } @@ -109,6 +113,7 @@ final class Event: ModelObject, Storable { extension Event { enum CodingKeys: String, CodingKey { case _id = "id" + case _lastUpdate = "lastUpdate" case _creator = "creator" case _club = "club" case _creationDate = "creationDate" @@ -120,6 +125,7 @@ extension Event { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(creator, forKey: ._creator) try container.encode(club, forKey: ._club) try container.encode(creationDate, forKey: ._creationDate) diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index f51fe5e..740a730 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -11,13 +11,15 @@ import Algorithms import SwiftUI @Observable -final class GroupStage: ModelObject, Storable { +final class GroupStage: ModelObject, SyncedStorable, SideStorable { + static func resourceName() -> String { "group-stages" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } static var relationshipNames: [String] = [] var id: String = Store.randomId() + var lastUpdate: Date var tournament: String var index: Int var size: Int @@ -35,7 +37,10 @@ final class GroupStage: ModelObject, Storable { } } + var storeId: String? = nil + internal init(tournament: String, index: Int, size: Int, matchFormat: MatchFormat? = nil, startDate: Date? = nil, name: String? = nil, step: Int = 0) { + self.lastUpdate = Date() self.tournament = tournament self.index = index self.size = size @@ -107,7 +112,7 @@ final class GroupStage: ModelObject, Storable { } fileprivate func _createMatch(index: Int) -> Match { - let match: Match = Match(groupStage: self.id, + let match: Match = Match(groupStage: self.id, index: index, matchFormat: self.matchFormat, name: self.localizedMatchUpLabel(for: index)) @@ -128,12 +133,8 @@ final class GroupStage: ModelObject, Storable { matches.append(newMatch) } - do { - try self.tournamentStore.matches.addOrUpdate(contentOfs: matches) - try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) - } catch { - Logger.error(error) - } + self.tournamentStore.matches.addOrUpdate(contentOfs: matches) + self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) } func playedMatches() -> [Match] { @@ -151,19 +152,15 @@ final class GroupStage: ModelObject, Storable { clearScoreCache() if hasEnded(), let tournament = tournamentObject() { - do { - let teams = teams(true) - for (index, team) in teams.enumerated() { - team.qualified = index < tournament.qualifiedPerGroupStage - if team.bracketPosition != nil && team.qualified == false { - tournamentObject()?.resetTeamScores(in: team.bracketPosition) - team.bracketPosition = nil - } + let teams = teams(true) + for (index, team) in teams.enumerated() { + team.qualified = index < tournament.qualifiedPerGroupStage + if team.bracketPosition != nil && team.qualified == false { + tournamentObject()?.resetTeamScores(in: team.bracketPosition) + team.bracketPosition = nil } - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) } + self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) let groupStagesAreOverAtFirstStep = tournament.groupStagesAreOver(atStep: 0) let nextStepGroupStages = tournament.groupStages(atStep: 1) @@ -171,11 +168,7 @@ final class GroupStage: ModelObject, Storable { if groupStagesAreOverAtFirstStep, nextStepGroupStages.isEmpty || groupStagesAreOverAtSecondStep == true, tournament.groupStageLoserBracketAreOver(), tournament.rounds().isEmpty { tournament.endDate = Date() - do { - try DataStore.shared.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) - } + DataStore.shared.tournaments.addOrUpdate(instance: tournament) } } } @@ -332,11 +325,7 @@ final class GroupStage: ModelObject, Storable { } private func _removeMatches() { - do { - try self.tournamentStore.matches.delete(contentOfs: _matches()) - } catch { - Logger.error(error) - } + self.tournamentStore.matches.delete(contentOfs: _matches()) } private func _numberOfMatchesToBuild() -> Int { @@ -485,11 +474,7 @@ final class GroupStage: ModelObject, Storable { playedMatches.forEach { match in match.matchFormat = matchFormat } - do { - try self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches) - } catch { - Logger.error(error) - } + self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches) } func pasteData() -> String { @@ -507,10 +492,10 @@ final class GroupStage: ModelObject, Storable { return teams(true).firstIndex(of: team) } - override func deleteDependencies() throws { + override func deleteDependencies() { let matches = self._matches() for match in matches { - try match.deleteDependencies() + match.deleteDependencies() } self.tournamentStore.matches.deleteDependencies(matches) } @@ -519,6 +504,7 @@ final class GroupStage: ModelObject, Storable { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(String.self, forKey: ._id) + lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate) tournament = try container.decode(String.self, forKey: ._tournament) index = try container.decode(Int.self, forKey: ._index) size = try container.decode(Int.self, forKey: ._size) @@ -532,6 +518,8 @@ final class GroupStage: ModelObject, Storable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(storeId, forKey: ._storeId) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(tournament, forKey: ._tournament) try container.encode(index, forKey: ._index) try container.encode(size, forKey: ._size) @@ -553,6 +541,8 @@ final class GroupStage: ModelObject, Storable { extension GroupStage { enum CodingKeys: String, CodingKey { case _id = "id" + case _storeId = "storeId" + case _lastUpdate = "lastUpdate" case _tournament = "tournament" case _index = "index" case _size = "size" diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 7fee09c..f44eb8e 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -9,7 +9,7 @@ import Foundation import LeStorage @Observable -final class Match: ModelObject, Storable { +final class Match: ModelObject, SyncedStorable, SideStorable { static func resourceName() -> String { "matches" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } @@ -23,6 +23,7 @@ final class Match: ModelObject, Storable { var byeState: Bool = false var id: String = Store.randomId() + var lastUpdate: Date var round: String? var groupStage: String? var startDate: Date? @@ -40,7 +41,10 @@ final class Match: ModelObject, Storable { private(set) var courtIndex: Int? var confirmed: Bool = false + var storeId: String? = nil + init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) { + self.lastUpdate = Date() self.round = round self.groupStage = groupStage self.startDate = startDate @@ -78,13 +82,13 @@ final class Match: ModelObject, Storable { // MARK: - - override func deleteDependencies() throws { + override func deleteDependencies() { guard let tournament = self.currentTournament() else { return } let teamScores = self.teamScores for teamScore in teamScores { - try teamScore.deleteDependencies() + teamScore.deleteDependencies() } tournament.tournamentStore.teamScores.deleteDependencies(teamScores) } @@ -207,11 +211,7 @@ defer { endDate = nil followingMatch()?.cleanScheduleAndSave(nil) _loserMatch()?.cleanScheduleAndSave(nil) - do { - try self.tournamentStore.matches.addOrUpdate(instance: self) - } catch { - Logger.error(error) - } + self.tournamentStore.matches.addOrUpdate(instance: self) } func resetMatch() { @@ -227,22 +227,14 @@ defer { func resetScores() { teamScores.forEach({ $0.score = nil }) - do { - try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) } func teamWillBeWalkOut(_ team: TeamRegistration) { resetMatch() let existingTeamScore = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team) existingTeamScore.walkOut = 1 - do { - try self.tournamentStore.teamScores.addOrUpdate(instance: existingTeamScore) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.addOrUpdate(instance: existingTeamScore) } func luckyLosers() -> [TeamRegistration] { @@ -260,19 +252,11 @@ defer { let position = matchIndex * 2 + teamPosition.rawValue let previousScores = teamScores.filter({ $0.luckyLoser == position }) - do { - try self.tournamentStore.teamScores.delete(contentOfs: previousScores) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.delete(contentOfs: previousScores) let teamScoreLuckyLoser = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team) teamScoreLuckyLoser.luckyLoser = position - do { - try self.tournamentStore.teamScores.addOrUpdate(instance: teamScoreLuckyLoser) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.addOrUpdate(instance: teamScoreLuckyLoser) } func disableMatch() { @@ -373,32 +357,19 @@ defer { disabled = state if disabled { - do { - try self.tournamentStore.teamScores.delete(contentOfs: teamScores) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.delete(contentOfs: teamScores) } if state == true { let teams = teams() for team in teams { if isSeededBy(team: team) { team.bracketPosition = nil - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } catch { - Logger.error(error) - } + self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) } } } //byeState = false - do { - try self.tournamentStore.matches.addOrUpdate(instance: self) - } catch { - Logger.error(error) - } - + self.tournamentStore.matches.addOrUpdate(instance: self) if single == false { _toggleLoserMatchDisableState(state) if forward { @@ -506,11 +477,7 @@ defer { teamScoreWalkout.walkOut = 0 let teamScoreWinning = teamScore(teamPosition.otherTeam) ?? TeamScore(match: id, team: team(teamPosition.otherTeam)) teamScoreWinning.walkOut = nil - do { - try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning]) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning]) if endDate == nil { endDate = Date() @@ -555,11 +522,7 @@ defer { teamScoreOne.score = matchDescriptor.teamOneScores.joined(separator: ",") let teamScoreTwo = teamScore(.two) ?? TeamScore(match: id, team: team(.two)) teamScoreTwo.score = matchDescriptor.teamTwoScores.joined(separator: ",") - do { - try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo]) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo]) matchFormat = matchDescriptor.matchFormat } @@ -572,11 +535,7 @@ defer { let ids = newTeamScores.map { $0.id } let teamScores = teamScores.filter({ ids.contains($0.id) == false }) if teamScores.isEmpty == false { - do { - try self.tournamentStore.teamScores.delete(contentOfs: teamScores) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.delete(contentOfs: teamScores) followingMatch()?.resetTeamScores(outsideOf: []) _loserMatch()?.resetTeamScores(outsideOf: []) } @@ -598,11 +557,8 @@ defer { func updateTeamScores() { let teams = getOrCreateTeamScores() - do { - try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) - } + + self.tournamentStore.teamScores.addOrUpdate(contentOfs: teams) resetTeamScores(outsideOf: teams) if teams.isEmpty == false { updateFollowingMatchTeamScore() @@ -883,6 +839,8 @@ defer { enum CodingKeys: String, CodingKey { case _id = "id" + case _storeId = "storeId" + case _lastUpdate = "lastUpdate" case _round = "round" case _groupStage = "groupStage" case _startDate = "startDate" @@ -905,6 +863,8 @@ defer { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(storeId, forKey: ._storeId) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(round, forKey: ._round) try container.encode(groupStage, forKey: ._groupStage) try container.encode(startDate, forKey: ._startDate) @@ -923,7 +883,7 @@ defer { func insertOnServer() { self.tournamentStore.matches.writeChangeAndInsertOnServer(instance: self) for teamScore in self.teamScores { - try teamScore.insertOnServer() + teamScore.insertOnServer() } } diff --git a/PadelClub/Data/MonthData.swift b/PadelClub/Data/MonthData.swift index 676ccd6..a7a102b 100644 --- a/PadelClub/Data/MonthData.swift +++ b/PadelClub/Data/MonthData.swift @@ -10,7 +10,7 @@ import SwiftUI import LeStorage @Observable -final class MonthData : ModelObject, Storable { +final class MonthData: ModelObject, Storable { static func resourceName() -> String { return "month-data" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } @@ -85,7 +85,7 @@ final class MonthData : ModelObject, Storable { } } - override func deleteDependencies() throws { + override func deleteDependencies() { } enum CodingKeys: String, CodingKey { diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 92d29cf..ca1afa4 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -9,13 +9,14 @@ import Foundation import LeStorage @Observable -final class PlayerRegistration: ModelObject, Storable { +final class PlayerRegistration: ModelObject, SyncedStorable, SideStorable { static func resourceName() -> String { "player-registrations" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } static var relationshipNames: [String] = ["teamRegistration"] var id: String = Store.randomId() + var lastUpdate: Date var teamRegistration: String? var firstName: String var lastName: String @@ -39,7 +40,10 @@ final class PlayerRegistration: ModelObject, Storable { var hasArrived: Bool = false + var storeId: String? = nil + init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false) { + self.lastUpdate = Date() self.teamRegistration = teamRegistration self.firstName = firstName self.lastName = lastName @@ -61,6 +65,7 @@ final class PlayerRegistration: ModelObject, Storable { } internal init(importedPlayer: ImportedPlayer) { + self.lastUpdate = Date() self.teamRegistration = "" self.firstName = (importedPlayer.firstName ?? "").trimmed.capitalized self.lastName = (importedPlayer.lastName ?? "").trimmed.uppercased() @@ -77,6 +82,7 @@ final class PlayerRegistration: ModelObject, Storable { } internal init?(federalData: [String], sex: Int, sexUnknown: Bool) { + self.lastUpdate = Date() let _lastName = federalData[0].trimmed.uppercased() let _firstName = federalData[1].trimmed.capitalized if _lastName.isEmpty && _firstName.isEmpty { return nil } @@ -323,6 +329,8 @@ final class PlayerRegistration: ModelObject, Storable { enum CodingKeys: String, CodingKey { case _id = "id" + case _storeId = "storeId" + case _lastUpdate = "lastUpdate" case _teamRegistration = "teamRegistration" case _firstName = "firstName" case _lastName = "lastName" @@ -348,6 +356,8 @@ final class PlayerRegistration: ModelObject, Storable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(storeId, forKey: ._storeId) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(teamRegistration, forKey: ._teamRegistration) try container.encode(firstName, forKey: ._firstName) @@ -454,16 +464,6 @@ final class PlayerRegistration: ModelObject, Storable { } -extension PlayerRegistration: Hashable { - static func == (lhs: PlayerRegistration, rhs: PlayerRegistration) -> Bool { - lhs.id == rhs.id - } - - func hash(into hasher: inout Hasher) { - hasher.combine(id) - } -} - extension PlayerRegistration: PlayerHolder { func getAssimilatedAsMaleRank() -> Int? { nil diff --git a/PadelClub/Data/README.md b/PadelClub/Data/README.md index 334dd01..48056ab 100644 --- a/PadelClub/Data/README.md +++ b/PadelClub/Data/README.md @@ -18,4 +18,3 @@ Dans Django: Enfin, revenir dans Xcode, ouvrir ServerDataTests et lancer le test mis à jour - diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index c618811..e1e4ed5 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -10,13 +10,15 @@ import LeStorage import SwiftUI @Observable -final class Round: ModelObject, Storable { +final class Round: ModelObject, SyncedStorable, SideStorable { + static func resourceName() -> String { "rounds" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } static var relationshipNames: [String] = [] var id: String = Store.randomId() + var lastUpdate: Date var tournament: String var index: Int var parent: String? @@ -25,7 +27,10 @@ final class Round: ModelObject, Storable { var groupStageLoserBracket: Bool = false var loserBracketMode: LoserBracketMode = .automatic + var storeId: String? = nil + internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { + self.lastUpdate = Date() self.tournament = tournament self.index = index self.parent = parent @@ -397,11 +402,7 @@ defer { // Logger.error(error) // } } - do { - try self.tournamentStore.matches.addOrUpdate(contentOfs: _matches) - } catch { - Logger.error(error) - } + self.tournamentStore.matches.addOrUpdate(contentOfs: _matches) } var cumulativeMatchCount: Int { @@ -550,11 +551,7 @@ defer { func updateTournamentState() { if let tournamentObject = tournamentObject(), index == 0, isUpperBracket(), hasEnded() { tournamentObject.endDate = Date() - do { - try DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject) - } catch { - Logger.error(error) - } + DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject) } } @@ -596,12 +593,8 @@ defer { } func deleteLoserBracket() { - do { - let loserRounds = loserRounds() - try self.tournamentStore.rounds.delete(contentOfs: loserRounds) - } catch { - Logger.error(error) - } + let loserRounds = loserRounds() + self.tournamentStore.rounds.delete(contentOfs: loserRounds) } func buildLoserBracket() { @@ -620,12 +613,7 @@ defer { round.parent = id //parent return round } - - do { - try self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) - } catch { - Logger.error(error) - } + self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) let matchCount = RoundRule.numberOfMatches(forTeams: currentRoundMatchCount) let matches = (0.. Bool { - lhs.id == rhs.id - } - +extension Round: Selectable { func selectionLabel(index: Int) -> String { if let parentRound { diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index aa4f940..0173702 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -10,13 +10,14 @@ import LeStorage import SwiftUI @Observable -final class TeamRegistration: ModelObject, Storable { +final class TeamRegistration: ModelObject, SyncedStorable, SideStorable { static func resourceName() -> String { "team-registrations" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } static var relationshipNames: [String] = [] var id: String = Store.randomId() + var lastUpdate: Date var tournament: String var groupStage: String? var registrationDate: Date? @@ -39,7 +40,12 @@ final class TeamRegistration: ModelObject, Storable { var finalRanking: Int? var pointsEarned: Int? + var storeId: String? = nil + init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) { + + self.storeId = tournament + self.lastUpdate = Date() self.tournament = tournament self.groupStage = groupStage self.registrationDate = registrationDate @@ -74,23 +80,19 @@ final class TeamRegistration: ModelObject, Storable { func deleteTeamScores() { let ts = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id }) - do { - try self.tournamentStore.teamScores.delete(contentOfs: ts) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.delete(contentOfs: ts) } - override func deleteDependencies() throws { + override func deleteDependencies() { let unsortedPlayers = unsortedPlayers() for player in unsortedPlayers { - try player.deleteDependencies() + player.deleteDependencies() } self.tournamentStore.playerRegistrations.deleteDependencies(unsortedPlayers) let teamScores = teamScores() for teamScore in teamScores { - try teamScore.deleteDependencies() + teamScore.deleteDependencies() } self.tournamentStore.teamScores.deleteDependencies(teamScores) } @@ -98,11 +100,7 @@ final class TeamRegistration: ModelObject, Storable { func hasArrived(isHere: Bool = false) { let unsortedPlayers = unsortedPlayers() unsortedPlayers.forEach({ $0.hasArrived = !isHere }) - do { - try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers) - } catch { - Logger.error(error) - } + self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers) } func isHere() -> Bool { @@ -301,11 +299,7 @@ final class TeamRegistration: ModelObject, Storable { if let groupStage { let matches = self.tournamentStore.matches.filter({ $0.groupStage == groupStage }).map { $0.id } let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) - do { - try tournamentStore.teamScores.delete(contentOfs: teamScores) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.delete(contentOfs: teamScores) } //groupStageObject()?._matches().forEach({ $0.updateTeamScores() }) groupStage = nil @@ -315,11 +309,7 @@ final class TeamRegistration: ModelObject, Storable { func resetBracketPosition() { let matches = self.tournamentStore.matches.filter({ $0.groupStage == nil }).map { $0.id } let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) - do { - try tournamentStore.teamScores.delete(contentOfs: teamScores) - } catch { - Logger.error(error) - } + self.tournamentStore.teamScores.delete(contentOfs: teamScores) self.bracketPosition = nil } @@ -389,11 +379,7 @@ final class TeamRegistration: ModelObject, Storable { func updatePlayers(_ players: Set, inTournamentCategory tournamentCategory: TournamentCategory) { let previousPlayers = Set(unsortedPlayers()) let playersToRemove = previousPlayers.subtracting(players) - do { - try self.tournamentStore.playerRegistrations.delete(contentOfs: playersToRemove) - } catch { - Logger.error(error) - } + self.tournamentStore.playerRegistrations.delete(contentOfs: playersToRemove) setWeight(from: Array(players), inTournamentCategory: tournamentCategory) players.forEach { player in @@ -525,6 +511,8 @@ final class TeamRegistration: ModelObject, Storable { enum CodingKeys: String, CodingKey { case _id = "id" + case _lastUpdate = "lastUpdate" + case _storeId = "storeId" case _tournament = "tournament" case _groupStage = "groupStage" case _registrationDate = "registrationDate" @@ -551,6 +539,8 @@ final class TeamRegistration: ModelObject, Storable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(storeId, forKey: ._storeId) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(tournament, forKey: ._tournament) try container.encode(groupStage, forKey: ._groupStage) try container.encode(registrationDate, forKey: ._registrationDate) @@ -582,16 +572,6 @@ final class TeamRegistration: ModelObject, Storable { } -extension TeamRegistration: Hashable { - static func == (lhs: TeamRegistration, rhs: TeamRegistration) -> Bool { - lhs.id == rhs.id - } - - func hash(into hasher: inout Hasher) { - hasher.combine(id) - } -} - enum TeamDataSource: Int, Codable { case beachPadel } diff --git a/PadelClub/Data/TeamScore.swift b/PadelClub/Data/TeamScore.swift index 9a7c94b..b996fca 100644 --- a/PadelClub/Data/TeamScore.swift +++ b/PadelClub/Data/TeamScore.swift @@ -9,22 +9,26 @@ import Foundation import LeStorage @Observable -final class TeamScore: ModelObject, Storable { - +final class TeamScore: ModelObject, SyncedStorable, SideStorable { + static func resourceName() -> String { "team-scores" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } static var relationshipNames: [String] = ["match"] var id: String = Store.randomId() + var lastUpdate: Date var match: String var teamRegistration: String? //var playerRegistrations: [String] = [] var score: String? var walkOut: Int? var luckyLoser: Int? + + var storeId: String? = nil init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) { + self.lastUpdate = Date() self.match = match self.teamRegistration = teamRegistration // self.playerRegistrations = playerRegistrations @@ -34,6 +38,7 @@ final class TeamScore: ModelObject, Storable { } init(match: String, team: TeamRegistration?) { + self.lastUpdate = Date() self.match = match if let team { self.teamRegistration = team.id @@ -72,6 +77,8 @@ final class TeamScore: ModelObject, Storable { enum CodingKeys: String, CodingKey { case _id = "id" + case _storeId = "storeId" + case _lastUpdate = "lastUpdate" case _match = "match" case _teamRegistration = "teamRegistration" //case _playerRegistrations = "playerRegistrations" @@ -84,6 +91,8 @@ final class TeamScore: ModelObject, Storable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(storeId, forKey: ._storeId) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(match, forKey: ._match) try container.encode(teamRegistration, forKey: ._teamRegistration) try container.encode(score, forKey: ._score) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 31f6bbb..bc5f7e0 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -10,13 +10,15 @@ import LeStorage import SwiftUI @Observable -final class Tournament : ModelObject, Storable { +final class Tournament: ModelObject, SyncedStorable { + static func resourceName() -> String { "tournaments" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } static var relationshipNames: [String] = [] var id: String = Store.randomId() + var lastUpdate: Date var event: String? var name: String? var startDate: Date @@ -58,12 +60,15 @@ final class Tournament : ModelObject, Storable { var hidePointsEarned: Bool = false var publishRankings: Bool = false var loserBracketMode: LoserBracketMode = .automatic + + var storeId: String? { return nil } @ObservationIgnored var navigationPath: [Screen] = [] enum CodingKeys: String, CodingKey { case _id = "id" + case _lastUpdate = "lastUpdate" case _event = "event" case _creator = "creator" case _name = "name" @@ -110,6 +115,7 @@ final class Tournament : ModelObject, Storable { } internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { + self.lastUpdate = Date() self.event = event self.name = name self.startDate = startDate @@ -153,6 +159,7 @@ final class Tournament : ModelObject, Storable { required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(String.self, forKey: ._id) + lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate) event = try container.decodeIfPresent(String.self, forKey: ._event) name = try container.decodeIfPresent(String.self, forKey: ._name) startDate = try container.decode(Date.self, forKey: ._startDate) @@ -230,6 +237,7 @@ final class Tournament : ModelObject, Storable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: ._id) + try container.encode(lastUpdate, forKey: ._lastUpdate) try container.encode(event, forKey: ._event) try container.encode(name, forKey: ._name) @@ -321,23 +329,23 @@ final class Tournament : ModelObject, Storable { return TournamentStore.instance(tournamentId: self.id) } - override func deleteDependencies() throws { + override func deleteDependencies() { let store = self.tournamentStore let teams = self.tournamentStore.teamRegistrations - for team in teams { - try team.deleteDependencies() + for team in Array(teams) { + team.deleteDependencies() } store.teamRegistrations.deleteDependencies(teams) let groups = self.tournamentStore.groupStages for group in groups { - try group.deleteDependencies() + group.deleteDependencies() } store.groupStages.deleteDependencies(groups) let rounds = self.tournamentStore.rounds for round in rounds { - try round.deleteDependencies() + round.deleteDependencies() } store.rounds.deleteDependencies(rounds) @@ -1056,17 +1064,8 @@ defer { } } - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teamsToImport) - } catch { - Logger.error(error) - } - do { - try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: teams.flatMap { $0.players }) - } catch { - Logger.error(error) - } - + self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teamsToImport) + self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: teams.flatMap { $0.players }) if state() == .build && groupStageCount > 0 && groupStageTeams().isEmpty { setGroupStage(randomize: groupStageSortMode == .random) @@ -1316,12 +1315,7 @@ defer { } } - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) - } catch { - Logger.error(error) - } - + self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) return rankings } @@ -1336,11 +1330,7 @@ defer { teams.forEach { team in team.lockedWeight = team.weight } - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) - } + self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) } func unlockRegistration() { @@ -1349,13 +1339,8 @@ defer { teams.forEach { team in team.lockedWeight = nil } - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) - } + self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) } - func updateWeights() { let teams = self.unsortedTeams() @@ -1363,17 +1348,9 @@ defer { let players = team.unsortedPlayers() players.forEach { $0.setComputedRank(in: self) } team.setWeight(from: players, inTournamentCategory: tournamentCategory) - do { - try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - } catch { - Logger.error(error) - } - } - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) + self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) } + self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) } func updateRank(to newDate: Date?) async throws { @@ -1388,11 +1365,7 @@ defer { let monthData: MonthData = MonthData(monthKey: formatted) monthData.maleUnrankedValue = lastRankMan monthData.femaleUnrankedValue = lastRankWoman - do { - try DataStore.shared.monthData.addOrUpdate(instance: monthData) - } catch { - Logger.error(error) - } + DataStore.shared.monthData.addOrUpdate(instance: monthData) } } @@ -1685,12 +1658,7 @@ defer { _groupStages.append(groupStage) } - do { - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: _groupStages) - } catch { - Logger.error(error) - } - + self.tournamentStore.groupStages.addOrUpdate(contentOfs: _groupStages) refreshGroupStages() } @@ -1707,11 +1675,7 @@ defer { return Round(tournament: id, index: $0, matchFormat: roundSmartMatchFormat($0), loserBracketMode: loserBracketMode) } - do { - try self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) - } catch { - Logger.error(error) - } + self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) let matchCount = RoundRule.numberOfMatches(forTeams: bracketTeamCount()) let matches = (0.. Bool { @@ -2097,11 +2045,7 @@ defer { let lastStep = lastStep() + 1 for i in 0.. Bool { - lhs.id == rhs.id - } - - func hash(into hasher: inout Hasher) { - hasher.combine(id) - } -} - extension Tournament: FederalTournamentHolder { func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String { diff --git a/PadelClub/Data/TournamentStore.swift b/PadelClub/Data/TournamentStore.swift index 6cb2fc7..3413068 100644 --- a/PadelClub/Data/TournamentStore.swift +++ b/PadelClub/Data/TournamentStore.swift @@ -12,9 +12,6 @@ import SwiftUI class TournamentStore: Store, ObservableObject { static func instance(tournamentId: String) -> TournamentStore { -// if StoreCenter.main.userId == nil { -// fatalError("cant request store without id") -// } return StoreCenter.main.store(identifier: tournamentId, parameter: "tournament") } @@ -44,13 +41,13 @@ class TournamentStore: Store, ObservableObject { } #endif - self.groupStages = self.registerCollection(synchronized: synchronized, indexed: indexed) - self.rounds = self.registerCollection(synchronized: synchronized, indexed: indexed) - self.teamRegistrations = self.registerCollection(synchronized: synchronized, indexed: indexed) - self.playerRegistrations = self.registerCollection(synchronized: synchronized, indexed: indexed) - self.matches = self.registerCollection(synchronized: synchronized, indexed: indexed) - self.teamScores = self.registerCollection(synchronized: synchronized, indexed: indexed) - self.matchSchedulers = self.registerCollection(synchronized: false, indexed: indexed) + self.groupStages = self.registerSynchronizedCollection(indexed: indexed) + self.rounds = self.registerSynchronizedCollection(indexed: indexed) + self.teamRegistrations = self.registerSynchronizedCollection(indexed: indexed) + self.playerRegistrations = self.registerSynchronizedCollection(indexed: indexed) + self.matches = self.registerSynchronizedCollection(indexed: indexed) + self.teamScores = self.registerSynchronizedCollection(indexed: indexed) + self.matchSchedulers = self.registerCollection(indexed: indexed) self.loadCollectionsFromServerIfNoFile() diff --git a/PadelClub/Utils/SourceFileManager.swift b/PadelClub/Utils/SourceFileManager.swift index ac06446..5313241 100644 --- a/PadelClub/Utils/SourceFileManager.swift +++ b/PadelClub/Utils/SourceFileManager.swift @@ -26,9 +26,9 @@ class SourceFileManager { if !fileManager.fileExists(atPath: directoryURL.path) { // Directory does not exist, create it try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) - print("Directory created at: \(directoryURL)") +// print("Directory created at: \(directoryURL)") } else { - print("Directory already exists at: \(directoryURL)") +// print("Directory already exists at: \(directoryURL)") } } catch { print("Error: \(error)") diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index b04b4f0..5ad196a 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -242,11 +242,7 @@ struct ClubDetailView: View { } .onDisappear { if displayContext == .edition && clubDeleted == false { - do { - try dataStore.clubs.addOrUpdate(instance: club) - } catch { - Logger.error(error) - } + dataStore.clubs.addOrUpdate(instance: club) } } .onAppear { diff --git a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift index 15f41b1..adf8694 100644 --- a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift +++ b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift @@ -108,21 +108,13 @@ struct LoserBracketFromGroupStageView: View { let placeCount = displayableMatches.isEmpty ? currentGroupStageLoserBracketsInitialPlace : max(currentGroupStageLoserBracketsInitialPlace, displayableMatches.map({ $0.index }).max()! + 2) let match = Match(round: loserBracket.id, index: placeCount, matchFormat: loserBracket.matchFormat) match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix()) place" - do { - try tournamentStore.matches.addOrUpdate(instance: match) - } catch { - Logger.error(error) - } + tournamentStore.matches.addOrUpdate(instance: match) } private func _deleteAllMatches() { let displayableMatches = loserBracket.playedMatches().sorted(by: \.index) - do { - try tournamentStore.matches.delete(contentOfs: displayableMatches) - } catch { - Logger.error(error) - } + tournamentStore.matches.delete(contentOfs: displayableMatches) } @@ -205,15 +197,7 @@ struct GroupStageLoserBracketMatchFooterView: View { match.name = "\(newIndexValidated)\(newIndexValidated.ordinalFormattedSuffix()) place" - do { - try match.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) - } catch { - Logger.error(error) - } - do { - try match.tournamentStore.matches.addOrUpdate(instance: match) - } catch { - Logger.error(error) - } - } + match.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) + match.tournamentStore.matches.addOrUpdate(instance: match) + } } diff --git a/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift b/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift index 91dedc7..c69ffb9 100644 --- a/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift +++ b/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift @@ -13,7 +13,7 @@ struct TournamentSubscriptionView: View { let federalTournament: FederalTournament let build: any TournamentBuildHolder - let user: User + let user: CustomUser @State private var selectedPlayers: [ImportedPlayer] @State private var contactType: ContactType? = nil @@ -21,7 +21,7 @@ struct TournamentSubscriptionView: View { @State private var didSendMessage: Bool = false @State private var didSaveInCalendar: Bool = false - init(federalTournament: FederalTournament, build: any TournamentBuildHolder, user: User) { + init(federalTournament: FederalTournament, build: any TournamentBuildHolder, user: CustomUser) { self.federalTournament = federalTournament self.build = build self.user = user diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 6efa24b..f095bc2 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -42,11 +42,7 @@ struct RoundSettingsView: View { Menu { Button("Retirer du tableau") { team.resetBracketPosition() - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } catch { - Logger.error(error) - } + self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) } } label: { TeamRowView(team: team) @@ -63,11 +59,7 @@ struct RoundSettingsView: View { RowButtonView("Valider l'état du tableau", role: .destructive) { tournament.shouldVerifyBracket = false - do { - try dataStore.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) - } + dataStore.tournaments.addOrUpdate(instance: tournament) } } footer: { Text("Suite à un changement dans votre liste d'inscrits, veuillez vérifier l'intégrité de votre tableau et valider que tout est ok.") @@ -121,16 +113,9 @@ struct RoundSettingsView: View { return match } - do { - try tournamentStore.rounds.addOrUpdate(instance: round) - } catch { - Logger.error(error) - } - do { - try tournamentStore.matches.addOrUpdate(contentOfs: matches) - } catch { - Logger.error(error) - } + tournamentStore.rounds.addOrUpdate(instance: round) + tournamentStore.matches.addOrUpdate(contentOfs: matches) + round.buildLoserBracket() matches.filter { $0.disabled }.forEach { $0._toggleLoserMatchDisableState(true) @@ -141,12 +126,12 @@ struct RoundSettingsView: View { Section { if let lastRound = tournament.rounds().first { // first is final, last round RowButtonView("Supprimer " + lastRound.roundTitle(), role: .destructive) { + let teams = lastRound.seeds() + teams.forEach { team in + team.resetBracketPosition() + } + tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) do { - let teams = lastRound.seeds() - teams.forEach { team in - team.resetBracketPosition() - } - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) try tournamentStore.rounds.delete(instance: lastRound) } catch { Logger.error(error) @@ -159,11 +144,7 @@ struct RoundSettingsView: View { RowButtonView("Synchroniser les noms des matchs") { let allRoundMatches = tournament.allRoundMatches() allRoundMatches.forEach({ $0.name = $0.roundTitle() }) - do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches) - } catch { - Logger.error(error) - } + self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches) } } } @@ -182,16 +163,8 @@ struct RoundSettingsView: View { match.teamScores } - do { - try tournamentStore.teamScores.delete(contentOfs: ts) - } catch { - Logger.error(error) - } - do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) - } catch { - Logger.error(error) - } + tournamentStore.teamScores.delete(contentOfs: ts) + tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) tournament.allRounds().forEach({ round in round.enableRound() }) diff --git a/PadelClub/Views/Tournament/Screen/AddTeamView.swift b/PadelClub/Views/Tournament/Screen/AddTeamView.swift index a86f18a..466ddff 100644 --- a/PadelClub/Views/Tournament/Screen/AddTeamView.swift +++ b/PadelClub/Views/Tournament/Screen/AddTeamView.swift @@ -10,7 +10,7 @@ import LeStorage import CoreData struct AddTeamView: View { - + @Environment(\.dismiss) var dismiss private var fetchRequest: FetchRequest @@ -45,7 +45,7 @@ struct AddTeamView: View { @State private var displayWarningNotEnoughCharacter: Bool = false @State private var testMessageIndex: Int = 0 @State private var presentLocalMultiplayerSearch: Bool = false - + var tournamentStore: TournamentStore { return self.tournament.tournamentStore } @@ -61,11 +61,11 @@ struct AddTeamView: View { createdPlayers.insert(player) createdPlayerIds.insert(player.id) } - + _createdPlayers = .init(wrappedValue: createdPlayers) _createdPlayerIds = .init(wrappedValue: createdPlayerIds) } - + let request: NSFetchRequest = ImportedPlayer.fetchRequest() request.sortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)] request.fetchLimit = 1000 @@ -77,10 +77,10 @@ struct AddTeamView: View { _textHeight = .init(wrappedValue: Self._calculateHeight(text: pasteString)) cancelShouldDismiss = true } - + fetchRequest = FetchRequest(fetchRequest: request, animation: .default) } - + var body: some View { if let pasteString, pasteString.isEmpty == false, fetchPlayers.isEmpty == false { computedBody @@ -89,7 +89,7 @@ struct AddTeamView: View { computedBody } } - + var computedBody: some View { List(selection: $createdPlayerIds) { _buildingTeamView() @@ -119,7 +119,7 @@ struct AddTeamView: View { Button("Créer l'équipe quand même") { _createTeam(checkDuplicates: false, checkHomonym: false) } - + Button("Annuler", role: .cancel) { confirmHomonym = false } @@ -132,7 +132,7 @@ struct AddTeamView: View { Button("Créer l'équipe quand même") { _createTeam(checkDuplicates: false, checkHomonym: true) } - + Button("Annuler", role: .cancel) { confirmDuplicate = false } @@ -213,11 +213,11 @@ struct AddTeamView: View { .buttonBorderShape(.capsule) } } - + ToolbarItem(placement: .topBarTrailing) { Button { let generalString = UIPasteboard.general.string ?? "" - + #if targetEnvironment(simulator) let s = testMessages[testMessageIndex % testMessages.count] handlePasteString(s) @@ -241,15 +241,15 @@ struct AddTeamView: View { .navigationTitle(editedTeam == nil ? "Ajouter une équipe" : "Modifier l'équipe") .environment(\.editMode, Binding.constant(EditMode.active)) } - + private func _isEditingTeam() -> Bool { createdPlayerIds.isEmpty == false || editedTeam != nil || pasteString != nil } - + var unsortedPlayers: [PlayerRegistration] { tournament.unsortedPlayers() } - + @ViewBuilder private func _managementView() -> some View { Section { @@ -261,7 +261,7 @@ struct AddTeamView: View { Text("Cherchez dans la base fédérale de \(rankSourceDate.monthYearFormatted), vous y trouverez tous les joueurs ayant participé à au moins un tournoi dans les 12 derniers mois.") } } - + if tournament.isAnimation(), createdPlayers.isEmpty == true { Section { RowButtonView("Ajouter plusieurs joueurs du club") { @@ -271,7 +271,7 @@ struct AddTeamView: View { Text("Crée une équipe par joueur sélectionné") } } - + Section { RowButtonView("Créer un non classé / non licencié") { if let pasteString, pasteString.isEmpty == false { @@ -284,7 +284,7 @@ struct AddTeamView: View { Text("Si le joueur n'a pas encore de licence ou n'a pas encore participé à une compétition, vous pouvez le créer vous-même.") } } - + private func _addPlayerSex() -> Int { switch tournament.tournamentCategory { case .men, .unlisted: @@ -296,11 +296,11 @@ struct AddTeamView: View { } } - + private func _filterOption() -> PlayerFilterOption { return tournament.tournamentCategory.playerFilterOption } - + private func _currentSelection() -> Set { var currentSelection = Set() createdPlayerIds.compactMap { id in @@ -310,7 +310,7 @@ struct AddTeamView: View { player.setComputedRank(in: tournament) currentSelection.insert(player) } - + createdPlayerIds.compactMap { id in createdPlayers.first(where: { id == $0.id }) }.forEach { @@ -326,7 +326,7 @@ struct AddTeamView: View { }.forEach { player in currentSelection.append(player.license) } - + createdPlayerIds.compactMap { id in createdPlayers.first(where: { id == $0.id }) }.forEach { @@ -334,7 +334,7 @@ struct AddTeamView: View { } return currentSelection } - + private func _isDuplicate() -> Bool { if tournament.isAnimation() { return false } let ids : [String?] = _currentSelectionIds() @@ -343,15 +343,15 @@ struct AddTeamView: View { } return false } - - private func _createTeam(checkDuplicates: Bool, checkHomonym: Bool) { + + private func _createTeam(checkDuplicates: Bool, checkHomonym: Bool) { if checkDuplicates && _isDuplicate() { confirmDuplicate = true return } let players = _currentSelection() - + if checkHomonym { homonyms = players.filter({ $0.hasHomonym() }) if homonyms.isEmpty == false { @@ -359,31 +359,23 @@ struct AddTeamView: View { return } } - + let team = tournament.addTeam(players) - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } catch { - Logger.error(error) - } - do { - try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - } catch { - Logger.error(error) - } - - pasteString = nil - editableTextField = "" - - if team.players().count > 1 { - createdPlayers.removeAll() - createdPlayerIds.removeAll() - dismiss() - } else { - editedTeam = team - } + self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + + pasteString = nil + editableTextField = "" + + if team.players().count > 1 { + createdPlayers.removeAll() + createdPlayerIds.removeAll() + dismiss() + } else { + editedTeam = team + } } - + private func _updateTeam(checkDuplicates: Bool) { guard let editedTeam else { return } if checkDuplicates && _isDuplicate() { @@ -393,17 +385,9 @@ struct AddTeamView: View { let players = _currentSelection() editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory) - do { - try self.tournamentStore.teamRegistrations.addOrUpdate(instance: editedTeam) - } catch { - Logger.error(error) - } - do { - try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - } catch { - Logger.error(error) - } - + self.tournamentStore.teamRegistrations.addOrUpdate(instance: editedTeam) + self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + pasteString = nil editableTextField = "" @@ -411,7 +395,7 @@ struct AddTeamView: View { dismiss() } } - + // Calculating the height based on the content of the TextEditor static private func _calculateHeight(text: String) -> CGFloat { let size = CGSize(width: UIScreen.main.bounds.width - 32, height: .infinity) @@ -424,15 +408,23 @@ struct AddTeamView: View { ) return max(boundingRect.height + 20, 40) // Add some padding and set a minimum height } - - @ViewBuilder - private func _buildingTeamView() -> some View { + + struct PasteStringSection: View { + + let pasteString: String? + @Binding var editableTextField: String + @Binding var textHeight: CGFloat + @FocusState var focusedField: AddTeamView.FocusField? + var handlePasteString: (String) -> Void + @Binding var displayWarningNotEnoughCharacter: Bool + + var body: some View { if let pasteString { Section { TextEditor(text: $editableTextField) .frame(height: textHeight) .onChange(of: editableTextField) { - textHeight = Self._calculateHeight(text: pasteString) + textHeight = AddTeamView._calculateHeight(text: pasteString) } .focused($focusedField, equals: .pasteField) .toolbar { @@ -464,128 +456,111 @@ struct AddTeamView: View { FooterButtonView("effacer le texte") { self.focusedField = nil self.editableTextField = "" - self.pasteString = nil + self.handlePasteString("") } } } } - - Section { - ForEach(createdPlayerIds.sorted(), id: \.self) { id in - if let p = createdPlayers.first(where: { $0.id == id }) { - VStack(alignment: .leading, spacing: 0) { - if let player = unsortedPlayers.first(where: { ($0.licenceId == p.licenceId && $0.licenceId != nil) }), editedTeam?.includes(player: player) == false { - Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerAgeInadequate(player: p) { - Text("Âge invalide !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerRankInadequate(player: p) { - Text("Trop bien classé !").foregroundStyle(.logoRed).bold() - } - PlayerView(player: p).tag(p.id) - .environment(tournament) - } - } - if let p = fetchPlayers.first(where: { $0.license == id }) { - VStack(alignment: .leading, spacing: 0) { - if let pasteString, pasteString.isEmpty == false, unsortedPlayers.first(where: { $0.licenceId == p.license }) != nil { - Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerAgeInadequate(player: p) { - Text("Âge invalide !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerRankInadequate(player: p) { - Text("Trop bien classé !").foregroundStyle(.logoRed).bold() - } - ImportedPlayerView(player: p).tag(p.license!) - } - } - } - if editedTeam == nil { - if createdPlayerIds.isEmpty { - RowButtonView("Bloquer une place") { - _createTeam(checkDuplicates: false, checkHomonym: false) - } - } else { - RowButtonView("Ajouter l'équipe") { - _createTeam(checkDuplicates: true, checkHomonym: true) - } - } - } else { - RowButtonView("Confirmer") { - _updateTeam(checkDuplicates: false) - dismiss() - } - } - } header: { - let _currentSelection = _currentSelection() - let selectedSortedTeams = tournament.selectedSortedTeams() - let rank = _currentSelection.map { - $0.computedRank - }.reduce(0, +) - let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count - if _currentSelection.isEmpty == false, tournament.hideWeight() == false, rank > 0 { - HStack(spacing: 16.0) { - VStack(alignment: .leading, spacing: 0) { - Text("Rang").font(.caption) - Text("#" + (teamIndex + 1).formatted()) - } + } + } - VStack(alignment: .leading, spacing: 0) { - Text("Poids").font(.caption) - Text(rank.formatted()) - } - Spacer() - VStack(alignment: .trailing, spacing: 0) { - Text("").font(.caption) - Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count)) - } - } -// } else { -// Text("Préparation de l'équipe") - } - } - + @ViewBuilder + private func _buildingTeamView() -> some View { + + PasteStringSection( + pasteString: pasteString, + editableTextField: $editableTextField, + textHeight: $textHeight, + focusedField: _focusedField, + handlePasteString: handlePasteString, + displayWarningNotEnoughCharacter: $displayWarningNotEnoughCharacter + ) + + TeamSelectionSection( + createdPlayerIds: createdPlayerIds, + createdPlayers: createdPlayers, + unsortedPlayers: unsortedPlayers, + fetchPlayers: fetchPlayers, + editedTeam: editedTeam, + pasteString: pasteString, + tournament: tournament, + _createTeam: _createTeam, + _updateTeam: _updateTeam, + dismiss: dismiss, + _currentSelection: _currentSelection + ) + if let pasteString, pasteString.isEmpty == false { - let sortedPlayers = _searchFilteredPlayers() - - if sortedPlayers.isEmpty { - ContentUnavailableView { - Label("Aucun résultat", systemImage: "person.2.slash") - } description: { - Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.") - } actions: { - RowButtonView("Créer un joueur non classé") { - selectionSearchField = pasteString - } - - RowButtonView("Chercher dans la base") { - presentPlayerSearch = true - } + let sortedPlayers = _searchFilteredPlayers() + + if sortedPlayers.isEmpty { + ContentUnavailableView { + Label("Aucun résultat", systemImage: "person.2.slash") + } description: { + Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.") + } actions: { + RowButtonView("Créer un joueur non classé") { + selectionSearchField = pasteString + } - RowButtonView("Effacer cette recherche") { - self.pasteString = nil - self.editableTextField = "" - } + RowButtonView("Chercher dans la base") { + presentPlayerSearch = true } - } else { - _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString) + RowButtonView("Effacer cette recherche") { + self.pasteString = nil + self.editableTextField = "" + } } + } else { - _managementView() + _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString) } + } else { + _managementView() + } } - + +// +// if let pasteString, pasteString.isEmpty == false { +// let sortedPlayers = _searchFilteredPlayers() +// +// if sortedPlayers.isEmpty { +// ContentUnavailableView { +// Label("Aucun résultat", systemImage: "person.2.slash") +// } description: { +// Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.") +// } actions: { +// RowButtonView("Créer un joueur non classé") { +// selectionSearchField = pasteString +// } +// +// RowButtonView("Chercher dans la base") { +// presentPlayerSearch = true +// } +// +// RowButtonView("Effacer cette recherche") { +// self.pasteString = nil +// self.editableTextField = "" +// } +// } +// +// } else { +// _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString) +// } +// } else { +// _managementView() +// } +// } + @MainActor func hitForSearch(_ ip: ImportedPlayer, _ pasteString: String?) -> Int { guard let pasteString else { return 0 } let _searchForHit = pasteString.hashValue if searchForHit != _searchForHit { - DispatchQueue.main.async { + DispatchQueue.main.async { searchForHit = _searchForHit hitsForSearch = [:] } @@ -615,7 +590,7 @@ struct AddTeamView: View { } return 1 } - + @MainActor private func handlePasteString(_ first: String) { if first.isEmpty == false { @@ -633,7 +608,7 @@ struct AddTeamView: View { @ViewBuilder private func _listOfPlayers(searchFilteredPlayers: [ImportedPlayer], pasteString: String) -> some View { let sortedPlayers = _sortedPlayers(searchFilteredPlayers: searchFilteredPlayers, pasteString: pasteString) - + Section { ForEach(sortedPlayers) { player in ImportedPlayerView(player: player).tag(player.license!) @@ -644,7 +619,7 @@ struct AddTeamView: View { } } - + private func _searchFilteredPlayers() -> [ImportedPlayer] { if searchField.isEmpty { return Array(fetchPlayers) @@ -652,12 +627,171 @@ struct AddTeamView: View { return fetchPlayers.filter({ $0.contains(searchField) }) } } - + private func _sortedPlayers(searchFilteredPlayers: [ImportedPlayer], pasteString: String) -> [ImportedPlayer] { return searchFilteredPlayers.sorted(by: { hitForSearch($0, pasteString) > hitForSearch($1, pasteString) }) } } +struct TeamSelectionSection: View { + let createdPlayerIds: Set + let createdPlayers: Set + let unsortedPlayers: [PlayerRegistration] + let fetchPlayers: FetchedResults + let editedTeam: TeamRegistration? + let pasteString: String? + let tournament: Tournament + let _createTeam: (Bool, Bool) -> Void + let _updateTeam: (Bool) -> Void + let dismiss: DismissAction + let _currentSelection: () -> Set + + var body: some View { + Section { + PlayerList(createdPlayerIds: createdPlayerIds, + createdPlayers: createdPlayers, + unsortedPlayers: unsortedPlayers, + fetchPlayers: fetchPlayers, + editedTeam: editedTeam, + pasteString: pasteString, + tournament: tournament) + + ActionButton(editedTeam: editedTeam, + createdPlayerIds: createdPlayerIds, + _createTeam: _createTeam, + _updateTeam: _updateTeam, + dismiss: dismiss) + } header: { + TeamHeader(tournament: tournament, + _currentSelection: _currentSelection) + } + } +} + +struct PlayerList: View { + let createdPlayerIds: Set + let createdPlayers: Set + let unsortedPlayers: [PlayerRegistration] + let fetchPlayers: FetchedResults + let editedTeam: TeamRegistration? + let pasteString: String? + let tournament: Tournament + + var body: some View { + ForEach(createdPlayerIds.sorted(), id: \.self) { id in + if let p = createdPlayers.first(where: { $0.id == id }) { + CreatedPlayerView(player: p, unsortedPlayers: unsortedPlayers, editedTeam: editedTeam, tournament: tournament) + } + if let p = fetchPlayers.first(where: { $0.license == id }) { + FetchedPlayerView(player: p, unsortedPlayers: unsortedPlayers, pasteString: pasteString, tournament: tournament) + } + } + } +} + +struct CreatedPlayerView: View { + let player: PlayerRegistration + let unsortedPlayers: [PlayerRegistration] + let editedTeam: TeamRegistration? + let tournament: Tournament + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + if let existingPlayer = unsortedPlayers.first(where: { ($0.licenceId == player.licenceId && $0.licenceId != nil) }), editedTeam?.includes(player: existingPlayer) == false { + Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerAgeInadequate(player: player) { + Text("Âge invalide !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerRankInadequate(player: player) { + Text("Trop bien classé !").foregroundStyle(.logoRed).bold() + } + PlayerView(player: player).tag(player.id) + .environment(tournament) + } + } +} + +struct FetchedPlayerView: View { + let player: ImportedPlayer + let unsortedPlayers: [PlayerRegistration] + let pasteString: String? + let tournament: Tournament + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + if let pasteString, pasteString.isEmpty == false, unsortedPlayers.first(where: { $0.licenceId == player.license }) != nil { + Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerAgeInadequate(player: player) { + Text("Âge invalide !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerRankInadequate(player: player) { + Text("Trop bien classé !").foregroundStyle(.logoRed).bold() + } + ImportedPlayerView(player: player).tag(player.license!) + } + } +} + +struct ActionButton: View { + let editedTeam: TeamRegistration? + let createdPlayerIds: Set + let _createTeam: (Bool, Bool) -> Void + let _updateTeam: (Bool) -> Void + let dismiss: DismissAction + + var body: some View { + if editedTeam == nil { + if createdPlayerIds.isEmpty { + RowButtonView("Bloquer une place") { + _createTeam(false, false) + } + } else { + RowButtonView("Ajouter l'équipe") { + _createTeam(true, true) + } + } + } else { + RowButtonView("Confirmer") { + _updateTeam(false) + dismiss() + } + } + } +} + +struct TeamHeader: View { + let tournament: Tournament + let _currentSelection: () -> Set + + var body: some View { + let currentSelection = _currentSelection() + let selectedSortedTeams = tournament.selectedSortedTeams() + let rank = currentSelection.map { $0.computedRank }.reduce(0, +) + let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count + + if !currentSelection.isEmpty, !tournament.hideWeight(), rank > 0 { + HStack(spacing: 16.0) { + VStack(alignment: .leading, spacing: 0) { + Text("Rang").font(.caption) + Text("#" + (teamIndex + 1).formatted()) + } + + VStack(alignment: .leading, spacing: 0) { + Text("Poids").font(.caption) + Text(rank.formatted()) + } + Spacer() + VStack(alignment: .trailing, spacing: 0) { + Text("").font(.caption) + Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count)) + } + } + } + } +} + let testMessages = [ "Anthony dovetta ( 3620578 K )et christophe capeau ( 4666443v)", """ @@ -684,6 +818,6 @@ Tullou Benjamin 8990867f """, """ Sms Julien La Croix +33622886688 -Salut Raz, c'est ! Ju Lacroix J'espère que tu vas bien depuis le temps! Est-ce que tu peux nous inscrire au 1000 de Bandol avec Derek Gerson stp? +Salut Raz, c'est ! Ju Lacroix J'espère que tu vas bien depuis le temps! Est-ce que tu peux nous inscrire au 1000 de Bandol avec Derek Gerson stp? """ ] diff --git a/PadelClub/Views/Tournament/Subscription/Guard.swift b/PadelClub/Views/Tournament/Subscription/Guard.swift index 64cbdac..bee8592 100644 --- a/PadelClub/Views/Tournament/Subscription/Guard.swift +++ b/PadelClub/Views/Tournament/Subscription/Guard.swift @@ -20,7 +20,6 @@ import LeStorage var updateListenerTask: Task? = nil - override init() { super.init() diff --git a/PadelClub/Views/Tournament/Subscription/Purchase.swift b/PadelClub/Views/Tournament/Subscription/Purchase.swift index 103b5e4..06e1304 100644 --- a/PadelClub/Views/Tournament/Subscription/Purchase.swift +++ b/PadelClub/Views/Tournament/Subscription/Purchase.swift @@ -8,13 +8,15 @@ import Foundation import LeStorage -class Purchase: ModelObject, Storable { +class Purchase: ModelObject, SyncedStorable { + static func resourceName() -> String { return "purchases" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } static var relationshipNames: [String] = [] var id: UInt64 + var lastUpdate: Date var user: String var purchaseDate: Date var productId: String @@ -22,8 +24,11 @@ class Purchase: ModelObject, Storable { var revocationDate: Date? = nil var expirationDate: Date? = nil + var storeId: String? { return nil } + init(user: String, transactionId: UInt64, purchaseDate: Date, productId: String, quantity: Int? = nil, revocationDate: Date? = nil, expirationDate: Date? = nil) { self.id = transactionId + self.lastUpdate = Date() self.user = user self.purchaseDate = purchaseDate self.productId = productId @@ -34,6 +39,7 @@ class Purchase: ModelObject, Storable { enum CodingKeys: String, CodingKey, CaseIterable { case id + case lastUpdate case user case purchaseDate case productId @@ -56,6 +62,7 @@ class Purchase: ModelObject, Storable { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: .id) + try container.encode(self.lastUpdate, forKey: .lastUpdate) try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user) try container.encode(self.purchaseDate, forKey: .purchaseDate) try container.encode(self.productId, forKey: .productId) @@ -68,6 +75,7 @@ class Purchase: ModelObject, Storable { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(UInt64.self, forKey: .id) + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: .lastUpdate) ?? Date() self.user = try container.decodeEncrypted(key: .user) self.purchaseDate = try container.decode(Date.self, forKey: .purchaseDate) self.productId = try container.decode(String.self, forKey: .productId) diff --git a/PadelClub/Views/User/AccountView.swift b/PadelClub/Views/User/AccountView.swift index a45da3e..07771af 100644 --- a/PadelClub/Views/User/AccountView.swift +++ b/PadelClub/Views/User/AccountView.swift @@ -10,7 +10,7 @@ import LeStorage struct AccountView: View { - var user: User + var user: CustomUser var handler: () -> () var body: some View { diff --git a/PadelClub/Views/User/LoginView.swift b/PadelClub/Views/User/LoginView.swift index d66f8d7..57e36d4 100644 --- a/PadelClub/Views/User/LoginView.swift +++ b/PadelClub/Views/User/LoginView.swift @@ -74,7 +74,7 @@ struct LoginView: View { } } - var handler: (User) -> () + var handler: (CustomUser) -> () var body: some View { @@ -195,7 +195,7 @@ struct LoginView: View { self.isLoading = true do { let service = try StoreCenter.main.service() - let user: User = try await service.login( + let user: CustomUser = try await service.login( username: self.username, password: self.password) self.dataStore.user = user diff --git a/PadelClub/Views/User/UserCreationView.swift b/PadelClub/Views/User/UserCreationView.swift index a20ebb4..60a5a31 100644 --- a/PadelClub/Views/User/UserCreationView.swift +++ b/PadelClub/Views/User/UserCreationView.swift @@ -232,7 +232,7 @@ struct UserCreationFormView: View { country: country) let service: Services = try StoreCenter.main.service() - let _: User = try await service.createAccount(user: userCreationForm) + let _: CustomUser = try await service.createAccount(user: userCreationForm) DispatchQueue.main.async { self.isLoading = false diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 3947937..8d44e9c 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -31,7 +31,7 @@ final class ServerDataTests: XCTestCase { func login() async throws { // print("LOGIN!") - let _: User = try await StoreCenter.main.service().login(username: self.username, password: self.password) + let _: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password) } func testClub() async throws { @@ -48,6 +48,7 @@ final class ServerDataTests: XCTestCase { club.courtCount = 3 let inserted_club: Club = try await StoreCenter.main.service().post(club) + assert(inserted_club.lastUpdate == club.lastUpdate) assert(inserted_club.name == club.name) assert(inserted_club.acronym == club.acronym) assert(inserted_club.zipCode == club.zipCode) @@ -59,6 +60,7 @@ final class ServerDataTests: XCTestCase { assert(inserted_club.broadcastCode != nil) inserted_club.phone = "123456" + inserted_club.lastUpdate = Date() let updated_club: Club = try await StoreCenter.main.service().put(inserted_club) assert(updated_club.phone == inserted_club.phone) @@ -66,7 +68,7 @@ final class ServerDataTests: XCTestCase { } func testLogin() async throws { - let user: User = try await StoreCenter.main.service().login(username: self.username, password: self.password) + let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password) assert(user.username == "test") } @@ -87,6 +89,7 @@ final class ServerDataTests: XCTestCase { let e = try await StoreCenter.main.service().post(event) assert(e.name == event.name) + assert(e.lastUpdate == event.lastUpdate) assert(e.tenupId == event.tenupId) } @@ -102,6 +105,7 @@ final class ServerDataTests: XCTestCase { let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: true, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual) let t = try await StoreCenter.main.service().post(tournament) + assert(t.lastUpdate.formatted() == tournament.lastUpdate.formatted()) assert(t.event == tournament.event) assert(t.name == tournament.name) assert(t.startDate.formatted() == tournament.startDate.formatted()) @@ -151,9 +155,12 @@ final class ServerDataTests: XCTestCase { } let groupStage = GroupStage(tournament: tournamentId, index: 2, size: 3, matchFormat: MatchFormat.nineGames, startDate: Date(), name: "Yeah!", step: 1) + groupStage.storeId = "123" let gs: GroupStage = try await StoreCenter.main.service().post(groupStage) assert(gs.tournament == groupStage.tournament) + assert(gs.storeId == groupStage.storeId) + assert(gs.lastUpdate == groupStage.lastUpdate) assert(gs.name == groupStage.name) assert(gs.index == groupStage.index) assert(gs.size == groupStage.size) @@ -175,9 +182,12 @@ final class ServerDataTests: XCTestCase { let parentRoundId = rounds.first?.id let round = Round(tournament: tournamentId, index: 1, parent: parentRoundId, matchFormat: MatchFormat.nineGames, startDate: Date(), groupStageLoserBracket: false, loserBracketMode: .manual) + round.storeId = "abc" let r: Round = try await StoreCenter.main.service().post(round) + assert(r.storeId == round.storeId) assert(r.tournament == round.tournament) + assert(r.lastUpdate == round.lastUpdate) assert(r.index == round.index) assert(r.parent == round.parent) assert(r.matchFormat == round.matchFormat) @@ -201,10 +211,13 @@ final class ServerDataTests: XCTestCase { } let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true) + teamRegistration.storeId = "123" let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration) + assert(tr.storeId == teamRegistration.storeId) assert(tr.tournament == teamRegistration.tournament) + assert(tr.lastUpdate == teamRegistration.lastUpdate) assert(tr.groupStage == teamRegistration.groupStage) assert(tr.registrationDate != nil) assert(tr.callDate != nil) @@ -234,8 +247,11 @@ final class ServerDataTests: XCTestCase { } let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true) + playerRegistration.storeId = "123" let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration) + assert(pr.storeId == playerRegistration.storeId) + assert(pr.lastName == playerRegistration.lastName) assert(pr.teamRegistration == playerRegistration.teamRegistration) assert(pr.firstName == playerRegistration.firstName) assert(pr.lastName == playerRegistration.lastName) @@ -267,8 +283,11 @@ final class ServerDataTests: XCTestCase { let parentRoundId = rounds.first?.id let match: Match = Match(round: parentRoundId, groupStage: nil, startDate: Date(), endDate: Date(), index: 2, matchFormat: MatchFormat.twoSets, servingTeamId: teamRegistrationId, winningTeamId: teamRegistrationId, losingTeamId: teamRegistrationId, disabled: true, courtIndex: 1, confirmed: true) + match.storeId = "123" let m: Match = try await StoreCenter.main.service().post(match) + assert(m.storeId == match.storeId) + assert(m.lastUpdate == match.lastUpdate) assert(m.round == match.round) assert(m.groupStage == match.groupStage) assert(m.startDate != nil) @@ -297,8 +316,11 @@ final class ServerDataTests: XCTestCase { return } let teamScore = TeamScore(match: matchId, teamRegistration: teamRegistrationId, score: "6/6", walkOut: 1, luckyLoser: 1) + teamScore.storeId = "!23" let ts: TeamScore = try await StoreCenter.main.service().post(teamScore) + assert(ts.storeId == teamScore.storeId) + assert(ts.lastUpdate == teamScore.lastUpdate) assert(ts.match == teamScore.match) assert(ts.teamRegistration == teamScore.teamRegistration) assert(ts.score == teamScore.score) @@ -318,6 +340,7 @@ final class ServerDataTests: XCTestCase { let court = Court(index: 1, club: clubId, name: "Philippe Chatrier", exitAllowed: true, indoor: true) let c: Court = try await StoreCenter.main.service().post(court) + assert(c.lastUpdate == court.lastUpdate) assert(c.club == court.club) assert(c.name == court.name) assert(c.index == court.index) @@ -337,6 +360,7 @@ final class ServerDataTests: XCTestCase { let dateInterval = DateInterval(event: eventId, courtIndex: 1, startDate: Date(), endDate: Date()) let di: PadelClub.DateInterval = try await StoreCenter.main.service().post(dateInterval) + assert(di.lastUpdate == dateInterval.lastUpdate) assert(di.event == dateInterval.event) assert(di.courtIndex == dateInterval.courtIndex) assert(di.startDate.formatted() == dateInterval.startDate.formatted()) @@ -354,10 +378,12 @@ final class ServerDataTests: XCTestCase { let transactionId = UInt64.random(in: 0...100000) let quantity = Int.random(in: 0...10) - let purchase: Purchase = Purchase(user: userId, transactionId: transactionId, purchaseDate: Date(), productId: "app.padelclub.productId", quantity: quantity, revocationDate: Date(), expirationDate: Date()) + let purchase: Purchase = Purchase(transactionId: transactionId, purchaseDate: Date(), productId: "app.padelclub.productId", quantity: quantity, revocationDate: Date(), expirationDate: Date()) + let p: Purchase = try await StoreCenter.main.service().post(purchase) assert(p.id == purchase.id) + assert(p.lastUpdate == purchase.lastUpdate) assert(p.user == purchase.user) assert(p.productId == purchase.productId) assert(p.purchaseDate.formatted() == purchase.purchaseDate.formatted()) diff --git a/PadelClubTests/SynchronizationTests.swift b/PadelClubTests/SynchronizationTests.swift new file mode 100644 index 0000000..0c2313d --- /dev/null +++ b/PadelClubTests/SynchronizationTests.swift @@ -0,0 +1,33 @@ +// +// SynchronizationTests.swift +// PadelClubTests +// +// Created by Laurent Morvillier on 09/10/2024. +// + +import Testing +import LeStorage +@testable import PadelClub + +struct SynchronizationTests { + + let username: String = "laurent" + let password: String = "StaxKikoo12" + + init() { + StoreCenter.main.synchronizationApiURL = "http://127.0.0.1:8000/roads/" + } + + @Test func synchronizationTest() async throws { + + _ = try await self.login() + try await StoreCenter.main.synchronizeLastUpdates() + + } + + func login() async throws -> CustomUser { + let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password) + return user + } + +} diff --git a/PadelClubTests/UserDataTests.swift b/PadelClubTests/UserDataTests.swift index 4980845..8b848d5 100644 --- a/PadelClubTests/UserDataTests.swift +++ b/PadelClubTests/UserDataTests.swift @@ -24,8 +24,8 @@ final class UserDataTests: XCTestCase { func testUserCreation() async throws { - let userCreationForm = UserCreationForm(user: User.placeHolder(), username: self.username, password: self.password, firstName: "jean", lastName: "coco", email: "test@lolomo.com", phone: "0123", country: "France") - let user: User = try await StoreCenter.main.service().createAccount(user: userCreationForm) + let userCreationForm = UserCreationForm(user: CustomUser.placeHolder(), username: self.username, password: self.password, firstName: "jean", lastName: "coco", email: "test@lolomo.com", phone: "0123", country: "France") + let user: CustomUser = try await StoreCenter.main.service().createAccount(user: userCreationForm) assert(user.username == userCreationForm.username) assert(user.firstName == userCreationForm.firstName) @@ -36,8 +36,8 @@ final class UserDataTests: XCTestCase { } - func login() async throws -> User { - let user: User = try await StoreCenter.main.service().login(username: self.username, password: self.password) + func login() async throws -> CustomUser { + let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password) return user } From 0dd3dc16bd7fda526f9f871e9cd678668c9bf56d Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 25 Oct 2024 15:37:04 +0200 Subject: [PATCH 02/43] create generator to make base Swift classes --- PadelClub.xcodeproj/project.pbxproj | 254 ++++++++ PadelClub/Data/Club.swift | 127 ++-- PadelClub/Data/Court.swift | 44 +- PadelClub/Data/CustomUser.swift | 265 ++++---- PadelClub/Data/DataStore.swift | 8 +- PadelClub/Data/DateInterval.swift | 55 +- PadelClub/Data/Event.swift | 52 +- PadelClub/Data/Gen/BaseClub.swift | 139 +++++ PadelClub/Data/Gen/BaseCourt.swift | 89 +++ PadelClub/Data/Gen/BaseCustomUser.swift | 197 ++++++ PadelClub/Data/Gen/BaseDateInterval.swift | 78 +++ PadelClub/Data/Gen/BaseEvent.swift | 95 +++ PadelClub/Data/Gen/BaseGroupStage.swift | 110 ++++ PadelClub/Data/Gen/BaseMatch.swift | 158 +++++ PadelClub/Data/Gen/BaseMatchScheduler.swift | 138 +++++ PadelClub/Data/Gen/BaseMonthData.swift | 113 ++++ .../Data/Gen/BasePlayerRegistration.swift | 188 ++++++ PadelClub/Data/Gen/BasePurchase.swift | 95 +++ PadelClub/Data/Gen/BaseRound.swift | 110 ++++ PadelClub/Data/Gen/BaseTeamRegistration.swift | 202 ++++++ PadelClub/Data/Gen/BaseTeamScore.swift | 101 +++ PadelClub/Data/Gen/BaseTournament.swift | 396 ++++++++++++ PadelClub/Data/Gen/Club.json | 90 +++ PadelClub/Data/Gen/Court.json | 47 ++ PadelClub/Data/Gen/CustomUser.json | 136 +++++ PadelClub/Data/Gen/DateInterval.json | 38 ++ PadelClub/Data/Gen/Event.json | 53 ++ PadelClub/Data/Gen/GroupStage.json | 64 ++ PadelClub/Data/Gen/Match.json | 93 +++ PadelClub/Data/Gen/MatchScheduler.json | 75 +++ PadelClub/Data/Gen/MonthData.json | 72 +++ PadelClub/Data/Gen/PlayerRegistration.json | 118 ++++ PadelClub/Data/Gen/Purchase.json | 56 ++ PadelClub/Data/Gen/Round.json | 64 ++ PadelClub/Data/Gen/TeamRegistration.json | 129 ++++ PadelClub/Data/Gen/TeamScore.json | 55 ++ PadelClub/Data/Gen/Tournament.json | 221 +++++++ PadelClub/Data/Gen/generator.py | 504 +++++++++++++++ PadelClub/Data/GroupStage.swift | 114 ++-- PadelClub/Data/Match.swift | 181 +++--- PadelClub/Data/MatchScheduler.swift | 75 +-- PadelClub/Data/MonthData.swift | 29 +- PadelClub/Data/PlayerRegistration.swift | 301 ++++----- PadelClub/Data/Round.swift | 146 ++--- PadelClub/Data/TeamRegistration.swift | 178 +++--- PadelClub/Data/TeamScore.swift | 107 ++-- PadelClub/Data/Tournament.swift | 577 +++++++++--------- .../CodingContainer+Extensions.swift | 8 +- .../NumberFormatter+Extensions.swift | 4 + PadelClub/Utils/FileImportManager.swift | 2 +- .../Views/Cashier/CashierDetailView.swift | 6 +- .../Views/Cashier/CashierSettingsView.swift | 30 +- .../LoserBracketFromGroupStageView.swift | 2 +- PadelClub/Views/Navigation/MainView.swift | 4 +- .../Player/Components/PlayerPayView.swift | 12 +- .../Player/Components/PlayerPopoverView.swift | 2 +- .../Components/PlayerSexPickerView.swift | 22 +- PadelClub/Views/Round/RoundSettingsView.swift | 2 +- .../Views/Tournament/Subscription/Guard.swift | 14 +- .../Tournament/Subscription/Purchase.swift | 118 ++-- .../Subscription/SubscriptionInfoView.swift | 2 +- .../Views/Tournament/TournamentView.swift | 8 +- 62 files changed, 5513 insertions(+), 1260 deletions(-) create mode 100644 PadelClub/Data/Gen/BaseClub.swift create mode 100644 PadelClub/Data/Gen/BaseCourt.swift create mode 100644 PadelClub/Data/Gen/BaseCustomUser.swift create mode 100644 PadelClub/Data/Gen/BaseDateInterval.swift create mode 100644 PadelClub/Data/Gen/BaseEvent.swift create mode 100644 PadelClub/Data/Gen/BaseGroupStage.swift create mode 100644 PadelClub/Data/Gen/BaseMatch.swift create mode 100644 PadelClub/Data/Gen/BaseMatchScheduler.swift create mode 100644 PadelClub/Data/Gen/BaseMonthData.swift create mode 100644 PadelClub/Data/Gen/BasePlayerRegistration.swift create mode 100644 PadelClub/Data/Gen/BasePurchase.swift create mode 100644 PadelClub/Data/Gen/BaseRound.swift create mode 100644 PadelClub/Data/Gen/BaseTeamRegistration.swift create mode 100644 PadelClub/Data/Gen/BaseTeamScore.swift create mode 100644 PadelClub/Data/Gen/BaseTournament.swift create mode 100644 PadelClub/Data/Gen/Club.json create mode 100644 PadelClub/Data/Gen/Court.json create mode 100644 PadelClub/Data/Gen/CustomUser.json create mode 100644 PadelClub/Data/Gen/DateInterval.json create mode 100644 PadelClub/Data/Gen/Event.json create mode 100644 PadelClub/Data/Gen/GroupStage.json create mode 100644 PadelClub/Data/Gen/Match.json create mode 100644 PadelClub/Data/Gen/MatchScheduler.json create mode 100644 PadelClub/Data/Gen/MonthData.json create mode 100644 PadelClub/Data/Gen/PlayerRegistration.json create mode 100644 PadelClub/Data/Gen/Purchase.json create mode 100644 PadelClub/Data/Gen/Round.json create mode 100644 PadelClub/Data/Gen/TeamRegistration.json create mode 100644 PadelClub/Data/Gen/TeamScore.json create mode 100644 PadelClub/Data/Gen/Tournament.json create mode 100644 PadelClub/Data/Gen/generator.py diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 8de2d45..6d8cb6a 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -22,6 +22,98 @@ C45BAE3B2BC6DF10002EEC8A /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; }; C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; C4607A7D2C04DDE2004CB781 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */; }; + C488C7E92CC7D16F0082001F /* generator.py in Resources */ = {isa = PBXBuildFile; fileRef = C488C7E52CC7D1660082001F /* generator.py */; }; + C488C7EA2CC7D16F0082001F /* generator.py in Resources */ = {isa = PBXBuildFile; fileRef = C488C7E52CC7D1660082001F /* generator.py */; }; + C488C7EB2CC7D16F0082001F /* generator.py in Resources */ = {isa = PBXBuildFile; fileRef = C488C7E52CC7D1660082001F /* generator.py */; }; + C488C7F12CC7D22D0082001F /* Club.json in Sources */ = {isa = PBXBuildFile; fileRef = C488C7EC2CC7D2290082001F /* Club.json */; }; + C488C7F22CC7D22D0082001F /* Club.json in Sources */ = {isa = PBXBuildFile; fileRef = C488C7EC2CC7D2290082001F /* Club.json */; }; + C488C7FF2CC7DCB80082001F /* BaseClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C7FE2CC7DCB80082001F /* BaseClub.swift */; }; + C488C8002CC7DCB80082001F /* BaseClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C7FE2CC7DCB80082001F /* BaseClub.swift */; }; + C488C8012CC7DCB80082001F /* BaseClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C7FE2CC7DCB80082001F /* BaseClub.swift */; }; + C488C8032CC7E1E40082001F /* BaseCourt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8022CC7E1E40082001F /* BaseCourt.swift */; }; + C488C8042CC7E1E40082001F /* BaseCourt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8022CC7E1E40082001F /* BaseCourt.swift */; }; + C488C8052CC7E1E40082001F /* BaseCourt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8022CC7E1E40082001F /* BaseCourt.swift */; }; + C488C8202CC7E4240082001F /* Event.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8162CC7E4240082001F /* Event.json */; }; + C488C8212CC7E4240082001F /* Court.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8132CC7E4240082001F /* Court.json */; }; + C488C8222CC7E4240082001F /* Tournament.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81F2CC7E4240082001F /* Tournament.json */; }; + C488C8232CC7E4240082001F /* CustomUser.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8142CC7E4240082001F /* CustomUser.json */; }; + C488C8242CC7E4240082001F /* Round.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81C2CC7E4240082001F /* Round.json */; }; + C488C8252CC7E4240082001F /* MatchScheduler.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8192CC7E4240082001F /* MatchScheduler.json */; }; + C488C8262CC7E4240082001F /* DateInterval.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8152CC7E4240082001F /* DateInterval.json */; }; + C488C8272CC7E4240082001F /* TeamRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81D2CC7E4240082001F /* TeamRegistration.json */; }; + C488C8282CC7E4240082001F /* GroupStage.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8172CC7E4240082001F /* GroupStage.json */; }; + C488C8292CC7E4240082001F /* MonthData.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81A2CC7E4240082001F /* MonthData.json */; }; + C488C82A2CC7E4240082001F /* TeamScore.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81E2CC7E4240082001F /* TeamScore.json */; }; + C488C82B2CC7E4240082001F /* Match.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8182CC7E4240082001F /* Match.json */; }; + C488C82C2CC7E4240082001F /* PlayerRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81B2CC7E4240082001F /* PlayerRegistration.json */; }; + C488C82D2CC7E4240082001F /* BaseDateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8072CC7E4240082001F /* BaseDateInterval.swift */; }; + C488C82E2CC7E4240082001F /* BaseMonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80C2CC7E4240082001F /* BaseMonthData.swift */; }; + C488C82F2CC7E4240082001F /* BaseTeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */; }; + C488C8302CC7E4240082001F /* BaseGroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8092CC7E4240082001F /* BaseGroupStage.swift */; }; + C488C8312CC7E4240082001F /* BaseCustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8062CC7E4240082001F /* BaseCustomUser.swift */; }; + C488C8322CC7E4240082001F /* BaseMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80A2CC7E4240082001F /* BaseMatch.swift */; }; + C488C8332CC7E4240082001F /* BaseEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8082CC7E4240082001F /* BaseEvent.swift */; }; + C488C8342CC7E4240082001F /* BaseRound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80E2CC7E4240082001F /* BaseRound.swift */; }; + C488C8352CC7E4240082001F /* BaseMatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */; }; + C488C8372CC7E4240082001F /* BasePlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */; }; + C488C8382CC7E4240082001F /* BaseTeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8102CC7E4240082001F /* BaseTeamScore.swift */; }; + C488C8392CC7E4240082001F /* BaseTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8112CC7E4240082001F /* BaseTournament.swift */; }; + C488C83A2CC7E4240082001F /* Event.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8162CC7E4240082001F /* Event.json */; }; + C488C83B2CC7E4240082001F /* Court.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8132CC7E4240082001F /* Court.json */; }; + C488C83C2CC7E4240082001F /* Tournament.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81F2CC7E4240082001F /* Tournament.json */; }; + C488C83D2CC7E4240082001F /* CustomUser.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8142CC7E4240082001F /* CustomUser.json */; }; + C488C83E2CC7E4240082001F /* Round.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81C2CC7E4240082001F /* Round.json */; }; + C488C83F2CC7E4240082001F /* MatchScheduler.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8192CC7E4240082001F /* MatchScheduler.json */; }; + C488C8402CC7E4240082001F /* DateInterval.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8152CC7E4240082001F /* DateInterval.json */; }; + C488C8412CC7E4240082001F /* TeamRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81D2CC7E4240082001F /* TeamRegistration.json */; }; + C488C8422CC7E4240082001F /* GroupStage.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8172CC7E4240082001F /* GroupStage.json */; }; + C488C8432CC7E4240082001F /* MonthData.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81A2CC7E4240082001F /* MonthData.json */; }; + C488C8442CC7E4240082001F /* TeamScore.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81E2CC7E4240082001F /* TeamScore.json */; }; + C488C8452CC7E4240082001F /* Match.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8182CC7E4240082001F /* Match.json */; }; + C488C8462CC7E4240082001F /* PlayerRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81B2CC7E4240082001F /* PlayerRegistration.json */; }; + C488C8472CC7E4240082001F /* BaseDateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8072CC7E4240082001F /* BaseDateInterval.swift */; }; + C488C8482CC7E4240082001F /* BaseMonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80C2CC7E4240082001F /* BaseMonthData.swift */; }; + C488C8492CC7E4240082001F /* BaseTeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */; }; + C488C84A2CC7E4240082001F /* BaseGroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8092CC7E4240082001F /* BaseGroupStage.swift */; }; + C488C84B2CC7E4240082001F /* BaseCustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8062CC7E4240082001F /* BaseCustomUser.swift */; }; + C488C84C2CC7E4240082001F /* BaseMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80A2CC7E4240082001F /* BaseMatch.swift */; }; + C488C84D2CC7E4240082001F /* BaseEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8082CC7E4240082001F /* BaseEvent.swift */; }; + C488C84E2CC7E4240082001F /* BaseRound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80E2CC7E4240082001F /* BaseRound.swift */; }; + C488C84F2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */; }; + C488C8512CC7E4240082001F /* BasePlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */; }; + C488C8522CC7E4240082001F /* BaseTeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8102CC7E4240082001F /* BaseTeamScore.swift */; }; + C488C8532CC7E4240082001F /* BaseTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8112CC7E4240082001F /* BaseTournament.swift */; }; + C488C8542CC7E4240082001F /* BaseDateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8072CC7E4240082001F /* BaseDateInterval.swift */; }; + C488C8552CC7E4240082001F /* BaseMonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80C2CC7E4240082001F /* BaseMonthData.swift */; }; + C488C8562CC7E4240082001F /* BaseTeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */; }; + C488C8572CC7E4240082001F /* BaseGroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8092CC7E4240082001F /* BaseGroupStage.swift */; }; + C488C8582CC7E4240082001F /* BaseCustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8062CC7E4240082001F /* BaseCustomUser.swift */; }; + C488C8592CC7E4240082001F /* BaseMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80A2CC7E4240082001F /* BaseMatch.swift */; }; + C488C85A2CC7E4240082001F /* BaseEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8082CC7E4240082001F /* BaseEvent.swift */; }; + C488C85B2CC7E4240082001F /* BaseRound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80E2CC7E4240082001F /* BaseRound.swift */; }; + C488C85C2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */; }; + C488C85E2CC7E4240082001F /* BasePlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */; }; + C488C85F2CC7E4240082001F /* BaseTeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8102CC7E4240082001F /* BaseTeamScore.swift */; }; + C488C8602CC7E4240082001F /* BaseTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8112CC7E4240082001F /* BaseTournament.swift */; }; + C488C8612CC7E4240082001F /* Event.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8162CC7E4240082001F /* Event.json */; }; + C488C8622CC7E4240082001F /* Court.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8132CC7E4240082001F /* Court.json */; }; + C488C8632CC7E4240082001F /* Tournament.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81F2CC7E4240082001F /* Tournament.json */; }; + C488C8642CC7E4240082001F /* CustomUser.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8142CC7E4240082001F /* CustomUser.json */; }; + C488C8652CC7E4240082001F /* Round.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81C2CC7E4240082001F /* Round.json */; }; + C488C8662CC7E4240082001F /* MatchScheduler.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8192CC7E4240082001F /* MatchScheduler.json */; }; + C488C8672CC7E4240082001F /* DateInterval.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8152CC7E4240082001F /* DateInterval.json */; }; + C488C8682CC7E4240082001F /* TeamRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81D2CC7E4240082001F /* TeamRegistration.json */; }; + C488C8692CC7E4240082001F /* GroupStage.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8172CC7E4240082001F /* GroupStage.json */; }; + C488C86A2CC7E4240082001F /* MonthData.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81A2CC7E4240082001F /* MonthData.json */; }; + C488C86B2CC7E4240082001F /* TeamScore.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81E2CC7E4240082001F /* TeamScore.json */; }; + C488C86C2CC7E4240082001F /* Match.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8182CC7E4240082001F /* Match.json */; }; + C488C86D2CC7E4240082001F /* PlayerRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81B2CC7E4240082001F /* PlayerRegistration.json */; }; + C488C8712CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; }; + C488C8722CC816410082001F /* BasePurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C86F2CC816410082001F /* BasePurchase.swift */; }; + C488C8732CC816410082001F /* BasePurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C86F2CC816410082001F /* BasePurchase.swift */; }; + C488C8742CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; }; + C488C8752CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; }; + C488C8762CC816410082001F /* BasePurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C86F2CC816410082001F /* BasePurchase.swift */; }; C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; }; C49EF0192BD694290077B5AA /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; @@ -879,6 +971,37 @@ C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = SyncedProducts.storekit; sourceTree = ""; }; C45BAE432BCA753E002EEC8A /* Purchase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Purchase.swift; sourceTree = ""; }; C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICallsListView.swift; sourceTree = ""; }; + C488C7E52CC7D1660082001F /* generator.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = generator.py; sourceTree = ""; }; + C488C7EC2CC7D2290082001F /* Club.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Club.json; sourceTree = ""; }; + C488C7FE2CC7DCB80082001F /* BaseClub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseClub.swift; sourceTree = ""; }; + C488C8022CC7E1E40082001F /* BaseCourt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCourt.swift; sourceTree = ""; }; + C488C8062CC7E4240082001F /* BaseCustomUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCustomUser.swift; sourceTree = ""; }; + C488C8072CC7E4240082001F /* BaseDateInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDateInterval.swift; sourceTree = ""; }; + C488C8082CC7E4240082001F /* BaseEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseEvent.swift; sourceTree = ""; }; + C488C8092CC7E4240082001F /* BaseGroupStage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseGroupStage.swift; sourceTree = ""; }; + C488C80A2CC7E4240082001F /* BaseMatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMatch.swift; sourceTree = ""; }; + C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMatchScheduler.swift; sourceTree = ""; }; + C488C80C2CC7E4240082001F /* BaseMonthData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMonthData.swift; sourceTree = ""; }; + C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasePlayerRegistration.swift; sourceTree = ""; }; + C488C80E2CC7E4240082001F /* BaseRound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseRound.swift; sourceTree = ""; }; + C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTeamRegistration.swift; sourceTree = ""; }; + C488C8102CC7E4240082001F /* BaseTeamScore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTeamScore.swift; sourceTree = ""; }; + C488C8112CC7E4240082001F /* BaseTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTournament.swift; sourceTree = ""; }; + C488C8132CC7E4240082001F /* Court.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Court.json; sourceTree = ""; }; + C488C8142CC7E4240082001F /* CustomUser.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CustomUser.json; sourceTree = ""; }; + C488C8152CC7E4240082001F /* DateInterval.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DateInterval.json; sourceTree = ""; }; + C488C8162CC7E4240082001F /* Event.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Event.json; sourceTree = ""; }; + C488C8172CC7E4240082001F /* GroupStage.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = GroupStage.json; sourceTree = ""; }; + C488C8182CC7E4240082001F /* Match.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Match.json; sourceTree = ""; }; + C488C8192CC7E4240082001F /* MatchScheduler.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = MatchScheduler.json; sourceTree = ""; }; + C488C81A2CC7E4240082001F /* MonthData.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = MonthData.json; sourceTree = ""; }; + C488C81B2CC7E4240082001F /* PlayerRegistration.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = PlayerRegistration.json; sourceTree = ""; }; + C488C81C2CC7E4240082001F /* Round.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Round.json; sourceTree = ""; }; + C488C81D2CC7E4240082001F /* TeamRegistration.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = TeamRegistration.json; sourceTree = ""; }; + C488C81E2CC7E4240082001F /* TeamScore.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = TeamScore.json; sourceTree = ""; }; + C488C81F2CC7E4240082001F /* Tournament.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Tournament.json; sourceTree = ""; }; + C488C86F2CC816410082001F /* BasePurchase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasePurchase.swift; sourceTree = ""; }; + C488C8702CC816410082001F /* Purchase.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Purchase.json; sourceTree = ""; }; C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewModifier.swift; sourceTree = ""; }; C49EF0182BD694290077B5AA /* PurchaseListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseListView.swift; sourceTree = ""; }; C49EF01A2BD6A1E80077B5AA /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = ""; }; @@ -1317,9 +1440,48 @@ name = Frameworks; sourceTree = ""; }; + C488C7FD2CC7D6CD0082001F /* Gen */ = { + isa = PBXGroup; + children = ( + C488C7E52CC7D1660082001F /* generator.py */, + C488C7EC2CC7D2290082001F /* Club.json */, + C488C8132CC7E4240082001F /* Court.json */, + C488C8142CC7E4240082001F /* CustomUser.json */, + C488C8152CC7E4240082001F /* DateInterval.json */, + C488C8162CC7E4240082001F /* Event.json */, + C488C8172CC7E4240082001F /* GroupStage.json */, + C488C8182CC7E4240082001F /* Match.json */, + C488C8192CC7E4240082001F /* MatchScheduler.json */, + C488C81A2CC7E4240082001F /* MonthData.json */, + C488C81B2CC7E4240082001F /* PlayerRegistration.json */, + C488C8702CC816410082001F /* Purchase.json */, + C488C81C2CC7E4240082001F /* Round.json */, + C488C81D2CC7E4240082001F /* TeamRegistration.json */, + C488C81E2CC7E4240082001F /* TeamScore.json */, + C488C81F2CC7E4240082001F /* Tournament.json */, + C488C7FE2CC7DCB80082001F /* BaseClub.swift */, + C488C8022CC7E1E40082001F /* BaseCourt.swift */, + C488C8062CC7E4240082001F /* BaseCustomUser.swift */, + C488C8072CC7E4240082001F /* BaseDateInterval.swift */, + C488C8082CC7E4240082001F /* BaseEvent.swift */, + C488C8092CC7E4240082001F /* BaseGroupStage.swift */, + C488C80A2CC7E4240082001F /* BaseMatch.swift */, + C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */, + C488C80C2CC7E4240082001F /* BaseMonthData.swift */, + C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */, + C488C86F2CC816410082001F /* BasePurchase.swift */, + C488C80E2CC7E4240082001F /* BaseRound.swift */, + C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */, + C488C8102CC7E4240082001F /* BaseTeamScore.swift */, + C488C8112CC7E4240082001F /* BaseTournament.swift */, + ); + path = Gen; + sourceTree = ""; + }; C4A47D5F2B6D3B2D00ADC637 /* Data */ = { isa = PBXGroup; children = ( + C488C7FD2CC7D6CD0082001F /* Gen */, C411C9CC2BF21DAF003017AD /* README.md */, C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */, C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */, @@ -2138,8 +2300,23 @@ FF1F4B842BFA02A4000B4573 /* groupstagescore-template.html in Resources */, FF1F4B852BFA02A4000B4573 /* player-template.html in Resources */, FF1F4B862BFA02A4000B4573 /* groupstagerow-template.html in Resources */, + C488C8712CC816410082001F /* Purchase.json in Resources */, FF1F4B872BFA02A4000B4573 /* hiddenplayer-template.html in Resources */, + C488C7EB2CC7D16F0082001F /* generator.py in Resources */, FF1F4B882BFA02A4000B4573 /* bracket-template.html in Resources */, + C488C8202CC7E4240082001F /* Event.json in Resources */, + C488C8212CC7E4240082001F /* Court.json in Resources */, + C488C8222CC7E4240082001F /* Tournament.json in Resources */, + C488C8232CC7E4240082001F /* CustomUser.json in Resources */, + C488C8242CC7E4240082001F /* Round.json in Resources */, + C488C8252CC7E4240082001F /* MatchScheduler.json in Resources */, + C488C8262CC7E4240082001F /* DateInterval.json in Resources */, + C488C8272CC7E4240082001F /* TeamRegistration.json in Resources */, + C488C8282CC7E4240082001F /* GroupStage.json in Resources */, + C488C8292CC7E4240082001F /* MonthData.json in Resources */, + C488C82A2CC7E4240082001F /* TeamScore.json in Resources */, + C488C82B2CC7E4240082001F /* Match.json in Resources */, + C488C82C2CC7E4240082001F /* PlayerRegistration.json in Resources */, FF1F4B892BFA02A4000B4573 /* groupstagecol-template.html in Resources */, FF1F4B8A2BFA02A4000B4573 /* groupstage-template.html in Resources */, FF1F4B8B2BFA02A4000B4573 /* groupstageentrant-template.html in Resources */, @@ -2177,8 +2354,23 @@ FF4CC03D2C996C0600151637 /* groupstagescore-template.html in Resources */, FF4CC03E2C996C0600151637 /* player-template.html in Resources */, FF4CC03F2C996C0600151637 /* groupstagerow-template.html in Resources */, + C488C8752CC816410082001F /* Purchase.json in Resources */, FF4CC0402C996C0600151637 /* hiddenplayer-template.html in Resources */, + C488C7E92CC7D16F0082001F /* generator.py in Resources */, FF4CC0412C996C0600151637 /* bracket-template.html in Resources */, + C488C83A2CC7E4240082001F /* Event.json in Resources */, + C488C83B2CC7E4240082001F /* Court.json in Resources */, + C488C83C2CC7E4240082001F /* Tournament.json in Resources */, + C488C83D2CC7E4240082001F /* CustomUser.json in Resources */, + C488C83E2CC7E4240082001F /* Round.json in Resources */, + C488C83F2CC7E4240082001F /* MatchScheduler.json in Resources */, + C488C8402CC7E4240082001F /* DateInterval.json in Resources */, + C488C8412CC7E4240082001F /* TeamRegistration.json in Resources */, + C488C8422CC7E4240082001F /* GroupStage.json in Resources */, + C488C8432CC7E4240082001F /* MonthData.json in Resources */, + C488C8442CC7E4240082001F /* TeamScore.json in Resources */, + C488C8452CC7E4240082001F /* Match.json in Resources */, + C488C8462CC7E4240082001F /* PlayerRegistration.json in Resources */, FF4CC0422C996C0600151637 /* groupstagecol-template.html in Resources */, FF4CC0432C996C0600151637 /* groupstage-template.html in Resources */, FF4CC0442C996C0600151637 /* groupstageentrant-template.html in Resources */, @@ -2202,8 +2394,23 @@ FF70FBBC2C90584900129CC2 /* groupstagescore-template.html in Resources */, FF70FBBD2C90584900129CC2 /* player-template.html in Resources */, FF70FBBE2C90584900129CC2 /* groupstagerow-template.html in Resources */, + C488C8742CC816410082001F /* Purchase.json in Resources */, FF70FBBF2C90584900129CC2 /* hiddenplayer-template.html in Resources */, + C488C7EA2CC7D16F0082001F /* generator.py in Resources */, FF70FBC02C90584900129CC2 /* bracket-template.html in Resources */, + C488C8612CC7E4240082001F /* Event.json in Resources */, + C488C8622CC7E4240082001F /* Court.json in Resources */, + C488C8632CC7E4240082001F /* Tournament.json in Resources */, + C488C8642CC7E4240082001F /* CustomUser.json in Resources */, + C488C8652CC7E4240082001F /* Round.json in Resources */, + C488C8662CC7E4240082001F /* MatchScheduler.json in Resources */, + C488C8672CC7E4240082001F /* DateInterval.json in Resources */, + C488C8682CC7E4240082001F /* TeamRegistration.json in Resources */, + C488C8692CC7E4240082001F /* GroupStage.json in Resources */, + C488C86A2CC7E4240082001F /* MonthData.json in Resources */, + C488C86B2CC7E4240082001F /* TeamScore.json in Resources */, + C488C86C2CC7E4240082001F /* Match.json in Resources */, + C488C86D2CC7E4240082001F /* PlayerRegistration.json in Resources */, FF70FBC12C90584900129CC2 /* groupstagecol-template.html in Resources */, FF70FBC22C90584900129CC2 /* groupstage-template.html in Resources */, FF70FBC32C90584900129CC2 /* groupstageentrant-template.html in Resources */, @@ -2258,6 +2465,7 @@ FF7091682B90F79F00AB08DA /* TournamentCellView.swift in Sources */, FF6EC9042B9479F500EA7F5A /* Sequence+Extensions.swift in Sources */, FF9267FA2BCE78EC0080F940 /* CashierDetailView.swift in Sources */, + C488C8722CC816410082001F /* BasePurchase.swift in Sources */, FFE103082C353B7600684FC9 /* EventClubSettingsView.swift in Sources */, C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */, FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */, @@ -2278,6 +2486,18 @@ FF8F263B2BAD528600650388 /* EventCreationView.swift in Sources */, FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */, C4C01D982C481C0C0059087C /* CapsuleViewModifier.swift in Sources */, + C488C82D2CC7E4240082001F /* BaseDateInterval.swift in Sources */, + C488C82E2CC7E4240082001F /* BaseMonthData.swift in Sources */, + C488C82F2CC7E4240082001F /* BaseTeamRegistration.swift in Sources */, + C488C8302CC7E4240082001F /* BaseGroupStage.swift in Sources */, + C488C8312CC7E4240082001F /* BaseCustomUser.swift in Sources */, + C488C8322CC7E4240082001F /* BaseMatch.swift in Sources */, + C488C8332CC7E4240082001F /* BaseEvent.swift in Sources */, + C488C8342CC7E4240082001F /* BaseRound.swift in Sources */, + C488C8352CC7E4240082001F /* BaseMatchScheduler.swift in Sources */, + C488C8372CC7E4240082001F /* BasePlayerRegistration.swift in Sources */, + C488C8382CC7E4240082001F /* BaseTeamScore.swift in Sources */, + C488C8392CC7E4240082001F /* BaseTournament.swift in Sources */, FF6087EC2BE26A2F004E1E47 /* BroadcastView.swift in Sources */, FFF964552BC266CF00EEF017 /* SchedulerView.swift in Sources */, FFA1B1292BB71773006CE248 /* PadelClubButtonView.swift in Sources */, @@ -2320,6 +2540,7 @@ FF3795662B9399AA004EA093 /* Persistence.swift in Sources */, FFCF76072C3BE9BC006C8C3D /* CloseDatePicker.swift in Sources */, FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */, + C488C8052CC7E1E40082001F /* BaseCourt.swift in Sources */, FFF964502BC25E3700EEF017 /* PlanningView.swift in Sources */, FF967CEC2BAECB9900A9A3BD /* Match.swift in Sources */, FF8F264B2BAE0B4100650388 /* TournamentLevelPickerView.swift in Sources */, @@ -2432,6 +2653,7 @@ FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */, FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */, FFF1D2CB2C4A22B200C8D33D /* ExportFormat.swift in Sources */, + C488C8012CC7DCB80082001F /* BaseClub.swift in Sources */, FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */, C4A47D922B7BBBEC00ADC637 /* StoreItem.swift in Sources */, FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */, @@ -2535,6 +2757,7 @@ FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */, FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */, FF4CBF6D2C996C0600151637 /* EditablePlayerView.swift in Sources */, + C488C7F12CC7D22D0082001F /* Club.json in Sources */, FF4CBF6E2C996C0600151637 /* PlayerDetailView.swift in Sources */, FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */, FF4CBF702C996C0600151637 /* PresentationContext.swift in Sources */, @@ -2548,6 +2771,18 @@ FF4CBF782C996C0600151637 /* EventCreationView.swift in Sources */, FF4CBF792C996C0600151637 /* LocationManager.swift in Sources */, FF4CBF7A2C996C0600151637 /* CapsuleViewModifier.swift in Sources */, + C488C8472CC7E4240082001F /* BaseDateInterval.swift in Sources */, + C488C8482CC7E4240082001F /* BaseMonthData.swift in Sources */, + C488C8492CC7E4240082001F /* BaseTeamRegistration.swift in Sources */, + C488C84A2CC7E4240082001F /* BaseGroupStage.swift in Sources */, + C488C84B2CC7E4240082001F /* BaseCustomUser.swift in Sources */, + C488C84C2CC7E4240082001F /* BaseMatch.swift in Sources */, + C488C84D2CC7E4240082001F /* BaseEvent.swift in Sources */, + C488C84E2CC7E4240082001F /* BaseRound.swift in Sources */, + C488C84F2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */, + C488C8512CC7E4240082001F /* BasePlayerRegistration.swift in Sources */, + C488C8522CC7E4240082001F /* BaseTeamScore.swift in Sources */, + C488C8532CC7E4240082001F /* BaseTournament.swift in Sources */, FF4CBF7B2C996C0600151637 /* BroadcastView.swift in Sources */, FF4CBF7C2C996C0600151637 /* SchedulerView.swift in Sources */, FF4CBF7D2C996C0600151637 /* PadelClubButtonView.swift in Sources */, @@ -2564,6 +2799,7 @@ FF4CBF882C996C0600151637 /* TeamRowView.swift in Sources */, FF4CBF892C996C0600151637 /* ContactManager.swift in Sources */, FF4CBF8A2C996C0600151637 /* AppDelegate.swift in Sources */, + C488C8762CC816410082001F /* BasePurchase.swift in Sources */, FF4CBF8B2C996C0600151637 /* SubscriptionInfoView.swift in Sources */, FF4CBF8C2C996C0600151637 /* EditScoreView.swift in Sources */, FF4CBF8D2C996C0600151637 /* TournamentOrganizerView.swift in Sources */, @@ -2590,6 +2826,7 @@ FF4CBFA22C996C0600151637 /* Persistence.swift in Sources */, FF4CBFA32C996C0600151637 /* CloseDatePicker.swift in Sources */, FF4CBFA42C996C0600151637 /* BarButtonView.swift in Sources */, + C488C8042CC7E1E40082001F /* BaseCourt.swift in Sources */, FF4CBFA52C996C0600151637 /* PlanningView.swift in Sources */, FF4CBFA62C996C0600151637 /* Match.swift in Sources */, FF4CBFA72C996C0600151637 /* TournamentLevelPickerView.swift in Sources */, @@ -2702,6 +2939,7 @@ FF4CC0112C996C0600151637 /* PlayerView.swift in Sources */, FF4CC0122C996C0600151637 /* MatchDetailView.swift in Sources */, FF4CC0132C996C0600151637 /* ExportFormat.swift in Sources */, + C488C7FF2CC7DCB80082001F /* BaseClub.swift in Sources */, FF4CC0142C996C0600151637 /* PlayerBlockView.swift in Sources */, FF4CC0152C996C0600151637 /* StoreItem.swift in Sources */, FF4CC0162C996C0600151637 /* Selectable.swift in Sources */, @@ -2783,6 +3021,7 @@ FF70FAEA2C90584900129CC2 /* Calendar+Extensions.swift in Sources */, FF70FAEB2C90584900129CC2 /* TeamScore.swift in Sources */, FF70FAEC2C90584900129CC2 /* EditablePlayerView.swift in Sources */, + C488C7F22CC7D22D0082001F /* Club.json in Sources */, FF70FAED2C90584900129CC2 /* PlayerDetailView.swift in Sources */, FF70FAEE2C90584900129CC2 /* ListRowViewModifier.swift in Sources */, FF70FAEF2C90584900129CC2 /* PresentationContext.swift in Sources */, @@ -2796,6 +3035,18 @@ FF70FAF72C90584900129CC2 /* EventCreationView.swift in Sources */, FF70FAF82C90584900129CC2 /* LocationManager.swift in Sources */, FF70FAF92C90584900129CC2 /* CapsuleViewModifier.swift in Sources */, + C488C8542CC7E4240082001F /* BaseDateInterval.swift in Sources */, + C488C8552CC7E4240082001F /* BaseMonthData.swift in Sources */, + C488C8562CC7E4240082001F /* BaseTeamRegistration.swift in Sources */, + C488C8572CC7E4240082001F /* BaseGroupStage.swift in Sources */, + C488C8582CC7E4240082001F /* BaseCustomUser.swift in Sources */, + C488C8592CC7E4240082001F /* BaseMatch.swift in Sources */, + C488C85A2CC7E4240082001F /* BaseEvent.swift in Sources */, + C488C85B2CC7E4240082001F /* BaseRound.swift in Sources */, + C488C85C2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */, + C488C85E2CC7E4240082001F /* BasePlayerRegistration.swift in Sources */, + C488C85F2CC7E4240082001F /* BaseTeamScore.swift in Sources */, + C488C8602CC7E4240082001F /* BaseTournament.swift in Sources */, FF70FAFA2C90584900129CC2 /* BroadcastView.swift in Sources */, FF70FAFB2C90584900129CC2 /* SchedulerView.swift in Sources */, FF70FAFC2C90584900129CC2 /* PadelClubButtonView.swift in Sources */, @@ -2812,6 +3063,7 @@ FF70FB072C90584900129CC2 /* TeamRowView.swift in Sources */, FF70FB082C90584900129CC2 /* ContactManager.swift in Sources */, FF70FB092C90584900129CC2 /* AppDelegate.swift in Sources */, + C488C8732CC816410082001F /* BasePurchase.swift in Sources */, FF70FB0A2C90584900129CC2 /* SubscriptionInfoView.swift in Sources */, FF70FB0B2C90584900129CC2 /* EditScoreView.swift in Sources */, FF70FB0C2C90584900129CC2 /* TournamentOrganizerView.swift in Sources */, @@ -2838,6 +3090,7 @@ FF70FB212C90584900129CC2 /* Persistence.swift in Sources */, FF70FB222C90584900129CC2 /* CloseDatePicker.swift in Sources */, FF70FB232C90584900129CC2 /* BarButtonView.swift in Sources */, + C488C8032CC7E1E40082001F /* BaseCourt.swift in Sources */, FF70FB242C90584900129CC2 /* PlanningView.swift in Sources */, FF70FB252C90584900129CC2 /* Match.swift in Sources */, FF70FB262C90584900129CC2 /* TournamentLevelPickerView.swift in Sources */, @@ -2950,6 +3203,7 @@ FF70FB902C90584900129CC2 /* PlayerView.swift in Sources */, FF70FB912C90584900129CC2 /* MatchDetailView.swift in Sources */, FF70FB922C90584900129CC2 /* ExportFormat.swift in Sources */, + C488C8002CC7DCB80082001F /* BaseClub.swift in Sources */, FF70FB932C90584900129CC2 /* PlayerBlockView.swift in Sources */, FF70FB942C90584900129CC2 /* StoreItem.swift in Sources */, FF70FB952C90584900129CC2 /* Selectable.swift in Sources */, diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index b7dc5ad..41b6c2e 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -10,34 +10,34 @@ import SwiftUI import LeStorage @Observable -final class Club: ModelObject, SyncedStorable { +final class Club: BaseClub { - static func resourceName() -> String { return "clubs" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - var id: String = Store.randomId() - var lastUpdate: Date - var creator: String? - var name: String - var acronym: String - var phone: String? - var code: String? - //var federalClubData: Data? - var address: String? - var city: String? - var zipCode: String? - var latitude: Double? - var longitude: Double? - var courtCount: Int = 2 - var broadcastCode: String? -// var alphabeticalName: Bool = false +// static func resourceName() -> String { return "clubs" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] } +// static func filterByStoreIdentifier() -> Bool { return false } +// static var relationshipNames: [String] = [] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var creator: String? +// var name: String +// var acronym: String +// var phone: String? +// var code: String? +// //var federalClubData: Data? +// var address: String? +// var city: String? +// var zipCode: String? +// var latitude: Double? +// var longitude: Double? +// var courtCount: Int = 2 +// var broadcastCode: String? +//// var alphabeticalName: Bool = false var storeId: String? { return nil } internal init(creator: String? = nil, name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil, courtCount: Int = 2, broadcastCode: String? = nil) { - self.lastUpdate = Date() + super.init() self.name = name self.creator = creator @@ -52,12 +52,11 @@ final class Club: ModelObject, SyncedStorable { self.courtCount = courtCount self.broadcastCode = broadcastCode - super.init() } -// required init(from decoder: any Decoder) throws { -// fatalError("init(from:) has not been implemented") -// } + required init(from decoder: any Decoder) throws { + try super.init(from: decoder) + } override func copyFromServerInstance(_ instance: any Storable) -> Bool { guard let copy = instance as? Club else { return false } @@ -91,43 +90,43 @@ final class Club: ModelObject, SyncedStorable { DataStore.shared.courts.deleteDependencies(customizedCourts) } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _lastUpdate = "lastUpdate" - case _creator = "creator" - case _name = "name" - case _acronym = "acronym" - case _phone = "phone" - case _code = "code" - case _address = "address" - case _city = "city" - case _zipCode = "zipCode" - case _latitude = "latitude" - case _longitude = "longitude" - case _courtCount = "courtCount" - case _broadcastCode = "broadcastCode" -// case _alphabeticalName = "alphabeticalName" - } - - func encode(to encoder: Encoder) throws { - - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(creator, forKey: ._creator) - try container.encode(name, forKey: ._name) - try container.encode(acronym, forKey: ._acronym) - try container.encode(phone, forKey: ._phone) - try container.encode(code, forKey: ._code) - try container.encode(address, forKey: ._address) - try container.encode(city, forKey: ._city) - try container.encode(zipCode, forKey: ._zipCode) - try container.encode(latitude, forKey: ._latitude) - try container.encode(longitude, forKey: ._longitude) - try container.encode(courtCount, forKey: ._courtCount) - try container.encode(broadcastCode, forKey: ._broadcastCode) - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _lastUpdate = "lastUpdate" +// case _creator = "creator" +// case _name = "name" +// case _acronym = "acronym" +// case _phone = "phone" +// case _code = "code" +// case _address = "address" +// case _city = "city" +// case _zipCode = "zipCode" +// case _latitude = "latitude" +// case _longitude = "longitude" +// case _courtCount = "courtCount" +// case _broadcastCode = "broadcastCode" +//// case _alphabeticalName = "alphabeticalName" +// } +// +// func encode(to encoder: Encoder) throws { +// +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(creator, forKey: ._creator) +// try container.encode(name, forKey: ._name) +// try container.encode(acronym, forKey: ._acronym) +// try container.encode(phone, forKey: ._phone) +// try container.encode(code, forKey: ._code) +// try container.encode(address, forKey: ._address) +// try container.encode(city, forKey: ._city) +// try container.encode(zipCode, forKey: ._zipCode) +// try container.encode(latitude, forKey: ._latitude) +// try container.encode(longitude, forKey: ._longitude) +// try container.encode(courtCount, forKey: ._courtCount) +// try container.encode(broadcastCode, forKey: ._broadcastCode) +// } } diff --git a/PadelClub/Data/Court.swift b/PadelClub/Data/Court.swift index 3853c3a..d438f2e 100644 --- a/PadelClub/Data/Court.swift +++ b/PadelClub/Data/Court.swift @@ -10,28 +10,18 @@ import SwiftUI import LeStorage @Observable -final class Court : ModelObject, SyncedStorable { +final class Court: BaseCourt { - static func resourceName() -> String { return "courts" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - static func == (lhs: Court, rhs: Court) -> Bool { lhs.id == rhs.id } - var id: String = Store.randomId() - var lastUpdate: Date - var index: Int - var club: String - var name: String? - var exitAllowed: Bool = false - var indoor: Bool = false - var storeId: String? { return nil } init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) { + + super.init() + self.index = index self.lastUpdate = Date() self.club = club @@ -40,6 +30,10 @@ final class Court : ModelObject, SyncedStorable { self.indoor = indoor } + required init(from decoder: Decoder) throws { + fatalError("init(from:) has not been implemented") + } + func courtTitle() -> String { self.name ?? courtIndexTitle() } @@ -59,26 +53,4 @@ final class Court : ModelObject, SyncedStorable { override func deleteDependencies() { } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _lastUpdate = "lastUpdate" - case _index = "index" - case _club = "club" - case _name = "name" - case _exitAllowed = "exitAllowed" - case _indoor = "indoor" - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(index, forKey: ._index) - try container.encode(club, forKey: ._club) - try container.encode(name, forKey: ._name) - try container.encode(exitAllowed, forKey: ._exitAllowed) - try container.encode(indoor, forKey: ._indoor) - } - } diff --git a/PadelClub/Data/CustomUser.swift b/PadelClub/Data/CustomUser.swift index 3d0b71d..053f02f 100644 --- a/PadelClub/Data/CustomUser.swift +++ b/PadelClub/Data/CustomUser.swift @@ -15,50 +15,57 @@ enum UserRight: Int, Codable { } @Observable -class CustomUser: ModelObject, UserBase, SyncedStorable { +class CustomUser: BaseCustomUser, UserBase { - static func resourceName() -> String { "users" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - public var id: String = Store.randomId() - var lastUpdate: Date - public var username: String - public var email: String - var clubs: [String] = [] - var umpireCode: String? - var licenceId: String? - var firstName: String - var lastName: String - var phone: String? - var country: String? - - var summonsMessageBody : String? = nil - var summonsMessageSignature: String? = nil - var summonsAvailablePaymentMethods: String? = nil - var summonsDisplayFormat: Bool = false - var summonsDisplayEntryFee: Bool = false - var summonsUseFullCustomMessage: Bool = false - var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil - var bracketMatchFormatPreference: MatchFormat? - var groupStageMatchFormatPreference: MatchFormat? - var loserBracketMatchFormatPreference: MatchFormat? - var loserBracketMode: LoserBracketMode = .automatic - - var deviceId: String? +// static func resourceName() -> String { "users" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } +// static func filterByStoreIdentifier() -> Bool { return false } +// static var relationshipNames: [String] = [] +// +// public var id: String = Store.randomId() +// var lastUpdate: Date +// public var username: String +// public var email: String +// var clubs: [String] = [] +// var umpireCode: String? +// var licenceId: String? +// var firstName: String +// var lastName: String +// var phone: String? +// var country: String? +// +// var summonsMessageBody : String? = nil +// var summonsMessageSignature: String? = nil +// var summonsAvailablePaymentMethods: String? = nil +// var summonsDisplayFormat: Bool = false +// var summonsDisplayEntryFee: Bool = false +// var summonsUseFullCustomMessage: Bool = false +// var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil +// var bracketMatchFormatPreference: MatchFormat? +// var groupStageMatchFormatPreference: MatchFormat? +// var loserBracketMatchFormatPreference: MatchFormat? +// var loserBracketMode: LoserBracketMode = .automatic +// +// var deviceId: String? var storeId: String? { return nil } init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) { - self.lastUpdate = Date() - self.username = username - self.firstName = firstName - self.lastName = lastName - self.email = email - self.phone = phone - self.country = country - self.loserBracketMode = loserBracketMode + super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country, loserBracketMode: loserBracketMode) + + +// self.lastUpdate = Date() +// self.username = username +// self.firstName = firstName +// self.lastName = lastName +// self.email = email +// self.phone = phone +// self.country = country +// self.loserBracketMode = loserBracketMode + } + + required init(from decoder: Decoder) throws { + try super.init(from: decoder) } public func uuid() throws -> UUID { @@ -123,96 +130,96 @@ class CustomUser: ModelObject, UserBase, SyncedStorable { } } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _lastUpdate = "lastUpdate" - case _username = "username" - case _email = "email" - case _clubs = "clubs" - case _umpireCode = "umpireCode" - case _licenceId = "licenceId" - case _firstName = "firstName" - case _lastName = "lastName" - case _phone = "phone" - case _country = "country" - case _summonsMessageBody = "summonsMessageBody" - case _summonsMessageSignature = "summonsMessageSignature" - case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods" - case _summonsDisplayFormat = "summonsDisplayFormat" - case _summonsDisplayEntryFee = "summonsDisplayEntryFee" - case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage" - case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration" - case _bracketMatchFormatPreference = "bracketMatchFormatPreference" - case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference" - case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference" - case _deviceId = "deviceId" - case _loserBracketMode = "loserBracketMode" - } - - public required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - // Required properties - id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() - username = try container.decode(String.self, forKey: ._username) - email = try container.decode(String.self, forKey: ._email) - firstName = try container.decode(String.self, forKey: ._firstName) - lastName = try container.decode(String.self, forKey: ._lastName) - - // Optional properties - clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? [] - umpireCode = try container.decodeIfPresent(String.self, forKey: ._umpireCode) - licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId) - phone = try container.decodeIfPresent(String.self, forKey: ._phone) - country = try container.decodeIfPresent(String.self, forKey: ._country) - - // Summons-related properties - summonsMessageBody = try container.decodeIfPresent(String.self, forKey: ._summonsMessageBody) - summonsMessageSignature = try container.decodeIfPresent(String.self, forKey: ._summonsMessageSignature) - summonsAvailablePaymentMethods = try container.decodeIfPresent(String.self, forKey: ._summonsAvailablePaymentMethods) - summonsDisplayFormat = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayFormat) ?? false - summonsDisplayEntryFee = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayEntryFee) ?? false - summonsUseFullCustomMessage = try container.decodeIfPresent(Bool.self, forKey: ._summonsUseFullCustomMessage) ?? false - - // Match-related properties - matchFormatsDefaultDuration = try container.decodeIfPresent([MatchFormat: Int].self, forKey: ._matchFormatsDefaultDuration) - bracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._bracketMatchFormatPreference) - groupStageMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageMatchFormatPreference) - loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference) - loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(username, forKey: ._username) - try container.encode(email, forKey: ._email) - try container.encode(clubs, forKey: ._clubs) - - try container.encode(umpireCode, forKey: ._umpireCode) - try container.encode(licenceId, forKey: ._licenceId) - try container.encode(firstName, forKey: ._firstName) - try container.encode(lastName, forKey: ._lastName) - try container.encode(phone, forKey: ._phone) - try container.encode(country, forKey: ._country) - try container.encode(summonsMessageBody, forKey: ._summonsMessageBody) - try container.encode(summonsMessageSignature, forKey: ._summonsMessageSignature) - try container.encode(summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods) - try container.encode(summonsDisplayFormat, forKey: ._summonsDisplayFormat) - try container.encode(summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee) - try container.encode(summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage) - - try container.encode(matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration) - try container.encode(bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference) - try container.encode(groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference) - try container.encode(loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference) - try container.encode(deviceId, forKey: ._deviceId) - - try container.encode(loserBracketMode, forKey: ._loserBracketMode) - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _lastUpdate = "lastUpdate" +// case _username = "username" +// case _email = "email" +// case _clubs = "clubs" +// case _umpireCode = "umpireCode" +// case _licenceId = "licenceId" +// case _firstName = "firstName" +// case _lastName = "lastName" +// case _phone = "phone" +// case _country = "country" +// case _summonsMessageBody = "summonsMessageBody" +// case _summonsMessageSignature = "summonsMessageSignature" +// case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods" +// case _summonsDisplayFormat = "summonsDisplayFormat" +// case _summonsDisplayEntryFee = "summonsDisplayEntryFee" +// case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage" +// case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration" +// case _bracketMatchFormatPreference = "bracketMatchFormatPreference" +// case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference" +// case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference" +// case _deviceId = "deviceId" +// case _loserBracketMode = "loserBracketMode" +// } +// +// public required init(from decoder: Decoder) throws { +// let container = try decoder.container(keyedBy: CodingKeys.self) +// +// // Required properties +// id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() +// lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() +// username = try container.decode(String.self, forKey: ._username) +// email = try container.decode(String.self, forKey: ._email) +// firstName = try container.decode(String.self, forKey: ._firstName) +// lastName = try container.decode(String.self, forKey: ._lastName) +// +// // Optional properties +// clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? [] +// umpireCode = try container.decodeIfPresent(String.self, forKey: ._umpireCode) +// licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId) +// phone = try container.decodeIfPresent(String.self, forKey: ._phone) +// country = try container.decodeIfPresent(String.self, forKey: ._country) +// +// // Summons-related properties +// summonsMessageBody = try container.decodeIfPresent(String.self, forKey: ._summonsMessageBody) +// summonsMessageSignature = try container.decodeIfPresent(String.self, forKey: ._summonsMessageSignature) +// summonsAvailablePaymentMethods = try container.decodeIfPresent(String.self, forKey: ._summonsAvailablePaymentMethods) +// summonsDisplayFormat = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayFormat) ?? false +// summonsDisplayEntryFee = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayEntryFee) ?? false +// summonsUseFullCustomMessage = try container.decodeIfPresent(Bool.self, forKey: ._summonsUseFullCustomMessage) ?? false +// +// // Match-related properties +// matchFormatsDefaultDuration = try container.decodeIfPresent([MatchFormat: Int].self, forKey: ._matchFormatsDefaultDuration) +// bracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._bracketMatchFormatPreference) +// groupStageMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageMatchFormatPreference) +// loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference) +// loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic +// } +// +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(username, forKey: ._username) +// try container.encode(email, forKey: ._email) +// try container.encode(clubs, forKey: ._clubs) +// +// try container.encode(umpireCode, forKey: ._umpireCode) +// try container.encode(licenceId, forKey: ._licenceId) +// try container.encode(firstName, forKey: ._firstName) +// try container.encode(lastName, forKey: ._lastName) +// try container.encode(phone, forKey: ._phone) +// try container.encode(country, forKey: ._country) +// try container.encode(summonsMessageBody, forKey: ._summonsMessageBody) +// try container.encode(summonsMessageSignature, forKey: ._summonsMessageSignature) +// try container.encode(summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods) +// try container.encode(summonsDisplayFormat, forKey: ._summonsDisplayFormat) +// try container.encode(summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee) +// try container.encode(summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage) +// +// try container.encode(matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration) +// try container.encode(bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference) +// try container.encode(groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference) +// try container.encode(loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference) +// try container.encode(deviceId, forKey: ._deviceId) +// +// try container.encode(loserBracketMode, forKey: ._loserBracketMode) +// } static func placeHolder() -> CustomUser { return CustomUser(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 8991440..538a35c 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -215,14 +215,14 @@ class DataStore: ObservableObject { Guard.main.disconnect() StoreCenter.main.disconnect() - self.user = self._temporaryLocalUser.item ?? CustomUser.placeHolder() - self.user.clubs.removeAll() - // done after because otherwise folders remain for tournament in tournamendIds { StoreCenter.main.destroyStore(identifier: tournament.id) } - + + self.user = self._temporaryLocalUser.item ?? CustomUser.placeHolder() + self.user.clubs.removeAll() + } func copyToLocalServer(tournament: Tournament) { diff --git a/PadelClub/Data/DateInterval.swift b/PadelClub/Data/DateInterval.swift index c6a7a9f..864d9b3 100644 --- a/PadelClub/Data/DateInterval.swift +++ b/PadelClub/Data/DateInterval.swift @@ -10,26 +10,31 @@ import SwiftUI import LeStorage @Observable -final class DateInterval: ModelObject, SyncedStorable { +final class DateInterval: BaseDateInterval { - static func resourceName() -> String { return "date-intervals" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - var id: String = Store.randomId() - var lastUpdate: Date - var event: String - var courtIndex: Int - var startDate: Date - var endDate: Date +// static func resourceName() -> String { return "date-intervals" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return false } +// static var relationshipNames: [String] = [] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var event: String +// var courtIndex: Int +// var startDate: Date +// var endDate: Date internal init(event: String, courtIndex: Int, startDate: Date, endDate: Date) { - self.lastUpdate = Date() - self.event = event - self.courtIndex = courtIndex - self.startDate = startDate - self.endDate = endDate + super.init(event: event, courtIndex: courtIndex, startDate: startDate, endDate: endDate) +// self.lastUpdate = Date() +// self.event = event +// self.courtIndex = courtIndex +// self.startDate = startDate +// self.endDate = endDate + } + + required init(from decoder: any Decoder) throws { + try super.init(from: decoder) } var range: Range { @@ -51,14 +56,14 @@ final class DateInterval: ModelObject, SyncedStorable { override func deleteDependencies() { } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _lastUpdate = "lastUpdate" - case _event = "event" - case _courtIndex = "courtIndex" - case _startDate = "startDate" - case _endDate = "endDate" - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _lastUpdate = "lastUpdate" +// case _event = "event" +// case _courtIndex = "courtIndex" +// case _startDate = "startDate" +// case _endDate = "endDate" +// } func insertOnServer() throws { DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self) diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index 4b6a59f..85df1e4 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -10,29 +10,17 @@ import LeStorage import SwiftUI @Observable -final class Event: ModelObject, SyncedStorable { - - static func resourceName() -> String { return "events" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - var id: String = Store.randomId() - var lastUpdate: Date - var creator: String? - var club: String? - var creationDate: Date = Date() - var name: String? - var tenupId: String? +final class Event: BaseEvent { var storeId: String? { return nil } internal init(creator: String? = nil, club: String? = nil, name: String? = nil, tenupId: String? = nil) { - self.lastUpdate = Date() - self.creator = creator - self.club = club - self.name = name - self.tenupId = tenupId + super.init(creator: creator, club: club, name: name, tenupId: tenupId) + + } + + required init(from decoder: Decoder) throws { + try super.init(from: decoder) } override func deleteDependencies() { @@ -108,29 +96,3 @@ final class Event: ModelObject, SyncedStorable { } } - - -extension Event { - enum CodingKeys: String, CodingKey { - case _id = "id" - case _lastUpdate = "lastUpdate" - case _creator = "creator" - case _club = "club" - case _creationDate = "creationDate" - case _name = "name" - case _tenupId = "tenupId" - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(creator, forKey: ._creator) - try container.encode(club, forKey: ._club) - try container.encode(creationDate, forKey: ._creationDate) - try container.encode(name, forKey: ._name) - try container.encode(tenupId, forKey: ._tenupId) - } - -} diff --git a/PadelClub/Data/Gen/BaseClub.swift b/PadelClub/Data/Gen/BaseClub.swift new file mode 100644 index 0000000..8439123 --- /dev/null +++ b/PadelClub/Data/Gen/BaseClub.swift @@ -0,0 +1,139 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseClub: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "clubs" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var creator: String? = nil + var name: String = "" + var acronym: String = "" + var phone: String? = nil + var code: String? = nil + var address: String? = nil + var city: String? = nil + var zipCode: String? = nil + var latitude: Double? = nil + var longitude: Double? = nil + var courtCount: Int = 2 + var broadcastCode: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + creator: String? = nil, + name: String = "", + acronym: String = "", + phone: String? = nil, + code: String? = nil, + address: String? = nil, + city: String? = nil, + zipCode: String? = nil, + latitude: Double? = nil, + longitude: Double? = nil, + courtCount: Int = 2, + broadcastCode: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.creator = creator + self.name = name + self.acronym = acronym + self.phone = phone + self.code = code + self.address = address + self.city = city + self.zipCode = zipCode + self.latitude = latitude + self.longitude = longitude + self.courtCount = courtCount + self.broadcastCode = broadcastCode + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _creator = "creator" + case _name = "name" + case _acronym = "acronym" + case _phone = "phone" + case _code = "code" + case _address = "address" + case _city = "city" + case _zipCode = "zipCode" + case _latitude = "latitude" + case _longitude = "longitude" + case _courtCount = "courtCount" + case _broadcastCode = "broadcastCode" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil + self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? "" + self.acronym = try container.decodeIfPresent(String.self, forKey: ._acronym) ?? "" + self.phone = try container.decodeIfPresent(String.self, forKey: ._phone) ?? nil + self.code = try container.decodeIfPresent(String.self, forKey: ._code) ?? nil + self.address = try container.decodeIfPresent(String.self, forKey: ._address) ?? nil + self.city = try container.decodeIfPresent(String.self, forKey: ._city) ?? nil + self.zipCode = try container.decodeIfPresent(String.self, forKey: ._zipCode) ?? nil + self.latitude = try container.decodeIfPresent(Double.self, forKey: ._latitude) ?? nil + self.longitude = try container.decodeIfPresent(Double.self, forKey: ._longitude) ?? nil + self.courtCount = try container.decodeIfPresent(Int.self, forKey: ._courtCount) ?? 2 + self.broadcastCode = try container.decodeIfPresent(String.self, forKey: ._broadcastCode) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.creator, forKey: ._creator) + try container.encode(self.name, forKey: ._name) + try container.encode(self.acronym, forKey: ._acronym) + try container.encode(self.phone, forKey: ._phone) + try container.encode(self.code, forKey: ._code) + try container.encode(self.address, forKey: ._address) + try container.encode(self.city, forKey: ._city) + try container.encode(self.zipCode, forKey: ._zipCode) + try container.encode(self.latitude, forKey: ._latitude) + try container.encode(self.longitude, forKey: ._longitude) + try container.encode(self.courtCount, forKey: ._courtCount) + try container.encode(self.broadcastCode, forKey: ._broadcastCode) + } + + func creatorValue() -> CustomUser? { + guard let creator = self.creator else { return nil } + return Store.main.findById(creator) + } + + func copy(from other: any Storable) { + guard let club = other as? BaseClub else { return } + self.id = club.id + self.lastUpdate = club.lastUpdate + self.creator = club.creator + self.name = club.name + self.acronym = club.acronym + self.phone = club.phone + self.code = club.code + self.address = club.address + self.city = club.city + self.zipCode = club.zipCode + self.latitude = club.latitude + self.longitude = club.longitude + self.courtCount = club.courtCount + self.broadcastCode = club.broadcastCode + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseCourt.swift b/PadelClub/Data/Gen/BaseCourt.swift new file mode 100644 index 0000000..f3b4f5c --- /dev/null +++ b/PadelClub/Data/Gen/BaseCourt.swift @@ -0,0 +1,89 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseCourt: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "courts" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var index: Int = 0 + var club: String = "" + var name: String? = nil + var exitAllowed: Bool = false + var indoor: Bool = false + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + index: Int = 0, + club: String = "", + name: String? = nil, + exitAllowed: Bool = false, + indoor: Bool = false + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.index = index + self.club = club + self.name = name + self.exitAllowed = exitAllowed + self.indoor = indoor + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _index = "index" + case _club = "club" + case _name = "name" + case _exitAllowed = "exitAllowed" + case _indoor = "indoor" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 + self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? "" + self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil + self.exitAllowed = try container.decodeIfPresent(Bool.self, forKey: ._exitAllowed) ?? false + self.indoor = try container.decodeIfPresent(Bool.self, forKey: ._indoor) ?? false + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.index, forKey: ._index) + try container.encode(self.club, forKey: ._club) + try container.encode(self.name, forKey: ._name) + try container.encode(self.exitAllowed, forKey: ._exitAllowed) + try container.encode(self.indoor, forKey: ._indoor) + } + + func clubValue() -> Club? { + return Store.main.findById(club) + } + + func copy(from other: any Storable) { + guard let court = other as? BaseCourt else { return } + self.id = court.id + self.lastUpdate = court.lastUpdate + self.index = court.index + self.club = court.club + self.name = court.name + self.exitAllowed = court.exitAllowed + self.indoor = court.indoor + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseCustomUser.swift b/PadelClub/Data/Gen/BaseCustomUser.swift new file mode 100644 index 0000000..7c5463a --- /dev/null +++ b/PadelClub/Data/Gen/BaseCustomUser.swift @@ -0,0 +1,197 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseCustomUser: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "users" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var username: String = "" + var email: String = "" + var clubs: [String] = [] + var umpireCode: String? = nil + var licenceId: String? = nil + var firstName: String = "" + var lastName: String = "" + var phone: String? = nil + var country: String? = nil + var summonsMessageBody: String? = nil + var summonsMessageSignature: String? = nil + var summonsAvailablePaymentMethods: String? = nil + var summonsDisplayFormat: Bool = false + var summonsDisplayEntryFee: Bool = false + var summonsUseFullCustomMessage: Bool = false + var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil + var bracketMatchFormatPreference: MatchFormat? = nil + var groupStageMatchFormatPreference: MatchFormat? = nil + var loserBracketMatchFormatPreference: MatchFormat? = nil + var loserBracketMode: LoserBracketMode = .automatic + var deviceId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + username: String = "", + email: String = "", + clubs: [String] = [], + umpireCode: String? = nil, + licenceId: String? = nil, + firstName: String = "", + lastName: String = "", + phone: String? = nil, + country: String? = nil, + summonsMessageBody: String? = nil, + summonsMessageSignature: String? = nil, + summonsAvailablePaymentMethods: String? = nil, + summonsDisplayFormat: Bool = false, + summonsDisplayEntryFee: Bool = false, + summonsUseFullCustomMessage: Bool = false, + matchFormatsDefaultDuration: [MatchFormat: Int]? = nil, + bracketMatchFormatPreference: MatchFormat? = nil, + groupStageMatchFormatPreference: MatchFormat? = nil, + loserBracketMatchFormatPreference: MatchFormat? = nil, + loserBracketMode: LoserBracketMode = .automatic, + deviceId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.username = username + self.email = email + self.clubs = clubs + self.umpireCode = umpireCode + self.licenceId = licenceId + self.firstName = firstName + self.lastName = lastName + self.phone = phone + self.country = country + self.summonsMessageBody = summonsMessageBody + self.summonsMessageSignature = summonsMessageSignature + self.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods + self.summonsDisplayFormat = summonsDisplayFormat + self.summonsDisplayEntryFee = summonsDisplayEntryFee + self.summonsUseFullCustomMessage = summonsUseFullCustomMessage + self.matchFormatsDefaultDuration = matchFormatsDefaultDuration + self.bracketMatchFormatPreference = bracketMatchFormatPreference + self.groupStageMatchFormatPreference = groupStageMatchFormatPreference + self.loserBracketMatchFormatPreference = loserBracketMatchFormatPreference + self.loserBracketMode = loserBracketMode + self.deviceId = deviceId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _username = "username" + case _email = "email" + case _clubs = "clubs" + case _umpireCode = "umpireCode" + case _licenceId = "licenceId" + case _firstName = "firstName" + case _lastName = "lastName" + case _phone = "phone" + case _country = "country" + case _summonsMessageBody = "summonsMessageBody" + case _summonsMessageSignature = "summonsMessageSignature" + case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods" + case _summonsDisplayFormat = "summonsDisplayFormat" + case _summonsDisplayEntryFee = "summonsDisplayEntryFee" + case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage" + case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration" + case _bracketMatchFormatPreference = "bracketMatchFormatPreference" + case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference" + case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference" + case _loserBracketMode = "loserBracketMode" + case _deviceId = "deviceId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.username = try container.decodeIfPresent(String.self, forKey: ._username) ?? "" + self.email = try container.decodeIfPresent(String.self, forKey: ._email) ?? "" + self.clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? [] + self.umpireCode = try container.decodeIfPresent(String.self, forKey: ._umpireCode) ?? nil + self.licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId) ?? nil + self.firstName = try container.decodeIfPresent(String.self, forKey: ._firstName) ?? "" + self.lastName = try container.decodeIfPresent(String.self, forKey: ._lastName) ?? "" + self.phone = try container.decodeIfPresent(String.self, forKey: ._phone) ?? nil + self.country = try container.decodeIfPresent(String.self, forKey: ._country) ?? nil + self.summonsMessageBody = try container.decodeIfPresent(String.self, forKey: ._summonsMessageBody) ?? nil + self.summonsMessageSignature = try container.decodeIfPresent(String.self, forKey: ._summonsMessageSignature) ?? nil + self.summonsAvailablePaymentMethods = try container.decodeIfPresent(String.self, forKey: ._summonsAvailablePaymentMethods) ?? nil + self.summonsDisplayFormat = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayFormat) ?? false + self.summonsDisplayEntryFee = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayEntryFee) ?? false + self.summonsUseFullCustomMessage = try container.decodeIfPresent(Bool.self, forKey: ._summonsUseFullCustomMessage) ?? false + self.matchFormatsDefaultDuration = try container.decodeIfPresent([MatchFormat: Int].self, forKey: ._matchFormatsDefaultDuration) ?? nil + self.bracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._bracketMatchFormatPreference) ?? nil + self.groupStageMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageMatchFormatPreference) ?? nil + self.loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference) ?? nil + self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic + self.deviceId = try container.decodeIfPresent(String.self, forKey: ._deviceId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.username, forKey: ._username) + try container.encode(self.email, forKey: ._email) + try container.encode(self.clubs, forKey: ._clubs) + try container.encode(self.umpireCode, forKey: ._umpireCode) + try container.encode(self.licenceId, forKey: ._licenceId) + try container.encode(self.firstName, forKey: ._firstName) + try container.encode(self.lastName, forKey: ._lastName) + try container.encode(self.phone, forKey: ._phone) + try container.encode(self.country, forKey: ._country) + try container.encode(self.summonsMessageBody, forKey: ._summonsMessageBody) + try container.encode(self.summonsMessageSignature, forKey: ._summonsMessageSignature) + try container.encode(self.summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods) + try container.encode(self.summonsDisplayFormat, forKey: ._summonsDisplayFormat) + try container.encode(self.summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee) + try container.encode(self.summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage) + try container.encode(self.matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration) + try container.encode(self.bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference) + try container.encode(self.groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference) + try container.encode(self.loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference) + try container.encode(self.loserBracketMode, forKey: ._loserBracketMode) + try container.encode(self.deviceId, forKey: ._deviceId) + } + + func copy(from other: any Storable) { + guard let customuser = other as? BaseCustomUser else { return } + self.id = customuser.id + self.lastUpdate = customuser.lastUpdate + self.username = customuser.username + self.email = customuser.email + self.clubs = customuser.clubs + self.umpireCode = customuser.umpireCode + self.licenceId = customuser.licenceId + self.firstName = customuser.firstName + self.lastName = customuser.lastName + self.phone = customuser.phone + self.country = customuser.country + self.summonsMessageBody = customuser.summonsMessageBody + self.summonsMessageSignature = customuser.summonsMessageSignature + self.summonsAvailablePaymentMethods = customuser.summonsAvailablePaymentMethods + self.summonsDisplayFormat = customuser.summonsDisplayFormat + self.summonsDisplayEntryFee = customuser.summonsDisplayEntryFee + self.summonsUseFullCustomMessage = customuser.summonsUseFullCustomMessage + self.matchFormatsDefaultDuration = customuser.matchFormatsDefaultDuration + self.bracketMatchFormatPreference = customuser.bracketMatchFormatPreference + self.groupStageMatchFormatPreference = customuser.groupStageMatchFormatPreference + self.loserBracketMatchFormatPreference = customuser.loserBracketMatchFormatPreference + self.loserBracketMode = customuser.loserBracketMode + self.deviceId = customuser.deviceId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseDateInterval.swift b/PadelClub/Data/Gen/BaseDateInterval.swift new file mode 100644 index 0000000..1c678f0 --- /dev/null +++ b/PadelClub/Data/Gen/BaseDateInterval.swift @@ -0,0 +1,78 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseDateInterval: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "date-intervals" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var event: String = "" + var courtIndex: Int = 0 + var startDate: Date = Date() + var endDate: Date = Date() + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + event: String = "", + courtIndex: Int = 0, + startDate: Date = Date(), + endDate: Date = Date() + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.event = event + self.courtIndex = courtIndex + self.startDate = startDate + self.endDate = endDate + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _event = "event" + case _courtIndex = "courtIndex" + case _startDate = "startDate" + case _endDate = "endDate" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? "" + self.courtIndex = try container.decodeIfPresent(Int.self, forKey: ._courtIndex) ?? 0 + self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date() + self.endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? Date() + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.event, forKey: ._event) + try container.encode(self.courtIndex, forKey: ._courtIndex) + try container.encode(self.startDate, forKey: ._startDate) + try container.encode(self.endDate, forKey: ._endDate) + } + + func copy(from other: any Storable) { + guard let dateinterval = other as? BaseDateInterval else { return } + self.id = dateinterval.id + self.lastUpdate = dateinterval.lastUpdate + self.event = dateinterval.event + self.courtIndex = dateinterval.courtIndex + self.startDate = dateinterval.startDate + self.endDate = dateinterval.endDate + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseEvent.swift b/PadelClub/Data/Gen/BaseEvent.swift new file mode 100644 index 0000000..b184516 --- /dev/null +++ b/PadelClub/Data/Gen/BaseEvent.swift @@ -0,0 +1,95 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseEvent: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "events" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var creator: String? = nil + var club: String? = nil + var creationDate: Date = Date() + var name: String? = nil + var tenupId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + creator: String? = nil, + club: String? = nil, + creationDate: Date = Date(), + name: String? = nil, + tenupId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.creator = creator + self.club = club + self.creationDate = creationDate + self.name = name + self.tenupId = tenupId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _creator = "creator" + case _club = "club" + case _creationDate = "creationDate" + case _name = "name" + case _tenupId = "tenupId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil + self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? nil + self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date() + self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil + self.tenupId = try container.decodeIfPresent(String.self, forKey: ._tenupId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.creator, forKey: ._creator) + try container.encode(self.club, forKey: ._club) + try container.encode(self.creationDate, forKey: ._creationDate) + try container.encode(self.name, forKey: ._name) + try container.encode(self.tenupId, forKey: ._tenupId) + } + + func creatorValue() -> CustomUser? { + guard let creator = self.creator else { return nil } + return Store.main.findById(creator) + } + + func clubValue() -> Club? { + guard let club = self.club else { return nil } + return Store.main.findById(club) + } + + func copy(from other: any Storable) { + guard let event = other as? BaseEvent else { return } + self.id = event.id + self.lastUpdate = event.lastUpdate + self.creator = event.creator + self.club = event.club + self.creationDate = event.creationDate + self.name = event.name + self.tenupId = event.tenupId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseGroupStage.swift b/PadelClub/Data/Gen/BaseGroupStage.swift new file mode 100644 index 0000000..dd92f1d --- /dev/null +++ b/PadelClub/Data/Gen/BaseGroupStage.swift @@ -0,0 +1,110 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseGroupStage: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "group-stages" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return true } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var tournament: String = "" + var index: Int = 0 + var size: Int = 0 + var format: MatchFormat? = nil + var startDate: Date? = nil + var name: String? = nil + var step: Int = 0 + var storeId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + tournament: String = "", + index: Int = 0, + size: Int = 0, + format: MatchFormat? = nil, + startDate: Date? = nil, + name: String? = nil, + step: Int = 0, + storeId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.tournament = tournament + self.index = index + self.size = size + self.format = format + self.startDate = startDate + self.name = name + self.step = step + self.storeId = storeId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _tournament = "tournament" + case _index = "index" + case _size = "size" + case _format = "format" + case _startDate = "startDate" + case _name = "name" + case _step = "step" + case _storeId = "storeId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" + self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 + self.size = try container.decodeIfPresent(Int.self, forKey: ._size) ?? 0 + self.format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) ?? nil + self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil + self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil + self.step = try container.decodeIfPresent(Int.self, forKey: ._step) ?? 0 + self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.tournament, forKey: ._tournament) + try container.encode(self.index, forKey: ._index) + try container.encode(self.size, forKey: ._size) + try container.encode(self.format, forKey: ._format) + try container.encode(self.startDate, forKey: ._startDate) + try container.encode(self.name, forKey: ._name) + try container.encode(self.step, forKey: ._step) + try container.encode(self.storeId, forKey: ._storeId) + } + + func tournamentValue() -> Tournament? { + return Store.main.findById(tournament) + } + + func copy(from other: any Storable) { + guard let groupstage = other as? BaseGroupStage else { return } + self.id = groupstage.id + self.lastUpdate = groupstage.lastUpdate + self.tournament = groupstage.tournament + self.index = groupstage.index + self.size = groupstage.size + self.format = groupstage.format + self.startDate = groupstage.startDate + self.name = groupstage.name + self.step = groupstage.step + self.storeId = groupstage.storeId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseMatch.swift b/PadelClub/Data/Gen/BaseMatch.swift new file mode 100644 index 0000000..eeadae7 --- /dev/null +++ b/PadelClub/Data/Gen/BaseMatch.swift @@ -0,0 +1,158 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseMatch: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "matches" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return true } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var round: String? = nil + var groupStage: String? = nil + var startDate: Date? = nil + var endDate: Date? = nil + var index: Int = 0 + var format: MatchFormat? = nil + var servingTeamId: String? = nil + var winningTeamId: String? = nil + var losingTeamId: String? = nil + var name: String? = nil + var disabled: Bool = false + var courtIndex: Int? = nil + var confirmed: Bool = false + var storeId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + round: String? = nil, + groupStage: String? = nil, + startDate: Date? = nil, + endDate: Date? = nil, + index: Int = 0, + format: MatchFormat? = nil, + servingTeamId: String? = nil, + winningTeamId: String? = nil, + losingTeamId: String? = nil, + name: String? = nil, + disabled: Bool = false, + courtIndex: Int? = nil, + confirmed: Bool = false, + storeId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.round = round + self.groupStage = groupStage + self.startDate = startDate + self.endDate = endDate + self.index = index + self.format = format + self.servingTeamId = servingTeamId + self.winningTeamId = winningTeamId + self.losingTeamId = losingTeamId + self.name = name + self.disabled = disabled + self.courtIndex = courtIndex + self.confirmed = confirmed + self.storeId = storeId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _round = "round" + case _groupStage = "groupStage" + case _startDate = "startDate" + case _endDate = "endDate" + case _index = "index" + case _format = "format" + case _servingTeamId = "servingTeamId" + case _winningTeamId = "winningTeamId" + case _losingTeamId = "losingTeamId" + case _name = "name" + case _disabled = "disabled" + case _courtIndex = "courtIndex" + case _confirmed = "confirmed" + case _storeId = "storeId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.round = try container.decodeIfPresent(String.self, forKey: ._round) ?? nil + self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil + self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil + self.endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? nil + self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 + self.format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) ?? nil + self.servingTeamId = try container.decodeIfPresent(String.self, forKey: ._servingTeamId) ?? nil + self.winningTeamId = try container.decodeIfPresent(String.self, forKey: ._winningTeamId) ?? nil + self.losingTeamId = try container.decodeIfPresent(String.self, forKey: ._losingTeamId) ?? nil + self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil + self.disabled = try container.decodeIfPresent(Bool.self, forKey: ._disabled) ?? false + self.courtIndex = try container.decodeIfPresent(Int.self, forKey: ._courtIndex) ?? nil + self.confirmed = try container.decodeIfPresent(Bool.self, forKey: ._confirmed) ?? false + self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.round, forKey: ._round) + try container.encode(self.groupStage, forKey: ._groupStage) + try container.encode(self.startDate, forKey: ._startDate) + try container.encode(self.endDate, forKey: ._endDate) + try container.encode(self.index, forKey: ._index) + try container.encode(self.format, forKey: ._format) + try container.encode(self.servingTeamId, forKey: ._servingTeamId) + try container.encode(self.winningTeamId, forKey: ._winningTeamId) + try container.encode(self.losingTeamId, forKey: ._losingTeamId) + try container.encode(self.name, forKey: ._name) + try container.encode(self.disabled, forKey: ._disabled) + try container.encode(self.courtIndex, forKey: ._courtIndex) + try container.encode(self.confirmed, forKey: ._confirmed) + try container.encode(self.storeId, forKey: ._storeId) + } + + func roundValue() -> Round? { + guard let round = self.round else { return nil } + return self.store?.findById(round) + } + + func groupStageValue() -> GroupStage? { + guard let groupStage = self.groupStage else { return nil } + return self.store?.findById(groupStage) + } + + func copy(from other: any Storable) { + guard let match = other as? BaseMatch else { return } + self.id = match.id + self.lastUpdate = match.lastUpdate + self.round = match.round + self.groupStage = match.groupStage + self.startDate = match.startDate + self.endDate = match.endDate + self.index = match.index + self.format = match.format + self.servingTeamId = match.servingTeamId + self.winningTeamId = match.winningTeamId + self.losingTeamId = match.losingTeamId + self.name = match.name + self.disabled = match.disabled + self.courtIndex = match.courtIndex + self.confirmed = match.confirmed + self.storeId = match.storeId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseMatchScheduler.swift b/PadelClub/Data/Gen/BaseMatchScheduler.swift new file mode 100644 index 0000000..cc962bb --- /dev/null +++ b/PadelClub/Data/Gen/BaseMatchScheduler.swift @@ -0,0 +1,138 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseMatchScheduler: ModelObject, Storable, Codable { + + static func resourceName() -> String { return "match-schedulers" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var tournament: String = "" + var timeDifferenceLimit: Int = 0 + var loserBracketRotationDifference: Int = 0 + var upperBracketRotationDifference: Int = 0 + var accountUpperBracketBreakTime: Bool = false + var accountLoserBracketBreakTime: Bool = false + var randomizeCourts: Bool = false + var rotationDifferenceIsImportant: Bool = false + var shouldHandleUpperRoundSlice: Bool = false + var shouldEndRoundBeforeStartingNext: Bool = false + var groupStageChunkCount: Int? = nil + var overrideCourtsUnavailability: Bool = false + var shouldTryToFillUpCourtsAvailable: Bool = false + + init( + id: String = Store.randomId(), + tournament: String = "", + timeDifferenceLimit: Int = 0, + loserBracketRotationDifference: Int = 0, + upperBracketRotationDifference: Int = 0, + accountUpperBracketBreakTime: Bool = false, + accountLoserBracketBreakTime: Bool = false, + randomizeCourts: Bool = false, + rotationDifferenceIsImportant: Bool = false, + shouldHandleUpperRoundSlice: Bool = false, + shouldEndRoundBeforeStartingNext: Bool = false, + groupStageChunkCount: Int? = nil, + overrideCourtsUnavailability: Bool = false, + shouldTryToFillUpCourtsAvailable: Bool = false + ) { + super.init() + self.id = id + self.tournament = tournament + self.timeDifferenceLimit = timeDifferenceLimit + self.loserBracketRotationDifference = loserBracketRotationDifference + self.upperBracketRotationDifference = upperBracketRotationDifference + self.accountUpperBracketBreakTime = accountUpperBracketBreakTime + self.accountLoserBracketBreakTime = accountLoserBracketBreakTime + self.randomizeCourts = randomizeCourts + self.rotationDifferenceIsImportant = rotationDifferenceIsImportant + self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice + self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext + self.groupStageChunkCount = groupStageChunkCount + self.overrideCourtsUnavailability = overrideCourtsUnavailability + self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _tournament = "tournament" + case _timeDifferenceLimit = "timeDifferenceLimit" + case _loserBracketRotationDifference = "loserBracketRotationDifference" + case _upperBracketRotationDifference = "upperBracketRotationDifference" + case _accountUpperBracketBreakTime = "accountUpperBracketBreakTime" + case _accountLoserBracketBreakTime = "accountLoserBracketBreakTime" + case _randomizeCourts = "randomizeCourts" + case _rotationDifferenceIsImportant = "rotationDifferenceIsImportant" + case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice" + case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext" + case _groupStageChunkCount = "groupStageChunkCount" + case _overrideCourtsUnavailability = "overrideCourtsUnavailability" + case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" + self.timeDifferenceLimit = try container.decodeIfPresent(Int.self, forKey: ._timeDifferenceLimit) ?? 0 + self.loserBracketRotationDifference = try container.decodeIfPresent(Int.self, forKey: ._loserBracketRotationDifference) ?? 0 + self.upperBracketRotationDifference = try container.decodeIfPresent(Int.self, forKey: ._upperBracketRotationDifference) ?? 0 + self.accountUpperBracketBreakTime = try container.decodeIfPresent(Bool.self, forKey: ._accountUpperBracketBreakTime) ?? false + self.accountLoserBracketBreakTime = try container.decodeIfPresent(Bool.self, forKey: ._accountLoserBracketBreakTime) ?? false + self.randomizeCourts = try container.decodeIfPresent(Bool.self, forKey: ._randomizeCourts) ?? false + self.rotationDifferenceIsImportant = try container.decodeIfPresent(Bool.self, forKey: ._rotationDifferenceIsImportant) ?? false + self.shouldHandleUpperRoundSlice = try container.decodeIfPresent(Bool.self, forKey: ._shouldHandleUpperRoundSlice) ?? false + self.shouldEndRoundBeforeStartingNext = try container.decodeIfPresent(Bool.self, forKey: ._shouldEndRoundBeforeStartingNext) ?? false + self.groupStageChunkCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageChunkCount) ?? nil + self.overrideCourtsUnavailability = try container.decodeIfPresent(Bool.self, forKey: ._overrideCourtsUnavailability) ?? false + self.shouldTryToFillUpCourtsAvailable = try container.decodeIfPresent(Bool.self, forKey: ._shouldTryToFillUpCourtsAvailable) ?? false + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.tournament, forKey: ._tournament) + try container.encode(self.timeDifferenceLimit, forKey: ._timeDifferenceLimit) + try container.encode(self.loserBracketRotationDifference, forKey: ._loserBracketRotationDifference) + try container.encode(self.upperBracketRotationDifference, forKey: ._upperBracketRotationDifference) + try container.encode(self.accountUpperBracketBreakTime, forKey: ._accountUpperBracketBreakTime) + try container.encode(self.accountLoserBracketBreakTime, forKey: ._accountLoserBracketBreakTime) + try container.encode(self.randomizeCourts, forKey: ._randomizeCourts) + try container.encode(self.rotationDifferenceIsImportant, forKey: ._rotationDifferenceIsImportant) + try container.encode(self.shouldHandleUpperRoundSlice, forKey: ._shouldHandleUpperRoundSlice) + try container.encode(self.shouldEndRoundBeforeStartingNext, forKey: ._shouldEndRoundBeforeStartingNext) + try container.encode(self.groupStageChunkCount, forKey: ._groupStageChunkCount) + try container.encode(self.overrideCourtsUnavailability, forKey: ._overrideCourtsUnavailability) + try container.encode(self.shouldTryToFillUpCourtsAvailable, forKey: ._shouldTryToFillUpCourtsAvailable) + } + + func tournamentValue() -> Tournament? { + return Store.main.findById(tournament) + } + + func copy(from other: any Storable) { + guard let matchscheduler = other as? BaseMatchScheduler else { return } + self.id = matchscheduler.id + self.tournament = matchscheduler.tournament + self.timeDifferenceLimit = matchscheduler.timeDifferenceLimit + self.loserBracketRotationDifference = matchscheduler.loserBracketRotationDifference + self.upperBracketRotationDifference = matchscheduler.upperBracketRotationDifference + self.accountUpperBracketBreakTime = matchscheduler.accountUpperBracketBreakTime + self.accountLoserBracketBreakTime = matchscheduler.accountLoserBracketBreakTime + self.randomizeCourts = matchscheduler.randomizeCourts + self.rotationDifferenceIsImportant = matchscheduler.rotationDifferenceIsImportant + self.shouldHandleUpperRoundSlice = matchscheduler.shouldHandleUpperRoundSlice + self.shouldEndRoundBeforeStartingNext = matchscheduler.shouldEndRoundBeforeStartingNext + self.groupStageChunkCount = matchscheduler.groupStageChunkCount + self.overrideCourtsUnavailability = matchscheduler.overrideCourtsUnavailability + self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseMonthData.swift b/PadelClub/Data/Gen/BaseMonthData.swift new file mode 100644 index 0000000..691529a --- /dev/null +++ b/PadelClub/Data/Gen/BaseMonthData.swift @@ -0,0 +1,113 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseMonthData: ModelObject, Storable, Codable { + + static func resourceName() -> String { return "month-datas" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var monthKey: String = "" + var creationDate: Date = Date() + var maleUnrankedValue: Int? = nil + var femaleUnrankedValue: Int? = nil + var maleCount: Int? = nil + var femaleCount: Int? = nil + var anonymousCount: Int? = nil + var incompleteMode: Bool = false + var dataModelIdentifier: String? = nil + var fileModelIdentifier: String? = nil + + init( + id: String = Store.randomId(), + monthKey: String = "", + creationDate: Date = Date(), + maleUnrankedValue: Int? = nil, + femaleUnrankedValue: Int? = nil, + maleCount: Int? = nil, + femaleCount: Int? = nil, + anonymousCount: Int? = nil, + incompleteMode: Bool = false, + dataModelIdentifier: String? = nil, + fileModelIdentifier: String? = nil + ) { + super.init() + self.id = id + self.monthKey = monthKey + self.creationDate = creationDate + self.maleUnrankedValue = maleUnrankedValue + self.femaleUnrankedValue = femaleUnrankedValue + self.maleCount = maleCount + self.femaleCount = femaleCount + self.anonymousCount = anonymousCount + self.incompleteMode = incompleteMode + self.dataModelIdentifier = dataModelIdentifier + self.fileModelIdentifier = fileModelIdentifier + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _monthKey = "monthKey" + case _creationDate = "creationDate" + case _maleUnrankedValue = "maleUnrankedValue" + case _femaleUnrankedValue = "femaleUnrankedValue" + case _maleCount = "maleCount" + case _femaleCount = "femaleCount" + case _anonymousCount = "anonymousCount" + case _incompleteMode = "incompleteMode" + case _dataModelIdentifier = "dataModelIdentifier" + case _fileModelIdentifier = "fileModelIdentifier" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.monthKey = try container.decodeIfPresent(String.self, forKey: ._monthKey) ?? "" + self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date() + self.maleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._maleUnrankedValue) ?? nil + self.femaleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._femaleUnrankedValue) ?? nil + self.maleCount = try container.decodeIfPresent(Int.self, forKey: ._maleCount) ?? nil + self.femaleCount = try container.decodeIfPresent(Int.self, forKey: ._femaleCount) ?? nil + self.anonymousCount = try container.decodeIfPresent(Int.self, forKey: ._anonymousCount) ?? nil + self.incompleteMode = try container.decodeIfPresent(Bool.self, forKey: ._incompleteMode) ?? false + self.dataModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._dataModelIdentifier) ?? nil + self.fileModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._fileModelIdentifier) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.monthKey, forKey: ._monthKey) + try container.encode(self.creationDate, forKey: ._creationDate) + try container.encode(self.maleUnrankedValue, forKey: ._maleUnrankedValue) + try container.encode(self.femaleUnrankedValue, forKey: ._femaleUnrankedValue) + try container.encode(self.maleCount, forKey: ._maleCount) + try container.encode(self.femaleCount, forKey: ._femaleCount) + try container.encode(self.anonymousCount, forKey: ._anonymousCount) + try container.encode(self.incompleteMode, forKey: ._incompleteMode) + try container.encode(self.dataModelIdentifier, forKey: ._dataModelIdentifier) + try container.encode(self.fileModelIdentifier, forKey: ._fileModelIdentifier) + } + + func copy(from other: any Storable) { + guard let monthdata = other as? BaseMonthData else { return } + self.id = monthdata.id + self.monthKey = monthdata.monthKey + self.creationDate = monthdata.creationDate + self.maleUnrankedValue = monthdata.maleUnrankedValue + self.femaleUnrankedValue = monthdata.femaleUnrankedValue + self.maleCount = monthdata.maleCount + self.femaleCount = monthdata.femaleCount + self.anonymousCount = monthdata.anonymousCount + self.incompleteMode = monthdata.incompleteMode + self.dataModelIdentifier = monthdata.dataModelIdentifier + self.fileModelIdentifier = monthdata.fileModelIdentifier + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BasePlayerRegistration.swift b/PadelClub/Data/Gen/BasePlayerRegistration.swift new file mode 100644 index 0000000..4b146da --- /dev/null +++ b/PadelClub/Data/Gen/BasePlayerRegistration.swift @@ -0,0 +1,188 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "player-registrations" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return true } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var teamRegistration: String? = nil + var firstName: String = "" + var lastName: String = "" + var licenceId: String? = nil + var rank: Int? = nil + var paymentType: PlayerPaymentType? = nil + var sex: PlayerSexType? = nil + var tournamentPlayed: Int? = nil + var points: Double? = nil + var clubName: String? = nil + var ligueName: String? = nil + var assimilation: String? = nil + var phoneNumber: String? = nil + var email: String? = nil + var birthdate: String? = nil + var computedRank: Int = 0 + var source: PlayerDataSource? = nil + var hasArrived: Bool = false + var storeId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + teamRegistration: String? = nil, + firstName: String = "", + lastName: String = "", + licenceId: String? = nil, + rank: Int? = nil, + paymentType: PlayerPaymentType? = nil, + sex: PlayerSexType? = nil, + tournamentPlayed: Int? = nil, + points: Double? = nil, + clubName: String? = nil, + ligueName: String? = nil, + assimilation: String? = nil, + phoneNumber: String? = nil, + email: String? = nil, + birthdate: String? = nil, + computedRank: Int = 0, + source: PlayerDataSource? = nil, + hasArrived: Bool = false, + storeId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.teamRegistration = teamRegistration + self.firstName = firstName + self.lastName = lastName + self.licenceId = licenceId + self.rank = rank + self.paymentType = paymentType + self.sex = sex + self.tournamentPlayed = tournamentPlayed + self.points = points + self.clubName = clubName + self.ligueName = ligueName + self.assimilation = assimilation + self.phoneNumber = phoneNumber + self.email = email + self.birthdate = birthdate + self.computedRank = computedRank + self.source = source + self.hasArrived = hasArrived + self.storeId = storeId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _teamRegistration = "teamRegistration" + case _firstName = "firstName" + case _lastName = "lastName" + case _licenceId = "licenceId" + case _rank = "rank" + case _paymentType = "paymentType" + case _sex = "sex" + case _tournamentPlayed = "tournamentPlayed" + case _points = "points" + case _clubName = "clubName" + case _ligueName = "ligueName" + case _assimilation = "assimilation" + case _phoneNumber = "phoneNumber" + case _email = "email" + case _birthdate = "birthdate" + case _computedRank = "computedRank" + case _source = "source" + case _hasArrived = "hasArrived" + case _storeId = "storeId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil + self.firstName = try container.decodeIfPresent(String.self, forKey: ._firstName) ?? "" + self.lastName = try container.decodeIfPresent(String.self, forKey: ._lastName) ?? "" + self.licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId) ?? nil + self.rank = try container.decodeIfPresent(Int.self, forKey: ._rank) ?? nil + self.paymentType = try container.decodeIfPresent(PlayerPaymentType.self, forKey: ._paymentType) ?? nil + self.sex = try container.decodeIfPresent(PlayerSexType.self, forKey: ._sex) ?? nil + self.tournamentPlayed = try container.decodeIfPresent(Int.self, forKey: ._tournamentPlayed) ?? nil + self.points = try container.decodeIfPresent(Double.self, forKey: ._points) ?? nil + self.clubName = try container.decodeIfPresent(String.self, forKey: ._clubName) ?? nil + self.ligueName = try container.decodeIfPresent(String.self, forKey: ._ligueName) ?? nil + self.assimilation = try container.decodeIfPresent(String.self, forKey: ._assimilation) ?? nil + self.phoneNumber = try container.decodeIfPresent(String.self, forKey: ._phoneNumber) ?? nil + self.email = try container.decodeIfPresent(String.self, forKey: ._email) ?? nil + self.birthdate = try container.decodeIfPresent(String.self, forKey: ._birthdate) ?? nil + self.computedRank = try container.decodeIfPresent(Int.self, forKey: ._computedRank) ?? 0 + self.source = try container.decodeIfPresent(PlayerDataSource.self, forKey: ._source) ?? nil + self.hasArrived = try container.decodeIfPresent(Bool.self, forKey: ._hasArrived) ?? false + self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.teamRegistration, forKey: ._teamRegistration) + try container.encode(self.firstName, forKey: ._firstName) + try container.encode(self.lastName, forKey: ._lastName) + try container.encode(self.licenceId, forKey: ._licenceId) + try container.encode(self.rank, forKey: ._rank) + try container.encode(self.paymentType, forKey: ._paymentType) + try container.encode(self.sex, forKey: ._sex) + try container.encode(self.tournamentPlayed, forKey: ._tournamentPlayed) + try container.encode(self.points, forKey: ._points) + try container.encode(self.clubName, forKey: ._clubName) + try container.encode(self.ligueName, forKey: ._ligueName) + try container.encode(self.assimilation, forKey: ._assimilation) + try container.encode(self.phoneNumber, forKey: ._phoneNumber) + try container.encode(self.email, forKey: ._email) + try container.encode(self.birthdate, forKey: ._birthdate) + try container.encode(self.computedRank, forKey: ._computedRank) + try container.encode(self.source, forKey: ._source) + try container.encode(self.hasArrived, forKey: ._hasArrived) + try container.encode(self.storeId, forKey: ._storeId) + } + + func teamRegistrationValue() -> TeamRegistration? { + guard let teamRegistration = self.teamRegistration else { return nil } + return Store.main.findById(teamRegistration) + } + + func copy(from other: any Storable) { + guard let playerregistration = other as? BasePlayerRegistration else { return } + self.id = playerregistration.id + self.lastUpdate = playerregistration.lastUpdate + self.teamRegistration = playerregistration.teamRegistration + self.firstName = playerregistration.firstName + self.lastName = playerregistration.lastName + self.licenceId = playerregistration.licenceId + self.rank = playerregistration.rank + self.paymentType = playerregistration.paymentType + self.sex = playerregistration.sex + self.tournamentPlayed = playerregistration.tournamentPlayed + self.points = playerregistration.points + self.clubName = playerregistration.clubName + self.ligueName = playerregistration.ligueName + self.assimilation = playerregistration.assimilation + self.phoneNumber = playerregistration.phoneNumber + self.email = playerregistration.email + self.birthdate = playerregistration.birthdate + self.computedRank = playerregistration.computedRank + self.source = playerregistration.source + self.hasArrived = playerregistration.hasArrived + self.storeId = playerregistration.storeId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BasePurchase.swift b/PadelClub/Data/Gen/BasePurchase.swift new file mode 100644 index 0000000..4b544dc --- /dev/null +++ b/PadelClub/Data/Gen/BasePurchase.swift @@ -0,0 +1,95 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage + +class BasePurchase: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "purchases" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: UInt64 = 0 + var lastUpdate: Date = Date() + var user: String = "" + var purchaseDate: Date = Date() + var productId: String = "" + var quantity: Int? = nil + var revocationDate: Date? = nil + var expirationDate: Date? = nil + + init( + id: UInt64 = 0, + lastUpdate: Date = Date(), + user: String = "", + purchaseDate: Date = Date(), + productId: String = "", + quantity: Int? = nil, + revocationDate: Date? = nil, + expirationDate: Date? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.user = user + self.purchaseDate = purchaseDate + self.productId = productId + self.quantity = quantity + self.revocationDate = revocationDate + self.expirationDate = expirationDate + } + + enum CodingKeys: String, CodingKey { + case id = "id" + case lastUpdate = "lastUpdate" + case user = "user" + case purchaseDate = "purchaseDate" + case productId = "productId" + case quantity = "quantity" + case revocationDate = "revocationDate" + case expirationDate = "expirationDate" + } + + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(UInt64.self, forKey: .id) ?? 0 + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: .lastUpdate) ?? Date() + self.user = try container.decodeEncrypted(key: .user) + self.purchaseDate = try container.decodeIfPresent(Date.self, forKey: .purchaseDate) ?? Date() + self.productId = try container.decodeIfPresent(String.self, forKey: .productId) ?? "" + self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity) ?? nil + self.revocationDate = try container.decodeIfPresent(Date.self, forKey: .revocationDate) ?? nil + self.expirationDate = try container.decodeIfPresent(Date.self, forKey: .expirationDate) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: .id) + try container.encode(self.lastUpdate, forKey: .lastUpdate) + try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user) + try container.encode(self.purchaseDate, forKey: .purchaseDate) + try container.encode(self.productId, forKey: .productId) + try container.encode(self.quantity, forKey: .quantity) + try container.encode(self.revocationDate, forKey: .revocationDate) + try container.encode(self.expirationDate, forKey: .expirationDate) + } + + func userValue() -> CustomUser? { + return Store.main.findById(user) + } + + func copy(from other: any Storable) { + guard let purchase = other as? BasePurchase else { return } + self.id = purchase.id + self.lastUpdate = purchase.lastUpdate + self.user = purchase.user + self.purchaseDate = purchase.purchaseDate + self.productId = purchase.productId + self.quantity = purchase.quantity + self.revocationDate = purchase.revocationDate + self.expirationDate = purchase.expirationDate + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseRound.swift b/PadelClub/Data/Gen/BaseRound.swift new file mode 100644 index 0000000..d0b5402 --- /dev/null +++ b/PadelClub/Data/Gen/BaseRound.swift @@ -0,0 +1,110 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseRound: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "rounds" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return true } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var tournament: String = "" + var index: Int = 0 + var parent: String? = nil + var format: MatchFormat? = nil + var startDate: Date? = nil + var groupStageLoserBracket: Bool = false + var loserBracketMode: LoserBracketMode = .automatic + var storeId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + tournament: String = "", + index: Int = 0, + parent: String? = nil, + format: MatchFormat? = nil, + startDate: Date? = nil, + groupStageLoserBracket: Bool = false, + loserBracketMode: LoserBracketMode = .automatic, + storeId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.tournament = tournament + self.index = index + self.parent = parent + self.format = format + self.startDate = startDate + self.groupStageLoserBracket = groupStageLoserBracket + self.loserBracketMode = loserBracketMode + self.storeId = storeId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _tournament = "tournament" + case _index = "index" + case _parent = "parent" + case _format = "format" + case _startDate = "startDate" + case _groupStageLoserBracket = "groupStageLoserBracket" + case _loserBracketMode = "loserBracketMode" + case _storeId = "storeId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" + self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 + self.parent = try container.decodeIfPresent(String.self, forKey: ._parent) ?? nil + self.format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) ?? nil + self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil + self.groupStageLoserBracket = try container.decodeIfPresent(Bool.self, forKey: ._groupStageLoserBracket) ?? false + self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic + self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.tournament, forKey: ._tournament) + try container.encode(self.index, forKey: ._index) + try container.encode(self.parent, forKey: ._parent) + try container.encode(self.format, forKey: ._format) + try container.encode(self.startDate, forKey: ._startDate) + try container.encode(self.groupStageLoserBracket, forKey: ._groupStageLoserBracket) + try container.encode(self.loserBracketMode, forKey: ._loserBracketMode) + try container.encode(self.storeId, forKey: ._storeId) + } + + func tournamentValue() -> Tournament? { + return Store.main.findById(tournament) + } + + func copy(from other: any Storable) { + guard let round = other as? BaseRound else { return } + self.id = round.id + self.lastUpdate = round.lastUpdate + self.tournament = round.tournament + self.index = round.index + self.parent = round.parent + self.format = round.format + self.startDate = round.startDate + self.groupStageLoserBracket = round.groupStageLoserBracket + self.loserBracketMode = round.loserBracketMode + self.storeId = round.storeId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseTeamRegistration.swift b/PadelClub/Data/Gen/BaseTeamRegistration.swift new file mode 100644 index 0000000..007ef8d --- /dev/null +++ b/PadelClub/Data/Gen/BaseTeamRegistration.swift @@ -0,0 +1,202 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "team-registrations" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return true } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var tournament: String = "" + var groupStage: String? = nil + var registrationDate: Date? = nil + var callDate: Date? = nil + var bracketPosition: Int? = nil + var groupStagePosition: Int? = nil + var comment: String? = nil + var source: String? = nil + var sourceValue: String? = nil + var logo: String? = nil + var name: String? = nil + var walkOut: Bool = false + var wildCardBracket: Bool = false + var wildCardGroupStage: Bool = false + var weight: Int = 0 + var lockedWeight: Int? = nil + var confirmationDate: Date? = nil + var qualified: Bool = false + var finalRanking: Int? = nil + var pointsEarned: Int? = nil + var storeId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + tournament: String = "", + groupStage: String? = nil, + registrationDate: Date? = nil, + callDate: Date? = nil, + bracketPosition: Int? = nil, + groupStagePosition: Int? = nil, + comment: String? = nil, + source: String? = nil, + sourceValue: String? = nil, + logo: String? = nil, + name: String? = nil, + walkOut: Bool = false, + wildCardBracket: Bool = false, + wildCardGroupStage: Bool = false, + weight: Int = 0, + lockedWeight: Int? = nil, + confirmationDate: Date? = nil, + qualified: Bool = false, + finalRanking: Int? = nil, + pointsEarned: Int? = nil, + storeId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.tournament = tournament + self.groupStage = groupStage + self.registrationDate = registrationDate + self.callDate = callDate + self.bracketPosition = bracketPosition + self.groupStagePosition = groupStagePosition + self.comment = comment + self.source = source + self.sourceValue = sourceValue + self.logo = logo + self.name = name + self.walkOut = walkOut + self.wildCardBracket = wildCardBracket + self.wildCardGroupStage = wildCardGroupStage + self.weight = weight + self.lockedWeight = lockedWeight + self.confirmationDate = confirmationDate + self.qualified = qualified + self.finalRanking = finalRanking + self.pointsEarned = pointsEarned + self.storeId = storeId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _tournament = "tournament" + case _groupStage = "groupStage" + case _registrationDate = "registrationDate" + case _callDate = "callDate" + case _bracketPosition = "bracketPosition" + case _groupStagePosition = "groupStagePosition" + case _comment = "comment" + case _source = "source" + case _sourceValue = "sourceValue" + case _logo = "logo" + case _name = "name" + case _walkOut = "walkOut" + case _wildCardBracket = "wildCardBracket" + case _wildCardGroupStage = "wildCardGroupStage" + case _weight = "weight" + case _lockedWeight = "lockedWeight" + case _confirmationDate = "confirmationDate" + case _qualified = "qualified" + case _finalRanking = "finalRanking" + case _pointsEarned = "pointsEarned" + case _storeId = "storeId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" + self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil + self.registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate) ?? nil + self.callDate = try container.decodeIfPresent(Date.self, forKey: ._callDate) ?? nil + self.bracketPosition = try container.decodeIfPresent(Int.self, forKey: ._bracketPosition) ?? nil + self.groupStagePosition = try container.decodeIfPresent(Int.self, forKey: ._groupStagePosition) ?? nil + self.comment = try container.decodeIfPresent(String.self, forKey: ._comment) ?? nil + self.source = try container.decodeIfPresent(String.self, forKey: ._source) ?? nil + self.sourceValue = try container.decodeIfPresent(String.self, forKey: ._sourceValue) ?? nil + self.logo = try container.decodeIfPresent(String.self, forKey: ._logo) ?? nil + self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil + self.walkOut = try container.decodeIfPresent(Bool.self, forKey: ._walkOut) ?? false + self.wildCardBracket = try container.decodeIfPresent(Bool.self, forKey: ._wildCardBracket) ?? false + self.wildCardGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._wildCardGroupStage) ?? false + self.weight = try container.decodeIfPresent(Int.self, forKey: ._weight) ?? 0 + self.lockedWeight = try container.decodeIfPresent(Int.self, forKey: ._lockedWeight) ?? nil + self.confirmationDate = try container.decodeIfPresent(Date.self, forKey: ._confirmationDate) ?? nil + self.qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false + self.finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) ?? nil + self.pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned) ?? nil + self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.tournament, forKey: ._tournament) + try container.encode(self.groupStage, forKey: ._groupStage) + try container.encode(self.registrationDate, forKey: ._registrationDate) + try container.encode(self.callDate, forKey: ._callDate) + try container.encode(self.bracketPosition, forKey: ._bracketPosition) + try container.encode(self.groupStagePosition, forKey: ._groupStagePosition) + try container.encode(self.comment, forKey: ._comment) + try container.encode(self.source, forKey: ._source) + try container.encode(self.sourceValue, forKey: ._sourceValue) + try container.encode(self.logo, forKey: ._logo) + try container.encode(self.name, forKey: ._name) + try container.encode(self.walkOut, forKey: ._walkOut) + try container.encode(self.wildCardBracket, forKey: ._wildCardBracket) + try container.encode(self.wildCardGroupStage, forKey: ._wildCardGroupStage) + try container.encode(self.weight, forKey: ._weight) + try container.encode(self.lockedWeight, forKey: ._lockedWeight) + try container.encode(self.confirmationDate, forKey: ._confirmationDate) + try container.encode(self.qualified, forKey: ._qualified) + try container.encode(self.finalRanking, forKey: ._finalRanking) + try container.encode(self.pointsEarned, forKey: ._pointsEarned) + try container.encode(self.storeId, forKey: ._storeId) + } + + func groupStageValue() -> GroupStage? { + guard let groupStage = self.groupStage else { return nil } + return self.store?.findById(groupStage) + } + + func copy(from other: any Storable) { + guard let teamregistration = other as? BaseTeamRegistration else { return } + self.id = teamregistration.id + self.lastUpdate = teamregistration.lastUpdate + self.tournament = teamregistration.tournament + self.groupStage = teamregistration.groupStage + self.registrationDate = teamregistration.registrationDate + self.callDate = teamregistration.callDate + self.bracketPosition = teamregistration.bracketPosition + self.groupStagePosition = teamregistration.groupStagePosition + self.comment = teamregistration.comment + self.source = teamregistration.source + self.sourceValue = teamregistration.sourceValue + self.logo = teamregistration.logo + self.name = teamregistration.name + self.walkOut = teamregistration.walkOut + self.wildCardBracket = teamregistration.wildCardBracket + self.wildCardGroupStage = teamregistration.wildCardGroupStage + self.weight = teamregistration.weight + self.lockedWeight = teamregistration.lockedWeight + self.confirmationDate = teamregistration.confirmationDate + self.qualified = teamregistration.qualified + self.finalRanking = teamregistration.finalRanking + self.pointsEarned = teamregistration.pointsEarned + self.storeId = teamregistration.storeId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseTeamScore.swift b/PadelClub/Data/Gen/BaseTeamScore.swift new file mode 100644 index 0000000..b4c415f --- /dev/null +++ b/PadelClub/Data/Gen/BaseTeamScore.swift @@ -0,0 +1,101 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseTeamScore: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "team-scores" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return true } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var match: String = "" + var teamRegistration: String? = nil + var score: String? = nil + var walkOut: Int? = nil + var luckyLoser: Int? = nil + var storeId: String? = nil + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + match: String = "", + teamRegistration: String? = nil, + score: String? = nil, + walkOut: Int? = nil, + luckyLoser: Int? = nil, + storeId: String? = nil + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.match = match + self.teamRegistration = teamRegistration + self.score = score + self.walkOut = walkOut + self.luckyLoser = luckyLoser + self.storeId = storeId + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _match = "match" + case _teamRegistration = "teamRegistration" + case _score = "score" + case _walkOut = "walkOut" + case _luckyLoser = "luckyLoser" + case _storeId = "storeId" + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.match = try container.decodeIfPresent(String.self, forKey: ._match) ?? "" + self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil + self.score = try container.decodeIfPresent(String.self, forKey: ._score) ?? nil + self.walkOut = try container.decodeIfPresent(Int.self, forKey: ._walkOut) ?? nil + self.luckyLoser = try container.decodeIfPresent(Int.self, forKey: ._luckyLoser) ?? nil + self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.match, forKey: ._match) + try container.encode(self.teamRegistration, forKey: ._teamRegistration) + try container.encode(self.score, forKey: ._score) + try container.encode(self.walkOut, forKey: ._walkOut) + try container.encode(self.luckyLoser, forKey: ._luckyLoser) + try container.encode(self.storeId, forKey: ._storeId) + } + + func matchValue() -> Match? { + return self.store?.findById(match) + } + + func teamRegistrationValue() -> TeamRegistration? { + guard let teamRegistration = self.teamRegistration else { return nil } + return self.store?.findById(teamRegistration) + } + + func copy(from other: any Storable) { + guard let teamscore = other as? BaseTeamScore else { return } + self.id = teamscore.id + self.lastUpdate = teamscore.lastUpdate + self.match = teamscore.match + self.teamRegistration = teamscore.teamRegistration + self.score = teamscore.score + self.walkOut = teamscore.walkOut + self.luckyLoser = teamscore.luckyLoser + self.storeId = teamscore.storeId + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseTournament.swift b/PadelClub/Data/Gen/BaseTournament.swift new file mode 100644 index 0000000..ed5b100 --- /dev/null +++ b/PadelClub/Data/Gen/BaseTournament.swift @@ -0,0 +1,396 @@ +// Generated by SwiftModelGenerator +// Do not modify this file manually + +import Foundation +import LeStorage +import SwiftUI + +@Observable +class BaseTournament: ModelObject, SyncedStorable, Codable { + + static func resourceName() -> String { return "tournaments" } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } + static func filterByStoreIdentifier() -> Bool { return false } + + var id: String = Store.randomId() + var lastUpdate: Date = Date() + var event: String? = nil + var name: String? = nil + var startDate: Date = Date() + var endDate: Date? = nil + var creationDate: Date = Date() + var isPrivate: Bool = false + var groupStageFormat: MatchFormat? = nil + var roundFormat: MatchFormat? = nil + var loserRoundFormat: MatchFormat? = nil + var groupStageSortMode: GroupStageOrderingMode = GroupStageOrderingMode.snake + var groupStageCount: Int = 0 + var rankSourceDate: Date? = nil + var dayDuration: Int = 0 + var teamCount: Int = 0 + var teamSorting: TeamSortingType = TeamSortingType.inscriptionDate + var federalCategory: TournamentCategory = TournamentCategory.men + var federalLevelCategory: TournamentLevel = TournamentLevel.unlisted + var federalAgeCategory: FederalTournamentAge = FederalTournamentAge.unlisted + var closedRegistrationDate: Date? = nil + var groupStageAdditionalQualified: Int = 0 + var courtCount: Int = 2 + var prioritizeClubMembers: Bool = false + var qualifiedPerGroupStage: Int = 0 + var teamsPerGroupStage: Int = 0 + var entryFee: Double? = nil + var payment: TournamentPayment? = nil + var additionalEstimationDuration: Int = 0 + var isDeleted: Bool = false + var isCanceled: Bool = false + var publishTeams: Bool = false + var publishSummons: Bool = false + var publishGroupStages: Bool = false + var publishBrackets: Bool = false + var shouldVerifyGroupStage: Bool = false + var shouldVerifyBracket: Bool = false + var hideTeamsWeight: Bool = false + var publishTournament: Bool = false + var hidePointsEarned: Bool = false + var publishRankings: Bool = false + var loserBracketMode: LoserBracketMode = .automatic + + init( + id: String = Store.randomId(), + lastUpdate: Date = Date(), + event: String? = nil, + name: String? = nil, + startDate: Date = Date(), + endDate: Date? = nil, + creationDate: Date = Date(), + isPrivate: Bool = false, + groupStageFormat: MatchFormat? = nil, + roundFormat: MatchFormat? = nil, + loserRoundFormat: MatchFormat? = nil, + groupStageSortMode: GroupStageOrderingMode = GroupStageOrderingMode.snake, + groupStageCount: Int = 0, + rankSourceDate: Date? = nil, + dayDuration: Int = 0, + teamCount: Int = 0, + teamSorting: TeamSortingType = TeamSortingType.inscriptionDate, + federalCategory: TournamentCategory = TournamentCategory.men, + federalLevelCategory: TournamentLevel = TournamentLevel.unlisted, + federalAgeCategory: FederalTournamentAge = FederalTournamentAge.unlisted, + closedRegistrationDate: Date? = nil, + groupStageAdditionalQualified: Int = 0, + courtCount: Int = 2, + prioritizeClubMembers: Bool = false, + qualifiedPerGroupStage: Int = 0, + teamsPerGroupStage: Int = 0, + entryFee: Double? = nil, + payment: TournamentPayment? = nil, + additionalEstimationDuration: Int = 0, + isDeleted: Bool = false, + isCanceled: Bool = false, + publishTeams: Bool = false, + publishSummons: Bool = false, + publishGroupStages: Bool = false, + publishBrackets: Bool = false, + shouldVerifyGroupStage: Bool = false, + shouldVerifyBracket: Bool = false, + hideTeamsWeight: Bool = false, + publishTournament: Bool = false, + hidePointsEarned: Bool = false, + publishRankings: Bool = false, + loserBracketMode: LoserBracketMode = .automatic + ) { + super.init() + self.id = id + self.lastUpdate = lastUpdate + self.event = event + self.name = name + self.startDate = startDate + self.endDate = endDate + self.creationDate = creationDate + self.isPrivate = isPrivate + self.groupStageFormat = groupStageFormat + self.roundFormat = roundFormat + self.loserRoundFormat = loserRoundFormat + self.groupStageSortMode = groupStageSortMode + self.groupStageCount = groupStageCount + self.rankSourceDate = rankSourceDate + self.dayDuration = dayDuration + self.teamCount = teamCount + self.teamSorting = teamSorting + self.federalCategory = federalCategory + self.federalLevelCategory = federalLevelCategory + self.federalAgeCategory = federalAgeCategory + self.closedRegistrationDate = closedRegistrationDate + self.groupStageAdditionalQualified = groupStageAdditionalQualified + self.courtCount = courtCount + self.prioritizeClubMembers = prioritizeClubMembers + self.qualifiedPerGroupStage = qualifiedPerGroupStage + self.teamsPerGroupStage = teamsPerGroupStage + self.entryFee = entryFee + self.payment = payment + self.additionalEstimationDuration = additionalEstimationDuration + self.isDeleted = isDeleted + self.isCanceled = isCanceled + self.publishTeams = publishTeams + self.publishSummons = publishSummons + self.publishGroupStages = publishGroupStages + self.publishBrackets = publishBrackets + self.shouldVerifyGroupStage = shouldVerifyGroupStage + self.shouldVerifyBracket = shouldVerifyBracket + self.hideTeamsWeight = hideTeamsWeight + self.publishTournament = publishTournament + self.hidePointsEarned = hidePointsEarned + self.publishRankings = publishRankings + self.loserBracketMode = loserBracketMode + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _lastUpdate = "lastUpdate" + case _event = "event" + case _name = "name" + case _startDate = "startDate" + case _endDate = "endDate" + case _creationDate = "creationDate" + case _isPrivate = "isPrivate" + case _groupStageFormat = "groupStageFormat" + case _roundFormat = "roundFormat" + case _loserRoundFormat = "loserRoundFormat" + case _groupStageSortMode = "groupStageSortMode" + case _groupStageCount = "groupStageCount" + case _rankSourceDate = "rankSourceDate" + case _dayDuration = "dayDuration" + case _teamCount = "teamCount" + case _teamSorting = "teamSorting" + case _federalCategory = "federalCategory" + case _federalLevelCategory = "federalLevelCategory" + case _federalAgeCategory = "federalAgeCategory" + case _closedRegistrationDate = "closedRegistrationDate" + case _groupStageAdditionalQualified = "groupStageAdditionalQualified" + case _courtCount = "courtCount" + case _prioritizeClubMembers = "prioritizeClubMembers" + case _qualifiedPerGroupStage = "qualifiedPerGroupStage" + case _teamsPerGroupStage = "teamsPerGroupStage" + case _entryFee = "entryFee" + case _payment = "payment" + case _additionalEstimationDuration = "additionalEstimationDuration" + case _isDeleted = "isDeleted" + case _isCanceled = "isCanceled" + case _publishTeams = "publishTeams" + case _publishSummons = "publishSummons" + case _publishGroupStages = "publishGroupStages" + case _publishBrackets = "publishBrackets" + case _shouldVerifyGroupStage = "shouldVerifyGroupStage" + case _shouldVerifyBracket = "shouldVerifyBracket" + case _hideTeamsWeight = "hideTeamsWeight" + case _publishTournament = "publishTournament" + case _hidePointsEarned = "hidePointsEarned" + case _publishRankings = "publishRankings" + case _loserBracketMode = "loserBracketMode" + } + + private static func _decodePayment(container: KeyedDecodingContainer) throws -> TournamentPayment? { + let data = try container.decodeIfPresent(Data.self, forKey: ._payment) + + if let data { + do { + let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue) + let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue } + return TournamentPayment(rawValue: sequence[18]) + } catch { + Logger.error(error) + } + } + return nil + } + + private func _encodePayment(container: inout KeyedEncodingContainer) throws { + guard let payment else { + try container.encodeNil(forKey: ._payment) + return + } + + let max: Int = TournamentPayment.allCases.count + var sequence = (1...18).map { _ in Int.random(in: (0..) throws -> Bool { + let data = try container.decodeIfPresent(Data.self, forKey: ._isCanceled) + if let data { + do { + let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue) + let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue } + return Bool.decodeInt(sequence[18]) + } catch { + Logger.error(error) + } + } + return false + } + + private func _encodeIscanceled(container: inout KeyedEncodingContainer) throws { + let max: Int = 9 + var sequence = (1...18).map { _ in Int.random(in: (0...max)) } + sequence.append(self.isCanceled.encodedValue) + sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0...max ))} ) + + let stringCombo: [String] = sequence.map { $0.formatted() } + let joined: String = stringCombo.joined(separator: "") + if let data = joined.data(using: .utf8) { + let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue) + try container.encode(encryped, forKey: ._isCanceled) + } + } + + required init(from decoder: Decoder) throws { + super.init() + let container = try decoder.container(keyedBy: CodingKeys.self) + self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? nil + self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil + self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date() + self.endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? nil + self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date() + self.isPrivate = try container.decodeIfPresent(Bool.self, forKey: ._isPrivate) ?? false + self.groupStageFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageFormat) ?? nil + self.roundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._roundFormat) ?? nil + self.loserRoundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserRoundFormat) ?? nil + self.groupStageSortMode = try container.decodeIfPresent(GroupStageOrderingMode.self, forKey: ._groupStageSortMode) ?? GroupStageOrderingMode.snake + self.groupStageCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageCount) ?? 0 + self.rankSourceDate = try container.decodeIfPresent(Date.self, forKey: ._rankSourceDate) ?? nil + self.dayDuration = try container.decodeIfPresent(Int.self, forKey: ._dayDuration) ?? 0 + self.teamCount = try container.decodeIfPresent(Int.self, forKey: ._teamCount) ?? 0 + self.teamSorting = try container.decodeIfPresent(TeamSortingType.self, forKey: ._teamSorting) ?? TeamSortingType.inscriptionDate + self.federalCategory = try container.decodeIfPresent(TournamentCategory.self, forKey: ._federalCategory) ?? TournamentCategory.men + self.federalLevelCategory = try container.decodeIfPresent(TournamentLevel.self, forKey: ._federalLevelCategory) ?? TournamentLevel.unlisted + self.federalAgeCategory = try container.decodeIfPresent(FederalTournamentAge.self, forKey: ._federalAgeCategory) ?? FederalTournamentAge.unlisted + self.closedRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._closedRegistrationDate) ?? nil + self.groupStageAdditionalQualified = try container.decodeIfPresent(Int.self, forKey: ._groupStageAdditionalQualified) ?? 0 + self.courtCount = try container.decodeIfPresent(Int.self, forKey: ._courtCount) ?? 2 + self.prioritizeClubMembers = try container.decodeIfPresent(Bool.self, forKey: ._prioritizeClubMembers) ?? false + self.qualifiedPerGroupStage = try container.decodeIfPresent(Int.self, forKey: ._qualifiedPerGroupStage) ?? 0 + self.teamsPerGroupStage = try container.decodeIfPresent(Int.self, forKey: ._teamsPerGroupStage) ?? 0 + self.entryFee = try container.decodeIfPresent(Double.self, forKey: ._entryFee) ?? nil + self.payment = try Self._decodePayment(container: container) + self.additionalEstimationDuration = try container.decodeIfPresent(Int.self, forKey: ._additionalEstimationDuration) ?? 0 + self.isDeleted = try container.decodeIfPresent(Bool.self, forKey: ._isDeleted) ?? false + self.isCanceled = try Self._decodeIscanceled(container: container) + self.publishTeams = try container.decodeIfPresent(Bool.self, forKey: ._publishTeams) ?? false + self.publishSummons = try container.decodeIfPresent(Bool.self, forKey: ._publishSummons) ?? false + self.publishGroupStages = try container.decodeIfPresent(Bool.self, forKey: ._publishGroupStages) ?? false + self.publishBrackets = try container.decodeIfPresent(Bool.self, forKey: ._publishBrackets) ?? false + self.shouldVerifyGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyGroupStage) ?? false + self.shouldVerifyBracket = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyBracket) ?? false + self.hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false + self.publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false + self.hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false + self.publishRankings = try container.decodeIfPresent(Bool.self, forKey: ._publishRankings) ?? false + self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.id, forKey: ._id) + try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(self.event, forKey: ._event) + try container.encode(self.name, forKey: ._name) + try container.encode(self.startDate, forKey: ._startDate) + try container.encode(self.endDate, forKey: ._endDate) + try container.encode(self.creationDate, forKey: ._creationDate) + try container.encode(self.isPrivate, forKey: ._isPrivate) + try container.encode(self.groupStageFormat, forKey: ._groupStageFormat) + try container.encode(self.roundFormat, forKey: ._roundFormat) + try container.encode(self.loserRoundFormat, forKey: ._loserRoundFormat) + try container.encode(self.groupStageSortMode, forKey: ._groupStageSortMode) + try container.encode(self.groupStageCount, forKey: ._groupStageCount) + try container.encode(self.rankSourceDate, forKey: ._rankSourceDate) + try container.encode(self.dayDuration, forKey: ._dayDuration) + try container.encode(self.teamCount, forKey: ._teamCount) + try container.encode(self.teamSorting, forKey: ._teamSorting) + try container.encode(self.federalCategory, forKey: ._federalCategory) + try container.encode(self.federalLevelCategory, forKey: ._federalLevelCategory) + try container.encode(self.federalAgeCategory, forKey: ._federalAgeCategory) + try container.encode(self.closedRegistrationDate, forKey: ._closedRegistrationDate) + try container.encode(self.groupStageAdditionalQualified, forKey: ._groupStageAdditionalQualified) + try container.encode(self.courtCount, forKey: ._courtCount) + try container.encode(self.prioritizeClubMembers, forKey: ._prioritizeClubMembers) + try container.encode(self.qualifiedPerGroupStage, forKey: ._qualifiedPerGroupStage) + try container.encode(self.teamsPerGroupStage, forKey: ._teamsPerGroupStage) + try container.encode(self.entryFee, forKey: ._entryFee) + try _encodePayment(container: &container) + try container.encode(self.additionalEstimationDuration, forKey: ._additionalEstimationDuration) + try container.encode(self.isDeleted, forKey: ._isDeleted) + try _encodeIscanceled(container: &container) + try container.encode(self.publishTeams, forKey: ._publishTeams) + try container.encode(self.publishSummons, forKey: ._publishSummons) + try container.encode(self.publishGroupStages, forKey: ._publishGroupStages) + try container.encode(self.publishBrackets, forKey: ._publishBrackets) + try container.encode(self.shouldVerifyGroupStage, forKey: ._shouldVerifyGroupStage) + try container.encode(self.shouldVerifyBracket, forKey: ._shouldVerifyBracket) + try container.encode(self.hideTeamsWeight, forKey: ._hideTeamsWeight) + try container.encode(self.publishTournament, forKey: ._publishTournament) + try container.encode(self.hidePointsEarned, forKey: ._hidePointsEarned) + try container.encode(self.publishRankings, forKey: ._publishRankings) + try container.encode(self.loserBracketMode, forKey: ._loserBracketMode) + } + + func eventValue() -> Event? { + guard let event = self.event else { return nil } + return Store.main.findById(event) + } + + func copy(from other: any Storable) { + guard let tournament = other as? BaseTournament else { return } + self.id = tournament.id + self.lastUpdate = tournament.lastUpdate + self.event = tournament.event + self.name = tournament.name + self.startDate = tournament.startDate + self.endDate = tournament.endDate + self.creationDate = tournament.creationDate + self.isPrivate = tournament.isPrivate + self.groupStageFormat = tournament.groupStageFormat + self.roundFormat = tournament.roundFormat + self.loserRoundFormat = tournament.loserRoundFormat + self.groupStageSortMode = tournament.groupStageSortMode + self.groupStageCount = tournament.groupStageCount + self.rankSourceDate = tournament.rankSourceDate + self.dayDuration = tournament.dayDuration + self.teamCount = tournament.teamCount + self.teamSorting = tournament.teamSorting + self.federalCategory = tournament.federalCategory + self.federalLevelCategory = tournament.federalLevelCategory + self.federalAgeCategory = tournament.federalAgeCategory + self.closedRegistrationDate = tournament.closedRegistrationDate + self.groupStageAdditionalQualified = tournament.groupStageAdditionalQualified + self.courtCount = tournament.courtCount + self.prioritizeClubMembers = tournament.prioritizeClubMembers + self.qualifiedPerGroupStage = tournament.qualifiedPerGroupStage + self.teamsPerGroupStage = tournament.teamsPerGroupStage + self.entryFee = tournament.entryFee + self.payment = tournament.payment + self.additionalEstimationDuration = tournament.additionalEstimationDuration + self.isDeleted = tournament.isDeleted + self.isCanceled = tournament.isCanceled + self.publishTeams = tournament.publishTeams + self.publishSummons = tournament.publishSummons + self.publishGroupStages = tournament.publishGroupStages + self.publishBrackets = tournament.publishBrackets + self.shouldVerifyGroupStage = tournament.shouldVerifyGroupStage + self.shouldVerifyBracket = tournament.shouldVerifyBracket + self.hideTeamsWeight = tournament.hideTeamsWeight + self.publishTournament = tournament.publishTournament + self.hidePointsEarned = tournament.hidePointsEarned + self.publishRankings = tournament.publishRankings + self.loserBracketMode = tournament.loserBracketMode + } +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/Club.json b/PadelClub/Data/Gen/Club.json new file mode 100644 index 0000000..683e716 --- /dev/null +++ b/PadelClub/Data/Gen/Club.json @@ -0,0 +1,90 @@ +{ + "models": [ + { + "name": "Club", + "synchronizable": true, + "observable": true, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "creator", + "type": "String", + "optional": true, + "defaultValue": "nil", + "foreignKey": "CustomUser" + }, + { + "name": "name", + "type": "String", + "defaultValue": "\"\"" + }, + { + "name": "acronym", + "type": "String", + "defaultValue": "\"\"" + }, + { + "name": "phone", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "code", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "address", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "city", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "zipCode", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "latitude", + "type": "Double", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "longitude", + "type": "Double", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "courtCount", + "type": "Int", + "defaultValue": "2" + }, + { + "name": "broadcastCode", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/Court.json b/PadelClub/Data/Gen/Court.json new file mode 100644 index 0000000..f2037bb --- /dev/null +++ b/PadelClub/Data/Gen/Court.json @@ -0,0 +1,47 @@ +{ + "models": [ + { + "name": "Court", + "synchronizable": true, + "observable": true, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "index", + "type": "Int" + }, + { + "name": "club", + "type": "String", + "foreignKey": "Club" + }, + { + "name": "name", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "exitAllowed", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "indoor", + "type": "Bool", + "defaultValue": "false" + } + ], + "tokenExemptedMethods": [], + "filterByStoreIdentifier": false + } + ] +} diff --git a/PadelClub/Data/Gen/CustomUser.json b/PadelClub/Data/Gen/CustomUser.json new file mode 100644 index 0000000..b7e3595 --- /dev/null +++ b/PadelClub/Data/Gen/CustomUser.json @@ -0,0 +1,136 @@ +{ + "models": [ + { + "name": "CustomUser", + "resource_name": "users", + "synchronizable": true, + "observable": true, + "tokenExemptedMethods": ["post"], + "filterByStoreIdentifier": false, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "username", + "type": "String" + }, + { + "name": "email", + "type": "String" + }, + { + "name": "clubs", + "type": "[String]", + "defaultValue": "[]" + }, + { + "name": "umpireCode", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "licenceId", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "firstName", + "type": "String" + }, + { + "name": "lastName", + "type": "String" + }, + { + "name": "phone", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "country", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "summonsMessageBody", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "summonsMessageSignature", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "summonsAvailablePaymentMethods", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "summonsDisplayFormat", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "summonsDisplayEntryFee", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "summonsUseFullCustomMessage", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "matchFormatsDefaultDuration", + "type": "[MatchFormat: Int]", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "bracketMatchFormatPreference", + "type": "MatchFormat", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "groupStageMatchFormatPreference", + "type": "MatchFormat", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "loserBracketMatchFormatPreference", + "type": "MatchFormat", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "loserBracketMode", + "type": "LoserBracketMode", + "defaultValue": ".automatic" + }, + { + "name": "deviceId", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/DateInterval.json b/PadelClub/Data/Gen/DateInterval.json new file mode 100644 index 0000000..e046b16 --- /dev/null +++ b/PadelClub/Data/Gen/DateInterval.json @@ -0,0 +1,38 @@ +{ + "models": [ + { + "name": "DateInterval", + "synchronizable": true, + "observable": true, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "event", + "type": "String" + }, + { + "name": "courtIndex", + "type": "Int" + }, + { + "name": "startDate", + "type": "Date" + }, + { + "name": "endDate", + "type": "Date" + } + ], + "tokenExemptedMethods": [], + "filterByStoreIdentifier": false + } + ] +} diff --git a/PadelClub/Data/Gen/Event.json b/PadelClub/Data/Gen/Event.json new file mode 100644 index 0000000..b8f1bec --- /dev/null +++ b/PadelClub/Data/Gen/Event.json @@ -0,0 +1,53 @@ +{ + "models": [ + { + "name": "Event", + "synchronizable": true, + "observable": true, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "creator", + "type": "String", + "optional": true, + "defaultValue": "nil", + "foreignKey": "CustomUser" + }, + { + "name": "club", + "type": "String", + "optional": true, + "defaultValue": "nil", + "foreignKey": "Club" + }, + { + "name": "creationDate", + "type": "Date", + "defaultValue": "Date()" + }, + { + "name": "name", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "tenupId", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ], + "tokenExemptedMethods": [], + "filterByStoreIdentifier": false + } + ] +} diff --git a/PadelClub/Data/Gen/GroupStage.json b/PadelClub/Data/Gen/GroupStage.json new file mode 100644 index 0000000..eb0627e --- /dev/null +++ b/PadelClub/Data/Gen/GroupStage.json @@ -0,0 +1,64 @@ +{ + "models": [ + { + "name": "GroupStage", + "synchronizable": true, + "observable": true, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "tournament", + "type": "String", + "foreignKey": "Tournament" + }, + { + "name": "index", + "type": "Int" + }, + { + "name": "size", + "type": "Int" + }, + { + "name": "format", + "type": "MatchFormat", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "startDate", + "type": "Date", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "name", + "type": "String", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "step", + "type": "Int", + "defaultValue": "0" + }, + { + "name": "storeId", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ], + "tokenExemptedMethods": [], + "filterByStoreIdentifier": true + } + ] +} diff --git a/PadelClub/Data/Gen/Match.json b/PadelClub/Data/Gen/Match.json new file mode 100644 index 0000000..ef04e08 --- /dev/null +++ b/PadelClub/Data/Gen/Match.json @@ -0,0 +1,93 @@ +{ + "models": [ + { + "name": "Match", + "synchronizable": true, + "observable": true, + "tokenExemptedMethods": [], + "filterByStoreIdentifier": true, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "round", + "type": "String", + "optional": true, + "foreignKey": "Round*" + }, + { + "name": "groupStage", + "type": "String", + "optional": true, + "foreignKey": "GroupStage*" + }, + { + "name": "startDate", + "type": "Date", + "optional": true + }, + { + "name": "endDate", + "type": "Date", + "optional": true + }, + { + "name": "index", + "type": "Int" + }, + { + "name": "format", + "type": "MatchFormat", + "optional": true + }, + { + "name": "servingTeamId", + "type": "String", + "optional": true + }, + { + "name": "winningTeamId", + "type": "String", + "optional": true + }, + { + "name": "losingTeamId", + "type": "String", + "optional": true + }, + { + "name": "name", + "type": "String", + "optional": true + }, + { + "name": "disabled", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "courtIndex", + "type": "Int", + "optional": true + }, + { + "name": "confirmed", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "storeId", + "type": "String", + "optional": true + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/MatchScheduler.json b/PadelClub/Data/Gen/MatchScheduler.json new file mode 100644 index 0000000..500bc3d --- /dev/null +++ b/PadelClub/Data/Gen/MatchScheduler.json @@ -0,0 +1,75 @@ +{ + "models": [ + { + "name": "MatchScheduler", + "resource_name": "match-scheduler", + "synchronizable": false, + "observable": true, + "tokenExemptedMethods": [], + "filterByStoreIdentifier": false, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "tournament", + "type": "String", + "foreignKey": "Tournament" + }, + { + "name": "timeDifferenceLimit", + "type": "Int" + }, + { + "name": "loserBracketRotationDifference", + "type": "Int" + }, + { + "name": "upperBracketRotationDifference", + "type": "Int" + }, + { + "name": "accountUpperBracketBreakTime", + "type": "Bool" + }, + { + "name": "accountLoserBracketBreakTime", + "type": "Bool" + }, + { + "name": "randomizeCourts", + "type": "Bool" + }, + { + "name": "rotationDifferenceIsImportant", + "type": "Bool" + }, + { + "name": "shouldHandleUpperRoundSlice", + "type": "Bool" + }, + { + "name": "shouldEndRoundBeforeStartingNext", + "type": "Bool" + }, + { + "name": "groupStageChunkCount", + "type": "Int", + "optional": true + }, + { + "name": "overrideCourtsUnavailability", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "shouldTryToFillUpCourtsAvailable", + "type": "Bool", + "defaultValue": "false" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/MonthData.json b/PadelClub/Data/Gen/MonthData.json new file mode 100644 index 0000000..95c4d9b --- /dev/null +++ b/PadelClub/Data/Gen/MonthData.json @@ -0,0 +1,72 @@ +{ + "models": [ + { + "name": "MonthData", + "resource_name": "month-data", + "synchronizable": false, + "observable": true, + "tokenExemptedMethods": [], + "filterByStoreIdentifier": false, + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "monthKey", + "type": "String" + }, + { + "name": "creationDate", + "type": "Date" + }, + { + "name": "maleUnrankedValue", + "type": "Int", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "femaleUnrankedValue", + "type": "Int", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "maleCount", + "type": "Int", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "femaleCount", + "type": "Int", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "anonymousCount", + "type": "Int", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "incompleteMode", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "dataModelIdentifier", + "type": "String", + "optional": true + }, + { + "name": "fileModelIdentifier", + "type": "String", + "optional": true + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/PlayerRegistration.json b/PadelClub/Data/Gen/PlayerRegistration.json new file mode 100644 index 0000000..c457557 --- /dev/null +++ b/PadelClub/Data/Gen/PlayerRegistration.json @@ -0,0 +1,118 @@ +{ + "models": [ + { + "name": "PlayerRegistration", + "synchronizable": true, + "sideStorable": true, + "filterByStoreIdentifier": true, + "observable": true, + "relationshipNames": ["teamRegistration"], + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "teamRegistration", + "type": "String", + "optional": true, + "foreignKey": "TeamRegistration" + }, + { + "name": "firstName", + "type": "String" + }, + { + "name": "lastName", + "type": "String" + }, + { + "name": "licenceId", + "type": "String", + "optional": true + }, + { + "name": "rank", + "type": "Int", + "optional": true + }, + { + "name": "paymentType", + "type": "PlayerPaymentType", + "optional": true + }, + { + "name": "sex", + "type": "PlayerSexType", + "optional": true + }, + { + "name": "tournamentPlayed", + "type": "Int", + "optional": true + }, + { + "name": "points", + "type": "Double", + "optional": true + }, + { + "name": "clubName", + "type": "String", + "optional": true + }, + { + "name": "ligueName", + "type": "String", + "optional": true + }, + { + "name": "assimilation", + "type": "String", + "optional": true + }, + { + "name": "phoneNumber", + "type": "String", + "optional": true + }, + { + "name": "email", + "type": "String", + "optional": true + }, + { + "name": "birthdate", + "type": "String", + "optional": true + }, + { + "name": "computedRank", + "type": "Int", + "defaultValue": "0" + }, + { + "name": "source", + "type": "PlayerDataSource", + "optional": true + }, + { + "name": "hasArrived", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "storeId", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/Purchase.json b/PadelClub/Data/Gen/Purchase.json new file mode 100644 index 0000000..660d9a2 --- /dev/null +++ b/PadelClub/Data/Gen/Purchase.json @@ -0,0 +1,56 @@ +{ + "models": [ + { + "name": "Purchase", + "synchronizable": true, + "properties": [ + { + "name": "id", + "type": "UInt64", + "defaultValue": "0" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "user", + "type": "String", + "defaultValue": "\"\"", + "encryption": "standard", + "foreignKey": "CustomUser" + }, + { + "name": "purchaseDate", + "type": "Date" + }, + { + "name": "productId", + "type": "String", + "defaultValue": "\"\"" + }, + { + "name": "quantity", + "type": "Int", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "revocationDate", + "type": "Date", + "optional": true, + "defaultValue": "nil" + }, + { + "name": "expirationDate", + "type": "Date", + "optional": true, + "defaultValue": "nil" + } + ], + "tokenExemptedMethods": [], + "filterByStoreIdentifier": false, + "relationshipNames": [] + } + ] +} diff --git a/PadelClub/Data/Gen/Round.json b/PadelClub/Data/Gen/Round.json new file mode 100644 index 0000000..6004232 --- /dev/null +++ b/PadelClub/Data/Gen/Round.json @@ -0,0 +1,64 @@ +{ + "models": [ + { + "name": "Round", + "synchronizable": true, + "sideStorable": true, + "filterByStoreIdentifier": true, + "observable": true, + "relationshipNames": [], + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "tournament", + "type": "String", + "foreignKey": "Tournament" + }, + { + "name": "index", + "type": "Int" + }, + { + "name": "parent", + "type": "String", + "optional": true + }, + { + "name": "format", + "type": "MatchFormat", + "optional": true, + "private": true + }, + { + "name": "startDate", + "type": "Date", + "optional": true + }, + { + "name": "groupStageLoserBracket", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "loserBracketMode", + "type": "LoserBracketMode", + "defaultValue": ".automatic" + }, + { + "name": "storeId", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/TeamRegistration.json b/PadelClub/Data/Gen/TeamRegistration.json new file mode 100644 index 0000000..88ac151 --- /dev/null +++ b/PadelClub/Data/Gen/TeamRegistration.json @@ -0,0 +1,129 @@ +{ + "models": [ + { + "name": "TeamRegistration", + "synchronizable": true, + "sideStorable": true, + "filterByStoreIdentifier": true, + "observable": true, + "relationshipNames": [], + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "tournament", + "type": "String" + }, + { + "name": "groupStage", + "type": "String", + "optional": true, + "foreignKey": "GroupStage*" + }, + { + "name": "registrationDate", + "type": "Date", + "optional": true + }, + { + "name": "callDate", + "type": "Date", + "optional": true + }, + { + "name": "bracketPosition", + "type": "Int", + "optional": true + }, + { + "name": "groupStagePosition", + "type": "Int", + "optional": true + }, + { + "name": "comment", + "type": "String", + "optional": true + }, + { + "name": "source", + "type": "String", + "optional": true + }, + { + "name": "sourceValue", + "type": "String", + "optional": true + }, + { + "name": "logo", + "type": "String", + "optional": true + }, + { + "name": "name", + "type": "String", + "optional": true + }, + { + "name": "walkOut", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "wildCardBracket", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "wildCardGroupStage", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "weight", + "type": "Int", + "defaultValue": "0" + }, + { + "name": "lockedWeight", + "type": "Int", + "optional": true + }, + { + "name": "confirmationDate", + "type": "Date", + "optional": true + }, + { + "name": "qualified", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "finalRanking", + "type": "Int", + "optional": true + }, + { + "name": "pointsEarned", + "type": "Int", + "optional": true + }, + { + "name": "storeId", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/TeamScore.json b/PadelClub/Data/Gen/TeamScore.json new file mode 100644 index 0000000..7475164 --- /dev/null +++ b/PadelClub/Data/Gen/TeamScore.json @@ -0,0 +1,55 @@ +{ + "models": [ + { + "name": "TeamScore", + "synchronizable": true, + "sideStorable": true, + "filterByStoreIdentifier": true, + "observable": true, + "relationshipNames": ["match"], + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "match", + "type": "String", + "foreignKey": "Match*" + }, + { + "name": "teamRegistration", + "type": "String", + "optional": true, + "foreignKey": "TeamRegistration*" + }, + { + "name": "score", + "type": "String", + "optional": true + }, + { + "name": "walkOut", + "type": "Int", + "optional": true + }, + { + "name": "luckyLoser", + "type": "Int", + "optional": true + }, + { + "name": "storeId", + "type": "String", + "optional": true, + "defaultValue": "nil" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/Tournament.json b/PadelClub/Data/Gen/Tournament.json new file mode 100644 index 0000000..e5368f9 --- /dev/null +++ b/PadelClub/Data/Gen/Tournament.json @@ -0,0 +1,221 @@ +{ + "models": [ + { + "name": "Tournament", + "synchronizable": true, + "copyable": true, + "filterByStoreIdentifier": false, + "observable": true, + "relationshipNames": [], + "properties": [ + { + "name": "id", + "type": "String", + "defaultValue": "Store.randomId()" + }, + { + "name": "lastUpdate", + "type": "Date" + }, + { + "name": "event", + "type": "String", + "optional": true, + "foreignKey": "Event" + }, + { + "name": "name", + "type": "String", + "optional": true + }, + { + "name": "startDate", + "type": "Date" + }, + { + "name": "endDate", + "type": "Date", + "optional": true + }, + { + "name": "creationDate", + "type": "Date", + "private": true + }, + { + "name": "isPrivate", + "type": "Bool" + }, + { + "name": "groupStageFormat", + "type": "MatchFormat", + "optional": true, + "private": true + }, + { + "name": "roundFormat", + "type": "MatchFormat", + "optional": true, + "private": true + }, + { + "name": "loserRoundFormat", + "type": "MatchFormat", + "optional": true, + "private": true + }, + { + "name": "groupStageSortMode", + "type": "GroupStageOrderingMode", + "defaultValue": "GroupStageOrderingMode.snake" + }, + { + "name": "groupStageCount", + "type": "Int" + }, + { + "name": "rankSourceDate", + "type": "Date", + "optional": true + }, + { + "name": "dayDuration", + "type": "Int" + }, + { + "name": "teamCount", + "type": "Int" + }, + { + "name": "teamSorting", + "type": "TeamSortingType", + "defaultValue": "TeamSortingType.inscriptionDate" + }, + { + "name": "federalCategory", + "type": "TournamentCategory", + "defaultValue": "TournamentCategory.men" + }, + { + "name": "federalLevelCategory", + "type": "TournamentLevel", + "defaultValue": "TournamentLevel.unlisted" + }, + { + "name": "federalAgeCategory", + "type": "FederalTournamentAge", + "defaultValue": "FederalTournamentAge.unlisted" + }, + { + "name": "closedRegistrationDate", + "type": "Date", + "optional": true + }, + { + "name": "groupStageAdditionalQualified", + "type": "Int" + }, + { + "name": "courtCount", + "type": "Int", + "defaultValue": "2" + }, + { + "name": "prioritizeClubMembers", + "type": "Bool" + }, + { + "name": "qualifiedPerGroupStage", + "type": "Int" + }, + { + "name": "teamsPerGroupStage", + "type": "Int" + }, + { + "name": "entryFee", + "type": "Double", + "optional": true + }, + { + "name": "payment", + "type": "TournamentPayment", + "optional": true, + "defaultValue": "nil", + "encryption": "tournament_payment" + }, + { + "name": "additionalEstimationDuration", + "type": "Int", + "defaultValue": "0" + }, + { + "name": "isDeleted", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "isCanceled", + "type": "Bool", + "defaultValue": "false", + "encryption": "tournament_iscanceled" + }, + { + "name": "publishTeams", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "publishSummons", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "publishGroupStages", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "publishBrackets", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "shouldVerifyGroupStage", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "shouldVerifyBracket", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "hideTeamsWeight", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "publishTournament", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "hidePointsEarned", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "publishRankings", + "type": "Bool", + "defaultValue": "false" + }, + { + "name": "loserBracketMode", + "type": "LoserBracketMode", + "defaultValue": ".automatic" + } + ] + } + ] +} diff --git a/PadelClub/Data/Gen/generator.py b/PadelClub/Data/Gen/generator.py new file mode 100644 index 0000000..68cb248 --- /dev/null +++ b/PadelClub/Data/Gen/generator.py @@ -0,0 +1,504 @@ +import json +import re +import os +from pathlib import Path +from typing import Dict, List, Any +import argparse +import sys +import logging +from datetime import datetime +import inflect + +class SwiftModelGenerator: + def __init__(self, json_data: Dict[str, Any]): + self.data = json_data + self.pluralizer = inflect.engine() + + def generate_model(self, model_data: Dict[str, Any]) -> str: + model_name = model_data["name"] + is_sync = model_data.get("synchronizable", False) + is_observable = model_data.get("observable", False) + properties = model_data["properties"] + + # Get protocol specific configurations + resource = self.make_resource_name(model_name) + resource_name = model_data.get("resource_name", resource) + token_exempted = model_data.get("tokenExemptedMethods", []) + filter_by_store = model_data.get("filterByStoreIdentifier", False) + + lines = ["// Generated by SwiftModelGenerator", "// Do not modify this file manually", ""] + + # Import statement + lines.append("import Foundation") + lines.append("import LeStorage") + if is_observable: + lines.append("import SwiftUI") + lines.append("") + + # Class declaration + if is_observable: + lines.append("@Observable") + protocol = "SyncedStorable" if is_sync else "Storable" + lines.append(f"class Base{model_name}: ModelObject, {protocol}, Codable {{") + lines.append("") + + # Add SyncedStorable protocol requirements + lines.extend(self._generate_protocol_requirements(resource_name, token_exempted, filter_by_store)) + lines.append("") + + # Properties + for prop in properties: + swift_type = prop["type"] + if prop.get("optional", False): + swift_type += "?" + default_value = prop.get("defaultValue", "nil" if prop.get("optional", False) else self._get_default_value(swift_type)) + lines.append(f" var {prop['name']}: {swift_type} = {default_value}") + + lines.append("") + + # Add constructor + lines.extend(self._generate_constructor(model_name, properties)) + lines.append("") + + # CodingKeys + lines.extend(self._generate_coding_keys(properties, is_observable)) + lines.append("") + + # Encryption methods + encrypted_props = [p for p in properties if "encryption" in p] + if encrypted_props: + lines.extend(self._generate_encryption_methods(properties)) + lines.append("") + + # Codable implementation + lines.extend(self._generate_decoder(model_name, properties, is_observable)) + lines.append("") + lines.extend(self._generate_encoder(properties, is_observable)) + lines.append("") + + # Foreign Key convenience + foreign_key_methods = self._generate_foreign_key_methods(properties) + if foreign_key_methods: + lines.extend(foreign_key_methods) + # lines.append("") + + # Copy method + lines.extend(self._generate_copy_method(model_name, properties)) + + lines.append("}") + return "\n".join(lines) + + def _generate_constructor(self, model_name: str, properties: List[Dict[str, Any]]) -> List[str]: + """Generate a constructor with all properties as parameters with default values.""" + lines = [" init("] + + # Generate parameter list + params = [] + for prop in properties: + name = prop['name'] + type_name = prop['type'] + is_optional = prop.get("optional", False) + + # Always include a default value + default_value = prop.get("defaultValue") + if default_value is None: + if is_optional: + default_value = "nil" + else: + default_value = self._get_default_value(type_name) + + # Format the parameter with its type and default value + param = f" {name}: {type_name}" + if is_optional: + param += "?" + param += f" = {default_value}" + params.append(param) + + # Join parameters with commas + lines.extend([f"{param}," for param in params[:-1]]) + lines.append(f"{params[-1]}") # Last parameter without comma + + # Constructor body + lines.extend([ + " ) {", + " super.init()", + ]) + + # Property assignments + for prop in properties: + name = prop['name'] + lines.append(f" self.{name} = {name}") + + lines.append(" }") + return lines + + def _generate_foreign_key_methods(self, properties: List[Dict[str, Any]]) -> List[str]: + lines = [] + for prop in properties: + if "foreignKey" in prop: + foreign_key = prop["foreignKey"] + prop_name = prop["name"] + method_name = f"{prop_name}Value" + is_optional = prop.get("optional", False) + + lines.extend([f" func {method_name}() -> {foreign_key.rstrip('*')}? {{"]) + + if is_optional: + lines.extend([ + f" guard let {prop_name} = self.{prop_name} else {{ return nil }}" + ]) + + if foreign_key.endswith("*"): + foreign_key = foreign_key[:-1] # Remove the asterisk + lines.extend([ + f" return self.store?.findById({prop_name})" + ]) + else: + lines.extend([ + f" return Store.main.findById({prop_name})" + ]) + + lines.extend([" }", ""]) # Close the method and add a blank line + return lines + + def _generate_coding_keys(self, properties: List[Dict[str, Any]], is_observable: bool) -> List[str]: + lines = [" enum CodingKeys: String, CodingKey {"] + for prop in properties: + name = prop['name'] + # Add underscore prefix to case name if observable, but keep the string value without underscore + case_name = f"_{name}" if is_observable else name + lines.append(f" case {case_name} = \"{name}\"") + lines.append(" }") + return lines + + def _generate_encryption_methods(self, properties: List[Dict[str, Any]]) -> List[str]: + lines = [] + for prop in properties: + if "encryption" in prop: + name = prop['name'] + enc_type = prop['encryption'] + if enc_type == "tournament_payment": + lines.extend([ + f" private static func _decode{name.capitalize()}(container: KeyedDecodingContainer) throws -> TournamentPayment? {{", + f" let data = try container.decodeIfPresent(Data.self, forKey: ._{name})", + " ", + " if let data {", + " do {", + " let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue)", + " let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue }", + " return TournamentPayment(rawValue: sequence[18])", + " } catch {", + " Logger.error(error)", + " }", + " }", + " return nil", + " }", + "", + f" private func _encode{name.capitalize()}(container: inout KeyedEncodingContainer) throws {{", + f" guard let {name} else {{", + f" try container.encodeNil(forKey: ._{name})", + " return", + " }", + " ", + " let max: Int = TournamentPayment.allCases.count", + " var sequence = (1...18).map { _ in Int.random(in: (0..) throws -> Bool {{", + f" let data = try container.decodeIfPresent(Data.self, forKey: ._{name})", + " if let data {", + " do {", + " let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue)", + " let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue }", + " return Bool.decodeInt(sequence[18])", + " } catch {", + " Logger.error(error)", + " }", + " }", + " return false", + " }", + "", + f" private func _encode{name.capitalize()}(container: inout KeyedEncodingContainer) throws {{", + " let max: Int = 9", + " var sequence = (1...18).map { _ in Int.random(in: (0...max)) }", + f" sequence.append(self.{name}.encodedValue)", + " sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0...max ))} )", + " ", + " let stringCombo: [String] = sequence.map { $0.formatted() }", + " let joined: String = stringCombo.joined(separator: \"\")", + " if let data = joined.data(using: .utf8) {", + " let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue)", + f" try container.encode(encryped, forKey: ._{name})", + " }", + " }" + ]) + return lines + + def _generate_decoder(self, model_name: str, properties: List[Dict[str, Any]], is_observable: bool) -> List[str]: + lines = [" required init(from decoder: Decoder) throws {", + " super.init()", + " let container = try decoder.container(keyedBy: CodingKeys.self)"] + + for prop in properties: + name = prop['name'] + type_name = prop['type'] + is_optional = prop.get("optional", False) + default_value = prop.get("defaultValue", "nil" if is_optional else self._get_default_value(type_name)) + + # Use the correct case reference based on observable flag + case_ref = f"_{name}" if is_observable else name + + if "encryption" in prop: + enc_type = prop['encryption'] + if enc_type == "standard": + if is_optional: + lines.append(f" self.{name} = try container.decodeEncryptedIfPresent(key: .{case_ref})") + else: + lines.append(f" self.{name} = try container.decodeEncrypted(key: .{case_ref})") + elif enc_type in ["tournament_payment", "tournament_iscanceled"]: + lines.append(f" self.{name} = try Self._decode{name.capitalize()}(container: container)") + else: + lines.append(f" self.{name} = try container.decodeIfPresent({type_name}.self, forKey: .{case_ref}) ?? {default_value}") + + lines.append(" }") + return lines + + def _generate_encoder(self, properties: List[Dict[str, Any]], is_observable: bool) -> List[str]: + lines = [" func encode(to encoder: Encoder) throws {", + " var container = encoder.container(keyedBy: CodingKeys.self)"] + + for prop in properties: + name = prop['name'] + is_optional = prop.get('optional', False) + + # Use the correct case reference based on observable flag + case_ref = f"_{name}" if is_observable else name + + if "encryption" in prop: + enc_type = prop['encryption'] + if enc_type == "standard": + if is_optional: + lines.append(f" try container.encodeAndEncryptIfPresent(self.{name}?.data(using: .utf8), forKey: .{case_ref})") + else: + lines.append(f" try container.encodeAndEncryptIfPresent(self.{name}.data(using: .utf8), forKey: .{case_ref})") + elif enc_type in ["tournament_payment", "tournament_iscanceled"]: + lines.append(f" try _encode{name.capitalize()}(container: &container)") + else: + lines.append(f" try container.encode(self.{name}, forKey: .{case_ref})") + + lines.append(" }") + return lines + + def _generate_copy_method(self, model_name: str, properties: List[Dict[str, Any]]) -> List[str]: + + model_variable = model_name.lower() + lines = [f" func copy(from other: any Storable) {{"] + lines.append(f" guard let {model_variable} = other as? Base{model_name} else {{ return }}") + + for prop in properties: + name = prop['name'] + lines.append(f" self.{name} = {model_variable}.{name}") + + lines.append(" }") + return lines + + def _generate_protocol_requirements(self, resource_name: str, token_exempted: List[str], filter_by_store: bool) -> List[str]: + """Generate the static functions required by SyncedStorable protocol.""" + # Convert HTTP methods to proper format + formatted_methods = [f".{method.lower()}" for method in token_exempted] + methods_str = ", ".join(formatted_methods) if formatted_methods else "" + + return [ + f" static func resourceName() -> String {{ return \"{resource_name}\" }}", + f" static func tokenExemptedMethods() -> [HTTPMethod] {{ return [{methods_str}] }}", + f" static func filterByStoreIdentifier() -> Bool {{ return {str(filter_by_store).lower()} }}" + ] + + + def _get_default_value(self, type_name: str) -> str: + """Get default value for non-optional types""" + if "String" in type_name: + return "\"\"" + elif "Int" in type_name: + return "0" + elif "Double" in type_name: + return "0.0" + elif "Bool" in type_name: + return "false" + elif "Date" in type_name: + return "Date()" + else: + return "nil" # For any other type + + def get_output_filename(self, model_name: str) -> str: + """Generate the output filename for a model.""" + return f"Base{model_name}.swift" + + def make_resource_name(self, text): + p = inflect.engine() + # Split camelCase into words + words = re.findall('[A-Z][^A-Z]*', text) + + if not words: + words = [text] + + words = [word.lower() for word in words] + words[-1] = p.plural(words[-1]) + return '-'.join(words) + +def process_directory(input_dir: str, output_dir: str, logger: logging.Logger, dry_run: bool = False) -> int: + """Process all JSON files in the input directory and generate Swift models.""" + try: + input_path = validate_directory(input_dir) + if not dry_run: + output_path = validate_directory(output_dir, create=True) + + json_files = list(input_path.glob("*.json")) + if not json_files: + logger.warning(f"No JSON files found in '{input_dir}'") + return 0 + + logger.info(f"Found {len(json_files)} JSON files to process") + successful_files = 0 + + for json_file in json_files: + try: + with open(json_file, 'r') as f: + json_data = json.load(f) + + generator = SwiftModelGenerator(json_data) + + # Generate each model in the JSON file + for model in json_data["models"]: + model_name = model["name"] + swift_code = generator.generate_model(model) + + if dry_run: + logger.info(f"Would generate Base{model_name}.swift") + continue + + # Write to output file with Base prefix + output_file = output_path / generator.get_output_filename(model_name) + with open(output_file, 'w') as f: + f.write(swift_code) + + logger.info(f"Generated Base{model_name}.swift") + + successful_files += 1 + + except json.JSONDecodeError as e: + logger.error(f"Error parsing {json_file.name}: {e}") + except KeyError as e: + logger.error(f"Missing required key in {json_file.name}: {e}") + except Exception as e: + logger.error(f"Error processing {json_file.name}: {e}") + + return successful_files + + except Exception as e: + logger.error(f"Fatal error: {e}") + return 0 + + +def setup_logging(verbose: bool) -> logging.Logger: + """Configure logging based on verbosity level.""" + logger = logging.getLogger('SwiftModelGenerator') + handler = logging.StreamHandler() + + if verbose: + logger.setLevel(logging.DEBUG) + handler.setFormatter(logging.Formatter( + '%(asctime)s - %(levelname)s - %(message)s' + )) + else: + logger.setLevel(logging.INFO) + handler.setFormatter(logging.Formatter('%(message)s')) + + logger.addHandler(handler) + return logger + +def validate_directory(path: str, create: bool = False) -> Path: + """Validate and optionally create a directory.""" + dir_path = Path(path) + if dir_path.exists(): + if not dir_path.is_dir(): + raise ValueError(f"'{path}' exists but is not a directory") + elif create: + dir_path.mkdir(parents=True) + else: + raise ValueError(f"Directory '{path}' does not exist") + return dir_path + +def main(): + parser = argparse.ArgumentParser( + description="Generate Swift model classes from JSON definitions", + epilog="Example: %(prog)s -i ./model_definitions -o ../MyProject/Models" + ) + + parser.add_argument( + "-i", "--input-dir", + required=True, + help="Directory containing JSON model definitions" + ) + + parser.add_argument( + "-o", "--output-dir", + required=True, + help="Directory where Swift files will be generated" + ) + + parser.add_argument( + "-v", "--verbose", + action="store_true", + help="Enable verbose output" + ) + + parser.add_argument( + "--dry-run", + action="store_true", + help="Show what would be generated without actually creating files" + ) + + parser.add_argument( + "--version", + action="version", + version="%(prog)s 1.0.0" + ) + + args = parser.parse_args() + + # Setup logging + logger = setup_logging(args.verbose) + + # Process the directories + start_time = datetime.now() + logger.info("Starting model generation...") + + successful_files = process_directory( + args.input_dir, + args.output_dir, + logger, + args.dry_run + ) + + # Report results + duration = datetime.now() - start_time + logger.info(f"\nGeneration completed in {duration.total_seconds():.2f} seconds") + logger.info(f"Successfully processed {successful_files} files") + + # Return appropriate exit code + sys.exit(0 if successful_files > 0 else 1) + +if __name__ == "__main__": + main() diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index 740a730..e544016 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -11,22 +11,22 @@ import Algorithms import SwiftUI @Observable -final class GroupStage: ModelObject, SyncedStorable, SideStorable { +final class GroupStage: BaseGroupStage, SideStorable { - static func resourceName() -> String { "group-stages" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } - static var relationshipNames: [String] = [] - - var id: String = Store.randomId() - var lastUpdate: Date - var tournament: String - var index: Int - var size: Int - private var format: MatchFormat? - var startDate: Date? - var name: String? - var step: Int = 0 +// static func resourceName() -> String { "group-stages" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return true } +// static var relationshipNames: [String] = [] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var tournament: String +// var index: Int +// var size: Int +// private(set) var format: MatchFormat? +// var startDate: Date? +// var name: String? +// var step: Int = 0 var matchFormat: MatchFormat { get { @@ -37,17 +37,24 @@ final class GroupStage: ModelObject, SyncedStorable, SideStorable { } } - var storeId: String? = nil +// var storeId: String? = nil internal init(tournament: String, index: Int, size: Int, matchFormat: MatchFormat? = nil, startDate: Date? = nil, name: String? = nil, step: Int = 0) { - self.lastUpdate = Date() - self.tournament = tournament - self.index = index - self.size = size - self.format = matchFormat - self.startDate = startDate - self.name = name - self.step = step + + super.init(tournament: tournament, index: index, size: size, format: matchFormat, startDate: startDate, name: name, step: step) + +// self.lastUpdate = Date() +// self.tournament = tournament +// self.index = index +// self.size = size +// self.format = matchFormat +// self.startDate = startDate +// self.name = name +// self.step = step + } + + required init(from decoder: any Decoder) throws { + try super.init(from: decoder) } var tournamentStore: TournamentStore { @@ -114,7 +121,7 @@ final class GroupStage: ModelObject, SyncedStorable, SideStorable { fileprivate func _createMatch(index: Int) -> Match { let match: Match = Match(groupStage: self.id, index: index, - matchFormat: self.matchFormat, + format: self.matchFormat, name: self.localizedMatchUpLabel(for: index)) match.store = self.store return match @@ -500,34 +507,35 @@ final class GroupStage: ModelObject, SyncedStorable, SideStorable { self.tournamentStore.matches.deleteDependencies(matches) } - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - id = try container.decode(String.self, forKey: ._id) - lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate) - tournament = try container.decode(String.self, forKey: ._tournament) - index = try container.decode(Int.self, forKey: ._index) - size = try container.decode(Int.self, forKey: ._size) - format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) - startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) - name = try container.decodeIfPresent(String.self, forKey: ._name) - step = try container.decodeIfPresent(Int.self, forKey: ._step) ?? 0 - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(storeId, forKey: ._storeId) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(tournament, forKey: ._tournament) - try container.encode(index, forKey: ._index) - try container.encode(size, forKey: ._size) - try container.encode(format, forKey: ._format) - try container.encode(startDate, forKey: ._startDate) - try container.encode(name, forKey: ._name) - try container.encode(step, forKey: ._step) - } +// init(from decoder: Decoder) throws { +// let container = try decoder.container(keyedBy: CodingKeys.self) +// +// id = try container.decode(String.self, forKey: ._id) +// storeId = try container.decode(String.self, forKey: ._storeId) +// lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate) +// tournament = try container.decode(String.self, forKey: ._tournament) +// index = try container.decode(Int.self, forKey: ._index) +// size = try container.decode(Int.self, forKey: ._size) +// format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) +// startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) +// name = try container.decodeIfPresent(String.self, forKey: ._name) +// step = try container.decodeIfPresent(Int.self, forKey: ._step) ?? 0 +// } +// +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(storeId, forKey: ._storeId) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(tournament, forKey: ._tournament) +// try container.encode(index, forKey: ._index) +// try container.encode(size, forKey: ._size) +// try container.encode(format, forKey: ._format) +// try container.encode(startDate, forKey: ._startDate) +// try container.encode(name, forKey: ._name) +// try container.encode(step, forKey: ._step) +// } func insertOnServer() { self.tournamentStore.groupStages.writeChangeAndInsertOnServer(instance: self) diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index f44eb8e..43988ed 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -9,11 +9,12 @@ import Foundation import LeStorage @Observable -final class Match: ModelObject, SyncedStorable, SideStorable { - static func resourceName() -> String { "matches" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } - static var relationshipNames: [String] = ["round", "groupStage"] +final class Match: BaseMatch, SideStorable { + +// static func resourceName() -> String { "matches" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return true } +// static var relationshipNames: [String] = ["round", "groupStage"] static func setServerTitle(upperRound: Round, matchIndex: Int) -> String { if upperRound.index == 0 { return upperRound.roundTitle() } @@ -22,45 +23,52 @@ final class Match: ModelObject, SyncedStorable, SideStorable { var byeState: Bool = false - var id: String = Store.randomId() - var lastUpdate: Date - var round: String? - var groupStage: String? - var startDate: Date? - var endDate: Date? - var index: Int - private var format: MatchFormat? - //var court: String? - var servingTeamId: String? - var winningTeamId: String? - var losingTeamId: String? - //var broadcasted: Bool - var name: String? - //var order: Int - var disabled: Bool = false - private(set) var courtIndex: Int? - var confirmed: Bool = false - - var storeId: String? = nil - - init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) { - self.lastUpdate = Date() - self.round = round - self.groupStage = groupStage - self.startDate = startDate - self.endDate = endDate - self.index = index - self.format = matchFormat - //self.court = court - self.servingTeamId = servingTeamId - self.winningTeamId = winningTeamId - self.losingTeamId = losingTeamId - self.disabled = disabled - self.name = name - self.courtIndex = courtIndex - self.confirmed = confirmed -// self.broadcasted = broadcasted -// self.order = order +// var id: String = Store.randomId() +// var lastUpdate: Date +// var round: String? +// var groupStage: String? +// var startDate: Date? +// var endDate: Date? +// var index: Int +// var format: MatchFormat? +// //var court: String? +// var servingTeamId: String? +// var winningTeamId: String? +// var losingTeamId: String? +// //var broadcasted: Bool +// var name: String? +// //var order: Int +// var disabled: Bool = false +// private(set) var courtIndex: Int? +// var confirmed: Bool = false +// +// var storeId: String? = nil + + init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, format: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) { + + super.init(round: round, groupStage: groupStage, startDate: startDate, endDate: endDate, index: index, format: format, servingTeamId: servingTeamId, winningTeamId: winningTeamId, losingTeamId: losingTeamId, name: name, disabled: disabled, courtIndex: courtIndex, confirmed: confirmed) + +// self.lastUpdate = Date() +// self.round = round +// self.groupStage = groupStage +// self.startDate = startDate +// self.endDate = endDate +// self.index = index +// self.format = matchFormat +// //self.court = court +// self.servingTeamId = servingTeamId +// self.winningTeamId = winningTeamId +// self.losingTeamId = losingTeamId +// self.disabled = disabled +// self.name = name +// self.courtIndex = courtIndex +// self.confirmed = confirmed +//// self.broadcasted = broadcasted +//// self.order = order + } + + required init(from decoder: Decoder) throws { + try super.init(from: decoder) } var tournamentStore: TournamentStore { @@ -816,6 +824,7 @@ defer { var groupStageObject: GroupStage? { guard let groupStage else { return nil } return self.tournamentStore.groupStages.findById(groupStage) +// self.store?.findById(group) } var isLoserBracket: Bool { @@ -837,48 +846,48 @@ defer { } } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _storeId = "storeId" - case _lastUpdate = "lastUpdate" - case _round = "round" - case _groupStage = "groupStage" - case _startDate = "startDate" - case _endDate = "endDate" - case _index = "index" - case _format = "format" -// case _court = "court" - case _courtIndex = "courtIndex" - case _servingTeamId = "servingTeamId" - case _winningTeamId = "winningTeamId" - case _losingTeamId = "losingTeamId" -// case _broadcasted = "broadcasted" - case _name = "name" -// case _order = "order" - case _disabled = "disabled" - case _confirmed = "confirmed" - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(storeId, forKey: ._storeId) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(round, forKey: ._round) - try container.encode(groupStage, forKey: ._groupStage) - try container.encode(startDate, forKey: ._startDate) - try container.encode(endDate, forKey: ._endDate) - try container.encode(format, forKey: ._format) - try container.encode(servingTeamId, forKey: ._servingTeamId) - try container.encode(index, forKey: ._index) - try container.encode(winningTeamId, forKey: ._winningTeamId) - try container.encode(losingTeamId, forKey: ._losingTeamId) - try container.encode(name, forKey: ._name) - try container.encode(disabled, forKey: ._disabled) - try container.encode(courtIndex, forKey: ._courtIndex) - try container.encode(confirmed, forKey: ._confirmed) - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _storeId = "storeId" +// case _lastUpdate = "lastUpdate" +// case _round = "round" +// case _groupStage = "groupStage" +// case _startDate = "startDate" +// case _endDate = "endDate" +// case _index = "index" +// case _format = "format" +//// case _court = "court" +// case _courtIndex = "courtIndex" +// case _servingTeamId = "servingTeamId" +// case _winningTeamId = "winningTeamId" +// case _losingTeamId = "losingTeamId" +//// case _broadcasted = "broadcasted" +// case _name = "name" +//// case _order = "order" +// case _disabled = "disabled" +// case _confirmed = "confirmed" +// } +// +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(storeId, forKey: ._storeId) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(round, forKey: ._round) +// try container.encode(groupStage, forKey: ._groupStage) +// try container.encode(startDate, forKey: ._startDate) +// try container.encode(endDate, forKey: ._endDate) +// try container.encode(format, forKey: ._format) +// try container.encode(servingTeamId, forKey: ._servingTeamId) +// try container.encode(index, forKey: ._index) +// try container.encode(winningTeamId, forKey: ._winningTeamId) +// try container.encode(losingTeamId, forKey: ._losingTeamId) +// try container.encode(name, forKey: ._name) +// try container.encode(disabled, forKey: ._disabled) +// try container.encode(courtIndex, forKey: ._courtIndex) +// try container.encode(confirmed, forKey: ._confirmed) +// } func insertOnServer() { self.tournamentStore.matches.writeChangeAndInsertOnServer(instance: self) diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index 2ef29f9..d603864 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -10,27 +10,27 @@ import LeStorage import SwiftUI @Observable -final class MatchScheduler : ModelObject, Storable { +final class MatchScheduler : BaseMatchScheduler { - static func resourceName() -> String { return "match-scheduler" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - private(set) var id: String = Store.randomId() - var tournament: String - var timeDifferenceLimit: Int - var loserBracketRotationDifference: Int - var upperBracketRotationDifference: Int - var accountUpperBracketBreakTime: Bool - var accountLoserBracketBreakTime: Bool - var randomizeCourts: Bool - var rotationDifferenceIsImportant: Bool - var shouldHandleUpperRoundSlice: Bool - var shouldEndRoundBeforeStartingNext: Bool - var groupStageChunkCount: Int? - var overrideCourtsUnavailability: Bool = false - var shouldTryToFillUpCourtsAvailable: Bool = false +// static func resourceName() -> String { return "match-scheduler" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return false } +// static var relationshipNames: [String] = [] +// +// private(set) var id: String = Store.randomId() +// var tournament: String +// var timeDifferenceLimit: Int +// var loserBracketRotationDifference: Int +// var upperBracketRotationDifference: Int +// var accountUpperBracketBreakTime: Bool +// var accountLoserBracketBreakTime: Bool +// var randomizeCourts: Bool +// var rotationDifferenceIsImportant: Bool +// var shouldHandleUpperRoundSlice: Bool +// var shouldEndRoundBeforeStartingNext: Bool +// var groupStageChunkCount: Int? +// var overrideCourtsUnavailability: Bool = false +// var shouldTryToFillUpCourtsAvailable: Bool = false init(tournament: String, timeDifferenceLimit: Int = 5, @@ -43,6 +43,7 @@ final class MatchScheduler : ModelObject, Storable { shouldHandleUpperRoundSlice: Bool = true, shouldEndRoundBeforeStartingNext: Bool = true, groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false, shouldTryToFillUpCourtsAvailable: Bool = false) { + super.init() self.tournament = tournament self.timeDifferenceLimit = timeDifferenceLimit self.loserBracketRotationDifference = loserBracketRotationDifference @@ -58,22 +59,26 @@ final class MatchScheduler : ModelObject, Storable { self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _tournament = "tournament" - case _timeDifferenceLimit = "timeDifferenceLimit" - case _loserBracketRotationDifference = "loserBracketRotationDifference" - case _upperBracketRotationDifference = "upperBracketRotationDifference" - case _accountUpperBracketBreakTime = "accountUpperBracketBreakTime" - case _accountLoserBracketBreakTime = "accountLoserBracketBreakTime" - case _randomizeCourts = "randomizeCourts" - case _rotationDifferenceIsImportant = "rotationDifferenceIsImportant" - case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice" - case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext" - case _groupStageChunkCount = "groupStageChunkCount" - case _overrideCourtsUnavailability = "overrideCourtsUnavailability" - case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable" + required init(from decoder: any Decoder) throws { + try super.init(from: decoder) } + +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _tournament = "tournament" +// case _timeDifferenceLimit = "timeDifferenceLimit" +// case _loserBracketRotationDifference = "loserBracketRotationDifference" +// case _upperBracketRotationDifference = "upperBracketRotationDifference" +// case _accountUpperBracketBreakTime = "accountUpperBracketBreakTime" +// case _accountLoserBracketBreakTime = "accountLoserBracketBreakTime" +// case _randomizeCourts = "randomizeCourts" +// case _rotationDifferenceIsImportant = "rotationDifferenceIsImportant" +// case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice" +// case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext" +// case _groupStageChunkCount = "groupStageChunkCount" +// case _overrideCourtsUnavailability = "overrideCourtsUnavailability" +// case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable" +// } var courtsUnavailability: [DateInterval]? { guard let event = tournamentObject()?.eventObject() else { return nil } diff --git a/PadelClub/Data/MonthData.swift b/PadelClub/Data/MonthData.swift index a7a102b..ab49252 100644 --- a/PadelClub/Data/MonthData.swift +++ b/PadelClub/Data/MonthData.swift @@ -88,17 +88,22 @@ final class MonthData: ModelObject, Storable { override func deleteDependencies() { } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _monthKey = "monthKey" - case _creationDate = "creationDate" - case _maleUnrankedValue = "maleUnrankedValue" - case _femaleUnrankedValue = "femaleUnrankedValue" - case _maleCount = "maleCount" - case _femaleCount = "femaleCount" - case _anonymousCount = "anonymousCount" - case _incompleteMode = "incompleteMode" - case _dataModelIdentifier = "dataModelIdentifier" - case _fileModelIdentifier = "fileModelIdentifier" +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _monthKey = "monthKey" +// case _creationDate = "creationDate" +// case _maleUnrankedValue = "maleUnrankedValue" +// case _femaleUnrankedValue = "femaleUnrankedValue" +// case _maleCount = "maleCount" +// case _femaleCount = "femaleCount" +// case _anonymousCount = "anonymousCount" +// case _incompleteMode = "incompleteMode" +// case _dataModelIdentifier = "dataModelIdentifier" +// case _fileModelIdentifier = "fileModelIdentifier" +// } + + func copy(from other: any Storable) { + fatalError("should not happen") } + } diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index ca1afa4..f6ef828 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -9,41 +9,41 @@ import Foundation import LeStorage @Observable -final class PlayerRegistration: ModelObject, SyncedStorable, SideStorable { - static func resourceName() -> String { "player-registrations" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } - static var relationshipNames: [String] = ["teamRegistration"] - - var id: String = Store.randomId() - var lastUpdate: Date - var teamRegistration: String? - var firstName: String - var lastName: String - var licenceId: String? - var rank: Int? - var paymentType: PlayerPaymentType? - var sex: PlayerSexType? - - var tournamentPlayed: Int? - var points: Double? - var clubName: String? - var ligueName: String? - var assimilation: String? - - var phoneNumber: String? - var email: String? - var birthdate: String? - - var computedRank: Int = 0 - var source: PlayerDataSource? - - var hasArrived: Bool = false - - var storeId: String? = nil +final class PlayerRegistration: BasePlayerRegistration, SideStorable { +// static func resourceName() -> String { "player-registrations" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return true } +// static var relationshipNames: [String] = ["teamRegistration"] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var teamRegistration: String? +// var firstName: String +// var lastName: String +// var licenceId: String? +// var rank: Int? +// var paymentType: PlayerPaymentType? +// var sex: PlayerSexType? +// +// var tournamentPlayed: Int? +// var points: Double? +// var clubName: String? +// var ligueName: String? +// var assimilation: String? +// +// var phoneNumber: String? +// var email: String? +// var birthdate: String? +// +// var computedRank: Int = 0 +// var source: PlayerDataSource? +// +// var hasArrived: Bool = false +// +// var storeId: String? = nil init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false) { - self.lastUpdate = Date() + super.init() self.teamRegistration = teamRegistration self.firstName = firstName self.lastName = lastName @@ -65,7 +65,7 @@ final class PlayerRegistration: ModelObject, SyncedStorable, SideStorable { } internal init(importedPlayer: ImportedPlayer) { - self.lastUpdate = Date() + super.init() self.teamRegistration = "" self.firstName = (importedPlayer.firstName ?? "").trimmed.capitalized self.lastName = (importedPlayer.lastName ?? "").trimmed.uppercased() @@ -82,7 +82,7 @@ final class PlayerRegistration: ModelObject, SyncedStorable, SideStorable { } internal init?(federalData: [String], sex: Int, sexUnknown: Bool) { - self.lastUpdate = Date() + super.init() let _lastName = federalData[0].trimmed.uppercased() let _firstName = federalData[1].trimmed.capitalized if _lastName.isEmpty && _firstName.isEmpty { return nil } @@ -120,6 +120,10 @@ final class PlayerRegistration: ModelObject, SyncedStorable, SideStorable { } } + required init(from decoder: any Decoder) throws { + try super.init(from: decoder) + } + var tournamentStore: TournamentStore { if let store = self.store as? TournamentStore { return store @@ -327,120 +331,58 @@ final class PlayerRegistration: ModelObject, SyncedStorable, SideStorable { return false } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _storeId = "storeId" - case _lastUpdate = "lastUpdate" - case _teamRegistration = "teamRegistration" - case _firstName = "firstName" - case _lastName = "lastName" - case _licenceId = "licenceId" - case _rank = "rank" - case _paymentType = "paymentType" - case _sex = "sex" - case _tournamentPlayed = "tournamentPlayed" - case _points = "points" - case _clubName = "clubName" - case _ligueName = "ligueName" - case _assimilation = "assimilation" - case _birthdate = "birthdate" - case _phoneNumber = "phoneNumber" - case _email = "email" - case _computedRank = "computedRank" - case _source = "source" - case _hasArrived = "hasArrived" - - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(storeId, forKey: ._storeId) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(teamRegistration, forKey: ._teamRegistration) - - try container.encode(firstName, forKey: ._firstName) - try container.encode(lastName, forKey: ._lastName) - try container.encode(licenceId, forKey: ._licenceId) - try container.encode(rank, forKey: ._rank) - try container.encode(paymentType, forKey: ._paymentType) - try container.encode(sex, forKey: ._sex) - try container.encode(tournamentPlayed, forKey: ._tournamentPlayed) - try container.encode(points, forKey: ._points) - try container.encode(clubName, forKey: ._clubName) - try container.encode(ligueName, forKey: ._ligueName) - try container.encode(assimilation, forKey: ._assimilation) - try container.encode(phoneNumber, forKey: ._phoneNumber) - try container.encode(email, forKey: ._email) - try container.encode(birthdate, forKey: ._birthdate) - try container.encode(computedRank, forKey: ._computedRank) - try container.encode(source, forKey: ._source) - try container.encode(hasArrived, forKey: ._hasArrived) - } - - enum PlayerDataSource: Int, Codable { - case frenchFederation = 0 - case beachPadel = 1 - } - - enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable { - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } - - var id: Self { - self - } - - case female = 0 - case male = 1 - } - - enum PlayerPaymentType: Int, CaseIterable, Identifiable, Codable { - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _storeId = "storeId" +// case _lastUpdate = "lastUpdate" +// case _teamRegistration = "teamRegistration" +// case _firstName = "firstName" +// case _lastName = "lastName" +// case _licenceId = "licenceId" +// case _rank = "rank" +// case _paymentType = "paymentType" +// case _sex = "sex" +// case _tournamentPlayed = "tournamentPlayed" +// case _points = "points" +// case _clubName = "clubName" +// case _ligueName = "ligueName" +// case _assimilation = "assimilation" +// case _birthdate = "birthdate" +// case _phoneNumber = "phoneNumber" +// case _email = "email" +// case _computedRank = "computedRank" +// case _source = "source" +// case _hasArrived = "hasArrived" +// +// } +// +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(storeId, forKey: ._storeId) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(teamRegistration, forKey: ._teamRegistration) +// +// try container.encode(firstName, forKey: ._firstName) +// try container.encode(lastName, forKey: ._lastName) +// try container.encode(licenceId, forKey: ._licenceId) +// try container.encode(rank, forKey: ._rank) +// try container.encode(paymentType, forKey: ._paymentType) +// try container.encode(sex, forKey: ._sex) +// try container.encode(tournamentPlayed, forKey: ._tournamentPlayed) +// try container.encode(points, forKey: ._points) +// try container.encode(clubName, forKey: ._clubName) +// try container.encode(ligueName, forKey: ._ligueName) +// try container.encode(assimilation, forKey: ._assimilation) +// try container.encode(phoneNumber, forKey: ._phoneNumber) +// try container.encode(email, forKey: ._email) +// try container.encode(birthdate, forKey: ._birthdate) +// try container.encode(computedRank, forKey: ._computedRank) +// try container.encode(source, forKey: ._source) +// try container.encode(hasArrived, forKey: ._hasArrived) +// } - var id: Self { - self - } - - case cash = 0 - case lydia = 1 - case gift = 2 - case check = 3 - case paylib = 4 - case bankTransfer = 5 - case clubHouse = 6 - case creditCard = 7 - case forfeit = 8 - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .check: - return "Chèque" - case .cash: - return "Cash" - case .lydia: - return "Lydia" - case .paylib: - return "Paylib" - case .bankTransfer: - return "Virement" - case .clubHouse: - return "Clubhouse" - case .creditCard: - return "CB" - case .forfeit: - return "Forfait" - case .gift: - return "Offert" - } - } - } static func addon(for playerRank: Int, manMax: Int, womanMax: Int) -> Int { switch playerRank { @@ -514,3 +456,66 @@ extension PlayerRegistration: PlayerHolder { computedRank } } + +enum PlayerDataSource: Int, Codable { + case frenchFederation = 0 + case beachPadel = 1 +} + +enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable { + init?(rawValue: Int?) { + guard let value = rawValue else { return nil } + self.init(rawValue: value) + } + + var id: Self { + self + } + + case female = 0 + case male = 1 +} + +enum PlayerPaymentType: Int, CaseIterable, Identifiable, Codable { + init?(rawValue: Int?) { + guard let value = rawValue else { return nil } + self.init(rawValue: value) + } + + var id: Self { + self + } + + case cash = 0 + case lydia = 1 + case gift = 2 + case check = 3 + case paylib = 4 + case bankTransfer = 5 + case clubHouse = 6 + case creditCard = 7 + case forfeit = 8 + + func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { + switch self { + case .check: + return "Chèque" + case .cash: + return "Cash" + case .lydia: + return "Lydia" + case .paylib: + return "Paylib" + case .bankTransfer: + return "Virement" + case .clubHouse: + return "Clubhouse" + case .creditCard: + return "CB" + case .forfeit: + return "Forfait" + case .gift: + return "Offert" + } + } +} diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index e1e4ed5..957cdac 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -10,34 +10,41 @@ import LeStorage import SwiftUI @Observable -final class Round: ModelObject, SyncedStorable, SideStorable { +final class Round: BaseRound, SideStorable { - static func resourceName() -> String { "rounds" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } - static var relationshipNames: [String] = [] - - var id: String = Store.randomId() - var lastUpdate: Date - var tournament: String - var index: Int - var parent: String? - private(set) var format: MatchFormat? - var startDate: Date? - var groupStageLoserBracket: Bool = false - var loserBracketMode: LoserBracketMode = .automatic - - var storeId: String? = nil +// static func resourceName() -> String { "rounds" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return true } +// static var relationshipNames: [String] = [] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var tournament: String +// var index: Int +// var parent: String? +// private(set) var format: MatchFormat? +// var startDate: Date? +// var groupStageLoserBracket: Bool = false +// var loserBracketMode: LoserBracketMode = .automatic +// +// var storeId: String? = nil internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { - self.lastUpdate = Date() - self.tournament = tournament - self.index = index - self.parent = parent - self.format = matchFormat - self.startDate = startDate - self.groupStageLoserBracket = groupStageLoserBracket - self.loserBracketMode = loserBracketMode + + super.init(tournament: tournament, index: index, parent: parent, format: matchFormat, startDate: startDate, groupStageLoserBracket: groupStageLoserBracket, loserBracketMode: loserBracketMode) + +// self.lastUpdate = Date() +// self.tournament = tournament +// self.index = index +// self.parent = parent +// self.format = matchFormat +// self.startDate = startDate +// self.groupStageLoserBracket = groupStageLoserBracket +// self.loserBracketMode = loserBracketMode + } + + required init(from decoder: any Decoder) throws { + try super.init(from: decoder) } // MARK: - Computed dependencies @@ -542,7 +549,7 @@ defer { if let seedInterval = seedInterval(initialMode: initialMode) { return seedInterval.localizedLabel(displayStyle) } - print("Round pas trouvé", id, parent, index) +// print("Round pas trouvé", id, parent, index) return "Match de classement" } return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle) @@ -619,7 +626,7 @@ defer { let matches = (0.. String { "team-registrations" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } - static var relationshipNames: [String] = [] - - var id: String = Store.randomId() - var lastUpdate: Date - var tournament: String - var groupStage: String? - var registrationDate: Date? - var callDate: Date? - var bracketPosition: Int? - var groupStagePosition: Int? - var comment: String? - var source: String? - var sourceValue: String? - var logo: String? - var name: String? - - var walkOut: Bool = false - var wildCardBracket: Bool = false - var wildCardGroupStage: Bool = false - var weight: Int = 0 - var lockedWeight: Int? - var confirmationDate: Date? - var qualified: Bool = false - var finalRanking: Int? - var pointsEarned: Int? - - var storeId: String? = nil +final class TeamRegistration: BaseTeamRegistration, SideStorable { + +// static func resourceName() -> String { "team-registrations" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return true } +// static var relationshipNames: [String] = [] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var tournament: String +// var groupStage: String? +// var registrationDate: Date? +// var callDate: Date? +// var bracketPosition: Int? +// var groupStagePosition: Int? +// var comment: String? +// var source: String? +// var sourceValue: String? +// var logo: String? +// var name: String? +// +// var walkOut: Bool = false +// var wildCardBracket: Bool = false +// var wildCardGroupStage: Bool = false +// var weight: Int = 0 +// var lockedWeight: Int? +// var confirmationDate: Date? +// var qualified: Bool = false +// var finalRanking: Int? +// var pointsEarned: Int? +// +// var storeId: String? = nil init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) { - self.storeId = tournament - self.lastUpdate = Date() + super.init(storeId: tournament) + +// self.storeId = tournament self.tournament = tournament self.groupStage = groupStage self.registrationDate = registrationDate @@ -66,6 +68,10 @@ final class TeamRegistration: ModelObject, SyncedStorable, SideStorable { self.qualified = qualified } + required init(from decoder: any Decoder) throws { + try super.init(from: decoder) + } + var tournamentStore: TournamentStore { return TournamentStore.instance(tournamentId: self.tournament) } @@ -509,59 +515,59 @@ final class TeamRegistration: ModelObject, SyncedStorable, SideStorable { return nil } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _lastUpdate = "lastUpdate" - case _storeId = "storeId" - case _tournament = "tournament" - case _groupStage = "groupStage" - case _registrationDate = "registrationDate" - case _callDate = "callDate" - case _bracketPosition = "bracketPosition" - case _groupStagePosition = "groupStagePosition" - case _comment = "comment" - case _source = "source" - case _sourceValue = "sourceValue" - case _logo = "logo" - case _name = "name" - case _wildCardBracket = "wildCardBracket" - case _wildCardGroupStage = "wildCardGroupStage" - case _weight = "weight" - case _walkOut = "walkOut" - case _lockedWeight = "lockedWeight" - case _confirmationDate = "confirmationDate" - case _qualified = "qualified" - case _finalRanking = "finalRanking" - case _pointsEarned = "pointsEarned" - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(storeId, forKey: ._storeId) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(tournament, forKey: ._tournament) - try container.encode(groupStage, forKey: ._groupStage) - try container.encode(registrationDate, forKey: ._registrationDate) - try container.encode(callDate, forKey: ._callDate) - try container.encode(bracketPosition, forKey: ._bracketPosition) - try container.encode(groupStagePosition, forKey: ._groupStagePosition) - try container.encode(comment, forKey: ._comment) - try container.encode(source, forKey: ._source) - try container.encode(sourceValue, forKey: ._sourceValue) - try container.encode(logo, forKey: ._logo) - try container.encode(name, forKey: ._name) - try container.encode(walkOut, forKey: ._walkOut) - try container.encode(wildCardBracket, forKey: ._wildCardBracket) - try container.encode(wildCardGroupStage, forKey: ._wildCardGroupStage) - try container.encode(weight, forKey: ._weight) - try container.encode(lockedWeight, forKey: ._lockedWeight) - try container.encode(confirmationDate, forKey: ._confirmationDate) - try container.encode(qualified, forKey: ._qualified) - try container.encode(finalRanking, forKey: ._finalRanking) - try container.encode(pointsEarned, forKey: ._pointsEarned) - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _lastUpdate = "lastUpdate" +// case _storeId = "storeId" +// case _tournament = "tournament" +// case _groupStage = "groupStage" +// case _registrationDate = "registrationDate" +// case _callDate = "callDate" +// case _bracketPosition = "bracketPosition" +// case _groupStagePosition = "groupStagePosition" +// case _comment = "comment" +// case _source = "source" +// case _sourceValue = "sourceValue" +// case _logo = "logo" +// case _name = "name" +// case _wildCardBracket = "wildCardBracket" +// case _wildCardGroupStage = "wildCardGroupStage" +// case _weight = "weight" +// case _walkOut = "walkOut" +// case _lockedWeight = "lockedWeight" +// case _confirmationDate = "confirmationDate" +// case _qualified = "qualified" +// case _finalRanking = "finalRanking" +// case _pointsEarned = "pointsEarned" +// } +// +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(storeId, forKey: ._storeId) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(tournament, forKey: ._tournament) +// try container.encode(groupStage, forKey: ._groupStage) +// try container.encode(registrationDate, forKey: ._registrationDate) +// try container.encode(callDate, forKey: ._callDate) +// try container.encode(bracketPosition, forKey: ._bracketPosition) +// try container.encode(groupStagePosition, forKey: ._groupStagePosition) +// try container.encode(comment, forKey: ._comment) +// try container.encode(source, forKey: ._source) +// try container.encode(sourceValue, forKey: ._sourceValue) +// try container.encode(logo, forKey: ._logo) +// try container.encode(name, forKey: ._name) +// try container.encode(walkOut, forKey: ._walkOut) +// try container.encode(wildCardBracket, forKey: ._wildCardBracket) +// try container.encode(wildCardGroupStage, forKey: ._wildCardGroupStage) +// try container.encode(weight, forKey: ._weight) +// try container.encode(lockedWeight, forKey: ._lockedWeight) +// try container.encode(confirmationDate, forKey: ._confirmationDate) +// try container.encode(qualified, forKey: ._qualified) +// try container.encode(finalRanking, forKey: ._finalRanking) +// try container.encode(pointsEarned, forKey: ._pointsEarned) +// } func insertOnServer() { self.tournamentStore.teamRegistrations.writeChangeAndInsertOnServer(instance: self) diff --git a/PadelClub/Data/TeamScore.swift b/PadelClub/Data/TeamScore.swift index b996fca..945191f 100644 --- a/PadelClub/Data/TeamScore.swift +++ b/PadelClub/Data/TeamScore.swift @@ -9,44 +9,45 @@ import Foundation import LeStorage @Observable -final class TeamScore: ModelObject, SyncedStorable, SideStorable { +final class TeamScore: BaseTeamScore, SideStorable { + - static func resourceName() -> String { "team-scores" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } - static var relationshipNames: [String] = ["match"] - - var id: String = Store.randomId() - var lastUpdate: Date - var match: String - var teamRegistration: String? - //var playerRegistrations: [String] = [] - var score: String? - var walkOut: Int? - var luckyLoser: Int? - - var storeId: String? = nil +// static func resourceName() -> String { "team-scores" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return true } +// static var relationshipNames: [String] = ["match"] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var match: String +// var teamRegistration: String? +// //var playerRegistrations: [String] = [] +// var score: String? +// var walkOut: Int? +// var luckyLoser: Int? +// +// var storeId: String? = nil init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) { - self.lastUpdate = Date() - self.match = match - self.teamRegistration = teamRegistration -// self.playerRegistrations = playerRegistrations - self.score = score - self.walkOut = walkOut - self.luckyLoser = luckyLoser + super.init(match: match, teamRegistration: teamRegistration, score: score, walkOut: walkOut, luckyLoser: luckyLoser) + +// self.match = match +// self.teamRegistration = teamRegistration +//// self.playerRegistrations = playerRegistrations +// self.score = score +// self.walkOut = walkOut +// self.luckyLoser = luckyLoser } init(match: String, team: TeamRegistration?) { - self.lastUpdate = Date() - self.match = match + super.init(match: match) if let team { self.teamRegistration = team.id - //self.playerRegistrations = team.players().map { $0.id } } - self.score = nil - self.walkOut = nil - self.luckyLoser = nil + } + + required init(from decoder: Decoder) throws { + try super.init(from: decoder) } var tournamentStore: TournamentStore { @@ -75,30 +76,30 @@ final class TeamScore: ModelObject, SyncedStorable, SideStorable { return walkOut != nil } - enum CodingKeys: String, CodingKey { - case _id = "id" - case _storeId = "storeId" - case _lastUpdate = "lastUpdate" - case _match = "match" - case _teamRegistration = "teamRegistration" - //case _playerRegistrations = "playerRegistrations" - case _score = "score" - case _walkOut = "walkOut" - case _luckyLoser = "luckyLoser" - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(storeId, forKey: ._storeId) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(match, forKey: ._match) - try container.encode(teamRegistration, forKey: ._teamRegistration) - try container.encode(score, forKey: ._score) - try container.encode(walkOut, forKey: ._walkOut) - try container.encode(luckyLoser, forKey: ._luckyLoser) - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _storeId = "storeId" +// case _lastUpdate = "lastUpdate" +// case _match = "match" +// case _teamRegistration = "teamRegistration" +// //case _playerRegistrations = "playerRegistrations" +// case _score = "score" +// case _walkOut = "walkOut" +// case _luckyLoser = "luckyLoser" +// } +// +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(storeId, forKey: ._storeId) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(match, forKey: ._match) +// try container.encode(teamRegistration, forKey: ._teamRegistration) +// try container.encode(score, forKey: ._score) +// try container.encode(walkOut, forKey: ._walkOut) +// try container.encode(luckyLoser, forKey: ._luckyLoser) +// } func insertOnServer() { self.tournamentStore.teamScores.writeChangeAndInsertOnServer(instance: self) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index bc5f7e0..f9d60eb 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -10,112 +10,113 @@ import LeStorage import SwiftUI @Observable -final class Tournament: ModelObject, SyncedStorable { - - static func resourceName() -> String { "tournaments" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - var id: String = Store.randomId() - var lastUpdate: Date - var event: String? - var name: String? - var startDate: Date - var endDate: Date? - private(set) var creationDate: Date - var isPrivate: Bool - private(set) var groupStageFormat: MatchFormat? - private(set) var roundFormat: MatchFormat? - private(set) var loserRoundFormat: MatchFormat? - var groupStageSortMode: GroupStageOrderingMode - var groupStageCount: Int - var rankSourceDate: Date? - var dayDuration: Int - var teamCount: Int - var teamSorting: TeamSortingType - var federalCategory: TournamentCategory - var federalLevelCategory: TournamentLevel - var federalAgeCategory: FederalTournamentAge - var closedRegistrationDate: Date? - var groupStageAdditionalQualified: Int - var courtCount: Int = 2 - var prioritizeClubMembers: Bool - var qualifiedPerGroupStage: Int - var teamsPerGroupStage: Int - var entryFee: Double? - var payment: TournamentPayment? = nil - var additionalEstimationDuration: Int = 0 - var isDeleted: Bool = false - var isCanceled: Bool = false - var publishTeams: Bool = false - //var publishWaitingList: Bool = false - var publishSummons: Bool = false - var publishGroupStages: Bool = false - var publishBrackets: Bool = false - var shouldVerifyGroupStage: Bool = false - var shouldVerifyBracket: Bool = false - var hideTeamsWeight: Bool = false - var publishTournament: Bool = false - var hidePointsEarned: Bool = false - var publishRankings: Bool = false - var loserBracketMode: LoserBracketMode = .automatic +final class Tournament: BaseTournament { + +// static func resourceName() -> String { "tournaments" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return false } +// static var relationshipNames: [String] = [] +// +// var id: String = Store.randomId() +// var lastUpdate: Date +// var event: String? +// var name: String? +// var startDate: Date +// var endDate: Date? +// private(set) var creationDate: Date +// var isPrivate: Bool +// private(set) var groupStageFormat: MatchFormat? +// private(set) var roundFormat: MatchFormat? +// private(set) var loserRoundFormat: MatchFormat? +// var groupStageSortMode: GroupStageOrderingMode +// var groupStageCount: Int +// var rankSourceDate: Date? +// var dayDuration: Int +// var teamCount: Int +// var teamSorting: TeamSortingType +// var federalCategory: TournamentCategory +// var federalLevelCategory: TournamentLevel +// var federalAgeCategory: FederalTournamentAge +// var closedRegistrationDate: Date? +// var groupStageAdditionalQualified: Int +// var courtCount: Int = 2 +// var prioritizeClubMembers: Bool +// var qualifiedPerGroupStage: Int +// var teamsPerGroupStage: Int +// var entryFee: Double? +// var payment: TournamentPayment? = nil +// var additionalEstimationDuration: Int = 0 +// var isDeleted: Bool = false +// var isCanceled: Bool = false +// var publishTeams: Bool = false +// //var publishWaitingList: Bool = false +// var publishSummons: Bool = false +// var publishGroupStages: Bool = false +// var publishBrackets: Bool = false +// var shouldVerifyGroupStage: Bool = false +// var shouldVerifyBracket: Bool = false +// var hideTeamsWeight: Bool = false +// var publishTournament: Bool = false +// var hidePointsEarned: Bool = false +// var publishRankings: Bool = false +// var loserBracketMode: LoserBracketMode = .automatic var storeId: String? { return nil } @ObservationIgnored var navigationPath: [Screen] = [] - enum CodingKeys: String, CodingKey { - case _id = "id" - case _lastUpdate = "lastUpdate" - case _event = "event" - case _creator = "creator" - case _name = "name" - case _startDate = "startDate" - case _endDate = "endDate" - case _creationDate = "creationDate" - case _isPrivate = "isPrivate" - case _groupStageFormat = "groupStageFormat" - case _roundFormat = "roundFormat" - case _loserRoundFormat = "loserRoundFormat" - case _groupStageSortMode = "groupStageSortMode" - case _groupStageCount = "groupStageCount" - case _rankSourceDate = "rankSourceDate" - case _dayDuration = "dayDuration" - case _teamCount = "teamCount" - case _teamSorting = "teamSorting" - case _federalCategory = "federalCategory" - case _federalLevelCategory = "federalLevelCategory" - case _federalAgeCategory = "federalAgeCategory" - case _seedCount = "seedCount" - case _closedRegistrationDate = "closedRegistrationDate" - case _groupStageAdditionalQualified = "groupStageAdditionalQualified" - case _courtCount = "courtCount" - case _prioritizeClubMembers = "prioritizeClubMembers" - case _qualifiedPerGroupStage = "qualifiedPerGroupStage" - case _teamsPerGroupStage = "teamsPerGroupStage" - case _entryFee = "entryFee" - case _additionalEstimationDuration = "additionalEstimationDuration" - case _isDeleted = "isDeleted" - case _isCanceled = "localId" - case _payment = "globalId" - case _publishTeams = "publishTeams" - //case _publishWaitingList = "publishWaitingList" - case _publishSummons = "publishSummons" - case _publishGroupStages = "publishGroupStages" - case _publishBrackets = "publishBrackets" - case _shouldVerifyGroupStage = "shouldVerifyGroupStage" - case _shouldVerifyBracket = "shouldVerifyBracket" - case _hideTeamsWeight = "hideTeamsWeight" - case _publishTournament = "publishTournament" - case _hidePointsEarned = "hidePointsEarned" - case _publishRankings = "publishRankings" - case _loserBracketMode = "loserBracketMode" - } +// enum CodingKeys: String, CodingKey { +// case _id = "id" +// case _lastUpdate = "lastUpdate" +// case _event = "event" +// case _creator = "creator" +// case _name = "name" +// case _startDate = "startDate" +// case _endDate = "endDate" +// case _creationDate = "creationDate" +// case _isPrivate = "isPrivate" +// case _groupStageFormat = "groupStageFormat" +// case _roundFormat = "roundFormat" +// case _loserRoundFormat = "loserRoundFormat" +// case _groupStageSortMode = "groupStageSortMode" +// case _groupStageCount = "groupStageCount" +// case _rankSourceDate = "rankSourceDate" +// case _dayDuration = "dayDuration" +// case _teamCount = "teamCount" +// case _teamSorting = "teamSorting" +// case _federalCategory = "federalCategory" +// case _federalLevelCategory = "federalLevelCategory" +// case _federalAgeCategory = "federalAgeCategory" +// case _seedCount = "seedCount" +// case _closedRegistrationDate = "closedRegistrationDate" +// case _groupStageAdditionalQualified = "groupStageAdditionalQualified" +// case _courtCount = "courtCount" +// case _prioritizeClubMembers = "prioritizeClubMembers" +// case _qualifiedPerGroupStage = "qualifiedPerGroupStage" +// case _teamsPerGroupStage = "teamsPerGroupStage" +// case _entryFee = "entryFee" +// case _additionalEstimationDuration = "additionalEstimationDuration" +// case _isDeleted = "isDeleted" +// case _isCanceled = "localId" +// case _payment = "globalId" +// case _publishTeams = "publishTeams" +// //case _publishWaitingList = "publishWaitingList" +// case _publishSummons = "publishSummons" +// case _publishGroupStages = "publishGroupStages" +// case _publishBrackets = "publishBrackets" +// case _shouldVerifyGroupStage = "shouldVerifyGroupStage" +// case _shouldVerifyBracket = "shouldVerifyBracket" +// case _hideTeamsWeight = "hideTeamsWeight" +// case _publishTournament = "publishTournament" +// case _hidePointsEarned = "hidePointsEarned" +// case _publishRankings = "publishRankings" +// case _loserBracketMode = "loserBracketMode" +// } internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { - self.lastUpdate = Date() + super.init() + self.event = event self.name = name self.startDate = startDate @@ -157,173 +158,177 @@ final class Tournament: ModelObject, SyncedStorable { } required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(String.self, forKey: ._id) - lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate) - event = try container.decodeIfPresent(String.self, forKey: ._event) - name = try container.decodeIfPresent(String.self, forKey: ._name) - startDate = try container.decode(Date.self, forKey: ._startDate) - endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) - creationDate = try container.decode(Date.self, forKey: ._creationDate) - isPrivate = try container.decode(Bool.self, forKey: ._isPrivate) - groupStageFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageFormat) - roundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._roundFormat) - loserRoundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserRoundFormat) - groupStageSortMode = try container.decode(GroupStageOrderingMode.self, forKey: ._groupStageSortMode) - groupStageCount = try container.decode(Int.self, forKey: ._groupStageCount) - rankSourceDate = try container.decodeIfPresent(Date.self, forKey: ._rankSourceDate) - dayDuration = try container.decode(Int.self, forKey: ._dayDuration) - teamCount = try container.decode(Int.self, forKey: ._teamCount) - teamSorting = try container.decode(TeamSortingType.self, forKey: ._teamSorting) - federalCategory = try container.decode(TournamentCategory.self, forKey: ._federalCategory) - federalLevelCategory = try container.decode(TournamentLevel.self, forKey: ._federalLevelCategory) - federalAgeCategory = try container.decode(FederalTournamentAge.self, forKey: ._federalAgeCategory) - closedRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._closedRegistrationDate) - groupStageAdditionalQualified = try container.decode(Int.self, forKey: ._groupStageAdditionalQualified) - courtCount = try container.decode(Int.self, forKey: ._courtCount) - prioritizeClubMembers = try container.decode(Bool.self, forKey: ._prioritizeClubMembers) - qualifiedPerGroupStage = try container.decode(Int.self, forKey: ._qualifiedPerGroupStage) - teamsPerGroupStage = try container.decode(Int.self, forKey: ._teamsPerGroupStage) - entryFee = try container.decodeIfPresent(Double.self, forKey: ._entryFee) - payment = try Tournament._decodePayment(container: container) - additionalEstimationDuration = try container.decode(Int.self, forKey: ._additionalEstimationDuration) - isDeleted = try container.decode(Bool.self, forKey: ._isDeleted) - isCanceled = try Tournament._decodeCanceled(container: container) - publishTeams = try container.decodeIfPresent(Bool.self, forKey: ._publishTeams) ?? false - publishSummons = try container.decodeIfPresent(Bool.self, forKey: ._publishSummons) ?? false - publishGroupStages = try container.decodeIfPresent(Bool.self, forKey: ._publishGroupStages) ?? false - publishBrackets = try container.decodeIfPresent(Bool.self, forKey: ._publishBrackets) ?? false - shouldVerifyBracket = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyBracket) ?? false - shouldVerifyGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyGroupStage) ?? false - hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false - publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false - hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false - publishRankings = try container.decodeIfPresent(Bool.self, forKey: ._publishRankings) ?? false - loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic - } - - fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() - - fileprivate static func _decodePayment(container: KeyedDecodingContainer) throws -> TournamentPayment? { - let data = try container.decodeIfPresent(Data.self, forKey: ._payment) - - if let data { - do { - let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue) - let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue } - return TournamentPayment(rawValue: sequence[18]) - } catch { - Logger.error(error) - } - } - return nil - } - - fileprivate static func _decodeCanceled(container: KeyedDecodingContainer) throws -> Bool { - let data = try container.decodeIfPresent(Data.self, forKey: ._isCanceled) - if let data { - do { - let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue) - let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue } - return Bool.decodeInt(sequence[18]) - } catch { - Logger.error(error) - } - } - return false - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: ._id) - try container.encode(lastUpdate, forKey: ._lastUpdate) - try container.encode(event, forKey: ._event) - try container.encode(name, forKey: ._name) - - try container.encode(startDate, forKey: ._startDate) - try container.encode(endDate, forKey: ._endDate) - - try container.encode(creationDate, forKey: ._creationDate) - try container.encode(isPrivate, forKey: ._isPrivate) - - try container.encode(groupStageFormat, forKey: ._groupStageFormat) - try container.encode(roundFormat, forKey: ._roundFormat) - try container.encode(loserRoundFormat, forKey: ._loserRoundFormat) - - try container.encode(groupStageSortMode, forKey: ._groupStageSortMode) - try container.encode(groupStageCount, forKey: ._groupStageCount) - - try container.encode(rankSourceDate, forKey: ._rankSourceDate) - - try container.encode(dayDuration, forKey: ._dayDuration) - try container.encode(teamCount, forKey: ._teamCount) - try container.encode(teamSorting, forKey: ._teamSorting) - try container.encode(federalCategory, forKey: ._federalCategory) - try container.encode(federalLevelCategory, forKey: ._federalLevelCategory) - try container.encode(federalAgeCategory, forKey: ._federalAgeCategory) - - try container.encode(closedRegistrationDate, forKey: ._closedRegistrationDate) - - try container.encode(groupStageAdditionalQualified, forKey: ._groupStageAdditionalQualified) - try container.encode(courtCount, forKey: ._courtCount) - try container.encode(prioritizeClubMembers, forKey: ._prioritizeClubMembers) - try container.encode(qualifiedPerGroupStage, forKey: ._qualifiedPerGroupStage) - try container.encode(teamsPerGroupStage, forKey: ._teamsPerGroupStage) - try container.encode(entryFee, forKey: ._entryFee) - - try self._encodePayment(container: &container) - try container.encode(additionalEstimationDuration, forKey: ._additionalEstimationDuration) - try container.encode(isDeleted, forKey: ._isDeleted) - try self._encodeIsCanceled(container: &container) - try container.encode(publishTeams, forKey: ._publishTeams) - try container.encode(publishSummons, forKey: ._publishSummons) - try container.encode(publishBrackets, forKey: ._publishBrackets) - try container.encode(publishGroupStages, forKey: ._publishGroupStages) - try container.encode(shouldVerifyBracket, forKey: ._shouldVerifyBracket) - try container.encode(shouldVerifyGroupStage, forKey: ._shouldVerifyGroupStage) - try container.encode(hideTeamsWeight, forKey: ._hideTeamsWeight) - try container.encode(publishTournament, forKey: ._publishTournament) - try container.encode(hidePointsEarned, forKey: ._hidePointsEarned) - try container.encode(publishRankings, forKey: ._publishRankings) - try container.encode(loserBracketMode, forKey: ._loserBracketMode) - } - - fileprivate func _encodePayment(container: inout KeyedEncodingContainer) throws { - - guard let payment else { - try container.encodeNil(forKey: ._payment) - return - } - - let max: Int = TournamentPayment.allCases.count - var sequence = (1...18).map { _ in Int.random(in: (0..) throws { - - let max: Int = 9 - var sequence = (1...18).map { _ in Int.random(in: (0...max)) } - sequence.append(self.isCanceled.encodedValue) - sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0...max ))} ) - - let stringCombo: [String] = sequence.map { $0.formatted() } - let joined: String = stringCombo.joined(separator: "") - if let data = joined.data(using: .utf8) { - let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue) - try container.encode(encryped, forKey: ._isCanceled) - } - } + try super.init(from: decoder) + } + +// required init(from decoder: Decoder) throws { +// let container = try decoder.container(keyedBy: CodingKeys.self) +// id = try container.decode(String.self, forKey: ._id) +// lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate) +// event = try container.decodeIfPresent(String.self, forKey: ._event) +// name = try container.decodeIfPresent(String.self, forKey: ._name) +// startDate = try container.decode(Date.self, forKey: ._startDate) +// endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) +// creationDate = try container.decode(Date.self, forKey: ._creationDate) +// isPrivate = try container.decode(Bool.self, forKey: ._isPrivate) +// groupStageFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageFormat) +// roundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._roundFormat) +// loserRoundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserRoundFormat) +// groupStageSortMode = try container.decode(GroupStageOrderingMode.self, forKey: ._groupStageSortMode) +// groupStageCount = try container.decode(Int.self, forKey: ._groupStageCount) +// rankSourceDate = try container.decodeIfPresent(Date.self, forKey: ._rankSourceDate) +// dayDuration = try container.decode(Int.self, forKey: ._dayDuration) +// teamCount = try container.decode(Int.self, forKey: ._teamCount) +// teamSorting = try container.decode(TeamSortingType.self, forKey: ._teamSorting) +// federalCategory = try container.decode(TournamentCategory.self, forKey: ._federalCategory) +// federalLevelCategory = try container.decode(TournamentLevel.self, forKey: ._federalLevelCategory) +// federalAgeCategory = try container.decode(FederalTournamentAge.self, forKey: ._federalAgeCategory) +// closedRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._closedRegistrationDate) +// groupStageAdditionalQualified = try container.decode(Int.self, forKey: ._groupStageAdditionalQualified) +// courtCount = try container.decode(Int.self, forKey: ._courtCount) +// prioritizeClubMembers = try container.decode(Bool.self, forKey: ._prioritizeClubMembers) +// qualifiedPerGroupStage = try container.decode(Int.self, forKey: ._qualifiedPerGroupStage) +// teamsPerGroupStage = try container.decode(Int.self, forKey: ._teamsPerGroupStage) +// entryFee = try container.decodeIfPresent(Double.self, forKey: ._entryFee) +// payment = try Tournament._decodePayment(container: container) +// additionalEstimationDuration = try container.decode(Int.self, forKey: ._additionalEstimationDuration) +// isDeleted = try container.decode(Bool.self, forKey: ._isDeleted) +// isCanceled = try Tournament._decodeCanceled(container: container) +// publishTeams = try container.decodeIfPresent(Bool.self, forKey: ._publishTeams) ?? false +// publishSummons = try container.decodeIfPresent(Bool.self, forKey: ._publishSummons) ?? false +// publishGroupStages = try container.decodeIfPresent(Bool.self, forKey: ._publishGroupStages) ?? false +// publishBrackets = try container.decodeIfPresent(Bool.self, forKey: ._publishBrackets) ?? false +// shouldVerifyBracket = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyBracket) ?? false +// shouldVerifyGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyGroupStage) ?? false +// hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false +// publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false +// hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false +// publishRankings = try container.decodeIfPresent(Bool.self, forKey: ._publishRankings) ?? false +// loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic +// } +// +// fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() +// +// fileprivate static func _decodePayment(container: KeyedDecodingContainer) throws -> TournamentPayment? { +// let data = try container.decodeIfPresent(Data.self, forKey: ._payment) +// +// if let data { +// do { +// let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue) +// let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue } +// return TournamentPayment(rawValue: sequence[18]) +// } catch { +// Logger.error(error) +// } +// } +// return nil +// } +// +// fileprivate static func _decodeCanceled(container: KeyedDecodingContainer) throws -> Bool { +// let data = try container.decodeIfPresent(Data.self, forKey: ._isCanceled) +// if let data { +// do { +// let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue) +// let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue } +// return Bool.decodeInt(sequence[18]) +// } catch { +// Logger.error(error) +// } +// } +// return false +// } +// +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(id, forKey: ._id) +// try container.encode(lastUpdate, forKey: ._lastUpdate) +// try container.encode(event, forKey: ._event) +// try container.encode(name, forKey: ._name) +// +// try container.encode(startDate, forKey: ._startDate) +// try container.encode(endDate, forKey: ._endDate) +// +// try container.encode(creationDate, forKey: ._creationDate) +// try container.encode(isPrivate, forKey: ._isPrivate) +// +// try container.encode(groupStageFormat, forKey: ._groupStageFormat) +// try container.encode(roundFormat, forKey: ._roundFormat) +// try container.encode(loserRoundFormat, forKey: ._loserRoundFormat) +// +// try container.encode(groupStageSortMode, forKey: ._groupStageSortMode) +// try container.encode(groupStageCount, forKey: ._groupStageCount) +// +// try container.encode(rankSourceDate, forKey: ._rankSourceDate) +// +// try container.encode(dayDuration, forKey: ._dayDuration) +// try container.encode(teamCount, forKey: ._teamCount) +// try container.encode(teamSorting, forKey: ._teamSorting) +// try container.encode(federalCategory, forKey: ._federalCategory) +// try container.encode(federalLevelCategory, forKey: ._federalLevelCategory) +// try container.encode(federalAgeCategory, forKey: ._federalAgeCategory) +// +// try container.encode(closedRegistrationDate, forKey: ._closedRegistrationDate) +// +// try container.encode(groupStageAdditionalQualified, forKey: ._groupStageAdditionalQualified) +// try container.encode(courtCount, forKey: ._courtCount) +// try container.encode(prioritizeClubMembers, forKey: ._prioritizeClubMembers) +// try container.encode(qualifiedPerGroupStage, forKey: ._qualifiedPerGroupStage) +// try container.encode(teamsPerGroupStage, forKey: ._teamsPerGroupStage) +// try container.encode(entryFee, forKey: ._entryFee) +// +// try self._encodePayment(container: &container) +// try container.encode(additionalEstimationDuration, forKey: ._additionalEstimationDuration) +// try container.encode(isDeleted, forKey: ._isDeleted) +// try self._encodeIsCanceled(container: &container) +// try container.encode(publishTeams, forKey: ._publishTeams) +// try container.encode(publishSummons, forKey: ._publishSummons) +// try container.encode(publishBrackets, forKey: ._publishBrackets) +// try container.encode(publishGroupStages, forKey: ._publishGroupStages) +// try container.encode(shouldVerifyBracket, forKey: ._shouldVerifyBracket) +// try container.encode(shouldVerifyGroupStage, forKey: ._shouldVerifyGroupStage) +// try container.encode(hideTeamsWeight, forKey: ._hideTeamsWeight) +// try container.encode(publishTournament, forKey: ._publishTournament) +// try container.encode(hidePointsEarned, forKey: ._hidePointsEarned) +// try container.encode(publishRankings, forKey: ._publishRankings) +// try container.encode(loserBracketMode, forKey: ._loserBracketMode) +// } +// +// fileprivate func _encodePayment(container: inout KeyedEncodingContainer) throws { +// +// guard let payment else { +// try container.encodeNil(forKey: ._payment) +// return +// } +// +// let max: Int = TournamentPayment.allCases.count +// var sequence = (1...18).map { _ in Int.random(in: (0..) throws { +// +// let max: Int = 9 +// var sequence = (1...18).map { _ in Int.random(in: (0...max)) } +// sequence.append(self.isCanceled.encodedValue) +// sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0...max ))} ) +// +// let stringCombo: [String] = sequence.map { $0.formatted() } +// let joined: String = stringCombo.joined(separator: "") +// if let data = joined.data(using: .utf8) { +// let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue) +// try container.encode(encryped, forKey: ._isCanceled) +// } +// } var tournamentStore: TournamentStore { return TournamentStore.instance(tournamentId: self.id) @@ -373,21 +378,6 @@ final class Tournament: ModelObject, SyncedStorable { // MARK: - - /// Warning: if the enum has more than 10 cases, the payment algo is broken - enum TournamentPayment: Int, CaseIterable { - case free, unit, subscriptionUnit, unlimited - - var isSubscription: Bool { - switch self { - case .subscriptionUnit, .unlimited: - return true - default: - return false - } - } - - } - enum State { case initial case build @@ -479,8 +469,8 @@ final class Tournament: ModelObject, SyncedStorable { func shareURL(_ pageLink: PageLink = .matches) -> URL? { if pageLink == .clubBroadcast { let club = club() - print("club", club) - print("club broadcast code", club?.broadcastCode) +// print("club", club) +// print("club broadcast code", club?.broadcastCode) if let club, let broadcastCode = club.broadcastCode { return URLs.main.url.appending(path: "c/\(broadcastCode)") } else { @@ -947,7 +937,7 @@ defer { return self.selectedSortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.computedRank) } - func paidSelectedPlayers(type: PlayerRegistration.PlayerPaymentType) -> Double? { + func paidSelectedPlayers(type: PlayerPaymentType) -> Double? { if let entryFee { return Double(self.selectedSortedTeams().flatMap { $0.unsortedPlayers() }.filter { $0.paymentType == type }.count) * entryFee } else { @@ -1681,7 +1671,7 @@ defer { let matches = (0.. some View { DisclosureGroup { - ForEach(PlayerRegistration.PlayerPaymentType.allCases) { type in + ForEach(PlayerPaymentType.allCases) { type in PaymentTypeCashierRowView(tournaments: tournaments, type: type) } } label: { @@ -137,7 +137,7 @@ struct CashierDetailView: View { struct PaymentTypeCashierRowView: View { let tournaments: [Tournament] - let type: PlayerRegistration.PlayerPaymentType + let type: PlayerPaymentType @State private var value: Double? @@ -167,7 +167,7 @@ struct CashierDetailView: View { var body: some View { DisclosureGroup { let selectedPlayers = tournament.selectedPlayers() - ForEach(PlayerRegistration.PlayerPaymentType.allCases) { type in + ForEach(PlayerPaymentType.allCases) { type in let count = selectedPlayers.filter({ $0.paymentType == type }).count if count > 0 { LabeledContent { diff --git a/PadelClub/Views/Cashier/CashierSettingsView.swift b/PadelClub/Views/Cashier/CashierSettingsView.swift index fd51e73..d236a90 100644 --- a/PadelClub/Views/Cashier/CashierSettingsView.swift +++ b/PadelClub/Views/Cashier/CashierSettingsView.swift @@ -43,11 +43,7 @@ struct CashierSettingsView: View { players.forEach { player in player.hasArrived = true } - do { - try tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - } catch { - Logger.error(error) - } + tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) } } footer: { Text("Indique tous les joueurs sont là") @@ -59,11 +55,7 @@ struct CashierSettingsView: View { players.forEach { player in player.hasArrived = false } - do { - try tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - } catch { - Logger.error(error) - } + tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) } } footer: { Text("Indique qu'aucun joueur n'est arrivé") @@ -77,11 +69,7 @@ struct CashierSettingsView: View { player.paymentType = .gift } } - do { - try tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - } catch { - Logger.error(error) - } + tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) } } footer: { Text("Passe tous les joueurs qui n'ont pas réglé en offert") @@ -95,11 +83,7 @@ struct CashierSettingsView: View { players.forEach { player in player.paymentType = nil } - do { - try store.playerRegistrations.addOrUpdate(contentOfs: players) - } catch { - Logger.error(error) - } + store.playerRegistrations.addOrUpdate(contentOfs: players) } } footer: { Text("Remet à zéro le type d'encaissement de tous les joueurs") @@ -151,11 +135,7 @@ struct CashierSettingsView: View { } private func _save() { - do { - try dataStore.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) - } + dataStore.tournaments.addOrUpdate(instance: tournament) } } diff --git a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift index adf8694..469971e 100644 --- a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift +++ b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift @@ -106,7 +106,7 @@ struct LoserBracketFromGroupStageView: View { private func _addNewMatch() { let currentGroupStageLoserBracketsInitialPlace = tournament.groupStageLoserBracketsInitialPlace() let placeCount = displayableMatches.isEmpty ? currentGroupStageLoserBracketsInitialPlace : max(currentGroupStageLoserBracketsInitialPlace, displayableMatches.map({ $0.index }).max()! + 2) - let match = Match(round: loserBracket.id, index: placeCount, matchFormat: loserBracket.matchFormat) + let match = Match(round: loserBracket.id, index: placeCount, format: loserBracket.matchFormat) match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix()) place" tournamentStore.matches.addOrUpdate(instance: match) } diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 6fc905d..bf098ab 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -95,8 +95,8 @@ struct MainView: View { } .id(mainViewId) .onChange(of: dataStore.user.id) { - print("dataStore.user.id", dataStore.user.id) - print("StoreCenter.main.userId", StoreCenter.main.userId) + print("dataStore.user.id = ", dataStore.user.id) + print("StoreCenter.main.userId = ", StoreCenter.main.userId ?? "") if StoreCenter.main.userId == nil { // user disconnected navigation.path.removeLast(navigation.path.count) mainViewId = UUID() diff --git a/PadelClub/Views/Player/Components/PlayerPayView.swift b/PadelClub/Views/Player/Components/PlayerPayView.swift index 4382f14..e4e8b85 100644 --- a/PadelClub/Views/Player/Components/PlayerPayView.swift +++ b/PadelClub/Views/Player/Components/PlayerPayView.swift @@ -15,9 +15,9 @@ struct PlayerPayView: View { var body: some View { Picker(selection: $player.paymentType) { - Text("Non réglé").tag(nil as PlayerRegistration.PlayerPaymentType?) - ForEach(PlayerRegistration.PlayerPaymentType.allCases) { type in - Text(type.localizedLabel()).tag(type as PlayerRegistration.PlayerPaymentType?) + Text("Non réglé").tag(nil as PlayerPaymentType?) + ForEach(PlayerPaymentType.allCases) { type in + Text(type.localizedLabel()).tag(type as PlayerPaymentType?) } } label: { } @@ -29,10 +29,6 @@ struct PlayerPayView: View { } private func _save() { - do { - try tournamentStore.playerRegistrations.addOrUpdate(instance: player) - } catch { - Logger.error(error) - } + tournamentStore.playerRegistrations.addOrUpdate(instance: player) } } diff --git a/PadelClub/Views/Player/Components/PlayerPopoverView.swift b/PadelClub/Views/Player/Components/PlayerPopoverView.swift index 9bd893b..2a283a3 100644 --- a/PadelClub/Views/Player/Components/PlayerPopoverView.swift +++ b/PadelClub/Views/Player/Components/PlayerPopoverView.swift @@ -251,7 +251,7 @@ struct PlayerPopoverView: View { } func createManualPlayer() { - let playerRegistration = PlayerRegistration(firstName: firstName.trimmedMultiline, lastName: lastName.trimmedMultiline, licenceId: license.trimmedMultiline.isEmpty ? nil : license, rank: rank, sex: PlayerRegistration.PlayerSexType(rawValue: sex)) + let playerRegistration = PlayerRegistration(firstName: firstName.trimmedMultiline, lastName: lastName.trimmedMultiline, licenceId: license.trimmedMultiline.isEmpty ? nil : license, rank: rank, sex: PlayerSexType(rawValue: sex)) self.creationCompletionHandler(playerRegistration) } diff --git a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift index 91e7527..f3cf674 100644 --- a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift +++ b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift @@ -23,8 +23,8 @@ struct PlayerSexPickerView: View { Text(player.playerLabel()) Spacer() Picker(selection: $player.sex) { - Text("Homme").tag(PlayerRegistration.PlayerSexType.male as PlayerRegistration.PlayerSexType?) - Text("Femme").tag(PlayerRegistration.PlayerSexType.female as PlayerRegistration.PlayerSexType?) + Text("Homme").tag(PlayerSexType.male as PlayerSexType?) + Text("Femme").tag(PlayerSexType.female as PlayerSexType?) } label: { } @@ -37,19 +37,11 @@ struct PlayerSexPickerView: View { } private func _save() { - do { - player.setComputedRank(in: tournament) - try tournamentStore.playerRegistrations.addOrUpdate(instance: player) - if let team = player.team() { - team.updateWeight(inTournamentCategory: tournament.tournamentCategory) - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } - - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + player.setComputedRank(in: tournament) + tournamentStore.playerRegistrations.addOrUpdate(instance: player) + if let team = player.team() { + team.updateWeight(inTournamentCategory: tournament.tournamentCategory) + tournamentStore.teamRegistrations.addOrUpdate(instance: team) } } diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index f095bc2..f0c23ab 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -92,7 +92,7 @@ struct RoundSettingsView: View { var currentIndex = 0 let matches = (0.. Tournament.TournamentPayment? { + func paymentForNewTournament() -> TournamentPayment? { switch self.currentPlan { case .monthlyUnlimited: - return Tournament.TournamentPayment.unlimited + return TournamentPayment.unlimited case .fivePerMonth: if let purchaseDate = self.currentBestPurchase?.purchaseDate { let tournaments = DataStore.shared.tournaments.filter { $0.creationDate > purchaseDate && $0.payment == .subscriptionUnit && $0.isCanceled == false } if tournaments.count < StoreItem.five { - return Tournament.TournamentPayment.subscriptionUnit + return TournamentPayment.subscriptionUnit } } return self._paymentWithoutSubscription() @@ -260,21 +260,21 @@ import LeStorage } - fileprivate func _paymentWithoutSubscription() -> Tournament.TournamentPayment? { + fileprivate func _paymentWithoutSubscription() -> TournamentPayment? { let freelyPayed: Int = DataStore.shared.tournaments.filter { $0.payment == .free && $0.isCanceled == false }.count if freelyPayed < 1 { - return Tournament.TournamentPayment.free + return 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 TournamentPayment.unit } return nil } var remainingTournaments: Int { - let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == Tournament.TournamentPayment.unit }.count + let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == TournamentPayment.unit }.count let tournamentCreditCount = self._purchasedTournamentCount() // let notDeletedTournamentCount = DataStore.shared.tournaments.filter { $0.isDeleted == false }.count diff --git a/PadelClub/Views/Tournament/Subscription/Purchase.swift b/PadelClub/Views/Tournament/Subscription/Purchase.swift index 06e1304..e7cfc24 100644 --- a/PadelClub/Views/Tournament/Subscription/Purchase.swift +++ b/PadelClub/Views/Tournament/Subscription/Purchase.swift @@ -8,45 +8,51 @@ import Foundation import LeStorage -class Purchase: ModelObject, SyncedStorable { +class Purchase: BasePurchase { - static func resourceName() -> String { return "purchases" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - var id: UInt64 - var lastUpdate: Date - var user: String - var purchaseDate: Date - var productId: String - var quantity: Int? - var revocationDate: Date? = nil - var expirationDate: Date? = nil +// static func resourceName() -> String { return "purchases" } +// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } +// static func filterByStoreIdentifier() -> Bool { return false } +// static var relationshipNames: [String] = [] +// +// var id: UInt64 +// var lastUpdate: Date +// var user: String +// var purchaseDate: Date +// var productId: String +// var quantity: Int? +// var revocationDate: Date? = nil +// var expirationDate: Date? = nil var storeId: String? { return nil } init(user: String, transactionId: UInt64, purchaseDate: Date, productId: String, quantity: Int? = nil, revocationDate: Date? = nil, expirationDate: Date? = nil) { - self.id = transactionId - self.lastUpdate = Date() - self.user = user - self.purchaseDate = purchaseDate - self.productId = productId - self.quantity = quantity - self.revocationDate = revocationDate - self.expirationDate = expirationDate + super.init(id: transactionId, user: user, purchaseDate: purchaseDate, productId: productId, quantity: quantity, revocationDate: revocationDate, expirationDate: expirationDate) + +// self.id = transactionId +// self.lastUpdate = Date() +// self.user = user +// self.purchaseDate = purchaseDate +// self.productId = productId +// self.quantity = quantity +// self.revocationDate = revocationDate +// self.expirationDate = expirationDate } - enum CodingKeys: String, CodingKey, CaseIterable { - case id - case lastUpdate - case user - case purchaseDate - case productId - case quantity - case revocationDate - case expirationDate + required init(from decoder: Decoder) throws { + try super.init(from: decoder) } + +// enum CodingKeys: String, CodingKey, CaseIterable { +// case id +// case lastUpdate +// case user +// case purchaseDate +// case productId +// case quantity +// case revocationDate +// case expirationDate +// } func isValid() -> Bool { guard self.revocationDate == nil else { @@ -58,30 +64,30 @@ class Purchase: ModelObject, SyncedStorable { return expiration > Date() } - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(self.id, forKey: .id) - try container.encode(self.lastUpdate, forKey: .lastUpdate) - try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user) - try container.encode(self.purchaseDate, forKey: .purchaseDate) - try container.encode(self.productId, forKey: .productId) - try container.encode(self.quantity, forKey: .quantity) - try container.encode(self.revocationDate, forKey: .revocationDate) - try container.encode(self.expirationDate, forKey: .expirationDate) - } - - required init(from decoder: any Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - self.id = try container.decode(UInt64.self, forKey: .id) - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: .lastUpdate) ?? Date() - self.user = try container.decodeEncrypted(key: .user) - self.purchaseDate = try container.decode(Date.self, forKey: .purchaseDate) - self.productId = try container.decode(String.self, forKey: .productId) - self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity) - self.revocationDate = try container.decodeIfPresent(Date.self, forKey: .revocationDate) - self.expirationDate = try container.decodeIfPresent(Date.self, forKey: .expirationDate) - } +// func encode(to encoder: Encoder) throws { +// var container = encoder.container(keyedBy: CodingKeys.self) +// +// try container.encode(self.id, forKey: .id) +// try container.encode(self.lastUpdate, forKey: .lastUpdate) +// try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user) +// try container.encode(self.purchaseDate, forKey: .purchaseDate) +// try container.encode(self.productId, forKey: .productId) +// try container.encode(self.quantity, forKey: .quantity) +// try container.encode(self.revocationDate, forKey: .revocationDate) +// try container.encode(self.expirationDate, forKey: .expirationDate) +// } +// +// required init(from decoder: any Decoder) throws { +// let container = try decoder.container(keyedBy: CodingKeys.self) +// +// self.id = try container.decode(UInt64.self, forKey: .id) +// self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: .lastUpdate) ?? Date() +// self.user = try container.decodeEncrypted(key: .user) +// self.purchaseDate = try container.decode(Date.self, forKey: .purchaseDate) +// self.productId = try container.decode(String.self, forKey: .productId) +// self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity) +// self.revocationDate = try container.decodeIfPresent(Date.self, forKey: .revocationDate) +// self.expirationDate = try container.decodeIfPresent(Date.self, forKey: .expirationDate) +// } } diff --git a/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift b/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift index 4566f43..e3a21db 100644 --- a/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift @@ -10,7 +10,7 @@ import TipKit struct SubscriptionInfoView: View { - @State var payment: Tournament.TournamentPayment? = .free + @State var payment: TournamentPayment? = .free var body: some View { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index e12017a..c5645c6 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -210,16 +210,12 @@ struct TournamentView: View { } .onAppear { TournamentRunningTip.isRunning = tournament.state() == .running - Logger.log("Payment = \(String(describing: self.tournament.payment)), canceled = \(self.tournament.isCanceled)") + Logger.log("Tournament Id = \(self.tournament.id), Payment = \(String(describing: self.tournament.payment))") } } private func _save() { - do { - try dataStore.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) - } + dataStore.tournaments.addOrUpdate(instance: tournament) } } From d6bd8842c336700e53e6d1d4e56a714fa041651c Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 30 Oct 2024 14:38:15 +0100 Subject: [PATCH 03/43] minor update --- PadelClub.xcodeproj/project.pbxproj | 8 ++++ PadelClub/AppDelegate.swift | 1 - PadelClub/Data/Court.swift | 2 +- PadelClub/Views/Club/ClubDetailView.swift | 14 +++--- .../Navigation/Umpire/NetworkStatusView.swift | 45 +++++++++++++++++++ .../Views/Navigation/Umpire/UmpireView.swift | 11 ++--- 6 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 6d8cb6a..8b64a60 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -114,6 +114,9 @@ C488C8742CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; }; C488C8752CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; }; C488C8762CC816410082001F /* BasePurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C86F2CC816410082001F /* BasePurchase.swift */; }; + C488C8822CCBE8FC0082001F /* NetworkStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */; }; + C488C8832CCBE8FC0082001F /* NetworkStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */; }; + C488C8842CCBE8FC0082001F /* NetworkStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */; }; C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; }; C49EF0192BD694290077B5AA /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; @@ -1002,6 +1005,7 @@ C488C81F2CC7E4240082001F /* Tournament.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Tournament.json; sourceTree = ""; }; C488C86F2CC816410082001F /* BasePurchase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasePurchase.swift; sourceTree = ""; }; C488C8702CC816410082001F /* Purchase.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Purchase.json; sourceTree = ""; }; + C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkStatusView.swift; sourceTree = ""; }; C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewModifier.swift; sourceTree = ""; }; C49EF0182BD694290077B5AA /* PurchaseListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseListView.swift; sourceTree = ""; }; C49EF01A2BD6A1E80077B5AA /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = ""; }; @@ -1810,6 +1814,7 @@ children = ( FF3F74F52B919E45004CFE0E /* UmpireView.swift */, FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */, + C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */, ); path = Umpire; sourceTree = ""; @@ -2635,6 +2640,7 @@ FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */, FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */, FF967D092BAF3D4000A9A3BD /* TeamDetailView.swift in Sources */, + C488C8822CCBE8FC0082001F /* NetworkStatusView.swift in Sources */, FF5DA18F2BB9268800A33061 /* GroupStagesSettingsView.swift in Sources */, FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */, FF1F4B752BFA00FC000B4573 /* HtmlGenerator.swift in Sources */, @@ -2772,6 +2778,7 @@ FF4CBF792C996C0600151637 /* LocationManager.swift in Sources */, FF4CBF7A2C996C0600151637 /* CapsuleViewModifier.swift in Sources */, C488C8472CC7E4240082001F /* BaseDateInterval.swift in Sources */, + C488C8832CCBE8FC0082001F /* NetworkStatusView.swift in Sources */, C488C8482CC7E4240082001F /* BaseMonthData.swift in Sources */, C488C8492CC7E4240082001F /* BaseTeamRegistration.swift in Sources */, C488C84A2CC7E4240082001F /* BaseGroupStage.swift in Sources */, @@ -3036,6 +3043,7 @@ FF70FAF82C90584900129CC2 /* LocationManager.swift in Sources */, FF70FAF92C90584900129CC2 /* CapsuleViewModifier.swift in Sources */, C488C8542CC7E4240082001F /* BaseDateInterval.swift in Sources */, + C488C8842CCBE8FC0082001F /* NetworkStatusView.swift in Sources */, C488C8552CC7E4240082001F /* BaseMonthData.swift in Sources */, C488C8562CC7E4240082001F /* BaseTeamRegistration.swift in Sources */, C488C8572CC7E4240082001F /* BaseGroupStage.swift in Sources */, diff --git a/PadelClub/AppDelegate.swift b/PadelClub/AppDelegate.swift index e3d9e73..12a9138 100644 --- a/PadelClub/AppDelegate.swift +++ b/PadelClub/AppDelegate.swift @@ -20,7 +20,6 @@ class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDel UIApplication.shared.registerForRemoteNotifications() UNUserNotificationCenter.current().delegate = self - Logger.log("didFinishLaunchingWithOptions") return true } diff --git a/PadelClub/Data/Court.swift b/PadelClub/Data/Court.swift index d438f2e..d5bf930 100644 --- a/PadelClub/Data/Court.swift +++ b/PadelClub/Data/Court.swift @@ -31,7 +31,7 @@ final class Court: BaseCourt { } required init(from decoder: Decoder) throws { - fatalError("init(from:) has not been implemented") + try super.init(from: decoder) } func courtTitle() -> String { diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index 5ad196a..313e933 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -282,15 +282,11 @@ struct ClubDetailView: View { } private func _deleteClub() { - do { - clubDeleted = true - dataStore.user.clubs.removeAll(where: { $0 == club.id }) - self.dataStore.saveUser() - try dataStore.clubs.deleteById(club.id) - dismiss() - } catch { - Logger.error(error) - } + clubDeleted = true + dataStore.user.clubs.removeAll(where: { $0 == club.id }) + self.dataStore.saveUser() + dataStore.clubs.delete(instance: club) + dismiss() } } diff --git a/PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift b/PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift new file mode 100644 index 0000000..07f8ca1 --- /dev/null +++ b/PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift @@ -0,0 +1,45 @@ +// +// NetworkStatusView.swift +// PadelClub +// +// Created by Laurent Morvillier on 25/10/2024. +// + +import SwiftUI +import LeStorage + +struct NetworkStatusView: View { + + @State private var isConnected = false + @State private var timer: Timer? + + var body: some View { + Image(systemName: self.isConnected ? "network" : "network.slash") + .resizable() + .scaledToFit() + + .onAppear { + self._defineStatus() + // Start the timer when the view appears + timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in + withAnimation { + self._defineStatus() + } + } + } + .onDisappear { + // Clean up timer when view disappears + timer?.invalidate() + timer = nil + } + } + + fileprivate func _defineStatus() { + self.isConnected = LeStorage.NetworkMonitor.shared.isConnected + } + +} + +#Preview { + NetworkStatusView() +} diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index 347c4ba..d57292c 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -178,11 +178,12 @@ struct UmpireView: View { .toolbar { #if DEBUG ToolbarItem(placement: .topBarTrailing) { - if StoreCenter.main.collectionsCanSynchronize { - Image(systemName: "checkmark.icloud") - } else { - Image(systemName: "icloud.slash") - } + NetworkStatusView() +// if StoreCenter.main.collectionsCanSynchronize { +// Image(systemName: "checkmark.icloud") +// } else { +// Image(systemName: "icloud.slash") +// } } #endif } From f1b351a13f778c681c795b563447b5c1aa14fe6b Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 8 Nov 2024 19:12:08 +0100 Subject: [PATCH 04/43] use generated class --- PadelClub/Data/MonthData.swift | 62 +++------------------------------- 1 file changed, 5 insertions(+), 57 deletions(-) diff --git a/PadelClub/Data/MonthData.swift b/PadelClub/Data/MonthData.swift index ab49252..78bed5a 100644 --- a/PadelClub/Data/MonthData.swift +++ b/PadelClub/Data/MonthData.swift @@ -10,44 +10,16 @@ import SwiftUI import LeStorage @Observable -final class MonthData: ModelObject, Storable { - - static func resourceName() -> String { return "month-data" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } - static var relationshipNames: [String] = [] - - private(set) var id: String = Store.randomId() - private(set) var monthKey: String - private(set) var creationDate: Date - var maleUnrankedValue: Int? = nil - var femaleUnrankedValue: Int? = nil - var maleCount: Int? = nil - var femaleCount: Int? = nil - var anonymousCount: Int? = nil - var incompleteMode: Bool = false - var dataModelIdentifier: String? - var fileModelIdentifier: String? +final class MonthData: BaseMonthData { init(monthKey: String) { + super.init() self.monthKey = monthKey self.creationDate = Date() } required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - id = try container.decode(String.self, forKey: ._id) - monthKey = try container.decode(String.self, forKey: ._monthKey) - creationDate = try container.decode(Date.self, forKey: ._creationDate) - maleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._maleUnrankedValue) - femaleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._femaleUnrankedValue) - maleCount = try container.decodeIfPresent(Int.self, forKey: ._maleCount) - femaleCount = try container.decodeIfPresent(Int.self, forKey: ._femaleCount) - anonymousCount = try container.decodeIfPresent(Int.self, forKey: ._anonymousCount) - incompleteMode = try container.decodeIfPresent(Bool.self, forKey: ._incompleteMode) ?? false - dataModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._dataModelIdentifier) ?? nil - fileModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._fileModelIdentifier) ?? nil - + try super.init(from: decoder) } func total() -> Int { @@ -77,33 +49,9 @@ final class MonthData: ModelObject, Storable { currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0 currentMonthData.femaleCount = lastDataSourceFemaleUnranked?.1 currentMonthData.anonymousCount = anonymousCount - do { - try DataStore.shared.monthData.addOrUpdate(instance: currentMonthData) - } catch { - Logger.error(error) - } + DataStore.shared.monthData.addOrUpdate(instance: currentMonthData) + } } - - override func deleteDependencies() { - } - -// enum CodingKeys: String, CodingKey { -// case _id = "id" -// case _monthKey = "monthKey" -// case _creationDate = "creationDate" -// case _maleUnrankedValue = "maleUnrankedValue" -// case _femaleUnrankedValue = "femaleUnrankedValue" -// case _maleCount = "maleCount" -// case _femaleCount = "femaleCount" -// case _anonymousCount = "anonymousCount" -// case _incompleteMode = "incompleteMode" -// case _dataModelIdentifier = "dataModelIdentifier" -// case _fileModelIdentifier = "fileModelIdentifier" -// } - - func copy(from other: any Storable) { - fatalError("should not happen") - } } From fee94a2b7d9420dc6fdb4a6ad35308b957569d3c Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 11 Nov 2024 15:00:15 +0100 Subject: [PATCH 05/43] add storeId for MatchScheduler --- PadelClub/Data/Gen/BaseMatchScheduler.swift | 13 ++++++++++--- PadelClub/Data/Gen/BaseMonthData.swift | 4 ++-- PadelClub/Data/Gen/MatchScheduler.json | 5 +++++ PadelClub/Data/MatchScheduler.swift | 2 +- PadelClub/Data/README.md | 1 + 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/PadelClub/Data/Gen/BaseMatchScheduler.swift b/PadelClub/Data/Gen/BaseMatchScheduler.swift index cc962bb..2fbcda4 100644 --- a/PadelClub/Data/Gen/BaseMatchScheduler.swift +++ b/PadelClub/Data/Gen/BaseMatchScheduler.swift @@ -8,7 +8,7 @@ import SwiftUI @Observable class BaseMatchScheduler: ModelObject, Storable, Codable { - static func resourceName() -> String { return "match-schedulers" } + static func resourceName() -> String { return "match-scheduler" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } @@ -26,6 +26,7 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { var groupStageChunkCount: Int? = nil var overrideCourtsUnavailability: Bool = false var shouldTryToFillUpCourtsAvailable: Bool = false + var storeId: String? = nil init( id: String = Store.randomId(), @@ -41,7 +42,8 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { shouldEndRoundBeforeStartingNext: Bool = false, groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false, - shouldTryToFillUpCourtsAvailable: Bool = false + shouldTryToFillUpCourtsAvailable: Bool = false, + storeId: String? = nil ) { super.init() self.id = id @@ -58,6 +60,7 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { self.groupStageChunkCount = groupStageChunkCount self.overrideCourtsUnavailability = overrideCourtsUnavailability self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable + self.storeId = storeId } enum CodingKeys: String, CodingKey { @@ -75,6 +78,7 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { case _groupStageChunkCount = "groupStageChunkCount" case _overrideCourtsUnavailability = "overrideCourtsUnavailability" case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable" + case _storeId = "storeId" } required init(from decoder: Decoder) throws { @@ -94,6 +98,7 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { self.groupStageChunkCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageChunkCount) ?? nil self.overrideCourtsUnavailability = try container.decodeIfPresent(Bool.self, forKey: ._overrideCourtsUnavailability) ?? false self.shouldTryToFillUpCourtsAvailable = try container.decodeIfPresent(Bool.self, forKey: ._shouldTryToFillUpCourtsAvailable) ?? false + self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil } func encode(to encoder: Encoder) throws { @@ -112,6 +117,7 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { try container.encode(self.groupStageChunkCount, forKey: ._groupStageChunkCount) try container.encode(self.overrideCourtsUnavailability, forKey: ._overrideCourtsUnavailability) try container.encode(self.shouldTryToFillUpCourtsAvailable, forKey: ._shouldTryToFillUpCourtsAvailable) + try container.encode(self.storeId, forKey: ._storeId) } func tournamentValue() -> Tournament? { @@ -134,5 +140,6 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { self.groupStageChunkCount = matchscheduler.groupStageChunkCount self.overrideCourtsUnavailability = matchscheduler.overrideCourtsUnavailability self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable + self.storeId = matchscheduler.storeId } -} \ No newline at end of file +} diff --git a/PadelClub/Data/Gen/BaseMonthData.swift b/PadelClub/Data/Gen/BaseMonthData.swift index 691529a..5ee2df1 100644 --- a/PadelClub/Data/Gen/BaseMonthData.swift +++ b/PadelClub/Data/Gen/BaseMonthData.swift @@ -8,7 +8,7 @@ import SwiftUI @Observable class BaseMonthData: ModelObject, Storable, Codable { - static func resourceName() -> String { return "month-datas" } + static func resourceName() -> String { return "month-data" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } @@ -110,4 +110,4 @@ class BaseMonthData: ModelObject, Storable, Codable { self.dataModelIdentifier = monthdata.dataModelIdentifier self.fileModelIdentifier = monthdata.fileModelIdentifier } -} \ No newline at end of file +} diff --git a/PadelClub/Data/Gen/MatchScheduler.json b/PadelClub/Data/Gen/MatchScheduler.json index 500bc3d..a9611b1 100644 --- a/PadelClub/Data/Gen/MatchScheduler.json +++ b/PadelClub/Data/Gen/MatchScheduler.json @@ -68,6 +68,11 @@ "name": "shouldTryToFillUpCourtsAvailable", "type": "Bool", "defaultValue": "false" + }, + { + "name": "storeId", + "type": "String", + "optional": true } ] } diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index d603864..27ee626 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -10,7 +10,7 @@ import LeStorage import SwiftUI @Observable -final class MatchScheduler : BaseMatchScheduler { +final class MatchScheduler: BaseMatchScheduler, SideStorable { // static func resourceName() -> String { return "match-scheduler" } // static func tokenExemptedMethods() -> [HTTPMethod] { return [] } diff --git a/PadelClub/Data/README.md b/PadelClub/Data/README.md index 48056ab..e63debb 100644 --- a/PadelClub/Data/README.md +++ b/PadelClub/Data/README.md @@ -10,6 +10,7 @@ Dans Swift: Dans Django: - Ajouter le champ dans la classe +- Si c'est une ForeignKey, toujours mettre un related_name sinon la synchro casse - S'il c'est un champ dans **CustomUser**: - Ajouter le champ à la méthode fields_for_update - Ajouter le champ dans UserSerializer > create > create_user dans serializers.py From 4b2dbf9c4625143e434d08abbdeb85a267000234 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 11 Nov 2024 17:41:32 +0100 Subject: [PATCH 06/43] Adds TournamentLibrary to manage TournamentStore, that do not inherit Store from now on --- PadelClub.xcodeproj/project.pbxproj | 8 ++++++ PadelClub/Data/GroupStage.swift | 2 +- PadelClub/Data/MatchScheduler.swift | 3 +- PadelClub/Data/Round.swift | 6 ++-- PadelClub/Data/TeamRegistration.swift | 2 +- PadelClub/Data/Tournament.swift | 2 +- PadelClub/Data/TournamentLibrary.swift | 27 ++++++++++++++++++ PadelClub/Data/TournamentStore.swift | 39 +++++++++++++++----------- PadelClub/Utils/Patcher.swift | 2 +- 9 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 PadelClub/Data/TournamentLibrary.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 8b64a60..1531660 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -126,6 +126,9 @@ C49EF03C2BE15AF80077B5AA /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */; }; C49EF0422BE23BF50077B5AA /* PaymentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0412BE23BF50077B5AA /* PaymentTests.swift */; }; C49EF0442BE286780077B5AA /* CryptoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0432BE286780077B5AA /* CryptoKey.swift */; }; + C4A36F582CE2626A003738C6 /* TournamentLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */; }; + C4A36F592CE2626A003738C6 /* TournamentLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */; }; + C4A36F5A2CE2626A003738C6 /* TournamentLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */; }; 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 */; }; @@ -1014,6 +1017,7 @@ C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Crypto.swift"; sourceTree = ""; }; C49EF0412BE23BF50077B5AA /* PaymentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTests.swift; sourceTree = ""; }; C49EF0432BE286780077B5AA /* CryptoKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoKey.swift; sourceTree = ""; }; + C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentLibrary.swift; 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 = ""; }; @@ -1489,6 +1493,7 @@ C411C9CC2BF21DAF003017AD /* README.md */, C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */, C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */, + C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */, C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */, C4A47D592B6D383C00ADC637 /* Tournament.swift */, FF967CE72BAEC70100A9A3BD /* GroupStage.swift */, @@ -2537,6 +2542,7 @@ FF4AB6BB2B9256D50002987F /* SearchViewModel.swift in Sources */, FF967CF32BAECC0B00A9A3BD /* PlayerRegistration.swift in Sources */, FF4AB6BF2B92577A0002987F /* ImportedPlayerView.swift in Sources */, + C4A36F5A2CE2626A003738C6 /* TournamentLibrary.swift in Sources */, FF1162872BD004AD000C4809 /* EditingTeamView.swift in Sources */, FF6EC9062B947A1000EA7F5A /* NetworkManagerError.swift in Sources */, C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */, @@ -2915,6 +2921,7 @@ FF4CBFF32C996C0600151637 /* MatchListView.swift in Sources */, FF4CBFF42C996C0600151637 /* PadelClubApp.swift in Sources */, FF4CBFF52C996C0600151637 /* TournamentSettingsView.swift in Sources */, + C4A36F582CE2626A003738C6 /* TournamentLibrary.swift in Sources */, FF4CBFF62C996C0600151637 /* String+Crypto.swift in Sources */, FF4CBFF72C996C0600151637 /* GroupStageTeamReplacementView.swift in Sources */, FF4CBFF82C996C0600151637 /* TabItemModifier.swift in Sources */, @@ -3180,6 +3187,7 @@ FF70FB722C90584900129CC2 /* MatchListView.swift in Sources */, FF70FB732C90584900129CC2 /* PadelClubApp.swift in Sources */, FF70FB742C90584900129CC2 /* TournamentSettingsView.swift in Sources */, + C4A36F592CE2626A003738C6 /* TournamentLibrary.swift in Sources */, FF70FB752C90584900129CC2 /* String+Crypto.swift in Sources */, FF70FB762C90584900129CC2 /* GroupStageTeamReplacementView.swift in Sources */, FF70FB772C90584900129CC2 /* TabItemModifier.swift in Sources */, diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index e544016..c67559c 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -58,7 +58,7 @@ final class GroupStage: BaseGroupStage, SideStorable { } var tournamentStore: TournamentStore { - return TournamentStore.instance(tournamentId: self.tournament) + return TournamentLibrary.shared.store(tournamentId: self.tournament) } // MARK: - Computed dependencies diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index 27ee626..eccb7a4 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -90,7 +90,8 @@ final class MatchScheduler: BaseMatchScheduler, SideStorable { } var tournamentStore: TournamentStore { - return TournamentStore.instance(tournamentId: self.tournament) + return TournamentLibrary.shared.store(tournamentId: self.tournament) +// TournamentStore.instance(tournamentId: self.tournament) } func tournamentObject() -> Tournament? { diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index 957cdac..941f29e 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -50,7 +50,7 @@ final class Round: BaseRound, SideStorable { // MARK: - Computed dependencies var tournamentStore: TournamentStore { - return TournamentStore.instance(tournamentId: self.tournament) + return TournamentLibrary.shared.store(tournamentId: self.tournament) } func tournamentObject() -> Tournament? { @@ -200,7 +200,7 @@ defer { return previousMatch.teams().first } } else if let parent = upperBracketTopMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId { - return tournamentStore.findById(parent) + return self.store?.findById(parent) } case .two: if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 + 1 }) { @@ -212,7 +212,7 @@ defer { return previousMatch.teams().first } } else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId { - return tournamentStore.findById(parent) + return self.store?.findById(parent) } } diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 0b51e7d..996b737 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -73,7 +73,7 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { } var tournamentStore: TournamentStore { - return TournamentStore.instance(tournamentId: self.tournament) + return TournamentLibrary.shared.store(tournamentId: self.tournament) } // MARK: - Computed dependencies diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index f9d60eb..2ec009b 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -331,7 +331,7 @@ final class Tournament: BaseTournament { // } var tournamentStore: TournamentStore { - return TournamentStore.instance(tournamentId: self.id) + return TournamentLibrary.shared.store(tournamentId: self.id) } override func deleteDependencies() { diff --git a/PadelClub/Data/TournamentLibrary.swift b/PadelClub/Data/TournamentLibrary.swift new file mode 100644 index 0000000..948a65e --- /dev/null +++ b/PadelClub/Data/TournamentLibrary.swift @@ -0,0 +1,27 @@ +// +// TournamentLibrary.swift +// PadelClub +// +// Created by Laurent Morvillier on 11/11/2024. +// + +import Foundation +import LeStorage + +class TournamentLibrary { + + static let shared: TournamentLibrary = TournamentLibrary() + + fileprivate var _stores: [String : TournamentStore] = [:] + + func store(tournamentId: String) -> TournamentStore { + if let store = self._stores[tournamentId] { + return store + } + let store = StoreCenter.main.store(identifier: tournamentId) + let tournamentStore = TournamentStore(store: store) + self._stores[tournamentId] = tournamentStore + return tournamentStore + } + +} diff --git a/PadelClub/Data/TournamentStore.swift b/PadelClub/Data/TournamentStore.swift index 3413068..ef27a3a 100644 --- a/PadelClub/Data/TournamentStore.swift +++ b/PadelClub/Data/TournamentStore.swift @@ -9,11 +9,9 @@ import Foundation import LeStorage import SwiftUI -class TournamentStore: Store, ObservableObject { +class TournamentStore: ObservableObject { - static func instance(tournamentId: String) -> TournamentStore { - return StoreCenter.main.store(identifier: tournamentId, parameter: "tournament") - } + var store: Store fileprivate(set) var groupStages: StoredCollection = StoredCollection.placeholder() fileprivate(set) var matches: StoredCollection = StoredCollection.placeholder() @@ -24,13 +22,20 @@ class TournamentStore: Store, ObservableObject { fileprivate(set) var matchSchedulers: StoredCollection = StoredCollection.placeholder() - convenience init(tournament: Tournament) { - self.init(identifier: tournament.id, parameter: "tournament") +// convenience init(tournament: Tournament) { +// let store = StoreCenter.main.store(identifier: tournament.id) +// self.init(store: store) +// self._initialize() +// } + + init(store: Store) { + self.store = store + self._initialize() } - required init(identifier: String, parameter: String) { + fileprivate func _initialize() { - super.init(identifier: identifier, parameter: parameter) +// super.init(identifier: identifier, parameter: parameter) var synchronized: Bool = true let indexed: Bool = true @@ -41,15 +46,15 @@ class TournamentStore: Store, ObservableObject { } #endif - self.groupStages = self.registerSynchronizedCollection(indexed: indexed) - self.rounds = self.registerSynchronizedCollection(indexed: indexed) - self.teamRegistrations = self.registerSynchronizedCollection(indexed: indexed) - self.playerRegistrations = self.registerSynchronizedCollection(indexed: indexed) - self.matches = self.registerSynchronizedCollection(indexed: indexed) - self.teamScores = self.registerSynchronizedCollection(indexed: indexed) - self.matchSchedulers = self.registerCollection(indexed: indexed) - - self.loadCollectionsFromServerIfNoFile() + self.groupStages = self.store.registerSynchronizedCollection(indexed: indexed) + self.rounds = self.store.registerSynchronizedCollection(indexed: indexed) + self.teamRegistrations = self.store.registerSynchronizedCollection(indexed: indexed) + self.playerRegistrations = self.store.registerSynchronizedCollection(indexed: indexed) + self.matches = self.store.registerSynchronizedCollection(indexed: indexed) + self.teamScores = self.store.registerSynchronizedCollection(indexed: indexed) + self.matchSchedulers = self.store.registerCollection(indexed: indexed) + + self.store.loadCollectionsFromServerIfNoFile() } diff --git a/PadelClub/Utils/Patcher.swift b/PadelClub/Utils/Patcher.swift index 66a71ee..0ef8dc6 100644 --- a/PadelClub/Utils/Patcher.swift +++ b/PadelClub/Utils/Patcher.swift @@ -131,7 +131,7 @@ class Patcher { for tournament in DataStore.shared.tournaments { let store = tournament.tournamentStore - let identifier = StoreIdentifier(value: tournament.id, parameterName: "tournament") + let identifier = tournament.id Task { From c85dbad3ca724f5e5fbb472b10982906066222f8 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 11 Nov 2024 18:47:54 +0100 Subject: [PATCH 07/43] Fix issues --- PadelClub/Data/Match.swift | 9 +++------ PadelClub/Data/Tournament.swift | 7 +++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 43988ed..ec04697 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -72,8 +72,8 @@ final class Match: BaseMatch, SideStorable { } var tournamentStore: TournamentStore { - if let store = self.store as? TournamentStore { - return store + if let id = self.store?.identifier { + return TournamentLibrary.shared.store(tournamentId: id) } fatalError("missing store for \(String(describing: type(of: self)))") } @@ -91,14 +91,11 @@ final class Match: BaseMatch, SideStorable { // MARK: - override func deleteDependencies() { - guard let tournament = self.currentTournament() else { - return - } let teamScores = self.teamScores for teamScore in teamScores { teamScore.deleteDependencies() } - tournament.tournamentStore.teamScores.deleteDependencies(teamScores) + self.tournamentStore.teamScores.deleteDependencies(teamScores) } func indexInRound(in matches: [Match]? = nil) -> Int { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 2ec009b..87acac4 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -355,6 +355,13 @@ final class Tournament: BaseTournament { store.rounds.deleteDependencies(rounds) store.matchSchedulers.deleteDependencies(self._matchSchedulers()) + + if let event = self.eventObject() { + if event.tournaments.count == 1 && event.tournaments.first?.id == self.id { + DataStore.shared.events.deleteDependencies([event]) + } + } + } // MARK: - Computed Dependencies From 3feea9b22f2f27f925969e8db82d66504a9146a6 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 18 Nov 2024 17:25:18 +0100 Subject: [PATCH 08/43] Change to server url configuration --- PadelClub/Data/DataStore.swift | 14 ++-- PadelClub/Utils/Patcher.swift | 89 ----------------------- PadelClub/Utils/URLs.swift | 10 +-- PadelClubTests/ServerDataTests.swift | 3 +- PadelClubTests/SynchronizationTests.swift | 2 +- PadelClubTests/TokenExemptionTests.swift | 2 +- PadelClubTests/UserDataTests.swift | 2 +- 7 files changed, 18 insertions(+), 104 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 538a35c..dc5b308 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -52,17 +52,20 @@ class DataStore: ObservableObject { init() { let store = Store.main - let serverURL: String = URLs.api.rawValue StoreCenter.main.blackListUserName("apple-test") + let httpScheme: String = URLs.httpScheme.rawValue + let domain: String = URLs.activationHost.rawValue + #if DEBUG - if let server = PListReader.readString(plist: "local", key: "server") { - StoreCenter.main.synchronizationApiURL = server + if let scheme = PListReader.readString(plist: "local", key: "server_scheme"), + let domain = PListReader.readString(plist: "local", key: "server_domain"){ + StoreCenter.main.configureURLs(httpScheme: scheme, domain: domain) } else { - StoreCenter.main.synchronizationApiURL = serverURL + StoreCenter.main.configureURLs(httpScheme: httpScheme, domain: domain) } #else - StoreCenter.main.synchronizationApiURL = serverURL + StoreCenter.main.configureURLs(httpScheme: httpScheme, domain: domain) #endif StoreCenter.main.logsFailedAPICalls() @@ -77,7 +80,6 @@ class DataStore: ObservableObject { StoreCenter.main.forceNoSynchronization = !synchronized - Logger.log("Sync URL: \(StoreCenter.main.synchronizationApiURL ?? "none"), sync: \(synchronized) ") let indexed: Bool = true self.clubs = store.registerSynchronizedCollection(indexed: indexed) diff --git a/PadelClub/Utils/Patcher.swift b/PadelClub/Utils/Patcher.swift index 0ef8dc6..cefbf60 100644 --- a/PadelClub/Utils/Patcher.swift +++ b/PadelClub/Utils/Patcher.swift @@ -66,99 +66,10 @@ class Patcher { fileprivate static func _importDataFromDev() throws { - let devServices = Services(url: "https://xlr.alwaysdata.net/roads/") - guard devServices.hasToken() else { - return - } - guard StoreCenter.main.synchronizationApiURL == "https://padelclub.app/roads/" else { - return - } - - guard let userId = StoreCenter.main.userId else { - return - } - - try StoreCenter.main.migrateToken(devServices) - - - let myClubs: [Club] = DataStore.shared.clubs.filter { $0.creator == userId } - let clubIds: [String] = myClubs.map { $0.id } - - myClubs.forEach { club in - DataStore.shared.clubs.insertIntoCurrentService(item: club) - - let courts = DataStore.shared.courts.filter { clubIds.contains($0.club) } - for court in courts { - DataStore.shared.courts.insertIntoCurrentService(item: court) - } - } - - DataStore.shared.user.clubs = Array(clubIds) - DataStore.shared.saveUser() - - DataStore.shared.events.insertAllIntoCurrentService() - DataStore.shared.tournaments.insertAllIntoCurrentService() - DataStore.shared.dateIntervals.insertAllIntoCurrentService() - - for tournament in DataStore.shared.tournaments { - let store = tournament.tournamentStore - - Task { // need to wait for the collections to load - try await Task.sleep(until: .now + .seconds(2)) - - store.teamRegistrations.insertAllIntoCurrentService() - store.rounds.insertAllIntoCurrentService() - store.groupStages.insertAllIntoCurrentService() - store.matches.insertAllIntoCurrentService() - store.playerRegistrations.insertAllIntoCurrentService() - store.teamScores.insertAllIntoCurrentService() - - } - } - } fileprivate static func _patchMissingMatches() { - guard let url = StoreCenter.main.synchronizationApiURL else { - return - } - guard url == "https://padelclub.app/roads/" else { - return - } - let services = Services(url: url) - - for tournament in DataStore.shared.tournaments { - - let store = tournament.tournamentStore - let identifier = tournament.id - - Task { - - do { - // if nothing is online we upload the data - let matches: [Match] = try await services.get(identifier: identifier) - if matches.isEmpty { - store.matches.insertAllIntoCurrentService() - } - - let playerRegistrations: [PlayerRegistration] = try await services.get(identifier: identifier) - if playerRegistrations.isEmpty { - store.playerRegistrations.insertAllIntoCurrentService() - } - - let teamScores: [TeamScore] = try await services.get(identifier: identifier) - if teamScores.isEmpty { - store.teamScores.insertAllIntoCurrentService() - } - - } catch { - Logger.error(error) - } - - } - } - } } diff --git a/PadelClub/Utils/URLs.swift b/PadelClub/Utils/URLs.swift index 16e0fba..777e77a 100644 --- a/PadelClub/Utils/URLs.swift +++ b/PadelClub/Utils/URLs.swift @@ -8,23 +8,23 @@ import Foundation enum URLs: String, Identifiable { - + case httpScheme = "https://" #if DEBUG case activationHost = "xlr.alwaysdata.net" case main = "https://xlr.alwaysdata.net/" - case api = "https://xlr.alwaysdata.net/roads/" +// case api = "https://xlr.alwaysdata.net/roads/" #elseif TESTFLIGHT case activationHost = "xlr.alwaysdata.net" case main = "https://xlr.alwaysdata.net/" - case api = "https://xlr.alwaysdata.net/roads/" +// case api = "https://xlr.alwaysdata.net/roads/" #elseif PRODTEST case activationHost = "padelclub.app" case main = "https://padelclub.app/" - case api = "https://padelclub.app/roads/" +// case api = "https://padelclub.app/roads/" #else case activationHost = "padelclub.app" case main = "https://padelclub.app/" - case api = "https://padelclub.app/roads/" +// case api = "https://padelclub.app/roads/" #endif case subscriptions = "https://apple.co/2Th4vqI" diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 8d44e9c..d71c522 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -15,7 +15,8 @@ final class ServerDataTests: XCTestCase { let password: String = "MyPass1234--" override func setUpWithError() throws { - StoreCenter.main.synchronizationApiURL = "http://127.0.0.1:8000/roads/" +// StoreCenter.main.synchronizationApiURL = "http://127.0.0.1:8000/roads/" + StoreCenter.main.configureURLs(httpScheme: "http://", domain: "127.0.0.1:8000") Task { do { try await self.login() diff --git a/PadelClubTests/SynchronizationTests.swift b/PadelClubTests/SynchronizationTests.swift index 0c2313d..1312527 100644 --- a/PadelClubTests/SynchronizationTests.swift +++ b/PadelClubTests/SynchronizationTests.swift @@ -15,7 +15,7 @@ struct SynchronizationTests { let password: String = "StaxKikoo12" init() { - StoreCenter.main.synchronizationApiURL = "http://127.0.0.1:8000/roads/" + StoreCenter.main.configureURLs(httpScheme: "http://", domain: "127.0.0.1:8000") } @Test func synchronizationTest() async throws { diff --git a/PadelClubTests/TokenExemptionTests.swift b/PadelClubTests/TokenExemptionTests.swift index 923be80..764cd2d 100644 --- a/PadelClubTests/TokenExemptionTests.swift +++ b/PadelClubTests/TokenExemptionTests.swift @@ -16,7 +16,7 @@ final class TokenExemptionTests: XCTestCase { let password: String = "MyPass1234--" override func setUpWithError() throws { - StoreCenter.main.synchronizationApiURL = "http://127.0.0.1:8000/roads/" + StoreCenter.main.configureURLs(httpScheme: "http://", domain: "127.0.0.1:8000") StoreCenter.main.disconnect() } diff --git a/PadelClubTests/UserDataTests.swift b/PadelClubTests/UserDataTests.swift index 8b848d5..5d10a59 100644 --- a/PadelClubTests/UserDataTests.swift +++ b/PadelClubTests/UserDataTests.swift @@ -15,7 +15,7 @@ final class UserDataTests: XCTestCase { let password: String = "MyPass1234--" override func setUpWithError() throws { - StoreCenter.main.synchronizationApiURL = "http://127.0.0.1:8000/roads/" + StoreCenter.main.configureURLs(httpScheme: "http://", domain: "127.0.0.1:8000") } override func tearDownWithError() throws { From 786ad5ee456f3fdec298955824371dc4ac572481 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 28 Nov 2024 15:43:19 +0100 Subject: [PATCH 09/43] Regeneration of classes to handle relationships --- PadelClub/Data/Gen/BaseClub.swift | 11 +++- PadelClub/Data/Gen/BaseCourt.swift | 11 +++- PadelClub/Data/Gen/BaseCustomUser.swift | 9 ++- PadelClub/Data/Gen/BaseDateInterval.swift | 9 ++- PadelClub/Data/Gen/BaseEvent.swift | 12 +++- PadelClub/Data/Gen/BaseGroupStage.swift | 13 +++- PadelClub/Data/Gen/BaseMatch.swift | 12 +++- PadelClub/Data/Gen/BaseMatchScheduler.swift | 8 ++- PadelClub/Data/Gen/BaseMonthData.swift | 6 +- .../Data/Gen/BasePlayerRegistration.swift | 11 +++- PadelClub/Data/Gen/BasePurchase.swift | 11 +++- PadelClub/Data/Gen/BaseRound.swift | 11 +++- PadelClub/Data/Gen/BaseTeamRegistration.swift | 11 +++- PadelClub/Data/Gen/BaseTeamScore.swift | 12 +++- PadelClub/Data/Gen/BaseTournament.swift | 11 +++- PadelClub/Data/Gen/Club.json | 3 +- PadelClub/Data/Gen/Court.json | 3 +- PadelClub/Data/Gen/CustomUser.json | 3 +- PadelClub/Data/Gen/DateInterval.json | 3 +- PadelClub/Data/Gen/Event.json | 3 +- PadelClub/Data/Gen/GroupStage.json | 3 +- PadelClub/Data/Gen/Match.json | 3 +- PadelClub/Data/Gen/PlayerRegistration.json | 3 +- PadelClub/Data/Gen/Purchase.json | 3 +- PadelClub/Data/Gen/Round.json | 3 +- PadelClub/Data/Gen/TeamRegistration.json | 3 +- PadelClub/Data/Gen/TeamScore.json | 3 +- PadelClub/Data/Gen/Tournament.json | 3 +- PadelClub/Data/Gen/generator.py | 65 ++++++++++++++++++- PadelClub/Data/README.md | 14 +++- 30 files changed, 230 insertions(+), 46 deletions(-) diff --git a/PadelClub/Data/Gen/BaseClub.swift b/PadelClub/Data/Gen/BaseClub.swift index 8439123..df90ab9 100644 --- a/PadelClub/Data/Gen/BaseClub.swift +++ b/PadelClub/Data/Gen/BaseClub.swift @@ -81,7 +81,8 @@ class BaseClub: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? "" self.acronym = try container.decodeIfPresent(String.self, forKey: ._acronym) ?? "" @@ -99,7 +100,7 @@ class BaseClub: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.creator, forKey: ._creator) try container.encode(self.name, forKey: ._name) try container.encode(self.acronym, forKey: ._acronym) @@ -136,4 +137,10 @@ class BaseClub: ModelObject, SyncedStorable, Codable { self.courtCount = club.courtCount self.broadcastCode = club.broadcastCode } + static func relationships() -> [Relationship] { + return [ + Relationship(type: CustomUser.self, keyPath: \BaseClub.creator), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseCourt.swift b/PadelClub/Data/Gen/BaseCourt.swift index f3b4f5c..d9d55e5 100644 --- a/PadelClub/Data/Gen/BaseCourt.swift +++ b/PadelClub/Data/Gen/BaseCourt.swift @@ -53,7 +53,8 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? "" self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil @@ -64,7 +65,7 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.index, forKey: ._index) try container.encode(self.club, forKey: ._club) try container.encode(self.name, forKey: ._name) @@ -86,4 +87,10 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { self.exitAllowed = court.exitAllowed self.indoor = court.indoor } + static func relationships() -> [Relationship] { + return [ + Relationship(type: Club.self, keyPath: \BaseCourt.club), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseCustomUser.swift b/PadelClub/Data/Gen/BaseCustomUser.swift index 7c5463a..65eda94 100644 --- a/PadelClub/Data/Gen/BaseCustomUser.swift +++ b/PadelClub/Data/Gen/BaseCustomUser.swift @@ -117,7 +117,8 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.username = try container.decodeIfPresent(String.self, forKey: ._username) ?? "" self.email = try container.decodeIfPresent(String.self, forKey: ._email) ?? "" self.clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? [] @@ -144,7 +145,7 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.username, forKey: ._username) try container.encode(self.email, forKey: ._email) try container.encode(self.clubs, forKey: ._clubs) @@ -194,4 +195,8 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { self.loserBracketMode = customuser.loserBracketMode self.deviceId = customuser.deviceId } + static func relationships() -> [Relationship] { + return [] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseDateInterval.swift b/PadelClub/Data/Gen/BaseDateInterval.swift index 1c678f0..626c433 100644 --- a/PadelClub/Data/Gen/BaseDateInterval.swift +++ b/PadelClub/Data/Gen/BaseDateInterval.swift @@ -49,7 +49,8 @@ class BaseDateInterval: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? "" self.courtIndex = try container.decodeIfPresent(Int.self, forKey: ._courtIndex) ?? 0 self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date() @@ -59,7 +60,7 @@ class BaseDateInterval: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.event, forKey: ._event) try container.encode(self.courtIndex, forKey: ._courtIndex) try container.encode(self.startDate, forKey: ._startDate) @@ -75,4 +76,8 @@ class BaseDateInterval: ModelObject, SyncedStorable, Codable { self.startDate = dateinterval.startDate self.endDate = dateinterval.endDate } + static func relationships() -> [Relationship] { + return [] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseEvent.swift b/PadelClub/Data/Gen/BaseEvent.swift index b184516..d47bc8b 100644 --- a/PadelClub/Data/Gen/BaseEvent.swift +++ b/PadelClub/Data/Gen/BaseEvent.swift @@ -53,7 +53,8 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? nil self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date() @@ -64,7 +65,7 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.creator, forKey: ._creator) try container.encode(self.club, forKey: ._club) try container.encode(self.creationDate, forKey: ._creationDate) @@ -92,4 +93,11 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { self.name = event.name self.tenupId = event.tenupId } + static func relationships() -> [Relationship] { + return [ + Relationship(type: CustomUser.self, keyPath: \BaseEvent.creator), + Relationship(type: Club.self, keyPath: \BaseEvent.club), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseGroupStage.swift b/PadelClub/Data/Gen/BaseGroupStage.swift index dd92f1d..11f3669 100644 --- a/PadelClub/Data/Gen/BaseGroupStage.swift +++ b/PadelClub/Data/Gen/BaseGroupStage.swift @@ -65,7 +65,8 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 self.size = try container.decodeIfPresent(Int.self, forKey: ._size) ?? 0 @@ -79,7 +80,7 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.tournament, forKey: ._tournament) try container.encode(self.index, forKey: ._index) try container.encode(self.size, forKey: ._size) @@ -107,4 +108,10 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { self.step = groupstage.step self.storeId = groupstage.storeId } -} \ No newline at end of file + static func relationships() -> [Relationship] { + return [ + Relationship(type: Tournament.self, keyPath: \BaseGroupStage.tournament), + ] + } + +} diff --git a/PadelClub/Data/Gen/BaseMatch.swift b/PadelClub/Data/Gen/BaseMatch.swift index eeadae7..2f87ecc 100644 --- a/PadelClub/Data/Gen/BaseMatch.swift +++ b/PadelClub/Data/Gen/BaseMatch.swift @@ -89,7 +89,8 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.round = try container.decodeIfPresent(String.self, forKey: ._round) ?? nil self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil @@ -109,7 +110,7 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.round, forKey: ._round) try container.encode(self.groupStage, forKey: ._groupStage) try container.encode(self.startDate, forKey: ._startDate) @@ -155,4 +156,11 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { self.confirmed = match.confirmed self.storeId = match.storeId } + static func relationships() -> [Relationship] { + return [ + Relationship(type: Round.self, keyPath: \BaseMatch.round), + Relationship(type: GroupStage.self, keyPath: \BaseMatch.groupStage), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseMatchScheduler.swift b/PadelClub/Data/Gen/BaseMatchScheduler.swift index 2fbcda4..bd7163a 100644 --- a/PadelClub/Data/Gen/BaseMatchScheduler.swift +++ b/PadelClub/Data/Gen/BaseMatchScheduler.swift @@ -142,4 +142,10 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable self.storeId = matchscheduler.storeId } -} + static func relationships() -> [Relationship] { + return [ + Relationship(type: Tournament.self, keyPath: \BaseMatchScheduler.tournament), + ] + } + +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseMonthData.swift b/PadelClub/Data/Gen/BaseMonthData.swift index 5ee2df1..5bb1d57 100644 --- a/PadelClub/Data/Gen/BaseMonthData.swift +++ b/PadelClub/Data/Gen/BaseMonthData.swift @@ -110,4 +110,8 @@ class BaseMonthData: ModelObject, Storable, Codable { self.dataModelIdentifier = monthdata.dataModelIdentifier self.fileModelIdentifier = monthdata.fileModelIdentifier } -} + static func relationships() -> [Relationship] { + return [] + } + +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BasePlayerRegistration.swift b/PadelClub/Data/Gen/BasePlayerRegistration.swift index 4b146da..f82711b 100644 --- a/PadelClub/Data/Gen/BasePlayerRegistration.swift +++ b/PadelClub/Data/Gen/BasePlayerRegistration.swift @@ -109,7 +109,8 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil self.firstName = try container.decodeIfPresent(String.self, forKey: ._firstName) ?? "" self.lastName = try container.decodeIfPresent(String.self, forKey: ._lastName) ?? "" @@ -134,7 +135,7 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.teamRegistration, forKey: ._teamRegistration) try container.encode(self.firstName, forKey: ._firstName) try container.encode(self.lastName, forKey: ._lastName) @@ -185,4 +186,10 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { self.hasArrived = playerregistration.hasArrived self.storeId = playerregistration.storeId } + static func relationships() -> [Relationship] { + return [ + Relationship(type: TeamRegistration.self, keyPath: \BasePlayerRegistration.teamRegistration), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BasePurchase.swift b/PadelClub/Data/Gen/BasePurchase.swift index 4b544dc..e5ad6d6 100644 --- a/PadelClub/Data/Gen/BasePurchase.swift +++ b/PadelClub/Data/Gen/BasePurchase.swift @@ -56,7 +56,8 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(UInt64.self, forKey: .id) ?? 0 - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: .lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: .lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.user = try container.decodeEncrypted(key: .user) self.purchaseDate = try container.decodeIfPresent(Date.self, forKey: .purchaseDate) ?? Date() self.productId = try container.decodeIfPresent(String.self, forKey: .productId) ?? "" @@ -68,7 +69,7 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: .id) - try container.encode(self.lastUpdate, forKey: .lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: .lastUpdate) try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user) try container.encode(self.purchaseDate, forKey: .purchaseDate) try container.encode(self.productId, forKey: .productId) @@ -92,4 +93,10 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { self.revocationDate = purchase.revocationDate self.expirationDate = purchase.expirationDate } + static func relationships() -> [Relationship] { + return [ + Relationship(type: CustomUser.self, keyPath: \BasePurchase.user), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseRound.swift b/PadelClub/Data/Gen/BaseRound.swift index d0b5402..4115b21 100644 --- a/PadelClub/Data/Gen/BaseRound.swift +++ b/PadelClub/Data/Gen/BaseRound.swift @@ -65,7 +65,8 @@ class BaseRound: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 self.parent = try container.decodeIfPresent(String.self, forKey: ._parent) ?? nil @@ -79,7 +80,7 @@ class BaseRound: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.tournament, forKey: ._tournament) try container.encode(self.index, forKey: ._index) try container.encode(self.parent, forKey: ._parent) @@ -107,4 +108,10 @@ class BaseRound: ModelObject, SyncedStorable, Codable { self.loserBracketMode = round.loserBracketMode self.storeId = round.storeId } + static func relationships() -> [Relationship] { + return [ + Relationship(type: Tournament.self, keyPath: \BaseRound.tournament), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseTeamRegistration.swift b/PadelClub/Data/Gen/BaseTeamRegistration.swift index 007ef8d..bc22dcf 100644 --- a/PadelClub/Data/Gen/BaseTeamRegistration.swift +++ b/PadelClub/Data/Gen/BaseTeamRegistration.swift @@ -117,7 +117,8 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil self.registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate) ?? nil @@ -144,7 +145,7 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.tournament, forKey: ._tournament) try container.encode(self.groupStage, forKey: ._groupStage) try container.encode(self.registrationDate, forKey: ._registrationDate) @@ -199,4 +200,10 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { self.pointsEarned = teamregistration.pointsEarned self.storeId = teamregistration.storeId } + static func relationships() -> [Relationship] { + return [ + Relationship(type: GroupStage.self, keyPath: \BaseTeamRegistration.groupStage), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseTeamScore.swift b/PadelClub/Data/Gen/BaseTeamScore.swift index b4c415f..b9d6508 100644 --- a/PadelClub/Data/Gen/BaseTeamScore.swift +++ b/PadelClub/Data/Gen/BaseTeamScore.swift @@ -57,7 +57,8 @@ class BaseTeamScore: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.match = try container.decodeIfPresent(String.self, forKey: ._match) ?? "" self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil self.score = try container.decodeIfPresent(String.self, forKey: ._score) ?? nil @@ -69,7 +70,7 @@ class BaseTeamScore: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.match, forKey: ._match) try container.encode(self.teamRegistration, forKey: ._teamRegistration) try container.encode(self.score, forKey: ._score) @@ -98,4 +99,11 @@ class BaseTeamScore: ModelObject, SyncedStorable, Codable { self.luckyLoser = teamscore.luckyLoser self.storeId = teamscore.storeId } + static func relationships() -> [Relationship] { + return [ + Relationship(type: Match.self, keyPath: \BaseTeamScore.match), + Relationship(type: TeamRegistration.self, keyPath: \BaseTeamScore.teamRegistration), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseTournament.swift b/PadelClub/Data/Gen/BaseTournament.swift index ed5b100..d5c8a60 100644 --- a/PadelClub/Data/Gen/BaseTournament.swift +++ b/PadelClub/Data/Gen/BaseTournament.swift @@ -254,7 +254,8 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date() + let dateString = try container.decode(String.self, forKey: ._lastUpdate) + self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? nil self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date() @@ -300,7 +301,7 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(self.lastUpdate, forKey: ._lastUpdate) + try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.event, forKey: ._event) try container.encode(self.name, forKey: ._name) try container.encode(self.startDate, forKey: ._startDate) @@ -393,4 +394,10 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { self.publishRankings = tournament.publishRankings self.loserBracketMode = tournament.loserBracketMode } + static func relationships() -> [Relationship] { + return [ + Relationship(type: Event.self, keyPath: \BaseTournament.event), + ] + } + } \ No newline at end of file diff --git a/PadelClub/Data/Gen/Club.json b/PadelClub/Data/Gen/Club.json index 683e716..08f7b14 100644 --- a/PadelClub/Data/Gen/Club.json +++ b/PadelClub/Data/Gen/Club.json @@ -12,7 +12,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "creator", diff --git a/PadelClub/Data/Gen/Court.json b/PadelClub/Data/Gen/Court.json index f2037bb..46a6c61 100644 --- a/PadelClub/Data/Gen/Court.json +++ b/PadelClub/Data/Gen/Court.json @@ -12,7 +12,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "index", diff --git a/PadelClub/Data/Gen/CustomUser.json b/PadelClub/Data/Gen/CustomUser.json index b7e3595..c45cf14 100644 --- a/PadelClub/Data/Gen/CustomUser.json +++ b/PadelClub/Data/Gen/CustomUser.json @@ -15,7 +15,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "username", diff --git a/PadelClub/Data/Gen/DateInterval.json b/PadelClub/Data/Gen/DateInterval.json index e046b16..7e711c3 100644 --- a/PadelClub/Data/Gen/DateInterval.json +++ b/PadelClub/Data/Gen/DateInterval.json @@ -12,7 +12,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "event", diff --git a/PadelClub/Data/Gen/Event.json b/PadelClub/Data/Gen/Event.json index b8f1bec..38000dd 100644 --- a/PadelClub/Data/Gen/Event.json +++ b/PadelClub/Data/Gen/Event.json @@ -12,7 +12,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "creator", diff --git a/PadelClub/Data/Gen/GroupStage.json b/PadelClub/Data/Gen/GroupStage.json index eb0627e..4bb5e96 100644 --- a/PadelClub/Data/Gen/GroupStage.json +++ b/PadelClub/Data/Gen/GroupStage.json @@ -12,7 +12,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "tournament", diff --git a/PadelClub/Data/Gen/Match.json b/PadelClub/Data/Gen/Match.json index ef04e08..153bea5 100644 --- a/PadelClub/Data/Gen/Match.json +++ b/PadelClub/Data/Gen/Match.json @@ -14,7 +14,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "round", diff --git a/PadelClub/Data/Gen/PlayerRegistration.json b/PadelClub/Data/Gen/PlayerRegistration.json index c457557..f350772 100644 --- a/PadelClub/Data/Gen/PlayerRegistration.json +++ b/PadelClub/Data/Gen/PlayerRegistration.json @@ -15,7 +15,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "teamRegistration", diff --git a/PadelClub/Data/Gen/Purchase.json b/PadelClub/Data/Gen/Purchase.json index 660d9a2..bb8bdeb 100644 --- a/PadelClub/Data/Gen/Purchase.json +++ b/PadelClub/Data/Gen/Purchase.json @@ -11,7 +11,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "user", diff --git a/PadelClub/Data/Gen/Round.json b/PadelClub/Data/Gen/Round.json index 6004232..9120c80 100644 --- a/PadelClub/Data/Gen/Round.json +++ b/PadelClub/Data/Gen/Round.json @@ -15,7 +15,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "tournament", diff --git a/PadelClub/Data/Gen/TeamRegistration.json b/PadelClub/Data/Gen/TeamRegistration.json index 88ac151..2a25c51 100644 --- a/PadelClub/Data/Gen/TeamRegistration.json +++ b/PadelClub/Data/Gen/TeamRegistration.json @@ -15,7 +15,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "tournament", diff --git a/PadelClub/Data/Gen/TeamScore.json b/PadelClub/Data/Gen/TeamScore.json index 7475164..b283e88 100644 --- a/PadelClub/Data/Gen/TeamScore.json +++ b/PadelClub/Data/Gen/TeamScore.json @@ -15,7 +15,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "match", diff --git a/PadelClub/Data/Gen/Tournament.json b/PadelClub/Data/Gen/Tournament.json index e5368f9..3f24abf 100644 --- a/PadelClub/Data/Gen/Tournament.json +++ b/PadelClub/Data/Gen/Tournament.json @@ -15,7 +15,8 @@ }, { "name": "lastUpdate", - "type": "Date" + "type": "Date", + "option": "fractional" }, { "name": "event", diff --git a/PadelClub/Data/Gen/generator.py b/PadelClub/Data/Gen/generator.py index 68cb248..b2369c9 100644 --- a/PadelClub/Data/Gen/generator.py +++ b/PadelClub/Data/Gen/generator.py @@ -84,6 +84,11 @@ class SwiftModelGenerator: # Copy method lines.extend(self._generate_copy_method(model_name, properties)) + lines.append("") + + # Add relationships function + lines.extend(self._generate_relationships(model_name, properties)) + lines.append("") lines.append("}") return "\n".join(lines) @@ -255,6 +260,7 @@ class SwiftModelGenerator: type_name = prop['type'] is_optional = prop.get("optional", False) default_value = prop.get("defaultValue", "nil" if is_optional else self._get_default_value(type_name)) + option = prop.get("option") # Use the correct case reference based on observable flag case_ref = f"_{name}" if is_observable else name @@ -269,8 +275,19 @@ class SwiftModelGenerator: elif enc_type in ["tournament_payment", "tournament_iscanceled"]: lines.append(f" self.{name} = try Self._decode{name.capitalize()}(container: container)") else: - lines.append(f" self.{name} = try container.decodeIfPresent({type_name}.self, forKey: .{case_ref}) ?? {default_value}") - + # Handle Date with fractional option + if type_name == "Date" and option == "fractional": + if is_optional: + lines.append(f" if let dateString = try container.decodeIfPresent(String.self, forKey: .{case_ref}) {{") + lines.append(f" self.{name} = Date.iso8601FractionalFormatter.date(from: dateString)") + lines.append(" } else {") + lines.append(f" self.{name} = {default_value}") + lines.append(" }") + else: + lines.append(f" let dateString = try container.decode(String.self, forKey: .{case_ref})") + lines.append(f" self.{name} = Date.iso8601FractionalFormatter.date(from: dateString) ?? {default_value}") + else: + lines.append(f" self.{name} = try container.decodeIfPresent({type_name}.self, forKey: .{case_ref}) ?? {default_value}") lines.append(" }") return lines @@ -280,7 +297,9 @@ class SwiftModelGenerator: for prop in properties: name = prop['name'] + type_name = prop['type'] is_optional = prop.get('optional', False) + option = prop.get("option") # Use the correct case reference based on observable flag case_ref = f"_{name}" if is_observable else name @@ -295,7 +314,17 @@ class SwiftModelGenerator: elif enc_type in ["tournament_payment", "tournament_iscanceled"]: lines.append(f" try _encode{name.capitalize()}(container: &container)") else: - lines.append(f" try container.encode(self.{name}, forKey: .{case_ref})") + if type_name == "Date" and option == "fractional": + if is_optional: + lines.append(f" if let date = self.{name} {{") + lines.append(f" try container.encode(Date.iso8601FractionalFormatter.string(from: date), forKey: .{case_ref})") + lines.append(" } else {") + lines.append(f" try container.encodeNil(forKey: .{case_ref})") + lines.append(" }") + else: + lines.append(f" try container.encode(Date.iso8601FractionalFormatter.string(from: self.{name}), forKey: .{case_ref})") + else: + lines.append(f" try container.encode(self.{name}, forKey: .{case_ref})") lines.append(" }") return lines @@ -325,6 +354,36 @@ class SwiftModelGenerator: f" static func filterByStoreIdentifier() -> Bool {{ return {str(filter_by_store).lower()} }}" ] + def _generate_relationships(self, model_name, properties: List[Dict[str, Any]]) -> List[str]: + # Find all properties with foreign keys + foreign_key_props = [p for p in properties if "foreignKey" in p] + + if not foreign_key_props: + # If no foreign keys, return empty array + return [ + " static func relationships() -> [Relationship] {", + " return []", + " }" + ] + + lines = [ + " static func relationships() -> [Relationship] {", + " return [" + ] + + # Generate relationship entries + for prop in foreign_key_props: + name = prop["name"] + foreign_key = prop["foreignKey"].rstrip('*') # Remove asterisk if present + lines.append(f" Relationship(type: {foreign_key}.self, keyPath: \\Base{model_name}.{name}),") + + # Close the array and function + lines.extend([ + " ]", + " }" + ]) + + return lines def _get_default_value(self, type_name: str) -> str: """Get default value for non-optional types""" diff --git a/PadelClub/Data/README.md b/PadelClub/Data/README.md index e63debb..22a1795 100644 --- a/PadelClub/Data/README.md +++ b/PadelClub/Data/README.md @@ -1,4 +1,16 @@ -# Procédure d'ajout de champ dans une classe +# Procédure de création de classe +Dans Swift: +- Dans Data > Gen > créer un nouveau fichier json pour la classe + - Le paramètre "foreignKey" permet de générer une méthode qui récupère l'objet parent. Ajouter une étoile permet d'indiquer que l'on cherche l'objet dans le Store de l'objet et non le Store.main. +- Pour générer les fichiers, on se place dans le répertoire de generator.py et on lance la commande : python generator.py -i . -o . + +Dans Django: +- Les modèles de base doivent étendre BaseModel. +- Les modèles stockés dans des répertoires doivent étendre SideStoreModel. +- Les classes d'admin doivent étendre AutoUpdateAdmin + + +# Procédure d'ajout de champ dans une classe Dans Swift: - Ajouter le champ dans classe From eb1f69ec97097fa07a5c0a8328dc34602f082030 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 2 Dec 2024 15:04:12 +0100 Subject: [PATCH 10/43] Improves data generation --- PadelClub/Data/Club.swift | 2 -- PadelClub/Data/Court.swift | 2 -- PadelClub/Data/CustomUser.swift | 2 -- PadelClub/Data/Event.swift | 2 -- PadelClub/Data/Gen/BaseClub.swift | 18 +++++-------- PadelClub/Data/Gen/BaseCourt.swift | 16 ++++------- PadelClub/Data/Gen/BaseCustomUser.swift | 16 ++++------- PadelClub/Data/Gen/BaseDateInterval.swift | 16 ++++------- PadelClub/Data/Gen/BaseEvent.swift | 16 ++++------- PadelClub/Data/Gen/BaseGroupStage.swift | 27 +++++-------------- PadelClub/Data/Gen/BaseMatch.swift | 25 +++++------------ PadelClub/Data/Gen/BaseMatchScheduler.swift | 17 +++++------- PadelClub/Data/Gen/BaseMonthData.swift | 8 +++--- .../Data/Gen/BasePlayerRegistration.swift | 25 +++++------------ PadelClub/Data/Gen/BasePurchase.swift | 16 ++++------- PadelClub/Data/Gen/BaseRound.swift | 25 +++++------------ PadelClub/Data/Gen/BaseTeamRegistration.swift | 25 +++++------------ PadelClub/Data/Gen/BaseTeamScore.swift | 25 +++++------------ PadelClub/Data/Gen/BaseTournament.swift | 16 ++++------- PadelClub/Data/Gen/Club.json | 5 ---- PadelClub/Data/Gen/Court.json | 5 ---- PadelClub/Data/Gen/CustomUser.json | 5 ---- PadelClub/Data/Gen/DateInterval.json | 5 ---- PadelClub/Data/Gen/Event.json | 5 ---- PadelClub/Data/Gen/GroupStage.json | 11 -------- PadelClub/Data/Gen/Match.json | 10 ------- PadelClub/Data/Gen/MatchScheduler.json | 5 ---- PadelClub/Data/Gen/PlayerRegistration.json | 11 -------- PadelClub/Data/Gen/Purchase.json | 5 ---- PadelClub/Data/Gen/Round.json | 11 -------- PadelClub/Data/Gen/TeamRegistration.json | 11 -------- PadelClub/Data/Gen/TeamScore.json | 11 -------- PadelClub/Data/Gen/Tournament.json | 5 ---- PadelClub/Data/Gen/generator.py | 23 +++++++++------- PadelClub/Data/TeamRegistration.swift | 2 +- PadelClub/Data/Tournament.swift | 2 -- .../Tournament/Subscription/Purchase.swift | 2 -- 37 files changed, 99 insertions(+), 334 deletions(-) diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index 41b6c2e..19a727e 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -34,8 +34,6 @@ final class Club: BaseClub { // var broadcastCode: String? //// var alphabeticalName: Bool = false - var storeId: String? { return nil } - internal init(creator: String? = nil, name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil, courtCount: Int = 2, broadcastCode: String? = nil) { super.init() diff --git a/PadelClub/Data/Court.swift b/PadelClub/Data/Court.swift index d5bf930..925b9d9 100644 --- a/PadelClub/Data/Court.swift +++ b/PadelClub/Data/Court.swift @@ -16,8 +16,6 @@ final class Court: BaseCourt { lhs.id == rhs.id } - var storeId: String? { return nil } - init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) { super.init() diff --git a/PadelClub/Data/CustomUser.swift b/PadelClub/Data/CustomUser.swift index 053f02f..0624736 100644 --- a/PadelClub/Data/CustomUser.swift +++ b/PadelClub/Data/CustomUser.swift @@ -48,8 +48,6 @@ class CustomUser: BaseCustomUser, UserBase { // // var deviceId: String? - var storeId: String? { return nil } - init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) { super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country, loserBracketMode: loserBracketMode) diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index 85df1e4..d45ff18 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -12,8 +12,6 @@ import SwiftUI @Observable final class Event: BaseEvent { - var storeId: String? { return nil } - internal init(creator: String? = nil, club: String? = nil, name: String? = nil, tenupId: String? = nil) { super.init(creator: creator, club: club, name: name, tenupId: tenupId) diff --git a/PadelClub/Data/Gen/BaseClub.swift b/PadelClub/Data/Gen/BaseClub.swift index df90ab9..61c8fbb 100644 --- a/PadelClub/Data/Gen/BaseClub.swift +++ b/PadelClub/Data/Gen/BaseClub.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseClub: ModelObject, SyncedStorable, Codable { +class BaseClub: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "clubs" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() - var lastUpdate: Date = Date() var creator: String? = nil var name: String = "" var acronym: String = "" @@ -29,7 +28,6 @@ class BaseClub: ModelObject, SyncedStorable, Codable { init( id: String = Store.randomId(), - lastUpdate: Date = Date(), creator: String? = nil, name: String = "", acronym: String = "", @@ -45,7 +43,6 @@ class BaseClub: ModelObject, SyncedStorable, Codable { ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.creator = creator self.name = name self.acronym = acronym @@ -62,7 +59,6 @@ class BaseClub: ModelObject, SyncedStorable, Codable { enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _creator = "creator" case _name = "name" case _acronym = "acronym" @@ -78,11 +74,8 @@ class BaseClub: ModelObject, SyncedStorable, Codable { } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? "" self.acronym = try container.decodeIfPresent(String.self, forKey: ._acronym) ?? "" @@ -95,12 +88,12 @@ class BaseClub: ModelObject, SyncedStorable, Codable { self.longitude = try container.decodeIfPresent(Double.self, forKey: ._longitude) ?? nil self.courtCount = try container.decodeIfPresent(Int.self, forKey: ._courtCount) ?? 2 self.broadcastCode = try container.decodeIfPresent(String.self, forKey: ._broadcastCode) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.creator, forKey: ._creator) try container.encode(self.name, forKey: ._name) try container.encode(self.acronym, forKey: ._acronym) @@ -113,6 +106,7 @@ class BaseClub: ModelObject, SyncedStorable, Codable { try container.encode(self.longitude, forKey: ._longitude) try container.encode(self.courtCount, forKey: ._courtCount) try container.encode(self.broadcastCode, forKey: ._broadcastCode) + try super.encode(to: encoder) } func creatorValue() -> CustomUser? { @@ -123,7 +117,6 @@ class BaseClub: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let club = other as? BaseClub else { return } self.id = club.id - self.lastUpdate = club.lastUpdate self.creator = club.creator self.name = club.name self.acronym = club.acronym @@ -137,10 +130,11 @@ class BaseClub: ModelObject, SyncedStorable, Codable { self.courtCount = club.courtCount self.broadcastCode = club.broadcastCode } + static func relationships() -> [Relationship] { return [ Relationship(type: CustomUser.self, keyPath: \BaseClub.creator), ] } -} \ No newline at end of file +} diff --git a/PadelClub/Data/Gen/BaseCourt.swift b/PadelClub/Data/Gen/BaseCourt.swift index d9d55e5..bb3dbed 100644 --- a/PadelClub/Data/Gen/BaseCourt.swift +++ b/PadelClub/Data/Gen/BaseCourt.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseCourt: ModelObject, SyncedStorable, Codable { +class BaseCourt: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "courts" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() - var lastUpdate: Date = Date() var index: Int = 0 var club: String = "" var name: String? = nil @@ -22,7 +21,6 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { init( id: String = Store.randomId(), - lastUpdate: Date = Date(), index: Int = 0, club: String = "", name: String? = nil, @@ -31,7 +29,6 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.index = index self.club = club self.name = name @@ -41,7 +38,6 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _index = "index" case _club = "club" case _name = "name" @@ -50,27 +46,25 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? "" self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil self.exitAllowed = try container.decodeIfPresent(Bool.self, forKey: ._exitAllowed) ?? false self.indoor = try container.decodeIfPresent(Bool.self, forKey: ._indoor) ?? false + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.index, forKey: ._index) try container.encode(self.club, forKey: ._club) try container.encode(self.name, forKey: ._name) try container.encode(self.exitAllowed, forKey: ._exitAllowed) try container.encode(self.indoor, forKey: ._indoor) + try super.encode(to: encoder) } func clubValue() -> Club? { @@ -80,13 +74,13 @@ class BaseCourt: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let court = other as? BaseCourt else { return } self.id = court.id - self.lastUpdate = court.lastUpdate self.index = court.index self.club = court.club self.name = court.name self.exitAllowed = court.exitAllowed self.indoor = court.indoor } + static func relationships() -> [Relationship] { return [ Relationship(type: Club.self, keyPath: \BaseCourt.club), diff --git a/PadelClub/Data/Gen/BaseCustomUser.swift b/PadelClub/Data/Gen/BaseCustomUser.swift index 65eda94..bf5e67d 100644 --- a/PadelClub/Data/Gen/BaseCustomUser.swift +++ b/PadelClub/Data/Gen/BaseCustomUser.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseCustomUser: ModelObject, SyncedStorable, Codable { +class BaseCustomUser: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "users" } static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() - var lastUpdate: Date = Date() var username: String = "" var email: String = "" var clubs: [String] = [] @@ -38,7 +37,6 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { init( id: String = Store.randomId(), - lastUpdate: Date = Date(), username: String = "", email: String = "", clubs: [String] = [], @@ -63,7 +61,6 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.username = username self.email = email self.clubs = clubs @@ -89,7 +86,6 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _username = "username" case _email = "email" case _clubs = "clubs" @@ -114,11 +110,8 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.username = try container.decodeIfPresent(String.self, forKey: ._username) ?? "" self.email = try container.decodeIfPresent(String.self, forKey: ._email) ?? "" self.clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? [] @@ -140,12 +133,12 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { self.loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference) ?? nil self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic self.deviceId = try container.decodeIfPresent(String.self, forKey: ._deviceId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.username, forKey: ._username) try container.encode(self.email, forKey: ._email) try container.encode(self.clubs, forKey: ._clubs) @@ -167,12 +160,12 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { try container.encode(self.loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference) try container.encode(self.loserBracketMode, forKey: ._loserBracketMode) try container.encode(self.deviceId, forKey: ._deviceId) + try super.encode(to: encoder) } func copy(from other: any Storable) { guard let customuser = other as? BaseCustomUser else { return } self.id = customuser.id - self.lastUpdate = customuser.lastUpdate self.username = customuser.username self.email = customuser.email self.clubs = customuser.clubs @@ -195,6 +188,7 @@ class BaseCustomUser: ModelObject, SyncedStorable, Codable { self.loserBracketMode = customuser.loserBracketMode self.deviceId = customuser.deviceId } + static func relationships() -> [Relationship] { return [] } diff --git a/PadelClub/Data/Gen/BaseDateInterval.swift b/PadelClub/Data/Gen/BaseDateInterval.swift index 626c433..0ba359a 100644 --- a/PadelClub/Data/Gen/BaseDateInterval.swift +++ b/PadelClub/Data/Gen/BaseDateInterval.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseDateInterval: ModelObject, SyncedStorable, Codable { +class BaseDateInterval: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "date-intervals" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() - var lastUpdate: Date = Date() var event: String = "" var courtIndex: Int = 0 var startDate: Date = Date() @@ -21,7 +20,6 @@ class BaseDateInterval: ModelObject, SyncedStorable, Codable { init( id: String = Store.randomId(), - lastUpdate: Date = Date(), event: String = "", courtIndex: Int = 0, startDate: Date = Date(), @@ -29,7 +27,6 @@ class BaseDateInterval: ModelObject, SyncedStorable, Codable { ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.event = event self.courtIndex = courtIndex self.startDate = startDate @@ -38,7 +35,6 @@ class BaseDateInterval: ModelObject, SyncedStorable, Codable { enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _event = "event" case _courtIndex = "courtIndex" case _startDate = "startDate" @@ -46,36 +42,34 @@ class BaseDateInterval: ModelObject, SyncedStorable, Codable { } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? "" self.courtIndex = try container.decodeIfPresent(Int.self, forKey: ._courtIndex) ?? 0 self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date() self.endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? Date() + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.event, forKey: ._event) try container.encode(self.courtIndex, forKey: ._courtIndex) try container.encode(self.startDate, forKey: ._startDate) try container.encode(self.endDate, forKey: ._endDate) + try super.encode(to: encoder) } func copy(from other: any Storable) { guard let dateinterval = other as? BaseDateInterval else { return } self.id = dateinterval.id - self.lastUpdate = dateinterval.lastUpdate self.event = dateinterval.event self.courtIndex = dateinterval.courtIndex self.startDate = dateinterval.startDate self.endDate = dateinterval.endDate } + static func relationships() -> [Relationship] { return [] } diff --git a/PadelClub/Data/Gen/BaseEvent.swift b/PadelClub/Data/Gen/BaseEvent.swift index d47bc8b..ee7e5f2 100644 --- a/PadelClub/Data/Gen/BaseEvent.swift +++ b/PadelClub/Data/Gen/BaseEvent.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseEvent: ModelObject, SyncedStorable, Codable { +class BaseEvent: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "events" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() - var lastUpdate: Date = Date() var creator: String? = nil var club: String? = nil var creationDate: Date = Date() @@ -22,7 +21,6 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { init( id: String = Store.randomId(), - lastUpdate: Date = Date(), creator: String? = nil, club: String? = nil, creationDate: Date = Date(), @@ -31,7 +29,6 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.creator = creator self.club = club self.creationDate = creationDate @@ -41,7 +38,6 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _creator = "creator" case _club = "club" case _creationDate = "creationDate" @@ -50,27 +46,25 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? nil self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date() self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil self.tenupId = try container.decodeIfPresent(String.self, forKey: ._tenupId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.creator, forKey: ._creator) try container.encode(self.club, forKey: ._club) try container.encode(self.creationDate, forKey: ._creationDate) try container.encode(self.name, forKey: ._name) try container.encode(self.tenupId, forKey: ._tenupId) + try super.encode(to: encoder) } func creatorValue() -> CustomUser? { @@ -86,13 +80,13 @@ class BaseEvent: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let event = other as? BaseEvent else { return } self.id = event.id - self.lastUpdate = event.lastUpdate self.creator = event.creator self.club = event.club self.creationDate = event.creationDate self.name = event.name self.tenupId = event.tenupId } + static func relationships() -> [Relationship] { return [ Relationship(type: CustomUser.self, keyPath: \BaseEvent.creator), diff --git a/PadelClub/Data/Gen/BaseGroupStage.swift b/PadelClub/Data/Gen/BaseGroupStage.swift index 11f3669..c237546 100644 --- a/PadelClub/Data/Gen/BaseGroupStage.swift +++ b/PadelClub/Data/Gen/BaseGroupStage.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseGroupStage: ModelObject, SyncedStorable, Codable { +class BaseGroupStage: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "group-stages" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() - var lastUpdate: Date = Date() var tournament: String = "" var index: Int = 0 var size: Int = 0 @@ -21,23 +20,19 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { var startDate: Date? = nil var name: String? = nil var step: Int = 0 - var storeId: String? = nil init( id: String = Store.randomId(), - lastUpdate: Date = Date(), tournament: String = "", index: Int = 0, size: Int = 0, format: MatchFormat? = nil, startDate: Date? = nil, name: String? = nil, - step: Int = 0, - storeId: String? = nil + step: Int = 0 ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.tournament = tournament self.index = index self.size = size @@ -45,12 +40,10 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { self.startDate = startDate self.name = name self.step = step - self.storeId = storeId } enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _tournament = "tournament" case _index = "index" case _size = "size" @@ -58,15 +51,11 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { case _startDate = "startDate" case _name = "name" case _step = "step" - case _storeId = "storeId" } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 self.size = try container.decodeIfPresent(Int.self, forKey: ._size) ?? 0 @@ -74,13 +63,12 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil self.step = try container.decodeIfPresent(Int.self, forKey: ._step) ?? 0 - self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.tournament, forKey: ._tournament) try container.encode(self.index, forKey: ._index) try container.encode(self.size, forKey: ._size) @@ -88,7 +76,7 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { try container.encode(self.startDate, forKey: ._startDate) try container.encode(self.name, forKey: ._name) try container.encode(self.step, forKey: ._step) - try container.encode(self.storeId, forKey: ._storeId) + try super.encode(to: encoder) } func tournamentValue() -> Tournament? { @@ -98,7 +86,6 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let groupstage = other as? BaseGroupStage else { return } self.id = groupstage.id - self.lastUpdate = groupstage.lastUpdate self.tournament = groupstage.tournament self.index = groupstage.index self.size = groupstage.size @@ -106,12 +93,12 @@ class BaseGroupStage: ModelObject, SyncedStorable, Codable { self.startDate = groupstage.startDate self.name = groupstage.name self.step = groupstage.step - self.storeId = groupstage.storeId } + static func relationships() -> [Relationship] { return [ Relationship(type: Tournament.self, keyPath: \BaseGroupStage.tournament), ] } -} +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseMatch.swift b/PadelClub/Data/Gen/BaseMatch.swift index 2f87ecc..9328e70 100644 --- a/PadelClub/Data/Gen/BaseMatch.swift +++ b/PadelClub/Data/Gen/BaseMatch.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseMatch: ModelObject, SyncedStorable, Codable { +class BaseMatch: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "matches" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() - var lastUpdate: Date = Date() var round: String? = nil var groupStage: String? = nil var startDate: Date? = nil @@ -27,11 +26,9 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { var disabled: Bool = false var courtIndex: Int? = nil var confirmed: Bool = false - var storeId: String? = nil init( id: String = Store.randomId(), - lastUpdate: Date = Date(), round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, @@ -44,12 +41,10 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, - confirmed: Bool = false, - storeId: String? = nil + confirmed: Bool = false ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.round = round self.groupStage = groupStage self.startDate = startDate @@ -63,12 +58,10 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { self.disabled = disabled self.courtIndex = courtIndex self.confirmed = confirmed - self.storeId = storeId } enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _round = "round" case _groupStage = "groupStage" case _startDate = "startDate" @@ -82,15 +75,11 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { case _disabled = "disabled" case _courtIndex = "courtIndex" case _confirmed = "confirmed" - case _storeId = "storeId" } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.round = try container.decodeIfPresent(String.self, forKey: ._round) ?? nil self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil @@ -104,13 +93,12 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { self.disabled = try container.decodeIfPresent(Bool.self, forKey: ._disabled) ?? false self.courtIndex = try container.decodeIfPresent(Int.self, forKey: ._courtIndex) ?? nil self.confirmed = try container.decodeIfPresent(Bool.self, forKey: ._confirmed) ?? false - self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.round, forKey: ._round) try container.encode(self.groupStage, forKey: ._groupStage) try container.encode(self.startDate, forKey: ._startDate) @@ -124,7 +112,7 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { try container.encode(self.disabled, forKey: ._disabled) try container.encode(self.courtIndex, forKey: ._courtIndex) try container.encode(self.confirmed, forKey: ._confirmed) - try container.encode(self.storeId, forKey: ._storeId) + try super.encode(to: encoder) } func roundValue() -> Round? { @@ -140,7 +128,6 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let match = other as? BaseMatch else { return } self.id = match.id - self.lastUpdate = match.lastUpdate self.round = match.round self.groupStage = match.groupStage self.startDate = match.startDate @@ -154,8 +141,8 @@ class BaseMatch: ModelObject, SyncedStorable, Codable { self.disabled = match.disabled self.courtIndex = match.courtIndex self.confirmed = match.confirmed - self.storeId = match.storeId } + static func relationships() -> [Relationship] { return [ Relationship(type: Round.self, keyPath: \BaseMatch.round), diff --git a/PadelClub/Data/Gen/BaseMatchScheduler.swift b/PadelClub/Data/Gen/BaseMatchScheduler.swift index bd7163a..9334070 100644 --- a/PadelClub/Data/Gen/BaseMatchScheduler.swift +++ b/PadelClub/Data/Gen/BaseMatchScheduler.swift @@ -6,7 +6,7 @@ import LeStorage import SwiftUI @Observable -class BaseMatchScheduler: ModelObject, Storable, Codable { +class BaseMatchScheduler: BaseModelObject, Storable { static func resourceName() -> String { return "match-scheduler" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } @@ -26,7 +26,6 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { var groupStageChunkCount: Int? = nil var overrideCourtsUnavailability: Bool = false var shouldTryToFillUpCourtsAvailable: Bool = false - var storeId: String? = nil init( id: String = Store.randomId(), @@ -42,8 +41,7 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { shouldEndRoundBeforeStartingNext: Bool = false, groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false, - shouldTryToFillUpCourtsAvailable: Bool = false, - storeId: String? = nil + shouldTryToFillUpCourtsAvailable: Bool = false ) { super.init() self.id = id @@ -60,7 +58,6 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { self.groupStageChunkCount = groupStageChunkCount self.overrideCourtsUnavailability = overrideCourtsUnavailability self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable - self.storeId = storeId } enum CodingKeys: String, CodingKey { @@ -78,11 +75,9 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { case _groupStageChunkCount = "groupStageChunkCount" case _overrideCourtsUnavailability = "overrideCourtsUnavailability" case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable" - case _storeId = "storeId" } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" @@ -98,10 +93,10 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { self.groupStageChunkCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageChunkCount) ?? nil self.overrideCourtsUnavailability = try container.decodeIfPresent(Bool.self, forKey: ._overrideCourtsUnavailability) ?? false self.shouldTryToFillUpCourtsAvailable = try container.decodeIfPresent(Bool.self, forKey: ._shouldTryToFillUpCourtsAvailable) ?? false - self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) try container.encode(self.tournament, forKey: ._tournament) @@ -117,7 +112,7 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { try container.encode(self.groupStageChunkCount, forKey: ._groupStageChunkCount) try container.encode(self.overrideCourtsUnavailability, forKey: ._overrideCourtsUnavailability) try container.encode(self.shouldTryToFillUpCourtsAvailable, forKey: ._shouldTryToFillUpCourtsAvailable) - try container.encode(self.storeId, forKey: ._storeId) + try super.encode(to: encoder) } func tournamentValue() -> Tournament? { @@ -140,8 +135,8 @@ class BaseMatchScheduler: ModelObject, Storable, Codable { self.groupStageChunkCount = matchscheduler.groupStageChunkCount self.overrideCourtsUnavailability = matchscheduler.overrideCourtsUnavailability self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable - self.storeId = matchscheduler.storeId } + static func relationships() -> [Relationship] { return [ Relationship(type: Tournament.self, keyPath: \BaseMatchScheduler.tournament), diff --git a/PadelClub/Data/Gen/BaseMonthData.swift b/PadelClub/Data/Gen/BaseMonthData.swift index 5bb1d57..06d719e 100644 --- a/PadelClub/Data/Gen/BaseMonthData.swift +++ b/PadelClub/Data/Gen/BaseMonthData.swift @@ -6,7 +6,7 @@ import LeStorage import SwiftUI @Observable -class BaseMonthData: ModelObject, Storable, Codable { +class BaseMonthData: BaseModelObject, Storable { static func resourceName() -> String { return "month-data" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } @@ -66,7 +66,6 @@ class BaseMonthData: ModelObject, Storable, Codable { } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() self.monthKey = try container.decodeIfPresent(String.self, forKey: ._monthKey) ?? "" @@ -79,9 +78,10 @@ class BaseMonthData: ModelObject, Storable, Codable { self.incompleteMode = try container.decodeIfPresent(Bool.self, forKey: ._incompleteMode) ?? false self.dataModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._dataModelIdentifier) ?? nil self.fileModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._fileModelIdentifier) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) try container.encode(self.monthKey, forKey: ._monthKey) @@ -94,6 +94,7 @@ class BaseMonthData: ModelObject, Storable, Codable { try container.encode(self.incompleteMode, forKey: ._incompleteMode) try container.encode(self.dataModelIdentifier, forKey: ._dataModelIdentifier) try container.encode(self.fileModelIdentifier, forKey: ._fileModelIdentifier) + try super.encode(to: encoder) } func copy(from other: any Storable) { @@ -110,6 +111,7 @@ class BaseMonthData: ModelObject, Storable, Codable { self.dataModelIdentifier = monthdata.dataModelIdentifier self.fileModelIdentifier = monthdata.fileModelIdentifier } + static func relationships() -> [Relationship] { return [] } diff --git a/PadelClub/Data/Gen/BasePlayerRegistration.swift b/PadelClub/Data/Gen/BasePlayerRegistration.swift index f82711b..e925e55 100644 --- a/PadelClub/Data/Gen/BasePlayerRegistration.swift +++ b/PadelClub/Data/Gen/BasePlayerRegistration.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { +class BasePlayerRegistration: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "player-registrations" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() - var lastUpdate: Date = Date() var teamRegistration: String? = nil var firstName: String = "" var lastName: String = "" @@ -32,11 +31,9 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { var computedRank: Int = 0 var source: PlayerDataSource? = nil var hasArrived: Bool = false - var storeId: String? = nil init( id: String = Store.randomId(), - lastUpdate: Date = Date(), teamRegistration: String? = nil, firstName: String = "", lastName: String = "", @@ -54,12 +51,10 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, - hasArrived: Bool = false, - storeId: String? = nil + hasArrived: Bool = false ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.teamRegistration = teamRegistration self.firstName = firstName self.lastName = lastName @@ -78,12 +73,10 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { self.computedRank = computedRank self.source = source self.hasArrived = hasArrived - self.storeId = storeId } enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _teamRegistration = "teamRegistration" case _firstName = "firstName" case _lastName = "lastName" @@ -102,15 +95,11 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { case _computedRank = "computedRank" case _source = "source" case _hasArrived = "hasArrived" - case _storeId = "storeId" } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil self.firstName = try container.decodeIfPresent(String.self, forKey: ._firstName) ?? "" self.lastName = try container.decodeIfPresent(String.self, forKey: ._lastName) ?? "" @@ -129,13 +118,12 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { self.computedRank = try container.decodeIfPresent(Int.self, forKey: ._computedRank) ?? 0 self.source = try container.decodeIfPresent(PlayerDataSource.self, forKey: ._source) ?? nil self.hasArrived = try container.decodeIfPresent(Bool.self, forKey: ._hasArrived) ?? false - self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.teamRegistration, forKey: ._teamRegistration) try container.encode(self.firstName, forKey: ._firstName) try container.encode(self.lastName, forKey: ._lastName) @@ -154,7 +142,7 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { try container.encode(self.computedRank, forKey: ._computedRank) try container.encode(self.source, forKey: ._source) try container.encode(self.hasArrived, forKey: ._hasArrived) - try container.encode(self.storeId, forKey: ._storeId) + try super.encode(to: encoder) } func teamRegistrationValue() -> TeamRegistration? { @@ -165,7 +153,6 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let playerregistration = other as? BasePlayerRegistration else { return } self.id = playerregistration.id - self.lastUpdate = playerregistration.lastUpdate self.teamRegistration = playerregistration.teamRegistration self.firstName = playerregistration.firstName self.lastName = playerregistration.lastName @@ -184,8 +171,8 @@ class BasePlayerRegistration: ModelObject, SyncedStorable, Codable { self.computedRank = playerregistration.computedRank self.source = playerregistration.source self.hasArrived = playerregistration.hasArrived - self.storeId = playerregistration.storeId } + static func relationships() -> [Relationship] { return [ Relationship(type: TeamRegistration.self, keyPath: \BasePlayerRegistration.teamRegistration), diff --git a/PadelClub/Data/Gen/BasePurchase.swift b/PadelClub/Data/Gen/BasePurchase.swift index e5ad6d6..3a2b8cd 100644 --- a/PadelClub/Data/Gen/BasePurchase.swift +++ b/PadelClub/Data/Gen/BasePurchase.swift @@ -4,14 +4,13 @@ import Foundation import LeStorage -class BasePurchase: ModelObject, SyncedStorable, Codable { +class BasePurchase: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "purchases" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } var id: UInt64 = 0 - var lastUpdate: Date = Date() var user: String = "" var purchaseDate: Date = Date() var productId: String = "" @@ -21,7 +20,6 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { init( id: UInt64 = 0, - lastUpdate: Date = Date(), user: String = "", purchaseDate: Date = Date(), productId: String = "", @@ -31,7 +29,6 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.user = user self.purchaseDate = purchaseDate self.productId = productId @@ -42,7 +39,6 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { enum CodingKeys: String, CodingKey { case id = "id" - case lastUpdate = "lastUpdate" case user = "user" case purchaseDate = "purchaseDate" case productId = "productId" @@ -53,29 +49,27 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(UInt64.self, forKey: .id) ?? 0 - let dateString = try container.decode(String.self, forKey: .lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.user = try container.decodeEncrypted(key: .user) self.purchaseDate = try container.decodeIfPresent(Date.self, forKey: .purchaseDate) ?? Date() self.productId = try container.decodeIfPresent(String.self, forKey: .productId) ?? "" self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity) ?? nil self.revocationDate = try container.decodeIfPresent(Date.self, forKey: .revocationDate) ?? nil self.expirationDate = try container.decodeIfPresent(Date.self, forKey: .expirationDate) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: .id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: .lastUpdate) try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user) try container.encode(self.purchaseDate, forKey: .purchaseDate) try container.encode(self.productId, forKey: .productId) try container.encode(self.quantity, forKey: .quantity) try container.encode(self.revocationDate, forKey: .revocationDate) try container.encode(self.expirationDate, forKey: .expirationDate) + try super.encode(to: encoder) } func userValue() -> CustomUser? { @@ -85,7 +79,6 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let purchase = other as? BasePurchase else { return } self.id = purchase.id - self.lastUpdate = purchase.lastUpdate self.user = purchase.user self.purchaseDate = purchase.purchaseDate self.productId = purchase.productId @@ -93,6 +86,7 @@ class BasePurchase: ModelObject, SyncedStorable, Codable { self.revocationDate = purchase.revocationDate self.expirationDate = purchase.expirationDate } + static func relationships() -> [Relationship] { return [ Relationship(type: CustomUser.self, keyPath: \BasePurchase.user), diff --git a/PadelClub/Data/Gen/BaseRound.swift b/PadelClub/Data/Gen/BaseRound.swift index 4115b21..48efe3b 100644 --- a/PadelClub/Data/Gen/BaseRound.swift +++ b/PadelClub/Data/Gen/BaseRound.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseRound: ModelObject, SyncedStorable, Codable { +class BaseRound: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "rounds" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() - var lastUpdate: Date = Date() var tournament: String = "" var index: Int = 0 var parent: String? = nil @@ -21,23 +20,19 @@ class BaseRound: ModelObject, SyncedStorable, Codable { var startDate: Date? = nil var groupStageLoserBracket: Bool = false var loserBracketMode: LoserBracketMode = .automatic - var storeId: String? = nil init( id: String = Store.randomId(), - lastUpdate: Date = Date(), tournament: String = "", index: Int = 0, parent: String? = nil, format: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, - loserBracketMode: LoserBracketMode = .automatic, - storeId: String? = nil + loserBracketMode: LoserBracketMode = .automatic ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.tournament = tournament self.index = index self.parent = parent @@ -45,12 +40,10 @@ class BaseRound: ModelObject, SyncedStorable, Codable { self.startDate = startDate self.groupStageLoserBracket = groupStageLoserBracket self.loserBracketMode = loserBracketMode - self.storeId = storeId } enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _tournament = "tournament" case _index = "index" case _parent = "parent" @@ -58,15 +51,11 @@ class BaseRound: ModelObject, SyncedStorable, Codable { case _startDate = "startDate" case _groupStageLoserBracket = "groupStageLoserBracket" case _loserBracketMode = "loserBracketMode" - case _storeId = "storeId" } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0 self.parent = try container.decodeIfPresent(String.self, forKey: ._parent) ?? nil @@ -74,13 +63,12 @@ class BaseRound: ModelObject, SyncedStorable, Codable { self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil self.groupStageLoserBracket = try container.decodeIfPresent(Bool.self, forKey: ._groupStageLoserBracket) ?? false self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic - self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.tournament, forKey: ._tournament) try container.encode(self.index, forKey: ._index) try container.encode(self.parent, forKey: ._parent) @@ -88,7 +76,7 @@ class BaseRound: ModelObject, SyncedStorable, Codable { try container.encode(self.startDate, forKey: ._startDate) try container.encode(self.groupStageLoserBracket, forKey: ._groupStageLoserBracket) try container.encode(self.loserBracketMode, forKey: ._loserBracketMode) - try container.encode(self.storeId, forKey: ._storeId) + try super.encode(to: encoder) } func tournamentValue() -> Tournament? { @@ -98,7 +86,6 @@ class BaseRound: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let round = other as? BaseRound else { return } self.id = round.id - self.lastUpdate = round.lastUpdate self.tournament = round.tournament self.index = round.index self.parent = round.parent @@ -106,8 +93,8 @@ class BaseRound: ModelObject, SyncedStorable, Codable { self.startDate = round.startDate self.groupStageLoserBracket = round.groupStageLoserBracket self.loserBracketMode = round.loserBracketMode - self.storeId = round.storeId } + static func relationships() -> [Relationship] { return [ Relationship(type: Tournament.self, keyPath: \BaseRound.tournament), diff --git a/PadelClub/Data/Gen/BaseTeamRegistration.swift b/PadelClub/Data/Gen/BaseTeamRegistration.swift index bc22dcf..6c9e96d 100644 --- a/PadelClub/Data/Gen/BaseTeamRegistration.swift +++ b/PadelClub/Data/Gen/BaseTeamRegistration.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { +class BaseTeamRegistration: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "team-registrations" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() - var lastUpdate: Date = Date() var tournament: String = "" var groupStage: String? = nil var registrationDate: Date? = nil @@ -34,11 +33,9 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { var qualified: Bool = false var finalRanking: Int? = nil var pointsEarned: Int? = nil - var storeId: String? = nil init( id: String = Store.randomId(), - lastUpdate: Date = Date(), tournament: String = "", groupStage: String? = nil, registrationDate: Date? = nil, @@ -58,12 +55,10 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { confirmationDate: Date? = nil, qualified: Bool = false, finalRanking: Int? = nil, - pointsEarned: Int? = nil, - storeId: String? = nil + pointsEarned: Int? = nil ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.tournament = tournament self.groupStage = groupStage self.registrationDate = registrationDate @@ -84,12 +79,10 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { self.qualified = qualified self.finalRanking = finalRanking self.pointsEarned = pointsEarned - self.storeId = storeId } enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _tournament = "tournament" case _groupStage = "groupStage" case _registrationDate = "registrationDate" @@ -110,15 +103,11 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { case _qualified = "qualified" case _finalRanking = "finalRanking" case _pointsEarned = "pointsEarned" - case _storeId = "storeId" } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil self.registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate) ?? nil @@ -139,13 +128,12 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { self.qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false self.finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) ?? nil self.pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned) ?? nil - self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.tournament, forKey: ._tournament) try container.encode(self.groupStage, forKey: ._groupStage) try container.encode(self.registrationDate, forKey: ._registrationDate) @@ -166,7 +154,7 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { try container.encode(self.qualified, forKey: ._qualified) try container.encode(self.finalRanking, forKey: ._finalRanking) try container.encode(self.pointsEarned, forKey: ._pointsEarned) - try container.encode(self.storeId, forKey: ._storeId) + try super.encode(to: encoder) } func groupStageValue() -> GroupStage? { @@ -177,7 +165,6 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let teamregistration = other as? BaseTeamRegistration else { return } self.id = teamregistration.id - self.lastUpdate = teamregistration.lastUpdate self.tournament = teamregistration.tournament self.groupStage = teamregistration.groupStage self.registrationDate = teamregistration.registrationDate @@ -198,8 +185,8 @@ class BaseTeamRegistration: ModelObject, SyncedStorable, Codable { self.qualified = teamregistration.qualified self.finalRanking = teamregistration.finalRanking self.pointsEarned = teamregistration.pointsEarned - self.storeId = teamregistration.storeId } + static func relationships() -> [Relationship] { return [ Relationship(type: GroupStage.self, keyPath: \BaseTeamRegistration.groupStage), diff --git a/PadelClub/Data/Gen/BaseTeamScore.swift b/PadelClub/Data/Gen/BaseTeamScore.swift index b9d6508..dfcf662 100644 --- a/PadelClub/Data/Gen/BaseTeamScore.swift +++ b/PadelClub/Data/Gen/BaseTeamScore.swift @@ -6,77 +6,65 @@ import LeStorage import SwiftUI @Observable -class BaseTeamScore: ModelObject, SyncedStorable, Codable { +class BaseTeamScore: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "team-scores" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() - var lastUpdate: Date = Date() var match: String = "" var teamRegistration: String? = nil var score: String? = nil var walkOut: Int? = nil var luckyLoser: Int? = nil - var storeId: String? = nil init( id: String = Store.randomId(), - lastUpdate: Date = Date(), match: String = "", teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, - luckyLoser: Int? = nil, - storeId: String? = nil + luckyLoser: Int? = nil ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.match = match self.teamRegistration = teamRegistration self.score = score self.walkOut = walkOut self.luckyLoser = luckyLoser - self.storeId = storeId } enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _match = "match" case _teamRegistration = "teamRegistration" case _score = "score" case _walkOut = "walkOut" case _luckyLoser = "luckyLoser" - case _storeId = "storeId" } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.match = try container.decodeIfPresent(String.self, forKey: ._match) ?? "" self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil self.score = try container.decodeIfPresent(String.self, forKey: ._score) ?? nil self.walkOut = try container.decodeIfPresent(Int.self, forKey: ._walkOut) ?? nil self.luckyLoser = try container.decodeIfPresent(Int.self, forKey: ._luckyLoser) ?? nil - self.storeId = try container.decodeIfPresent(String.self, forKey: ._storeId) ?? nil + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.match, forKey: ._match) try container.encode(self.teamRegistration, forKey: ._teamRegistration) try container.encode(self.score, forKey: ._score) try container.encode(self.walkOut, forKey: ._walkOut) try container.encode(self.luckyLoser, forKey: ._luckyLoser) - try container.encode(self.storeId, forKey: ._storeId) + try super.encode(to: encoder) } func matchValue() -> Match? { @@ -91,14 +79,13 @@ class BaseTeamScore: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let teamscore = other as? BaseTeamScore else { return } self.id = teamscore.id - self.lastUpdate = teamscore.lastUpdate self.match = teamscore.match self.teamRegistration = teamscore.teamRegistration self.score = teamscore.score self.walkOut = teamscore.walkOut self.luckyLoser = teamscore.luckyLoser - self.storeId = teamscore.storeId } + static func relationships() -> [Relationship] { return [ Relationship(type: Match.self, keyPath: \BaseTeamScore.match), diff --git a/PadelClub/Data/Gen/BaseTournament.swift b/PadelClub/Data/Gen/BaseTournament.swift index d5c8a60..ff2e836 100644 --- a/PadelClub/Data/Gen/BaseTournament.swift +++ b/PadelClub/Data/Gen/BaseTournament.swift @@ -6,14 +6,13 @@ import LeStorage import SwiftUI @Observable -class BaseTournament: ModelObject, SyncedStorable, Codable { +class BaseTournament: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "tournaments" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() - var lastUpdate: Date = Date() var event: String? = nil var name: String? = nil var startDate: Date = Date() @@ -57,7 +56,6 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { init( id: String = Store.randomId(), - lastUpdate: Date = Date(), event: String? = nil, name: String? = nil, startDate: Date = Date(), @@ -101,7 +99,6 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { ) { super.init() self.id = id - self.lastUpdate = lastUpdate self.event = event self.name = name self.startDate = startDate @@ -146,7 +143,6 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { enum CodingKeys: String, CodingKey { case _id = "id" - case _lastUpdate = "lastUpdate" case _event = "event" case _name = "name" case _startDate = "startDate" @@ -251,11 +247,8 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { } required init(from decoder: Decoder) throws { - super.init() let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - let dateString = try container.decode(String.self, forKey: ._lastUpdate) - self.lastUpdate = Date.iso8601FractionalFormatter.date(from: dateString) ?? Date() self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? nil self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date() @@ -296,12 +289,12 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { self.hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false self.publishRankings = try container.decodeIfPresent(Bool.self, forKey: ._publishRankings) ?? false self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic + try super.init(from: decoder) } - func encode(to encoder: Encoder) throws { + override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) - try container.encode(Date.iso8601FractionalFormatter.string(from: self.lastUpdate), forKey: ._lastUpdate) try container.encode(self.event, forKey: ._event) try container.encode(self.name, forKey: ._name) try container.encode(self.startDate, forKey: ._startDate) @@ -342,6 +335,7 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { try container.encode(self.hidePointsEarned, forKey: ._hidePointsEarned) try container.encode(self.publishRankings, forKey: ._publishRankings) try container.encode(self.loserBracketMode, forKey: ._loserBracketMode) + try super.encode(to: encoder) } func eventValue() -> Event? { @@ -352,7 +346,6 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { func copy(from other: any Storable) { guard let tournament = other as? BaseTournament else { return } self.id = tournament.id - self.lastUpdate = tournament.lastUpdate self.event = tournament.event self.name = tournament.name self.startDate = tournament.startDate @@ -394,6 +387,7 @@ class BaseTournament: ModelObject, SyncedStorable, Codable { self.publishRankings = tournament.publishRankings self.loserBracketMode = tournament.loserBracketMode } + static func relationships() -> [Relationship] { return [ Relationship(type: Event.self, keyPath: \BaseTournament.event), diff --git a/PadelClub/Data/Gen/Club.json b/PadelClub/Data/Gen/Club.json index 08f7b14..201b30b 100644 --- a/PadelClub/Data/Gen/Club.json +++ b/PadelClub/Data/Gen/Club.json @@ -10,11 +10,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "creator", "type": "String", diff --git a/PadelClub/Data/Gen/Court.json b/PadelClub/Data/Gen/Court.json index 46a6c61..bac920c 100644 --- a/PadelClub/Data/Gen/Court.json +++ b/PadelClub/Data/Gen/Court.json @@ -10,11 +10,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "index", "type": "Int" diff --git a/PadelClub/Data/Gen/CustomUser.json b/PadelClub/Data/Gen/CustomUser.json index c45cf14..f8e2f0c 100644 --- a/PadelClub/Data/Gen/CustomUser.json +++ b/PadelClub/Data/Gen/CustomUser.json @@ -13,11 +13,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "username", "type": "String" diff --git a/PadelClub/Data/Gen/DateInterval.json b/PadelClub/Data/Gen/DateInterval.json index 7e711c3..58ac3a9 100644 --- a/PadelClub/Data/Gen/DateInterval.json +++ b/PadelClub/Data/Gen/DateInterval.json @@ -10,11 +10,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "event", "type": "String" diff --git a/PadelClub/Data/Gen/Event.json b/PadelClub/Data/Gen/Event.json index 38000dd..3cb0533 100644 --- a/PadelClub/Data/Gen/Event.json +++ b/PadelClub/Data/Gen/Event.json @@ -10,11 +10,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "creator", "type": "String", diff --git a/PadelClub/Data/Gen/GroupStage.json b/PadelClub/Data/Gen/GroupStage.json index 4bb5e96..ef61cf2 100644 --- a/PadelClub/Data/Gen/GroupStage.json +++ b/PadelClub/Data/Gen/GroupStage.json @@ -10,11 +10,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "tournament", "type": "String", @@ -50,12 +45,6 @@ "name": "step", "type": "Int", "defaultValue": "0" - }, - { - "name": "storeId", - "type": "String", - "optional": true, - "defaultValue": "nil" } ], "tokenExemptedMethods": [], diff --git a/PadelClub/Data/Gen/Match.json b/PadelClub/Data/Gen/Match.json index 153bea5..0da44c6 100644 --- a/PadelClub/Data/Gen/Match.json +++ b/PadelClub/Data/Gen/Match.json @@ -12,11 +12,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "round", "type": "String", @@ -82,11 +77,6 @@ "name": "confirmed", "type": "Bool", "defaultValue": "false" - }, - { - "name": "storeId", - "type": "String", - "optional": true } ] } diff --git a/PadelClub/Data/Gen/MatchScheduler.json b/PadelClub/Data/Gen/MatchScheduler.json index a9611b1..500bc3d 100644 --- a/PadelClub/Data/Gen/MatchScheduler.json +++ b/PadelClub/Data/Gen/MatchScheduler.json @@ -68,11 +68,6 @@ "name": "shouldTryToFillUpCourtsAvailable", "type": "Bool", "defaultValue": "false" - }, - { - "name": "storeId", - "type": "String", - "optional": true } ] } diff --git a/PadelClub/Data/Gen/PlayerRegistration.json b/PadelClub/Data/Gen/PlayerRegistration.json index f350772..c1a58e1 100644 --- a/PadelClub/Data/Gen/PlayerRegistration.json +++ b/PadelClub/Data/Gen/PlayerRegistration.json @@ -13,11 +13,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "teamRegistration", "type": "String", @@ -106,12 +101,6 @@ "name": "hasArrived", "type": "Bool", "defaultValue": "false" - }, - { - "name": "storeId", - "type": "String", - "optional": true, - "defaultValue": "nil" } ] } diff --git a/PadelClub/Data/Gen/Purchase.json b/PadelClub/Data/Gen/Purchase.json index bb8bdeb..1ce7169 100644 --- a/PadelClub/Data/Gen/Purchase.json +++ b/PadelClub/Data/Gen/Purchase.json @@ -9,11 +9,6 @@ "type": "UInt64", "defaultValue": "0" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "user", "type": "String", diff --git a/PadelClub/Data/Gen/Round.json b/PadelClub/Data/Gen/Round.json index 9120c80..1464de5 100644 --- a/PadelClub/Data/Gen/Round.json +++ b/PadelClub/Data/Gen/Round.json @@ -13,11 +13,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "tournament", "type": "String", @@ -52,12 +47,6 @@ "name": "loserBracketMode", "type": "LoserBracketMode", "defaultValue": ".automatic" - }, - { - "name": "storeId", - "type": "String", - "optional": true, - "defaultValue": "nil" } ] } diff --git a/PadelClub/Data/Gen/TeamRegistration.json b/PadelClub/Data/Gen/TeamRegistration.json index 2a25c51..d9b74a7 100644 --- a/PadelClub/Data/Gen/TeamRegistration.json +++ b/PadelClub/Data/Gen/TeamRegistration.json @@ -13,11 +13,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "tournament", "type": "String" @@ -117,12 +112,6 @@ "name": "pointsEarned", "type": "Int", "optional": true - }, - { - "name": "storeId", - "type": "String", - "optional": true, - "defaultValue": "nil" } ] } diff --git a/PadelClub/Data/Gen/TeamScore.json b/PadelClub/Data/Gen/TeamScore.json index b283e88..8257a86 100644 --- a/PadelClub/Data/Gen/TeamScore.json +++ b/PadelClub/Data/Gen/TeamScore.json @@ -13,11 +13,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "match", "type": "String", @@ -43,12 +38,6 @@ "name": "luckyLoser", "type": "Int", "optional": true - }, - { - "name": "storeId", - "type": "String", - "optional": true, - "defaultValue": "nil" } ] } diff --git a/PadelClub/Data/Gen/Tournament.json b/PadelClub/Data/Gen/Tournament.json index 3f24abf..43c734f 100644 --- a/PadelClub/Data/Gen/Tournament.json +++ b/PadelClub/Data/Gen/Tournament.json @@ -13,11 +13,6 @@ "type": "String", "defaultValue": "Store.randomId()" }, - { - "name": "lastUpdate", - "type": "Date", - "option": "fractional" - }, { "name": "event", "type": "String", diff --git a/PadelClub/Data/Gen/generator.py b/PadelClub/Data/Gen/generator.py index b2369c9..fc2b74e 100644 --- a/PadelClub/Data/Gen/generator.py +++ b/PadelClub/Data/Gen/generator.py @@ -39,7 +39,8 @@ class SwiftModelGenerator: if is_observable: lines.append("@Observable") protocol = "SyncedStorable" if is_sync else "Storable" - lines.append(f"class Base{model_name}: ModelObject, {protocol}, Codable {{") + base_class = "SyncedModelObject" if is_sync else "BaseModelObject" + lines.append(f"class Base{model_name}: {base_class}, {protocol} {{") lines.append("") # Add SyncedStorable protocol requirements @@ -61,7 +62,7 @@ class SwiftModelGenerator: lines.append("") # CodingKeys - lines.extend(self._generate_coding_keys(properties, is_observable)) + lines.extend(self._generate_coding_keys(properties, is_observable, is_sync)) lines.append("") # Encryption methods @@ -71,9 +72,9 @@ class SwiftModelGenerator: lines.append("") # Codable implementation - lines.extend(self._generate_decoder(model_name, properties, is_observable)) + lines.extend(self._generate_decoder(model_name, properties, is_observable, is_sync)) lines.append("") - lines.extend(self._generate_encoder(properties, is_observable)) + lines.extend(self._generate_encoder(properties, is_observable, is_sync)) lines.append("") # Foreign Key convenience @@ -166,13 +167,14 @@ class SwiftModelGenerator: lines.extend([" }", ""]) # Close the method and add a blank line return lines - def _generate_coding_keys(self, properties: List[Dict[str, Any]], is_observable: bool) -> List[str]: + def _generate_coding_keys(self, properties: List[Dict[str, Any]], is_observable: bool, is_sync: bool = False) -> List[str]: lines = [" enum CodingKeys: String, CodingKey {"] for prop in properties: name = prop['name'] # Add underscore prefix to case name if observable, but keep the string value without underscore case_name = f"_{name}" if is_observable else name lines.append(f" case {case_name} = \"{name}\"") + lines.append(" }") return lines @@ -250,9 +252,8 @@ class SwiftModelGenerator: ]) return lines - def _generate_decoder(self, model_name: str, properties: List[Dict[str, Any]], is_observable: bool) -> List[str]: + def _generate_decoder(self, model_name: str, properties: List[Dict[str, Any]], is_observable: bool, is_sync: bool = False) -> List[str]: lines = [" required init(from decoder: Decoder) throws {", - " super.init()", " let container = try decoder.container(keyedBy: CodingKeys.self)"] for prop in properties: @@ -288,11 +289,14 @@ class SwiftModelGenerator: lines.append(f" self.{name} = Date.iso8601FractionalFormatter.date(from: dateString) ?? {default_value}") else: lines.append(f" self.{name} = try container.decodeIfPresent({type_name}.self, forKey: .{case_ref}) ?? {default_value}") + + lines.append(" try super.init(from: decoder)") + lines.append(" }") return lines - def _generate_encoder(self, properties: List[Dict[str, Any]], is_observable: bool) -> List[str]: - lines = [" func encode(to encoder: Encoder) throws {", + def _generate_encoder(self, properties: List[Dict[str, Any]], is_observable: bool, is_sync: bool = False) -> List[str]: + lines = [" override func encode(to encoder: Encoder) throws {", " var container = encoder.container(keyedBy: CodingKeys.self)"] for prop in properties: @@ -326,6 +330,7 @@ class SwiftModelGenerator: else: lines.append(f" try container.encode(self.{name}, forKey: .{case_ref})") + lines.append(" try super.encode(to: encoder)") lines.append(" }") return lines diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 996b737..2325180 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -45,7 +45,7 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) { - super.init(storeId: tournament) + super.init() // self.storeId = tournament self.tournament = tournament diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 87acac4..efac161 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -61,8 +61,6 @@ final class Tournament: BaseTournament { // var publishRankings: Bool = false // var loserBracketMode: LoserBracketMode = .automatic - var storeId: String? { return nil } - @ObservationIgnored var navigationPath: [Screen] = [] diff --git a/PadelClub/Views/Tournament/Subscription/Purchase.swift b/PadelClub/Views/Tournament/Subscription/Purchase.swift index e7cfc24..14cb860 100644 --- a/PadelClub/Views/Tournament/Subscription/Purchase.swift +++ b/PadelClub/Views/Tournament/Subscription/Purchase.swift @@ -24,8 +24,6 @@ class Purchase: BasePurchase { // var revocationDate: Date? = nil // var expirationDate: Date? = nil - var storeId: String? { return nil } - init(user: String, transactionId: UInt64, purchaseDate: Date, productId: String, quantity: Int? = nil, revocationDate: Date? = nil, expirationDate: Date? = nil) { super.init(id: transactionId, user: user, purchaseDate: purchaseDate, productId: productId, quantity: quantity, revocationDate: revocationDate, expirationDate: expirationDate) From 62fd9c561065a73a6c614bcc77a2d77f121963b1 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 3 Dec 2024 20:35:06 +0100 Subject: [PATCH 11/43] Adds a way to share a tournament with others --- PadelClub.xcodeproj/project.pbxproj | 8 + PadelClub/AppDelegate.swift | 2 +- PadelClub/ViewModel/Screen.swift | 1 + PadelClub/Views/Components/Labels.swift | 6 + .../Navigation/Agenda/EventListView.swift | 17 ++ .../Views/Tournament/TournamentView.swift | 12 ++ PadelClub/Views/User/UserSearchView.swift | 168 ++++++++++++++++++ 7 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 PadelClub/Views/User/UserSearchView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 1531660..74a898c 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -17,6 +17,9 @@ C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4112B6D249E002A7B48 /* PadelClubTests.swift */; }; C425D41C2B6D249E002A7B48 /* PadelClubUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41B2B6D249E002A7B48 /* PadelClubUITests.swift */; }; C425D41E2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */; }; + C4339BFB2CFF7D68004E5F09 /* UserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */; }; + C4339BFC2CFF7D68004E5F09 /* UserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */; }; + C4339BFD2CFF7D68004E5F09 /* UserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */; }; C4489BE22C05BF5000043F3D /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.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 */; }; @@ -972,6 +975,7 @@ C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PadelClubUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C425D41B2B6D249E002A7B48 /* PadelClubUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITests.swift; sourceTree = ""; }; C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITestsLaunchTests.swift; sourceTree = ""; }; + C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearchView.swift; sourceTree = ""; }; C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugSettingsView.swift; 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 = ""; }; @@ -1543,6 +1547,7 @@ C4A47D852B7BA33F00ADC637 /* User */ = { isa = PBXGroup; children = ( + C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */, C4A47DB22B86387500ADC637 /* AccountView.swift */, C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */, C4A47DA52B83948E00ADC637 /* LoginView.swift */, @@ -2452,6 +2457,7 @@ C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */, FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */, FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */, + C4339BFB2CFF7D68004E5F09 /* UserSearchView.swift in Sources */, FF8F26412BADFC8700650388 /* TournamentInitView.swift in Sources */, C4A47D8A2B7BBB6500ADC637 /* SubscriptionView.swift in Sources */, FFD655D82C8DE27400E5B35E /* TournamentLookUpView.swift in Sources */, @@ -2830,6 +2836,7 @@ FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */, FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */, FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */, + C4339BFD2CFF7D68004E5F09 /* UserSearchView.swift in Sources */, FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */, FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */, FF4CBF9E2C996C0600151637 /* NetworkManagerError.swift in Sources */, @@ -3096,6 +3103,7 @@ FF70FB182C90584900129CC2 /* StoreManager.swift in Sources */, FF70FB192C90584900129CC2 /* SearchViewModel.swift in Sources */, FF70FB1A2C90584900129CC2 /* PlayerRegistration.swift in Sources */, + C4339BFC2CFF7D68004E5F09 /* UserSearchView.swift in Sources */, FF70FB1B2C90584900129CC2 /* ImportedPlayerView.swift in Sources */, FF70FB1C2C90584900129CC2 /* EditingTeamView.swift in Sources */, FF70FB1D2C90584900129CC2 /* NetworkManagerError.swift in Sources */, diff --git a/PadelClub/AppDelegate.swift b/PadelClub/AppDelegate.swift index 12a9138..14e6dcb 100644 --- a/PadelClub/AppDelegate.swift +++ b/PadelClub/AppDelegate.swift @@ -19,7 +19,7 @@ class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDel UIApplication.shared.registerForRemoteNotifications() UNUserNotificationCenter.current().delegate = self - + return true } diff --git a/PadelClub/ViewModel/Screen.swift b/PadelClub/ViewModel/Screen.swift index cb4d0d6..9ac761e 100644 --- a/PadelClub/ViewModel/Screen.swift +++ b/PadelClub/ViewModel/Screen.swift @@ -20,4 +20,5 @@ enum Screen: String, Codable { case broadcast case event case print + case share } diff --git a/PadelClub/Views/Components/Labels.swift b/PadelClub/Views/Components/Labels.swift index 7049334..a87e34a 100644 --- a/PadelClub/Views/Components/Labels.swift +++ b/PadelClub/Views/Components/Labels.swift @@ -31,6 +31,12 @@ struct LabelDelete: View { } } +struct ShareLabel: View { + var body: some View { + Label("Partager", systemImage: "square.and.arrow.up.fill") + } +} + struct LabelFilter: View { var body: some View { Label("Filtrer", systemImage: "line.3.horizontal.decrease.circle") diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 04d9741..eb848e7 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -16,6 +16,8 @@ struct EventListView: View { let tournaments: [FederalTournamentHolder] let sortAscending: Bool + + @State var showUserSearch: Bool = false var body: some View { let groupedTournamentsByDate = Dictionary(grouping: federalDataViewModel.filteredFederalTournaments(from: tournaments)) { $0.startDate.startOfMonth } @@ -118,6 +120,15 @@ struct EventListView: View { private func _tournamentView(_ tournament: Tournament) -> some View { NavigationLink(value: tournament) { TournamentCellView(tournament: tournament) + .popover(isPresented: self.$showUserSearch) { + UserSearchView { user in + do { + try StoreCenter.main.giveUserAccess(user.id, data: tournament) + } catch { + Logger.error(error) + } + } + } } .contextMenu { if tournament.hasEnded() == false { @@ -144,6 +155,12 @@ struct EventListView: View { } label: { LabelDelete() } + Button() { + self.showUserSearch = true + } label: { + ShareLabel().tint(.orange) + } + } #endif } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index c5645c6..f3c9992 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -111,6 +111,14 @@ struct TournamentView: View { } case .print: PrintSettingsView(tournament: tournament) + case .share: + UserSearchView { user in + do { + try StoreCenter.main.giveUserAccess(user.id, data: tournament) + } catch { + Logger.error(error) + } + } } } .environment(tournament) @@ -192,6 +200,10 @@ struct TournamentView: View { NavigationLink(value: Screen.print) { Label("Imprimer", systemImage: "printer") } + + NavigationLink(value: Screen.share) { + Label("Partager", systemImage: "square.and.arrow.up") + } Divider() diff --git a/PadelClub/Views/User/UserSearchView.swift b/PadelClub/Views/User/UserSearchView.swift new file mode 100644 index 0000000..809a5d0 --- /dev/null +++ b/PadelClub/Views/User/UserSearchView.swift @@ -0,0 +1,168 @@ +// +// UserSearchView.swift +// PadelClub +// +// Created by Laurent Morvillier on 03/12/2024. +// + +import Combine +import LeStorage +import SwiftUI + +class UserSearchViewModel: ObservableObject { + @Published var searchText = "" + @Published var users: [ShortUser] = [] + @Published var isLoading = false + @Published var error: String? + @Published var selectedUser: ShortUser? = nil + + private var cancellables = Set() + private var originalUsers: [ShortUser] = [] + private var lastSearchTerm = "" + + init() { + // Debounce search to avoid too many requests + $searchText + .removeDuplicates() + .debounce(for: .milliseconds(300), scheduler: RunLoop.main) + .sink { [weak self] searchTerm in + self?.handleSearch(searchTerm) + } + .store(in: &cancellables) + } + + private func handleSearch(_ searchTerm: String) { + guard !searchTerm.isEmpty else { + users = [] + return + } + + // If going backwards in search, filter existing results + if searchTerm.count < lastSearchTerm.count && !originalUsers.isEmpty { + filterExistingResults(searchTerm) + return + } + + // Otherwise, make a new request + performServerSearch(searchTerm) + } + + private func filterExistingResults(_ searchTerm: String) { + users = originalUsers.filter { user in + user.firstName.localizedCaseInsensitiveContains(searchTerm) + || user.lastName.localizedCaseInsensitiveContains(searchTerm) + } + } + + private func performServerSearch(_ searchTerm: String) { + isLoading = true + error = nil + + Task { + do { + let services = try StoreCenter.main.service() + let searchResults = try await services.searchUsers(string: searchTerm) + + await MainActor.run { + self.originalUsers = searchResults + self.users = searchResults + self.lastSearchTerm = searchTerm + self.isLoading = false + } + } catch { + await MainActor.run { + self.error = error.localizedDescription + self.isLoading = false + } + } + } + } +} + +struct UserSearchView: View { + @StateObject private var viewModel = UserSearchViewModel() + + var handler: (ShortUser) -> Void + + var body: some View { + NavigationView { + VStack { + searchField + + if viewModel.isLoading { + loadingView + } else if let error = viewModel.error { + errorView(error) + } else { + List { + ForEach(viewModel.users, id: \.id) { user in + let isSelected = (user.id == viewModel.selectedUser?.id) + UserRow(user: user, isSelected: isSelected) + .contentShape(Rectangle()) + .onTapGesture { + viewModel.selectedUser = user + } + } + } + .listStyle(PlainListStyle()) + } + } + .navigationTitle("Search Users") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Select") { + if let selectedUser = viewModel.selectedUser { + handler(selectedUser) + } + } + .disabled(viewModel.selectedUser == nil) + } + } + } + } + + private var searchField: some View { + TextField("Search users...", text: $viewModel.searchText) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding() + } + + private var loadingView: some View { + ProgressView() + .progressViewStyle(CircularProgressViewStyle()) + .scaleEffect(1.5) + .frame(maxHeight: .infinity) + } + + private func errorView(_ error: String) -> some View { + Text(error) + .foregroundColor(.red) + .frame(maxHeight: .infinity) + } + +} + +struct UserRow: View { + let user: ShortUser + let isSelected: Bool + + var body: some View { + HStack { + Text("\(user.firstName) \(user.lastName)") + Spacer() + if self.isSelected { + Image(systemName: "checkmark").tint(.logoOrange) + } + } + .padding(.vertical, 4) + } +} + +// Preview provider +struct UserSearchView_Previews: PreviewProvider { + static var previews: some View { + UserSearchView { user in + + } + } +} From 9ebdffddd7f22f270d2258deb772442b3fbbdce1 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 5 Dec 2024 12:21:47 +0100 Subject: [PATCH 12/43] cleanup generation and add tournament sharing --- PadelClub.xcodeproj/project.pbxproj | 16 +- PadelClub/Data/Gen/BaseClub.swift | 3 +- PadelClub/Data/Gen/BaseCourt.swift | 1 - PadelClub/Data/Gen/BaseCustomUser.swift | 10 +- PadelClub/Data/Gen/BaseDateInterval.swift | 1 - PadelClub/Data/Gen/BaseEvent.swift | 1 - PadelClub/Data/Gen/BaseGroupStage.swift | 1 - PadelClub/Data/Gen/BaseMatch.swift | 1 - PadelClub/Data/Gen/BaseMatchScheduler.swift | 1 - PadelClub/Data/Gen/BaseMonthData.swift | 1 - .../Data/Gen/BasePlayerRegistration.swift | 1 - PadelClub/Data/Gen/BasePurchase.swift | 1 - PadelClub/Data/Gen/BaseRound.swift | 1 - PadelClub/Data/Gen/BaseTeamRegistration.swift | 1 - PadelClub/Data/Gen/BaseTeamScore.swift | 1 - PadelClub/Data/Gen/BaseTournament.swift | 1 - PadelClub/Data/Gen/Court.json | 3 +- PadelClub/Data/Gen/CustomUser.json | 6 +- PadelClub/Data/Gen/DateInterval.json | 3 +- PadelClub/Data/Gen/Event.json | 3 +- PadelClub/Data/Gen/GroupStage.json | 3 +- PadelClub/Data/Gen/Match.json | 1 - PadelClub/Data/Gen/MatchScheduler.json | 1 - PadelClub/Data/Gen/MonthData.json | 1 - PadelClub/Data/Gen/PlayerRegistration.json | 1 - PadelClub/Data/Gen/Purchase.json | 1 - PadelClub/Data/Gen/Round.json | 1 - PadelClub/Data/Gen/TeamRegistration.json | 1 - PadelClub/Data/Gen/TeamScore.json | 1 - PadelClub/Data/Gen/Tournament.json | 1 - PadelClub/Data/Gen/generator.py | 6 +- .../Navigation/Agenda/EventListView.swift | 8 +- .../Views/Tournament/TournamentView.swift | 8 +- PadelClub/Views/User/ShareModelView.swift | 113 ++++++++++++ PadelClub/Views/User/UserSearchView.swift | 168 ------------------ 35 files changed, 143 insertions(+), 229 deletions(-) create mode 100644 PadelClub/Views/User/ShareModelView.swift delete mode 100644 PadelClub/Views/User/UserSearchView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 74a898c..5cc3b3c 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -17,9 +17,9 @@ C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4112B6D249E002A7B48 /* PadelClubTests.swift */; }; C425D41C2B6D249E002A7B48 /* PadelClubUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41B2B6D249E002A7B48 /* PadelClubUITests.swift */; }; C425D41E2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */; }; - C4339BFB2CFF7D68004E5F09 /* UserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */; }; - C4339BFC2CFF7D68004E5F09 /* UserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */; }; - C4339BFD2CFF7D68004E5F09 /* UserSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */; }; + C4339BFB2CFF7D68004E5F09 /* ShareModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */; }; + C4339BFC2CFF7D68004E5F09 /* ShareModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */; }; + C4339BFD2CFF7D68004E5F09 /* ShareModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */; }; C4489BE22C05BF5000043F3D /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.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 */; }; @@ -975,7 +975,7 @@ C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PadelClubUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C425D41B2B6D249E002A7B48 /* PadelClubUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITests.swift; sourceTree = ""; }; C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITestsLaunchTests.swift; sourceTree = ""; }; - C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearchView.swift; sourceTree = ""; }; + C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareModelView.swift; sourceTree = ""; }; C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugSettingsView.swift; 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 = ""; }; @@ -1547,7 +1547,7 @@ C4A47D852B7BA33F00ADC637 /* User */ = { isa = PBXGroup; children = ( - C4339BFA2CFF7D64004E5F09 /* UserSearchView.swift */, + C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */, C4A47DB22B86387500ADC637 /* AccountView.swift */, C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */, C4A47DA52B83948E00ADC637 /* LoginView.swift */, @@ -2457,7 +2457,7 @@ C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */, FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */, FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */, - C4339BFB2CFF7D68004E5F09 /* UserSearchView.swift in Sources */, + C4339BFB2CFF7D68004E5F09 /* ShareModelView.swift in Sources */, FF8F26412BADFC8700650388 /* TournamentInitView.swift in Sources */, C4A47D8A2B7BBB6500ADC637 /* SubscriptionView.swift in Sources */, FFD655D82C8DE27400E5B35E /* TournamentLookUpView.swift in Sources */, @@ -2836,7 +2836,7 @@ FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */, FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */, FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */, - C4339BFD2CFF7D68004E5F09 /* UserSearchView.swift in Sources */, + C4339BFD2CFF7D68004E5F09 /* ShareModelView.swift in Sources */, FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */, FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */, FF4CBF9E2C996C0600151637 /* NetworkManagerError.swift in Sources */, @@ -3103,7 +3103,7 @@ FF70FB182C90584900129CC2 /* StoreManager.swift in Sources */, FF70FB192C90584900129CC2 /* SearchViewModel.swift in Sources */, FF70FB1A2C90584900129CC2 /* PlayerRegistration.swift in Sources */, - C4339BFC2CFF7D68004E5F09 /* UserSearchView.swift in Sources */, + C4339BFC2CFF7D68004E5F09 /* ShareModelView.swift in Sources */, FF70FB1B2C90584900129CC2 /* ImportedPlayerView.swift in Sources */, FF70FB1C2C90584900129CC2 /* EditingTeamView.swift in Sources */, FF70FB1D2C90584900129CC2 /* NetworkManagerError.swift in Sources */, diff --git a/PadelClub/Data/Gen/BaseClub.swift b/PadelClub/Data/Gen/BaseClub.swift index 61c8fbb..ad4ec3e 100644 --- a/PadelClub/Data/Gen/BaseClub.swift +++ b/PadelClub/Data/Gen/BaseClub.swift @@ -10,7 +10,6 @@ class BaseClub: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "clubs" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var creator: String? = nil @@ -137,4 +136,4 @@ class BaseClub: SyncedModelObject, SyncedStorable { ] } -} +} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseCourt.swift b/PadelClub/Data/Gen/BaseCourt.swift index bb3dbed..10099bd 100644 --- a/PadelClub/Data/Gen/BaseCourt.swift +++ b/PadelClub/Data/Gen/BaseCourt.swift @@ -10,7 +10,6 @@ class BaseCourt: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "courts" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var index: Int = 0 diff --git a/PadelClub/Data/Gen/BaseCustomUser.swift b/PadelClub/Data/Gen/BaseCustomUser.swift index bf5e67d..1b61d70 100644 --- a/PadelClub/Data/Gen/BaseCustomUser.swift +++ b/PadelClub/Data/Gen/BaseCustomUser.swift @@ -10,7 +10,6 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "users" } static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var username: String = "" @@ -34,6 +33,7 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { var loserBracketMatchFormatPreference: MatchFormat? = nil var loserBracketMode: LoserBracketMode = .automatic var deviceId: String? = nil + var agents: [String] = [] init( id: String = Store.randomId(), @@ -57,7 +57,8 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { groupStageMatchFormatPreference: MatchFormat? = nil, loserBracketMatchFormatPreference: MatchFormat? = nil, loserBracketMode: LoserBracketMode = .automatic, - deviceId: String? = nil + deviceId: String? = nil, + agents: [String] = [] ) { super.init() self.id = id @@ -82,6 +83,7 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { self.loserBracketMatchFormatPreference = loserBracketMatchFormatPreference self.loserBracketMode = loserBracketMode self.deviceId = deviceId + self.agents = agents } enum CodingKeys: String, CodingKey { @@ -107,6 +109,7 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference" case _loserBracketMode = "loserBracketMode" case _deviceId = "deviceId" + case _agents = "agents" } required init(from decoder: Decoder) throws { @@ -133,6 +136,7 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { self.loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference) ?? nil self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic self.deviceId = try container.decodeIfPresent(String.self, forKey: ._deviceId) ?? nil + self.agents = try container.decodeIfPresent([String].self, forKey: ._agents) ?? [] try super.init(from: decoder) } @@ -160,6 +164,7 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { try container.encode(self.loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference) try container.encode(self.loserBracketMode, forKey: ._loserBracketMode) try container.encode(self.deviceId, forKey: ._deviceId) + try container.encode(self.agents, forKey: ._agents) try super.encode(to: encoder) } @@ -187,6 +192,7 @@ class BaseCustomUser: SyncedModelObject, SyncedStorable { self.loserBracketMatchFormatPreference = customuser.loserBracketMatchFormatPreference self.loserBracketMode = customuser.loserBracketMode self.deviceId = customuser.deviceId + self.agents = customuser.agents } static func relationships() -> [Relationship] { diff --git a/PadelClub/Data/Gen/BaseDateInterval.swift b/PadelClub/Data/Gen/BaseDateInterval.swift index 0ba359a..a429f9d 100644 --- a/PadelClub/Data/Gen/BaseDateInterval.swift +++ b/PadelClub/Data/Gen/BaseDateInterval.swift @@ -10,7 +10,6 @@ class BaseDateInterval: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "date-intervals" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var event: String = "" diff --git a/PadelClub/Data/Gen/BaseEvent.swift b/PadelClub/Data/Gen/BaseEvent.swift index ee7e5f2..4dcb703 100644 --- a/PadelClub/Data/Gen/BaseEvent.swift +++ b/PadelClub/Data/Gen/BaseEvent.swift @@ -10,7 +10,6 @@ class BaseEvent: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "events" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var creator: String? = nil diff --git a/PadelClub/Data/Gen/BaseGroupStage.swift b/PadelClub/Data/Gen/BaseGroupStage.swift index c237546..c5c9e1d 100644 --- a/PadelClub/Data/Gen/BaseGroupStage.swift +++ b/PadelClub/Data/Gen/BaseGroupStage.swift @@ -10,7 +10,6 @@ class BaseGroupStage: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "group-stages" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() var tournament: String = "" diff --git a/PadelClub/Data/Gen/BaseMatch.swift b/PadelClub/Data/Gen/BaseMatch.swift index 9328e70..f66ab28 100644 --- a/PadelClub/Data/Gen/BaseMatch.swift +++ b/PadelClub/Data/Gen/BaseMatch.swift @@ -10,7 +10,6 @@ class BaseMatch: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "matches" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() var round: String? = nil diff --git a/PadelClub/Data/Gen/BaseMatchScheduler.swift b/PadelClub/Data/Gen/BaseMatchScheduler.swift index 9334070..59cb283 100644 --- a/PadelClub/Data/Gen/BaseMatchScheduler.swift +++ b/PadelClub/Data/Gen/BaseMatchScheduler.swift @@ -10,7 +10,6 @@ class BaseMatchScheduler: BaseModelObject, Storable { static func resourceName() -> String { return "match-scheduler" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var tournament: String = "" diff --git a/PadelClub/Data/Gen/BaseMonthData.swift b/PadelClub/Data/Gen/BaseMonthData.swift index 06d719e..1f1a12c 100644 --- a/PadelClub/Data/Gen/BaseMonthData.swift +++ b/PadelClub/Data/Gen/BaseMonthData.swift @@ -10,7 +10,6 @@ class BaseMonthData: BaseModelObject, Storable { static func resourceName() -> String { return "month-data" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var monthKey: String = "" diff --git a/PadelClub/Data/Gen/BasePlayerRegistration.swift b/PadelClub/Data/Gen/BasePlayerRegistration.swift index e925e55..594aee1 100644 --- a/PadelClub/Data/Gen/BasePlayerRegistration.swift +++ b/PadelClub/Data/Gen/BasePlayerRegistration.swift @@ -10,7 +10,6 @@ class BasePlayerRegistration: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "player-registrations" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() var teamRegistration: String? = nil diff --git a/PadelClub/Data/Gen/BasePurchase.swift b/PadelClub/Data/Gen/BasePurchase.swift index 3a2b8cd..e10f087 100644 --- a/PadelClub/Data/Gen/BasePurchase.swift +++ b/PadelClub/Data/Gen/BasePurchase.swift @@ -8,7 +8,6 @@ class BasePurchase: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "purchases" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: UInt64 = 0 var user: String = "" diff --git a/PadelClub/Data/Gen/BaseRound.swift b/PadelClub/Data/Gen/BaseRound.swift index 48efe3b..7ea4249 100644 --- a/PadelClub/Data/Gen/BaseRound.swift +++ b/PadelClub/Data/Gen/BaseRound.swift @@ -10,7 +10,6 @@ class BaseRound: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "rounds" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() var tournament: String = "" diff --git a/PadelClub/Data/Gen/BaseTeamRegistration.swift b/PadelClub/Data/Gen/BaseTeamRegistration.swift index 6c9e96d..237d84f 100644 --- a/PadelClub/Data/Gen/BaseTeamRegistration.swift +++ b/PadelClub/Data/Gen/BaseTeamRegistration.swift @@ -10,7 +10,6 @@ class BaseTeamRegistration: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "team-registrations" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() var tournament: String = "" diff --git a/PadelClub/Data/Gen/BaseTeamScore.swift b/PadelClub/Data/Gen/BaseTeamScore.swift index dfcf662..2407f39 100644 --- a/PadelClub/Data/Gen/BaseTeamScore.swift +++ b/PadelClub/Data/Gen/BaseTeamScore.swift @@ -10,7 +10,6 @@ class BaseTeamScore: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "team-scores" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return true } var id: String = Store.randomId() var match: String = "" diff --git a/PadelClub/Data/Gen/BaseTournament.swift b/PadelClub/Data/Gen/BaseTournament.swift index ff2e836..3f29de8 100644 --- a/PadelClub/Data/Gen/BaseTournament.swift +++ b/PadelClub/Data/Gen/BaseTournament.swift @@ -10,7 +10,6 @@ class BaseTournament: SyncedModelObject, SyncedStorable { static func resourceName() -> String { return "tournaments" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static func filterByStoreIdentifier() -> Bool { return false } var id: String = Store.randomId() var event: String? = nil diff --git a/PadelClub/Data/Gen/Court.json b/PadelClub/Data/Gen/Court.json index bac920c..ec2bbe4 100644 --- a/PadelClub/Data/Gen/Court.json +++ b/PadelClub/Data/Gen/Court.json @@ -36,8 +36,7 @@ "defaultValue": "false" } ], - "tokenExemptedMethods": [], - "filterByStoreIdentifier": false + "tokenExemptedMethods": [] } ] } diff --git a/PadelClub/Data/Gen/CustomUser.json b/PadelClub/Data/Gen/CustomUser.json index f8e2f0c..ac32c3c 100644 --- a/PadelClub/Data/Gen/CustomUser.json +++ b/PadelClub/Data/Gen/CustomUser.json @@ -6,7 +6,6 @@ "synchronizable": true, "observable": true, "tokenExemptedMethods": ["post"], - "filterByStoreIdentifier": false, "properties": [ { "name": "id", @@ -125,6 +124,11 @@ "type": "String", "optional": true, "defaultValue": "nil" + }, + { + "name": "agents", + "type": "[String]", + "defaultValue": "[]" } ] } diff --git a/PadelClub/Data/Gen/DateInterval.json b/PadelClub/Data/Gen/DateInterval.json index 58ac3a9..b7c1532 100644 --- a/PadelClub/Data/Gen/DateInterval.json +++ b/PadelClub/Data/Gen/DateInterval.json @@ -27,8 +27,7 @@ "type": "Date" } ], - "tokenExemptedMethods": [], - "filterByStoreIdentifier": false + "tokenExemptedMethods": [] } ] } diff --git a/PadelClub/Data/Gen/Event.json b/PadelClub/Data/Gen/Event.json index 3cb0533..0fa8d00 100644 --- a/PadelClub/Data/Gen/Event.json +++ b/PadelClub/Data/Gen/Event.json @@ -42,8 +42,7 @@ "defaultValue": "nil" } ], - "tokenExemptedMethods": [], - "filterByStoreIdentifier": false + "tokenExemptedMethods": [] } ] } diff --git a/PadelClub/Data/Gen/GroupStage.json b/PadelClub/Data/Gen/GroupStage.json index ef61cf2..ecfdad5 100644 --- a/PadelClub/Data/Gen/GroupStage.json +++ b/PadelClub/Data/Gen/GroupStage.json @@ -47,8 +47,7 @@ "defaultValue": "0" } ], - "tokenExemptedMethods": [], - "filterByStoreIdentifier": true + "tokenExemptedMethods": [] } ] } diff --git a/PadelClub/Data/Gen/Match.json b/PadelClub/Data/Gen/Match.json index 0da44c6..913014c 100644 --- a/PadelClub/Data/Gen/Match.json +++ b/PadelClub/Data/Gen/Match.json @@ -5,7 +5,6 @@ "synchronizable": true, "observable": true, "tokenExemptedMethods": [], - "filterByStoreIdentifier": true, "properties": [ { "name": "id", diff --git a/PadelClub/Data/Gen/MatchScheduler.json b/PadelClub/Data/Gen/MatchScheduler.json index 500bc3d..34eeb63 100644 --- a/PadelClub/Data/Gen/MatchScheduler.json +++ b/PadelClub/Data/Gen/MatchScheduler.json @@ -6,7 +6,6 @@ "synchronizable": false, "observable": true, "tokenExemptedMethods": [], - "filterByStoreIdentifier": false, "properties": [ { "name": "id", diff --git a/PadelClub/Data/Gen/MonthData.json b/PadelClub/Data/Gen/MonthData.json index 95c4d9b..896038f 100644 --- a/PadelClub/Data/Gen/MonthData.json +++ b/PadelClub/Data/Gen/MonthData.json @@ -6,7 +6,6 @@ "synchronizable": false, "observable": true, "tokenExemptedMethods": [], - "filterByStoreIdentifier": false, "properties": [ { "name": "id", diff --git a/PadelClub/Data/Gen/PlayerRegistration.json b/PadelClub/Data/Gen/PlayerRegistration.json index c1a58e1..77302ef 100644 --- a/PadelClub/Data/Gen/PlayerRegistration.json +++ b/PadelClub/Data/Gen/PlayerRegistration.json @@ -4,7 +4,6 @@ "name": "PlayerRegistration", "synchronizable": true, "sideStorable": true, - "filterByStoreIdentifier": true, "observable": true, "relationshipNames": ["teamRegistration"], "properties": [ diff --git a/PadelClub/Data/Gen/Purchase.json b/PadelClub/Data/Gen/Purchase.json index 1ce7169..a8dd49b 100644 --- a/PadelClub/Data/Gen/Purchase.json +++ b/PadelClub/Data/Gen/Purchase.json @@ -45,7 +45,6 @@ } ], "tokenExemptedMethods": [], - "filterByStoreIdentifier": false, "relationshipNames": [] } ] diff --git a/PadelClub/Data/Gen/Round.json b/PadelClub/Data/Gen/Round.json index 1464de5..48a6cf7 100644 --- a/PadelClub/Data/Gen/Round.json +++ b/PadelClub/Data/Gen/Round.json @@ -4,7 +4,6 @@ "name": "Round", "synchronizable": true, "sideStorable": true, - "filterByStoreIdentifier": true, "observable": true, "relationshipNames": [], "properties": [ diff --git a/PadelClub/Data/Gen/TeamRegistration.json b/PadelClub/Data/Gen/TeamRegistration.json index d9b74a7..995d08e 100644 --- a/PadelClub/Data/Gen/TeamRegistration.json +++ b/PadelClub/Data/Gen/TeamRegistration.json @@ -4,7 +4,6 @@ "name": "TeamRegistration", "synchronizable": true, "sideStorable": true, - "filterByStoreIdentifier": true, "observable": true, "relationshipNames": [], "properties": [ diff --git a/PadelClub/Data/Gen/TeamScore.json b/PadelClub/Data/Gen/TeamScore.json index 8257a86..9165c44 100644 --- a/PadelClub/Data/Gen/TeamScore.json +++ b/PadelClub/Data/Gen/TeamScore.json @@ -4,7 +4,6 @@ "name": "TeamScore", "synchronizable": true, "sideStorable": true, - "filterByStoreIdentifier": true, "observable": true, "relationshipNames": ["match"], "properties": [ diff --git a/PadelClub/Data/Gen/Tournament.json b/PadelClub/Data/Gen/Tournament.json index 43c734f..c2ade84 100644 --- a/PadelClub/Data/Gen/Tournament.json +++ b/PadelClub/Data/Gen/Tournament.json @@ -4,7 +4,6 @@ "name": "Tournament", "synchronizable": true, "copyable": true, - "filterByStoreIdentifier": false, "observable": true, "relationshipNames": [], "properties": [ diff --git a/PadelClub/Data/Gen/generator.py b/PadelClub/Data/Gen/generator.py index fc2b74e..a8ece00 100644 --- a/PadelClub/Data/Gen/generator.py +++ b/PadelClub/Data/Gen/generator.py @@ -24,7 +24,6 @@ class SwiftModelGenerator: resource = self.make_resource_name(model_name) resource_name = model_data.get("resource_name", resource) token_exempted = model_data.get("tokenExemptedMethods", []) - filter_by_store = model_data.get("filterByStoreIdentifier", False) lines = ["// Generated by SwiftModelGenerator", "// Do not modify this file manually", ""] @@ -44,7 +43,7 @@ class SwiftModelGenerator: lines.append("") # Add SyncedStorable protocol requirements - lines.extend(self._generate_protocol_requirements(resource_name, token_exempted, filter_by_store)) + lines.extend(self._generate_protocol_requirements(resource_name, token_exempted)) lines.append("") # Properties @@ -347,7 +346,7 @@ class SwiftModelGenerator: lines.append(" }") return lines - def _generate_protocol_requirements(self, resource_name: str, token_exempted: List[str], filter_by_store: bool) -> List[str]: + def _generate_protocol_requirements(self, resource_name: str, token_exempted: List[str]) -> List[str]: """Generate the static functions required by SyncedStorable protocol.""" # Convert HTTP methods to proper format formatted_methods = [f".{method.lower()}" for method in token_exempted] @@ -356,7 +355,6 @@ class SwiftModelGenerator: return [ f" static func resourceName() -> String {{ return \"{resource_name}\" }}", f" static func tokenExemptedMethods() -> [HTTPMethod] {{ return [{methods_str}] }}", - f" static func filterByStoreIdentifier() -> Bool {{ return {str(filter_by_store).lower()} }}" ] def _generate_relationships(self, model_name, properties: List[Dict[str, Any]]) -> List[str]: diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index eb848e7..7743ff5 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -121,13 +121,7 @@ struct EventListView: View { NavigationLink(value: tournament) { TournamentCellView(tournament: tournament) .popover(isPresented: self.$showUserSearch) { - UserSearchView { user in - do { - try StoreCenter.main.giveUserAccess(user.id, data: tournament) - } catch { - Logger.error(error) - } - } + ShareModelView(instance: tournament) } } .contextMenu { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index f3c9992..ebe7651 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -112,13 +112,7 @@ struct TournamentView: View { case .print: PrintSettingsView(tournament: tournament) case .share: - UserSearchView { user in - do { - try StoreCenter.main.giveUserAccess(user.id, data: tournament) - } catch { - Logger.error(error) - } - } + ShareModelView(instance: tournament) } } .environment(tournament) diff --git a/PadelClub/Views/User/ShareModelView.swift b/PadelClub/Views/User/ShareModelView.swift new file mode 100644 index 0000000..90c6a45 --- /dev/null +++ b/PadelClub/Views/User/ShareModelView.swift @@ -0,0 +1,113 @@ +// +// UserSearchView.swift +// PadelClub +// +// Created by Laurent Morvillier on 03/12/2024. +// + +import Combine +import LeStorage +import SwiftUI + +class UserSearchViewModel: ObservableObject { + + @Published var searchText = "" + @Published var userNames: [ShortUser] = [] + + @Published var users: [String] = [] + @Published var availableUsers: [ShortUser] = [] + @Published var selectedUsers: [String] = [] + + init() { + Task { + do { + let service = try StoreCenter.main.service() + let userNames = try await service.getUserNames() + DispatchQueue.main.async { + self.userNames = userNames + self.availableUsers = self.users.compactMap { userId in + self.userNames.first(where: { $0.id == userId }) + } + } + } catch { + Logger.error(error) + } + } + } + + func userTapped(_ user: String) { + if let index = self.selectedUsers.firstIndex(of: user) { + self.selectedUsers.remove(at: index) + } else { + self.selectedUsers.append(user) + } + } + + func contains(_ user: String) -> Bool { + return self.selectedUsers.firstIndex(of: user) != nil + } +} + +struct ShareModelView : View { + @StateObject private var viewModel = UserSearchViewModel() + + let instance: T + + var body: some View { + NavigationView { + if !self.viewModel.availableUsers.isEmpty { + List { + ForEach(self.viewModel.availableUsers, id: \.id) { user in + let isSelected = viewModel.contains(user.id) + UserRow(user: user, isSelected: isSelected) + .contentShape(Rectangle()) + .onTapGesture { + self.viewModel.userTapped(user.id) + self._modifyAuthorizedUsersList() + } + } + } + .listStyle(PlainListStyle()) + .navigationTitle("Partage") + } else { + ContentUnavailableView("Si vous souhaitez partager votre tournoi avec d'autres utilisateurs, veuillez contacter notre support", image: "person.fill.xmark") + } + + }.onAppear { + self.viewModel.selectedUsers = StoreCenter.main.authorizedUsers(for: self.instance.stringId) + self.viewModel.users = DataStore.shared.user.agents + } + } + + fileprivate func _modifyAuthorizedUsersList() { + do { + try StoreCenter.main.setAuthorizedUsers(for: self.instance, users: self.viewModel.selectedUsers) + } catch { + Logger.error(error) + } + + } +} + +struct UserRow: View { + let user: ShortUser + let isSelected: Bool + + var body: some View { + HStack { + Text("\(user.firstName) \(user.lastName)") + Spacer() + if self.isSelected { + Image(systemName: "checkmark").foregroundStyle(.logoOrange) + } + } + .padding(.vertical, 4) + } +} + +// Preview provider +struct ShareModelView_Previews: PreviewProvider { + static var previews: some View { + ShareModelView(instance: Tournament.fake()) + } +} diff --git a/PadelClub/Views/User/UserSearchView.swift b/PadelClub/Views/User/UserSearchView.swift deleted file mode 100644 index 809a5d0..0000000 --- a/PadelClub/Views/User/UserSearchView.swift +++ /dev/null @@ -1,168 +0,0 @@ -// -// UserSearchView.swift -// PadelClub -// -// Created by Laurent Morvillier on 03/12/2024. -// - -import Combine -import LeStorage -import SwiftUI - -class UserSearchViewModel: ObservableObject { - @Published var searchText = "" - @Published var users: [ShortUser] = [] - @Published var isLoading = false - @Published var error: String? - @Published var selectedUser: ShortUser? = nil - - private var cancellables = Set() - private var originalUsers: [ShortUser] = [] - private var lastSearchTerm = "" - - init() { - // Debounce search to avoid too many requests - $searchText - .removeDuplicates() - .debounce(for: .milliseconds(300), scheduler: RunLoop.main) - .sink { [weak self] searchTerm in - self?.handleSearch(searchTerm) - } - .store(in: &cancellables) - } - - private func handleSearch(_ searchTerm: String) { - guard !searchTerm.isEmpty else { - users = [] - return - } - - // If going backwards in search, filter existing results - if searchTerm.count < lastSearchTerm.count && !originalUsers.isEmpty { - filterExistingResults(searchTerm) - return - } - - // Otherwise, make a new request - performServerSearch(searchTerm) - } - - private func filterExistingResults(_ searchTerm: String) { - users = originalUsers.filter { user in - user.firstName.localizedCaseInsensitiveContains(searchTerm) - || user.lastName.localizedCaseInsensitiveContains(searchTerm) - } - } - - private func performServerSearch(_ searchTerm: String) { - isLoading = true - error = nil - - Task { - do { - let services = try StoreCenter.main.service() - let searchResults = try await services.searchUsers(string: searchTerm) - - await MainActor.run { - self.originalUsers = searchResults - self.users = searchResults - self.lastSearchTerm = searchTerm - self.isLoading = false - } - } catch { - await MainActor.run { - self.error = error.localizedDescription - self.isLoading = false - } - } - } - } -} - -struct UserSearchView: View { - @StateObject private var viewModel = UserSearchViewModel() - - var handler: (ShortUser) -> Void - - var body: some View { - NavigationView { - VStack { - searchField - - if viewModel.isLoading { - loadingView - } else if let error = viewModel.error { - errorView(error) - } else { - List { - ForEach(viewModel.users, id: \.id) { user in - let isSelected = (user.id == viewModel.selectedUser?.id) - UserRow(user: user, isSelected: isSelected) - .contentShape(Rectangle()) - .onTapGesture { - viewModel.selectedUser = user - } - } - } - .listStyle(PlainListStyle()) - } - } - .navigationTitle("Search Users") - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button("Select") { - if let selectedUser = viewModel.selectedUser { - handler(selectedUser) - } - } - .disabled(viewModel.selectedUser == nil) - } - } - } - } - - private var searchField: some View { - TextField("Search users...", text: $viewModel.searchText) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .padding() - } - - private var loadingView: some View { - ProgressView() - .progressViewStyle(CircularProgressViewStyle()) - .scaleEffect(1.5) - .frame(maxHeight: .infinity) - } - - private func errorView(_ error: String) -> some View { - Text(error) - .foregroundColor(.red) - .frame(maxHeight: .infinity) - } - -} - -struct UserRow: View { - let user: ShortUser - let isSelected: Bool - - var body: some View { - HStack { - Text("\(user.firstName) \(user.lastName)") - Spacer() - if self.isSelected { - Image(systemName: "checkmark").tint(.logoOrange) - } - } - .padding(.vertical, 4) - } -} - -// Preview provider -struct UserSearchView_Previews: PreviewProvider { - static var previews: some View { - UserSearchView { user in - - } - } -} From ce4f0d11d7ec50a5868c6214760f15a6cc93904a Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 10 Dec 2024 16:52:44 +0100 Subject: [PATCH 13/43] Fixes and improvements --- PadelClub/Data/DataStore.swift | 12 ++++++------ PadelClub/Data/PlayerRegistration.swift | 9 ++++++--- PadelClub/Data/TeamScore.swift | 11 ++++++++--- PadelClub/Utils/URLs.swift | 2 +- .../Views/Navigation/Toolbox/DebugSettingsView.swift | 6 +++--- PadelClub/Views/User/ShareModelView.swift | 2 +- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index dc5b308..b2afec5 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -54,18 +54,18 @@ class DataStore: ObservableObject { let store = Store.main StoreCenter.main.blackListUserName("apple-test") - let httpScheme: String = URLs.httpScheme.rawValue +// let secureScheme = true let domain: String = URLs.activationHost.rawValue #if DEBUG - if let scheme = PListReader.readString(plist: "local", key: "server_scheme"), - let domain = PListReader.readString(plist: "local", key: "server_domain"){ - StoreCenter.main.configureURLs(httpScheme: scheme, domain: domain) + if let secure = PListReader.readBool(plist: "local", key: "secure_server"), + let domain = PListReader.readString(plist: "local", key: "server_domain") { + StoreCenter.main.configureURLs(secureScheme: secure, domain: domain) } else { - StoreCenter.main.configureURLs(httpScheme: httpScheme, domain: domain) + StoreCenter.main.configureURLs(secureScheme: true, domain: domain) } #else - StoreCenter.main.configureURLs(httpScheme: httpScheme, domain: domain) + StoreCenter.main.configureURLs(secureScheme: true, domain: domain) #endif StoreCenter.main.logsFailedAPICalls() diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index f6ef828..003024f 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -125,10 +125,13 @@ final class PlayerRegistration: BasePlayerRegistration, SideStorable { } var tournamentStore: TournamentStore { - if let store = self.store as? TournamentStore { - return store + guard let storeId else { + fatalError("missing store id for \(String(describing: type(of: self)))") } - fatalError("missing store for \(String(describing: type(of: self)))") + return TournamentLibrary.shared.store(tournamentId: storeId) +// if let store = self.store as? TournamentStore { +// return store +// } } var computedAge: Int? { diff --git a/PadelClub/Data/TeamScore.swift b/PadelClub/Data/TeamScore.swift index 945191f..c3fedde 100644 --- a/PadelClub/Data/TeamScore.swift +++ b/PadelClub/Data/TeamScore.swift @@ -51,10 +51,15 @@ final class TeamScore: BaseTeamScore, SideStorable { } var tournamentStore: TournamentStore { - if let store = self.store as? TournamentStore { - return store + guard let storeId else { + fatalError("missing store id for \(String(describing: type(of: self)))") } - fatalError("missing store for \(String(describing: type(of: self)))") + return TournamentLibrary.shared.store(tournamentId: storeId) +// +// if let store = self.store as? TournamentStore { +// return store +// } +// fatalError("missing store for \(String(describing: type(of: self)))") } // MARK: - Computed dependencies diff --git a/PadelClub/Utils/URLs.swift b/PadelClub/Utils/URLs.swift index 777e77a..d7d0222 100644 --- a/PadelClub/Utils/URLs.swift +++ b/PadelClub/Utils/URLs.swift @@ -8,7 +8,7 @@ import Foundation enum URLs: String, Identifiable { - case httpScheme = "https://" +// case httpScheme = "https://" #if DEBUG case activationHost = "xlr.alwaysdata.net" case main = "https://xlr.alwaysdata.net/" diff --git a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift index 84786f0..f2f7943 100644 --- a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift @@ -14,7 +14,7 @@ struct DebugSettingsView: View { LabeledContent("UUID", value: self._userId) LabeledContent("User Name", value: self._userName) LabeledContent("Token", value: self._token) - LabeledContent("Server", value: self._server) + LabeledContent("Server", value: self._apiURL) LabeledContent("Synchronized", value: self._synchronized) LabeledContent("CollectionsCanSynchronize", value: self._canSynchronize) } @@ -32,8 +32,8 @@ struct DebugSettingsView: View { return StoreCenter.main.token() ?? "" } - fileprivate var _server: String { - return PListReader.readString(plist: "local", key: "server") ?? "" + fileprivate var _apiURL: String { + return StoreCenter.main.apiURL ?? "not configured" } fileprivate var _synchronized: String { diff --git a/PadelClub/Views/User/ShareModelView.swift b/PadelClub/Views/User/ShareModelView.swift index 90c6a45..c281ccd 100644 --- a/PadelClub/Views/User/ShareModelView.swift +++ b/PadelClub/Views/User/ShareModelView.swift @@ -70,7 +70,7 @@ struct ShareModelView : View { .listStyle(PlainListStyle()) .navigationTitle("Partage") } else { - ContentUnavailableView("Si vous souhaitez partager votre tournoi avec d'autres utilisateurs, veuillez contacter notre support", image: "person.fill.xmark") + ContentUnavailableView("Si vous souhaitez partager votre tournoi avec d'autres utilisateurs, veuillez contacter notre support", image: "person.crop.circle.badge.xmark") } }.onAppear { From 8851315cf434687664502c6b20df3cf352b0ab8c Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 13 Dec 2024 11:14:09 +0100 Subject: [PATCH 14/43] Adds patch to add storeId to needing entities --- PadelClub/Data/DataStore.swift | 2 +- PadelClub/Utils/Patcher.swift | 175 ++++++++++++++++++++++++++++----- 2 files changed, 153 insertions(+), 24 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index b2afec5..6b52a36 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -127,7 +127,7 @@ class DataStore: ObservableObject { self._fixMissingEventCreatorIfNecessary(eventsCollection) } - if Store.main.collectionsAllLoaded() { + if Store.main.fileCollectionsAllLoaded() { Patcher.applyAllWhenApplicable() } diff --git a/PadelClub/Utils/Patcher.swift b/PadelClub/Utils/Patcher.swift index cefbf60..0b508be 100644 --- a/PadelClub/Utils/Patcher.swift +++ b/PadelClub/Utils/Patcher.swift @@ -13,9 +13,8 @@ enum PatchError: Error { } enum Patch: String, CaseIterable { - case alexisLeDu - case importDataFromDevToProd - case fixMissingMatches + case cleanLogs + case syncUpgrade var id: String { return "padelclub.app.patch.\(self.rawValue)" @@ -44,32 +43,162 @@ class Patcher { fileprivate static func _applyPatch(_ patch: Patch) throws { switch patch { - case .alexisLeDu: self._patchAlexisLeDu() - case .importDataFromDevToProd: try self._importDataFromDev() - case .fixMissingMatches: self._patchMissingMatches() + case .cleanLogs: self._cleanLogs() + case .syncUpgrade: self._syncUpgrade() } } +// +// fileprivate static func _patchAlexisLeDu() { +// guard StoreCenter.main.userId == "94f45ed2-8938-4c32-a4b6-e4525073dd33" else { return } +// +// let clubs = DataStore.shared.clubs +// StoreCenter.main.resetApiCalls(collection: clubs) +//// clubs.resetApiCalls() +// +// for club in clubs.filter({ $0.creator == "d5060b89-e979-4c19-bf78-e459a6ed5318"}) { +// club.creator = StoreCenter.main.userId +// clubs.writeChangeAndInsertOnServer(instance: club) +// } +// +// } +// +// fileprivate static func _importDataFromDev() throws { +// +// let devServices = Services(url: "https://xlr.alwaysdata.net/roads/") +// guard devServices.hasToken() else { +// return +// } +// guard StoreCenter.main.synchronizationApiURL == "https://padelclub.app/roads/" else { +// return +// } +// +// guard let userId = StoreCenter.main.userId else { +// return +// } +// +// try StoreCenter.main.migrateToken(devServices) +// +// +// let myClubs: [Club] = DataStore.shared.clubs.filter { $0.creator == userId } +// let clubIds: [String] = myClubs.map { $0.id } +// +// myClubs.forEach { club in +// DataStore.shared.clubs.insertIntoCurrentService(item: club) +// +// let courts = DataStore.shared.courts.filter { clubIds.contains($0.club) } +// for court in courts { +// DataStore.shared.courts.insertIntoCurrentService(item: court) +// } +// } +// +// DataStore.shared.user.clubs = Array(clubIds) +// DataStore.shared.saveUser() +// +// DataStore.shared.events.insertAllIntoCurrentService() +// DataStore.shared.tournaments.insertAllIntoCurrentService() +// DataStore.shared.dateIntervals.insertAllIntoCurrentService() +// +// for tournament in DataStore.shared.tournaments { +// let store = tournament.tournamentStore +// +// Task { // need to wait for the collections to load +// try await Task.sleep(until: .now + .seconds(2)) +// +// store.teamRegistrations.insertAllIntoCurrentService() +// store.rounds.insertAllIntoCurrentService() +// store.groupStages.insertAllIntoCurrentService() +// store.matches.insertAllIntoCurrentService() +// store.playerRegistrations.insertAllIntoCurrentService() +// store.teamScores.insertAllIntoCurrentService() +// +// } +// } +// +// } +// +// fileprivate static func _patchMissingMatches() { +// +// guard let url = StoreCenter.main.synchronizationApiURL else { +// return +// } +// guard url == "https://padelclub.app/roads/" else { +// return +// } +// let services = Services(url: url) +// +// for tournament in DataStore.shared.tournaments { +// +// let store = tournament.tournamentStore +// let identifier = StoreIdentifier(value: tournament.id, parameterName: "tournament") +// +// Task { +// +// do { +// // if nothing is online we upload the data +// let matches: [Match] = try await services.get(identifier: identifier) +// if matches.isEmpty { +// store.matches.insertAllIntoCurrentService() +// } +// +// let playerRegistrations: [PlayerRegistration] = try await services.get(identifier: identifier) +// if playerRegistrations.isEmpty { +// store.playerRegistrations.insertAllIntoCurrentService() +// } +// +// let teamScores: [TeamScore] = try await services.get(identifier: identifier) +// if teamScores.isEmpty { +// store.teamScores.insertAllIntoCurrentService() +// } +// +// } catch { +// Logger.error(error) +// } +// +// } +// } +// +// } - fileprivate static func _patchAlexisLeDu() { - guard StoreCenter.main.userId == "94f45ed2-8938-4c32-a4b6-e4525073dd33" else { return } - - let clubs = DataStore.shared.clubs - StoreCenter.main.resetApiCalls(collection: clubs) -// clubs.resetApiCalls() - - for club in clubs.filter({ $0.creator == "d5060b89-e979-4c19-bf78-e459a6ed5318"}) { - club.creator = StoreCenter.main.userId - clubs.writeChangeAndInsertOnServer(instance: club) - } - + fileprivate static func _cleanLogs() { +// StoreCenter.main.resetLoggingCollections() } - fileprivate static func _importDataFromDev() throws { - - } + fileprivate static func _syncUpgrade() { + for tournament in DataStore.shared.tournaments { + let id = tournament.id + + let store = TournamentLibrary.shared.store(tournamentId: tournament.id) + + for round in store.rounds { + round.storeId = id + } + store.rounds.addOrUpdate(contentOfs: store.rounds) + for groupStage in store.groupStages { + groupStage.storeId = id + } + store.groupStages.addOrUpdate(contentOfs: store.groupStages) + for teamRegistration in store.teamRegistrations { + teamRegistration.storeId = id + } + store.teamRegistrations.addOrUpdate(contentOfs: store.teamRegistrations) + for pr in store.playerRegistrations { + pr.storeId = id + } + store.playerRegistrations.addOrUpdate(contentOfs: store.playerRegistrations) + for match in store.matches { + match.storeId = id + } + store.matches.addOrUpdate(contentOfs: store.matches) + for ts in store.teamScores { + ts.storeId = id + } + store.teamScores.addOrUpdate(contentOfs: store.teamScores) + for ms in store.matchSchedulers { + ms.storeId = id + } + store.matchSchedulers.addOrUpdate(contentOfs: store.matchSchedulers) - fileprivate static func _patchMissingMatches() { - + } } } From 1210a784d0759da7a59c41fddbf9b1a5b81d0806 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 16 Dec 2024 13:55:53 +0100 Subject: [PATCH 15/43] update readme guide --- PadelClub/Data/README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/PadelClub/Data/README.md b/PadelClub/Data/README.md index a0f4424..413d288 100644 --- a/PadelClub/Data/README.md +++ b/PadelClub/Data/README.md @@ -5,24 +5,22 @@ Dans Swift: - Pour générer les fichiers, on se place dans le répertoire de generator.py et on lance la commande : python generator.py -i . -o . Dans Django: -- Les modèles de base doivent étendre BaseModel. -- Les modèles stockés dans des répertoires doivent étendre SideStoreModel. +- Les modèles de base doivent étendre BaseModel +- Les modèles stockés dans des répertoires doivent étendre SideStoreModel - Les classes d'admin doivent étendre AutoUpdateAdmin # Procédure d'ajout de champ dans une classe Dans Swift: -- Ajouter le champ dans classe -- Ajouter le champ dans le constructeur si possible -- Ajouter la codingKey correspondante -- Ajouter le champ dans l'encoding/decoding +- Ouvrir le fichier .json correspondant à la classe +- Regénérer la classe, voir ci-dessus pour la commande - Ouvrir **ServerDataTests** et ajouter un test sur le champ - Pour que les tests sur les dates fonctionnent, on peut tester date.formatted() par exemple Dans Django: - Ajouter le champ dans la classe -- Si c'est une ForeignKey, toujours mettre un related_name sinon la synchro casse +- Si c'est une ForeignKey, *toujours* mettre un related_name sinon la synchro casse - Si c'est un champ dans **CustomUser**: - Ajouter le champ à la méthode fields_for_update - Ajouter le champ dans UserSerializer > create > create_user dans serializers.py From ce6e2281e363c7a02d4d25e997fbe8a32391b6e4 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 16 Dec 2024 14:26:43 +0100 Subject: [PATCH 16/43] sets acronym when empty --- PadelClub/Views/Club/ClubSearchView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PadelClub/Views/Club/ClubSearchView.swift b/PadelClub/Views/Club/ClubSearchView.swift index 9c5378c..492e8b3 100644 --- a/PadelClub/Views/Club/ClubSearchView.swift +++ b/PadelClub/Views/Club/ClubSearchView.swift @@ -324,6 +324,8 @@ struct ClubSearchView: View { if clubToEdit.hasBeenCreated(by: StoreCenter.main.userId) { if clubToEdit.name.isEmpty { clubToEdit.name = clubMarker.nom.capitalized + } + if clubToEdit.acronym.isEmpty { clubToEdit.acronym = clubToEdit.automaticShortName().uppercased() } clubToEdit.code = clubMarker.clubID From 7a0d2a8da76ec57601ccd52275c4fae34e71a727 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 16 Dec 2024 16:24:02 +0100 Subject: [PATCH 17/43] Improve code --- .../Tournament/Screen/Components/TournamentStatusView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift index 9add4fd..799d634 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift @@ -48,9 +48,10 @@ struct TournamentStatusView: View { do { let event = tournament.eventObject() let isLastTournament = event?.tournaments.count == 1 - try dataStore.tournaments.delete(instance: tournament) if let event, isLastTournament { try dataStore.events.delete(instance: event) + } else { + try dataStore.tournaments.delete(instance: tournament) } if eventDismiss == false || isLastTournament { navigation.path = NavigationPath() From bdea94f472ba37e26085df6f35d0307f7d6f55aa Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 6 Jan 2025 15:38:41 +0100 Subject: [PATCH 18/43] cleanup --- PadelClub/Data/DataStore.swift | 118 ++++++++++++++++----------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index d4364d8..1c1942d 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -231,65 +231,65 @@ class DataStore: ObservableObject { } - func copyToLocalServer(tournament: Tournament) { - - Task { - do { - - if let url = PListReader.readString(plist: "local", key: "local_server"), - let login = PListReader.readString(plist: "local", key: "username"), - let pass = PListReader.readString(plist: "local", key: "password") { - let service = Services(url: url) - let _: CustomUser = try await service.login(username: login, password: pass) - - tournament.event = nil - _ = try await service.post(tournament) - - for groupStage in tournament.groupStages() { - _ = try await service.post(groupStage) - } - for round in tournament.rounds() { - try await self._insertRoundAndChildren(round: round, service: service) - } - for teamRegistration in tournament.unsortedTeams() { - _ = try await service.post(teamRegistration) - for playerRegistration in teamRegistration.unsortedPlayers() { - _ = try await service.post(playerRegistration) - } - } - for groupStage in tournament.groupStages() { - for match in groupStage._matches() { - try await self._insertMatch(match: match, service: service) - } - } - for round in tournament.allRounds() { - for match in round._matches() { - try await self._insertMatch(match: match, service: service) - } - } - - } - } catch { - Logger.error(error) - } - } - - } - - fileprivate func _insertRoundAndChildren(round: Round, service: Services) async throws { - _ = try await service.post(round) - for loserRound in round.loserRounds() { - try await self._insertRoundAndChildren(round: loserRound, service: service) - } - } - - fileprivate func _insertMatch(match: Match, service: Services) async throws { - _ = try await service.post(match) - for teamScore in match.teamScores { - _ = try await service.post(teamScore) - } - - } +// func copyToLocalServer(tournament: Tournament) { +// +// Task { +// do { +// +// if let url = PListReader.readString(plist: "local", key: "local_server"), +// let login = PListReader.readString(plist: "local", key: "username"), +// let pass = PListReader.readString(plist: "local", key: "password") { +// let service = Services(url: url) +// let _: CustomUser = try await service.login(username: login, password: pass) +// +// tournament.event = nil +// _ = try await service.post(tournament) +// +// for groupStage in tournament.groupStages() { +// _ = try await service.post(groupStage) +// } +// for round in tournament.rounds() { +// try await self._insertRoundAndChildren(round: round, service: service) +// } +// for teamRegistration in tournament.unsortedTeams() { +// _ = try await service.post(teamRegistration) +// for playerRegistration in teamRegistration.unsortedPlayers() { +// _ = try await service.post(playerRegistration) +// } +// } +// for groupStage in tournament.groupStages() { +// for match in groupStage._matches() { +// try await self._insertMatch(match: match, service: service) +// } +// } +// for round in tournament.allRounds() { +// for match in round._matches() { +// try await self._insertMatch(match: match, service: service) +// } +// } +// +// } +// } catch { +// Logger.error(error) +// } +// } +// +// } +// +// fileprivate func _insertRoundAndChildren(round: Round, service: Services) async throws { +// _ = try await service.post(round) +// for loserRound in round.loserRounds() { +// try await self._insertRoundAndChildren(round: loserRound, service: service) +// } +// } +// +// fileprivate func _insertMatch(match: Match, service: Services) async throws { +// _ = try await service.post(match) +// for teamScore in match.teamScores { +// _ = try await service.post(teamScore) +// } +// +// } // MARK: - Convenience From 780d93dfe21524f7881b65b3aae1cb89b9ffabd7 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 7 Jan 2025 10:15:23 +0100 Subject: [PATCH 19/43] update club to use the new copy system to receive its broadcast code --- PadelClub/Data/Club.swift | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index 0d79ea9..8c64362 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -12,13 +12,8 @@ import LeStorage @Observable final class Club: BaseClub { - override func copyFromServerInstance(_ instance: any Storable) -> Bool { - guard let copy = instance as? Club else { return false } - self.broadcastCode = copy.broadcastCode -// Logger.log("write code: \(self.broadcastCode)") - return true - } - + static var copyServerResponse: Bool { return true } + func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String { switch displayStyle { case .wide, .title: From 37660a1f124088d18a95cd127314c589f016d53e Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 16 Jan 2025 17:43:35 +0100 Subject: [PATCH 20/43] Fix issues --- PadelClub/Data/DataStore.swift | 4 ++- PadelClub/Data/TournamentLibrary.swift | 4 +++ PadelClub/Data/TournamentStore.swift | 14 +++++++++ .../Toolbox/DebugSettingsView.swift | 31 +++++++++++++++---- .../Shared/TournamentCellView.swift | 10 +++--- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 1c1942d..6d59761 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -102,7 +102,7 @@ class DataStore: ObservableObject { NotificationCenter.default.addObserver(self, selector: #selector(collectionDidLoad), name: NSNotification.Name.CollectionDidLoad, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(collectionDidUpdate), name: NSNotification.Name.CollectionDidChange, object: nil) - + } deinit { @@ -210,6 +210,8 @@ class DataStore: ObservableObject { let tournamendIds: [String] = self.tournaments.map { $0.id } + TournamentLibrary.shared.reset() + self.tournaments.reset() self.clubs.reset() self.courts.reset() diff --git a/PadelClub/Data/TournamentLibrary.swift b/PadelClub/Data/TournamentLibrary.swift index 948a65e..228e6e2 100644 --- a/PadelClub/Data/TournamentLibrary.swift +++ b/PadelClub/Data/TournamentLibrary.swift @@ -24,4 +24,8 @@ class TournamentLibrary { return tournamentStore } + func reset() { + self._stores.removeAll() + } + } diff --git a/PadelClub/Data/TournamentStore.swift b/PadelClub/Data/TournamentStore.swift index bdc5bb9..5439e30 100644 --- a/PadelClub/Data/TournamentStore.swift +++ b/PadelClub/Data/TournamentStore.swift @@ -56,6 +56,20 @@ class TournamentStore: ObservableObject { self.store.loadCollectionsFromServerIfNoFile() + NotificationCenter.default.addObserver( + self, + selector: #selector(_leStorageDidSynchronize), + name: NSNotification.Name.LeStorageDidSynchronize, + object: nil) + + } + + @objc func _leStorageDidSynchronize(notification: Notification) { + Logger.log("SYNCED > Match count = \(self.matches.count)") + } + + deinit { + NotificationCenter.default.removeObserver(self) } } diff --git a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift index f2f7943..246c3a7 100644 --- a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift @@ -11,12 +11,21 @@ import LeStorage struct DebugSettingsView: View { var body: some View { List { - LabeledContent("UUID", value: self._userId) - LabeledContent("User Name", value: self._userName) - LabeledContent("Token", value: self._token) - LabeledContent("Server", value: self._apiURL) - LabeledContent("Synchronized", value: self._synchronized) - LabeledContent("CollectionsCanSynchronize", value: self._canSynchronize) + + Section("Status") { + LabeledContent("Has Websocket Manager", value: self._hasWebSocketManager) + LabeledContent("Websocket ping", value: self._wsPingStatus) + LabeledContent("Websocket failure", value: self._wsFailure) + } + + Section("Settings") { + LabeledContent("UUID", value: self._userId) + LabeledContent("User Name", value: self._userName) + LabeledContent("Token", value: self._token) + LabeledContent("Server", value: self._apiURL) + LabeledContent("Synchronized", value: self._synchronized) + LabeledContent("CollectionsCanSynchronize", value: self._canSynchronize) + } } } @@ -47,6 +56,16 @@ struct DebugSettingsView: View { fileprivate var _canSynchronize: String { return "\(StoreCenter.main.collectionsCanSynchronize)" } + + fileprivate var _wsPingStatus: String { + return "\(StoreCenter.main.websocketPingStatus)" + } + fileprivate var _wsFailure: String { + return "\(StoreCenter.main.websocketFailure)" + } + fileprivate var _hasWebSocketManager: String { + return "\(StoreCenter.main.hasWebSocketManager)" + } } //#Preview { diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index e3b862d..dd7169c 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -42,13 +42,13 @@ struct TournamentCellView: View { } var teamCount: Int? { - if let tournament = tournament as? Tournament { - let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() - let count = hasStarted ? tournament.selectedSortedTeams().count : tournament.unsortedTeamsWithoutWO().count - return count - } else { + guard let tournament = tournament as? Tournament, DataStore.shared.tournaments.contains(where: {$0.id == self.tournament.holderId }) else { // we make sure the tournament is in the DataStore otherwise we keep instantiating TournamentStore after disconnections return nil } + + let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() + let count = hasStarted ? tournament.selectedSortedTeams().count : tournament.unsortedTeamsWithoutWO().count + return count } fileprivate func _spacing() -> CGFloat { From 18a0ed6d10c7e830ba9ae7b53c0ef661d43081cb Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 28 Jan 2025 16:27:07 +0100 Subject: [PATCH 21/43] update readme for cascading deletes in admin --- PadelClub/Data/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PadelClub/Data/README.md b/PadelClub/Data/README.md index 413d288..c61a0b2 100644 --- a/PadelClub/Data/README.md +++ b/PadelClub/Data/README.md @@ -7,7 +7,9 @@ Dans Swift: Dans Django: - Les modèles de base doivent étendre BaseModel - Les modèles stockés dans des répertoires doivent étendre SideStoreModel -- Les classes d'admin doivent étendre AutoUpdateAdmin +- Les classes d'admin doivent étendre SyncedObjectAdmin +- Les ForeignKey doivent toujours avoir on_delete=models.SET_NULL + - Pour se faciliter la vie dans l'admin, on veut que les delete effacent les enfants malgré tout. Il faut donc implémenter la méthode delete_dependencies(self) de la classe # Procédure d'ajout de champ dans une classe From a83e630d79d85f3e6af53d32b3107872e37c9d27 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 30 Jan 2025 15:47:11 +0100 Subject: [PATCH 22/43] Adds SideStorable --- PadelClub/Data/DrawLog.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Data/DrawLog.swift b/PadelClub/Data/DrawLog.swift index 55268f3..f2195ab 100644 --- a/PadelClub/Data/DrawLog.swift +++ b/PadelClub/Data/DrawLog.swift @@ -10,7 +10,7 @@ import SwiftUI import LeStorage @Observable -final class DrawLog: BaseDrawLog { +final class DrawLog: BaseDrawLog, SideStorable { func tournamentObject() -> Tournament? { Store.main.findById(self.tournament) From cdf89c33c4989d83fedf2ce59a9d0b4038d58cad Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 30 Jan 2025 15:47:31 +0100 Subject: [PATCH 23/43] Adds Drawlog api collection loader --- PadelClub/Data/DataStore.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 6d59761..67fed87 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -99,6 +99,7 @@ class DataStore: ObservableObject { StoreCenter.main.loadApiCallCollection(type: TeamRegistration.self) StoreCenter.main.loadApiCallCollection(type: Match.self) StoreCenter.main.loadApiCallCollection(type: TeamScore.self) + StoreCenter.main.loadApiCallCollection(type: DrawLog.self) NotificationCenter.default.addObserver(self, selector: #selector(collectionDidLoad), name: NSNotification.Name.CollectionDidLoad, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(collectionDidUpdate), name: NSNotification.Name.CollectionDidChange, object: nil) From 5795a3b62f416e89d5a07deb748a88778c397de5 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 30 Jan 2025 15:47:51 +0100 Subject: [PATCH 24/43] Fix crash --- PadelClub/Data/Tournament.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 209c8be..fdb5b4f 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -83,7 +83,7 @@ final class Tournament: BaseTournament { override func deleteDependencies() { let store = self.tournamentStore - let drawLogs = self.tournamentStore.drawLogs + let drawLogs = Array(self.tournamentStore.drawLogs) for drawLog in drawLogs { drawLog.deleteDependencies() } @@ -95,13 +95,13 @@ final class Tournament: BaseTournament { } store.teamRegistrations.deleteDependencies(teams) - let groups = self.tournamentStore.groupStages + let groups = Array(self.tournamentStore.groupStages) for group in groups { group.deleteDependencies() } store.groupStages.deleteDependencies(groups) - let rounds = self.tournamentStore.rounds + let rounds = Array(self.tournamentStore.rounds) for round in rounds { round.deleteDependencies() } @@ -109,11 +109,11 @@ final class Tournament: BaseTournament { store.matchSchedulers.deleteDependencies(self._matchSchedulers()) - if let event = self.eventObject() { - if event.tournaments.count == 1 && event.tournaments.first?.id == self.id { - DataStore.shared.events.deleteDependencies([event]) - } - } +// if let event = self.eventObject() { +// if event.tournaments.count == 1 && event.tournaments.first?.id == self.id { +// DataStore.shared.events.deleteDependencies([event]) +// } +// } } From edc00f05151fa10ce111b21b959ea851cb138022 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 30 Jan 2025 17:52:00 +0100 Subject: [PATCH 25/43] fix issue with disconnection by making TournamentStore optional --- PadelClub/Data/DataStore.swift | 24 +-- PadelClub/Data/DrawLog.swift | 4 +- PadelClub/Data/GroupStage.swift | 32 ++-- PadelClub/Data/Match.swift | 62 ++++---- PadelClub/Data/MatchScheduler.swift | 8 +- PadelClub/Data/PlayerRegistration.swift | 6 +- PadelClub/Data/Round.swift | 82 +++++----- PadelClub/Data/TeamRegistration.swift | 58 ++++--- PadelClub/Data/TeamScore.swift | 8 +- PadelClub/Data/Tournament.swift | 146 ++++++++++-------- PadelClub/Data/TournamentLibrary.swift | 4 +- PadelClub/Utils/Patcher.swift | 2 +- .../Views/Calling/CallSettingsView.swift | 6 +- PadelClub/Views/Calling/CallView.swift | 4 +- PadelClub/Views/Calling/SendToAllView.swift | 8 +- .../Views/Calling/TeamsCallingView.swift | 6 +- .../Views/Cashier/CashierSettingsView.swift | 2 +- .../Components/GroupStageSettingsView.swift | 12 +- .../Components/GroupStageTeamView.swift | 4 +- .../Views/GroupStage/GroupStageView.swift | 10 +- .../GroupStage/GroupStagesSettingsView.swift | 22 +-- .../Views/GroupStage/GroupStagesView.swift | 2 +- .../LoserBracketFromGroupStageView.swift | 12 +- .../Match/Components/MatchDateView.swift | 2 +- PadelClub/Views/Match/MatchDetailView.swift | 22 ++- PadelClub/Views/Match/MatchRowView.swift | 2 +- PadelClub/Views/Match/MatchSetupView.swift | 34 ++-- .../Navigation/Toolbox/ToolboxView.swift | 53 ++++--- .../GroupStageScheduleEditorView.swift | 4 +- .../LoserRoundScheduleEditorView.swift | 4 +- .../LoserRoundStepScheduleEditorView.swift | 4 +- .../Planning/MatchScheduleEditorView.swift | 4 +- .../Views/Planning/PlanningSettingsView.swift | 24 +-- PadelClub/Views/Planning/PlanningView.swift | 4 +- .../Planning/RoundScheduleEditorView.swift | 4 +- PadelClub/Views/Planning/SchedulerView.swift | 10 +- .../Components/PlayerSexPickerView.swift | 6 +- PadelClub/Views/Player/PlayerDetailView.swift | 6 +- PadelClub/Views/Player/PlayerView.swift | 4 +- PadelClub/Views/Round/DrawLogsView.swift | 2 +- .../Views/Round/LoserRoundSettingsView.swift | 8 +- PadelClub/Views/Round/LoserRoundView.swift | 2 +- PadelClub/Views/Round/RoundSettingsView.swift | 14 +- PadelClub/Views/Round/RoundView.swift | 8 +- PadelClub/Views/Score/EditScoreView.swift | 2 +- PadelClub/Views/Team/CoachListView.swift | 2 +- PadelClub/Views/Team/EditingTeamView.swift | 16 +- .../Views/Tournament/FileImportView.swift | 6 +- .../Views/Tournament/Screen/AddTeamView.swift | 10 +- .../Components/InscriptionInfoView.swift | 16 +- .../TournamentGeneralSettingsView.swift | 4 +- .../TournamentMatchFormatsSettingsView.swift | 6 +- .../Components/UpdateSourceRankDateView.swift | 6 +- .../Screen/InscriptionManagerView.swift | 6 +- .../Screen/TableStructureView.swift | 2 +- .../Screen/TournamentCashierView.swift | 52 ++++--- .../Screen/TournamentRankView.swift | 12 +- 57 files changed, 477 insertions(+), 408 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 67fed87..05a2984 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -302,10 +302,12 @@ class DataStore: ObservableObject { var runningMatches: [Match] = [] for tournament in lastTournaments { - let matches = tournament.tournamentStore.matches.filter { match in - match.disabled == false && match.isRunning() + if let store = tournament.tournamentStore { + let matches = store.matches.filter { match in + match.disabled == false && match.isRunning() + } + runningMatches.append(contentsOf: matches) } - runningMatches.append(contentsOf: matches) } return runningMatches } @@ -317,9 +319,11 @@ class DataStore: ObservableObject { var runningMatches: [Match] = [] for tournament in lastTournaments { - let matches = tournament.tournamentStore.matches.filter { match in - match.disabled == false && match.startDate != nil && match.endDate == nil } - runningMatches.append(contentsOf: matches) + if let store = tournament.tournamentStore { + let matches = store.matches.filter { match in + match.disabled == false && match.startDate != nil && match.endDate == nil } + runningMatches.append(contentsOf: matches) + } } return runningMatches } @@ -330,9 +334,11 @@ class DataStore: ObservableObject { var runningMatches: [Match] = [] for tournament in lastTournaments { - let matches = tournament.tournamentStore.matches.filter { match in - match.disabled == false && match.hasEnded() } - runningMatches.append(contentsOf: matches) + if let store = tournament.tournamentStore { + let matches = store.matches.filter { match in + match.disabled == false && match.hasEnded() } + runningMatches.append(contentsOf: matches) + } } return runningMatches.sorted(by: \.endDate!, order: .descending) } diff --git a/PadelClub/Data/DrawLog.swift b/PadelClub/Data/DrawLog.swift index f2195ab..a0be6e3 100644 --- a/PadelClub/Data/DrawLog.swift +++ b/PadelClub/Data/DrawLog.swift @@ -52,7 +52,7 @@ final class DrawLog: BaseDrawLog, SideStorable { switch drawType { case .seed: let roundIndex = RoundRule.roundIndex(fromMatchIndex: drawMatchIndex) - return tournamentStore.rounds.first(where: { $0.parent == nil && $0.index == roundIndex })?._matches().first(where: { $0.index == drawMatchIndex }) + return tournamentStore?.rounds.first(where: { $0.parent == nil && $0.index == roundIndex })?._matches().first(where: { $0.index == drawMatchIndex }) default: return nil } @@ -70,7 +70,7 @@ final class DrawLog: BaseDrawLog, SideStorable { return drawMatch()?.matchTitle() ?? "" } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return TournamentLibrary.shared.store(tournamentId: self.tournament) } diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index d2b5319..3542da6 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -22,14 +22,15 @@ final class GroupStage: BaseGroupStage, SideStorable { } } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return TournamentLibrary.shared.store(tournamentId: self.tournament) } // MARK: - Computed dependencies func _matches() -> [Match] { - return self.tournamentStore.matches.filter { $0.groupStage == self.id }.sorted(by: \.index) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter { $0.groupStage == self.id }.sorted(by: \.index) // Store.main.filter { $0.groupStage == self.id } } @@ -101,7 +102,7 @@ final class GroupStage: BaseGroupStage, SideStorable { returnMatches = returnMatches.filter({ $0.index >= matchCount * matchPhaseCount }) } do { - try self.tournamentStore.matches.delete(contentOfs: returnMatches) + try self.tournamentStore?.matches.delete(contentOfs: returnMatches) } catch { Logger.error(error) } @@ -127,8 +128,8 @@ final class GroupStage: BaseGroupStage, SideStorable { matches.append(newMatch) } - self.tournamentStore.matches.addOrUpdate(contentOfs: matches) - self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) + self.tournamentStore?.matches.addOrUpdate(contentOfs: matches) + self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores) } func buildMatches(keepExistingMatches: Bool = false) { @@ -153,8 +154,8 @@ final class GroupStage: BaseGroupStage, SideStorable { } do { - try self.tournamentStore.matches.addOrUpdate(contentOfs: matches) - try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) + try self.tournamentStore?.matches.addOrUpdate(contentOfs: matches) + try self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores) } catch { Logger.error(error) } @@ -192,11 +193,11 @@ final class GroupStage: BaseGroupStage, SideStorable { tournamentObject()?.shouldVerifyBracket = true } } - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) if let tournamentObject = tournamentObject() { try DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject) } - self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) let groupStagesAreOverAtFirstStep = tournament.groupStagesAreOver(atStep: 0) let nextStepGroupStages = tournament.groupStages(atStep: 1) @@ -388,7 +389,7 @@ final class GroupStage: BaseGroupStage, SideStorable { } private func _removeMatches() { - self.tournamentStore.matches.delete(contentOfs: _matches()) + self.tournamentStore?.matches.delete(contentOfs: _matches()) } private func _numberOfMatchesToBuild() -> Int { @@ -444,10 +445,11 @@ final class GroupStage: BaseGroupStage, SideStorable { } func unsortedTeams() -> [TeamRegistration] { + guard let tournamentStore = self.tournamentStore else { return [] } if step > 0 { - return self.tournamentStore.groupStages.filter({ $0.step == step - 1 }).compactMap({ $0.teams(true)[safe: index] }) + return tournamentStore.groupStages.filter({ $0.step == step - 1 }).compactMap({ $0.teams(true)[safe: index] }) } - return self.tournamentStore.teamRegistrations.filter { $0.groupStage == self.id && $0.groupStagePosition != nil } + return tournamentStore.teamRegistrations.filter { $0.groupStage == self.id && $0.groupStagePosition != nil } } @@ -570,7 +572,7 @@ final class GroupStage: BaseGroupStage, SideStorable { playedMatches.forEach { match in match.matchFormat = matchFormat } - self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches) + self.tournamentStore?.matches.addOrUpdate(contentOfs: playedMatches) } func pasteData() -> String { @@ -593,7 +595,7 @@ final class GroupStage: BaseGroupStage, SideStorable { for match in matches { match.deleteDependencies() } - self.tournamentStore.matches.deleteDependencies(matches) + self.tournamentStore?.matches.deleteDependencies(matches) } // init(from decoder: Decoder) throws { @@ -627,7 +629,7 @@ final class GroupStage: BaseGroupStage, SideStorable { // } func insertOnServer() { - self.tournamentStore.groupStages.writeChangeAndInsertOnServer(instance: self) + self.tournamentStore?.groupStages.writeChangeAndInsertOnServer(instance: self) for match in self._matches() { match.insertOnServer() } diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 43f177b..385913f 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -46,7 +46,7 @@ final class Match: BaseMatch, SideStorable { } } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { if let id = self.store?.identifier { return TournamentLibrary.shared.store(tournamentId: id) } @@ -60,7 +60,8 @@ final class Match: BaseMatch, SideStorable { // MARK: - Computed dependencies var teamScores: [TeamScore] { - return self.tournamentStore.teamScores.filter { $0.match == self.id } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.teamScores.filter { $0.match == self.id } } // MARK: - @@ -70,7 +71,7 @@ final class Match: BaseMatch, SideStorable { for teamScore in teamScores { teamScore.deleteDependencies() } - self.tournamentStore.teamScores.deleteDependencies(teamScores) + self.tournamentStore?.teamScores.deleteDependencies(teamScores) } func indexInRound(in matches: [Match]? = nil) -> Int { @@ -149,12 +150,12 @@ defer { func winner() -> TeamRegistration? { guard let winningTeamId else { return nil } - return self.tournamentStore.teamRegistrations.findById(winningTeamId) + return self.tournamentStore?.teamRegistrations.findById(winningTeamId) } func loser() -> TeamRegistration? { guard let losingTeamId else { return nil } - return self.tournamentStore.teamRegistrations.findById(losingTeamId) + return self.tournamentStore?.teamRegistrations.findById(losingTeamId) } @@ -183,7 +184,7 @@ defer { endDate = nil followingMatch()?.cleanScheduleAndSave(nil) _loserMatch()?.cleanScheduleAndSave(nil) - self.tournamentStore.matches.addOrUpdate(instance: self) + self.tournamentStore?.matches.addOrUpdate(instance: self) } func resetMatch() { @@ -200,14 +201,14 @@ defer { func resetScores() { teamScores.forEach({ $0.score = nil }) - self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) + self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores) } func teamWillBeWalkOut(_ team: TeamRegistration) { resetMatch() let existingTeamScore = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team) existingTeamScore.walkOut = 1 - self.tournamentStore.teamScores.addOrUpdate(instance: existingTeamScore) + self.tournamentStore?.teamScores.addOrUpdate(instance: existingTeamScore) } func luckyLosers() -> [TeamRegistration] { @@ -225,11 +226,11 @@ defer { let position = matchIndex * 2 + teamPosition.rawValue let previousScores = teamScores.filter({ $0.luckyLoser == position }) - self.tournamentStore.teamScores.delete(contentOfs: previousScores) + self.tournamentStore?.teamScores.delete(contentOfs: previousScores) let teamScoreLuckyLoser = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team) teamScoreLuckyLoser.luckyLoser = position - self.tournamentStore.teamScores.addOrUpdate(instance: teamScoreLuckyLoser) + self.tournamentStore?.teamScores.addOrUpdate(instance: teamScoreLuckyLoser) } func disableMatch() { @@ -264,14 +265,14 @@ defer { let isTopMatch = topMatchIndex + 1 == index let lookingForIndex = isTopMatch ? topMatchIndex : bottomMatchIndex - return self.tournamentStore.matches.first(where: { $0.round == round && $0.index == lookingForIndex }) + return self.tournamentStore?.matches.first(where: { $0.round == round && $0.index == lookingForIndex }) } private func _forwardMatch(inRound round: Round) -> Match? { guard let roundObjectNextRound = round.nextRound() else { return nil } let nextIndex = (index - 1) / 2 - return self.tournamentStore.matches.first(where: { $0.round == roundObjectNextRound.id && $0.index == nextIndex }) + return self.tournamentStore?.matches.first(where: { $0.round == roundObjectNextRound.id && $0.index == nextIndex }) } func _toggleForwardMatchDisableState(_ state: Bool) { @@ -339,7 +340,7 @@ defer { if disabled != currentState { do { - try self.tournamentStore.teamScores.delete(contentOfs: teamScores) + try self.tournamentStore?.teamScores.delete(contentOfs: teamScores) } catch { Logger.error(error) } @@ -349,7 +350,7 @@ defer { for team in teams { if isSeededBy(team: team) { team.bracketPosition = nil - self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } } } @@ -357,7 +358,7 @@ defer { roundObject?._cachedSeedInterval = nil name = nil do { - try self.tournamentStore.matches.addOrUpdate(instance: self) + try self.tournamentStore?.matches.addOrUpdate(instance: self) } catch { Logger.error(error) } @@ -374,7 +375,8 @@ defer { } func next() -> Match? { - let matches: [Match] = self.tournamentStore.matches.filter { $0.round == round && $0.index > index && $0.disabled == false } + guard let tournamentStore = self.tournamentStore else { return nil } + let matches: [Match] = tournamentStore.matches.filter { $0.round == round && $0.index > index && $0.disabled == false } return matches.sorted(by: \.index).first } @@ -384,7 +386,7 @@ defer { } func getFollowingMatch(fromNextRoundId nextRoundId: String) -> Match? { - return self.tournamentStore.matches.first(where: { $0.round == nextRoundId && $0.index == (index - 1) / 2 }) + return self.tournamentStore?.matches.first(where: { $0.round == nextRoundId && $0.index == (index - 1) / 2 }) } func getDuration() -> Int { @@ -417,7 +419,7 @@ defer { guard let roundObject else { return nil } let topPreviousRoundMatchIndex = topPreviousRoundMatchIndex() let roundObjectPreviousRoundId = roundObject.previousRound()?.id - return self.tournamentStore.matches.first(where: { match in + return self.tournamentStore?.matches.first(where: { match in match.round != nil && match.round == roundObjectPreviousRoundId && match.index == topPreviousRoundMatchIndex }) } @@ -426,7 +428,7 @@ defer { guard let roundObject else { return nil } let bottomPreviousRoundMatchIndex = bottomPreviousRoundMatchIndex() let roundObjectPreviousRoundId = roundObject.previousRound()?.id - return self.tournamentStore.matches.first(where: { match in + return self.tournamentStore?.matches.first(where: { match in match.round != nil && match.round == roundObjectPreviousRoundId && match.index == bottomPreviousRoundMatchIndex }) } @@ -463,8 +465,10 @@ defer { func previousMatches() -> [Match] { guard let roundObject else { return [] } + guard let tournamentStore = self.tournamentStore else { return [] } + let roundObjectPreviousRoundId = roundObject.previousRound()?.id - return self.tournamentStore.matches.filter { match in + return tournamentStore.matches.filter { match in match.round == roundObjectPreviousRoundId && (match.index == topPreviousRoundMatchIndex() || match.index == bottomPreviousRoundMatchIndex()) }.sorted(by: \.index) } @@ -483,7 +487,7 @@ defer { teamScoreWalkout.walkOut = 0 let teamScoreWinning = teamScore(teamPosition.otherTeam) ?? TeamScore(match: id, team: team(teamPosition.otherTeam)) teamScoreWinning.walkOut = nil - self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning]) + self.tournamentStore?.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning]) if endDate == nil { endDate = Date() @@ -542,7 +546,7 @@ defer { teamScoreOne.score = matchDescriptor.teamOneScores.joined(separator: ",") let teamScoreTwo = teamScore(.two) ?? TeamScore(match: id, team: team(.two)) teamScoreTwo.score = matchDescriptor.teamTwoScores.joined(separator: ",") - self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo]) + self.tournamentStore?.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo]) matchFormat = matchDescriptor.matchFormat } @@ -555,7 +559,7 @@ defer { let ids = newTeamScores.map { $0.id } let teamScores = teamScores.filter({ ids.contains($0.id) == false }) if teamScores.isEmpty == false { - self.tournamentStore.teamScores.delete(contentOfs: teamScores) + self.tournamentStore?.teamScores.delete(contentOfs: teamScores) followingMatch()?.resetTeamScores(outsideOf: []) _loserMatch()?.resetTeamScores(outsideOf: []) } @@ -578,7 +582,7 @@ defer { func updateTeamScores() { let teams = getOrCreateTeamScores() - self.tournamentStore.teamScores.addOrUpdate(contentOfs: teams) + self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teams) resetTeamScores(outsideOf: teams) if teams.isEmpty == false { updateFollowingMatchTeamScore() @@ -732,7 +736,8 @@ defer { } func scores() -> [TeamScore] { - return self.tournamentStore.teamScores.filter { $0.match == id } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.teamScores.filter { $0.match == id } } func teams() -> [TeamRegistration] { @@ -864,13 +869,12 @@ defer { var roundObject: Round? { guard let round else { return nil } - return self.tournamentStore.rounds.findById(round) + return self.tournamentStore?.rounds.findById(round) } var groupStageObject: GroupStage? { guard let groupStage else { return nil } - return self.tournamentStore.groupStages.findById(groupStage) -// self.store?.findById(group) + return self.tournamentStore?.groupStages.findById(groupStage) } var isLoserBracket: Bool { @@ -999,7 +1003,7 @@ defer { } func insertOnServer() { - self.tournamentStore.matches.writeChangeAndInsertOnServer(instance: self) + self.tournamentStore?.matches.writeChangeAndInsertOnServer(instance: self) for teamScore in self.teamScores { teamScore.insertOnServer() } diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index c0845a4..e7ad809 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -58,7 +58,7 @@ final class MatchScheduler: BaseMatchScheduler, SideStorable { return tournamentObject()?.additionalEstimationDuration ?? 0 } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return TournamentLibrary.shared.store(tournamentId: self.tournament) // TournamentStore.instance(tournamentId: self.tournament) } @@ -120,7 +120,7 @@ final class MatchScheduler: BaseMatchScheduler, SideStorable { groupStages.filter({ $0.startDate == nil || times.contains($0.startDate!) == false }).chunked(into: computedGroupStageChunkCount).forEach { groups in groups.forEach({ $0.startDate = lastDate }) do { - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: groups) + try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: groups) } catch { Logger.error(error) } @@ -141,7 +141,7 @@ final class MatchScheduler: BaseMatchScheduler, SideStorable { } } do { - try self.tournamentStore.matches.addOrUpdate(contentOfs: matches) + try self.tournamentStore?.matches.addOrUpdate(contentOfs: matches) } catch { Logger.error(error) } @@ -739,7 +739,7 @@ final class MatchScheduler: BaseMatchScheduler, SideStorable { } do { - try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) + try self.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches) } catch { Logger.error(error) } diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 86efd29..e6713b8 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -124,7 +124,7 @@ final class PlayerRegistration: BasePlayerRegistration, SideStorable { try super.init(from: decoder) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { guard let storeId else { fatalError("missing store id for \(String(describing: type(of: self)))") } @@ -198,7 +198,7 @@ final class PlayerRegistration: BasePlayerRegistration, SideStorable { func team() -> TeamRegistration? { guard let teamRegistration else { return nil } - return self.tournamentStore.teamRegistrations.findById(teamRegistration) + return self.tournamentStore?.teamRegistrations.findById(teamRegistration) } func isHere() -> Bool { @@ -417,7 +417,7 @@ final class PlayerRegistration: BasePlayerRegistration, SideStorable { } func insertOnServer() { - self.tournamentStore.playerRegistrations.writeChangeAndInsertOnServer(instance: self) + self.tournamentStore?.playerRegistrations.writeChangeAndInsertOnServer(instance: self) } } diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index ddc0270..f0514d1 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -34,7 +34,7 @@ final class Round: BaseRound, SideStorable { // MARK: - Computed dependencies - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return TournamentLibrary.shared.store(tournamentId: self.tournament) } @@ -43,13 +43,13 @@ final class Round: BaseRound, SideStorable { } func _matches() -> [Match] { - return self.tournamentStore.matches.filter { $0.round == self.id }.sorted(by: \.index) -// return Store.main.filter { $0.round == self.id } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter { $0.round == self.id }.sorted(by: \.index) } func getDisabledMatches() -> [Match] { - return self.tournamentStore.matches.filter { $0.round == self.id && $0.disabled == true } -// return Store.main.filter { $0.round == self.id && $0.disabled == true } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter { $0.round == self.id && $0.disabled == true } } // MARK: - @@ -86,8 +86,9 @@ final class Round: BaseRound, SideStorable { func previousMatches(ofMatch match: Match) -> [Match] { guard let previousRound = previousRound() else { return [] } + guard let tournamentStore = self.tournamentStore else { return [] } - return self.tournamentStore.matches.filter { + return tournamentStore.matches.filter { $0.round == previousRound.id && ($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex()) } @@ -114,7 +115,7 @@ final class Round: BaseRound, SideStorable { } func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? { - return self.tournamentStore.teamRegistrations.first(where: { + return self.tournamentStore?.teamRegistrations.first(where: { $0.bracketPosition != nil && ($0.bracketPosition! / 2) == matchIndex && ($0.bracketPosition! % 2) == team.rawValue @@ -122,7 +123,8 @@ final class Round: BaseRound, SideStorable { } func seeds(inMatchIndex matchIndex: Int) -> [TeamRegistration] { - return self.tournamentStore.teamRegistrations.filter { + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.teamRegistrations.filter { $0.tournament == tournament && $0.bracketPosition != nil @@ -137,9 +139,10 @@ final class Round: BaseRound, SideStorable { } func seeds() -> [TeamRegistration] { + guard let tournamentStore = self.tournamentStore else { return [] } let initialMatchIndex = RoundRule.matchIndex(fromRoundIndex: index) let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index) - return self.tournamentStore.teamRegistrations.filter { + return tournamentStore.teamRegistrations.filter { $0.bracketPosition != nil && ($0.bracketPosition! / 2) >= initialMatchIndex && ($0.bracketPosition! / 2) < initialMatchIndex + numberOfMatches @@ -158,12 +161,12 @@ final class Round: BaseRound, SideStorable { func losers() -> [TeamRegistration] { let teamIds: [String] = self._matches().compactMap { $0.losingTeamId } - return teamIds.compactMap { self.tournamentStore.teamRegistrations.findById($0) } + return teamIds.compactMap { self.tournamentStore?.teamRegistrations.findById($0) } } func winners() -> [TeamRegistration] { let teamIds: [String] = self._matches().compactMap { $0.winningTeamId } - return teamIds.compactMap { self.tournamentStore.teamRegistrations.findById($0) } + return teamIds.compactMap { self.tournamentStore?.teamRegistrations.findById($0) } } func teams() -> [TeamRegistration] { @@ -188,7 +191,7 @@ defer { return luckyLoser.team } else if let previousMatch = topPreviousRoundMatch(ofMatch: match, previousRound: previousRound) { if let teamId = previousMatch.winningTeamId { - return self.tournamentStore.teamRegistrations.findById(teamId) + return self.tournamentStore?.teamRegistrations.findById(teamId) } else if previousMatch.disabled { return previousMatch.teams().first } @@ -200,7 +203,7 @@ defer { return luckyLoser.team } else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match, previousRound: previousRound) { if let teamId = previousMatch.winningTeamId { - return self.tournamentStore.teamRegistrations.findById(teamId) + return self.tournamentStore?.teamRegistrations.findById(teamId) } else if previousMatch.disabled { return previousMatch.teams().first } @@ -265,7 +268,7 @@ defer { guard let previousRound else { return nil } let topPreviousRoundMatchIndex = match.topPreviousRoundMatchIndex() - return self.tournamentStore.matches.first(where: { + return self.tournamentStore?.matches.first(where: { $0.round == previousRound.id && $0.index == topPreviousRoundMatchIndex }) } @@ -281,20 +284,21 @@ defer { guard let previousRound else { return nil } let bottomPreviousRoundMatchIndex = match.bottomPreviousRoundMatchIndex() - return self.tournamentStore.matches.first(where: { + return self.tournamentStore?.matches.first(where: { $0.round == previousRound.id && $0.index == bottomPreviousRoundMatchIndex }) } func getMatch(atMatchIndexInRound matchIndexInRound: Int) -> Match? { - self.tournamentStore.matches.first(where: { + self.tournamentStore?.matches.first(where: { let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index) return $0.round == id && index == matchIndexInRound }) } func enabledMatches() -> [Match] { - return self.tournamentStore.matches.filter { $0.round == self.id && $0.disabled == false }.sorted(by: \.index) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter { $0.round == self.id && $0.disabled == false }.sorted(by: \.index) } // func displayableMatches() -> [Match] { @@ -331,11 +335,11 @@ defer { print("func previousRound of: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) } #endif - return self.tournamentStore.rounds.first(where: { $0.parent == parent && $0.index == index + 1 }) + return self.tournamentStore?.rounds.first(where: { $0.parent == parent && $0.index == index + 1 }) } func nextRound() -> Round? { - return self.tournamentStore.rounds.first(where: { $0.parent == parent && $0.index == index - 1 }) + return self.tournamentStore?.rounds.first(where: { $0.parent == parent && $0.index == index - 1 }) } func loserRounds(forRoundIndex roundIndex: Int) -> [Round] { @@ -402,7 +406,7 @@ defer { // Logger.error(error) // } } - self.tournamentStore.matches.addOrUpdate(contentOfs: _matches) + self.tournamentStore?.matches.addOrUpdate(contentOfs: _matches) } var cumulativeMatchCount: Int { @@ -462,9 +466,12 @@ defer { } #endif let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index) - let seedsAfterThisRound: [TeamRegistration] = self.tournamentStore.teamRegistrations.filter { - $0.bracketPosition != nil - && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex + var seedsAfterThisRound: [TeamRegistration] = [] + if let tournamentStore = tournamentStore { + seedsAfterThisRound = tournamentStore.teamRegistrations.filter { + $0.bracketPosition != nil + && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex + } } // let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: { @@ -523,9 +530,12 @@ defer { //print(seedInterval.localizedLabel()) return seedInterval } else { - let seedsAfterThisRound : [TeamRegistration] = self.tournamentStore.teamRegistrations.filter { - $0.bracketPosition != nil - && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex + var seedsAfterThisRound : [TeamRegistration] = [] + if let tournamentStore = self.tournamentStore { + seedsAfterThisRound = tournamentStore.teamRegistrations.filter { + $0.bracketPosition != nil + && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex + } } var seedsCount = seedsAfterThisRound.count @@ -594,7 +604,7 @@ defer { } func loserRounds() -> [Round] { - + guard let tournamentStore = self.tournamentStore else { return [] } #if _DEBUG_TIME //DEBUGING TIME let start = Date() defer { @@ -603,7 +613,7 @@ defer { } #endif - return self.tournamentStore.rounds.filter( { $0.parent == id }).sorted(by: \.index).reversed() + return tournamentStore.rounds.filter( { $0.parent == id }).sorted(by: \.index).reversed() } func loserRoundsAndChildren() -> [Round] { @@ -621,7 +631,7 @@ defer { func deleteLoserBracket() { let loserRounds = loserRounds() - self.tournamentStore.rounds.delete(contentOfs: loserRounds) + self.tournamentStore?.rounds.delete(contentOfs: loserRounds) } func buildLoserBracket() { @@ -640,7 +650,7 @@ defer { round.parent = id //parent return round } - self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) + self.tournamentStore?.rounds.addOrUpdate(contentOfs: rounds) let matchCount = RoundRule.numberOfMatches(forTeams: currentRoundMatchCount) let matches = (0.. Int { @@ -691,7 +701,7 @@ defer { playedMatches.forEach { match in match.matchFormat = updatedMatchFormat } - self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches) + self.tournamentStore?.matches.addOrUpdate(contentOfs: playedMatches) } override func deleteDependencies() { @@ -700,14 +710,14 @@ defer { match.deleteDependencies() } - self.tournamentStore.matches.deleteDependencies(matches) + self.tournamentStore?.matches.deleteDependencies(matches) let loserRounds = self.loserRounds() for round in loserRounds { round.deleteDependencies() } - self.tournamentStore.rounds.deleteDependencies(loserRounds) + self.tournamentStore?.rounds.deleteDependencies(loserRounds) } // enum CodingKeys: String, CodingKey { @@ -755,7 +765,7 @@ defer { // } func insertOnServer() { - self.tournamentStore.rounds.writeChangeAndInsertOnServer(instance: self) + self.tournamentStore?.rounds.writeChangeAndInsertOnServer(instance: self) for match in self._matches() { match.insertOnServer() } diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 7c4807b..0acad7f 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -72,21 +72,23 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { try super.init(from: decoder) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return TournamentLibrary.shared.store(tournamentId: self.tournament) } // MARK: - Computed dependencies func unsortedPlayers() -> [PlayerRegistration] { - return self.tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id } } // MARK: - func deleteTeamScores() { - let ts = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id }) - self.tournamentStore.teamScores.delete(contentOfs: ts) + guard let tournamentStore = self.tournamentStore else { return } + let ts = tournamentStore.teamScores.filter({ $0.teamRegistration == id }) + tournamentStore.teamScores.delete(contentOfs: ts) } override func deleteDependencies() { @@ -94,19 +96,19 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { for player in unsortedPlayers { player.deleteDependencies() } - self.tournamentStore.playerRegistrations.deleteDependencies(unsortedPlayers) + self.tournamentStore?.playerRegistrations.deleteDependencies(unsortedPlayers) let teamScores = teamScores() for teamScore in teamScores { teamScore.deleteDependencies() } - self.tournamentStore.teamScores.deleteDependencies(teamScores) + self.tournamentStore?.teamScores.deleteDependencies(teamScores) } func hasArrived(isHere: Bool = false) { let unsortedPlayers = unsortedPlayers() unsortedPlayers.forEach({ $0.hasArrived = !isHere }) - self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers) + self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers) } func isHere() -> Bool { @@ -146,7 +148,7 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { if let index = index(in: tournament.selectedSortedTeams()) { let drawLog = DrawLog(tournament: tournament.id, drawSeed: index, drawMatchIndex: match.index, drawTeamPosition: teamPosition, drawType: .seed) do { - try tournamentStore.drawLogs.addOrUpdate(instance: drawLog) + try tournamentStore?.drawLogs.addOrUpdate(instance: drawLog) } catch { Logger.error(error) } @@ -202,19 +204,23 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { } func teamScores() -> [TeamScore] { - return self.tournamentStore.teamScores.filter({ $0.teamRegistration == id }) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.teamScores.filter({ $0.teamRegistration == id }) } func wins() -> [Match] { - return self.tournamentStore.matches.filter({ $0.winningTeamId == id }) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter({ $0.winningTeamId == id }) } func loses() -> [Match] { - return self.tournamentStore.matches.filter({ $0.losingTeamId == id }) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter({ $0.losingTeamId == id }) } func matches() -> [Match] { - return self.tournamentStore.matches.filter({ $0.losingTeamId == id || $0.winningTeamId == id }) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter({ $0.losingTeamId == id || $0.winningTeamId == id }) } var tournamentCategory: TournamentCategory { @@ -328,10 +334,11 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { } func resetGroupeStagePosition() { + guard let tournamentStore = self.tournamentStore else { return } if let groupStage { - let matches = self.tournamentStore.matches.filter({ $0.groupStage == groupStage }).map { $0.id } - let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) - self.tournamentStore.teamScores.delete(contentOfs: teamScores) + let matches = tournamentStore.matches.filter({ $0.groupStage == groupStage }).map { $0.id } + let teamScores = tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) + tournamentStore.teamScores.delete(contentOfs: teamScores) } //groupStageObject()?._matches().forEach({ $0.updateTeamScores() }) groupStage = nil @@ -339,9 +346,10 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { } func resetBracketPosition() { - let matches = self.tournamentStore.matches.filter({ $0.groupStage == nil }).map { $0.id } - let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) - self.tournamentStore.teamScores.delete(contentOfs: teamScores) + guard let tournamentStore = self.tournamentStore else { return } + let matches = tournamentStore.matches.filter({ $0.groupStage == nil }).map { $0.id } + let teamScores = tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) + tournamentStore.teamScores.delete(contentOfs: teamScores) self.bracketPosition = nil } @@ -411,7 +419,7 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { func updatePlayers(_ players: Set, inTournamentCategory tournamentCategory: TournamentCategory) { let previousPlayers = Set(unsortedPlayers()) let playersToRemove = previousPlayers.subtracting(players) - self.tournamentStore.playerRegistrations.delete(contentOfs: playersToRemove) + self.tournamentStore?.playerRegistrations.delete(contentOfs: playersToRemove) setWeight(from: Array(players), inTournamentCategory: tournamentCategory) players.forEach { player in @@ -454,8 +462,8 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { typealias AreInIncreasingOrder = (PlayerRegistration, PlayerRegistration) -> Bool func players() -> [PlayerRegistration] { - - self.tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id }.sorted { (lhs, rhs) in + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id }.sorted { (lhs, rhs) in let predicates: [AreInIncreasingOrder] = [ { $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 }, { $0.rank ?? Int.max < $1.rank ?? Int.max }, @@ -503,19 +511,19 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { func groupStageObject() -> GroupStage? { guard let groupStage else { return nil } - return self.tournamentStore.groupStages.findById(groupStage) + return self.tournamentStore?.groupStages.findById(groupStage) } func initialRound() -> Round? { guard let bracketPosition else { return nil } let roundIndex = RoundRule.roundIndex(fromMatchIndex: bracketPosition / 2) - return self.tournamentStore.rounds.first(where: { $0.index == roundIndex }) + return self.tournamentStore?.rounds.first(where: { $0.index == roundIndex }) } func initialMatch() -> Match? { guard let bracketPosition else { return nil } guard let initialRoundObject = initialRound() else { return nil } - return self.tournamentStore.matches.first(where: { $0.round == initialRoundObject.id && $0.index == bracketPosition / 2 }) + return self.tournamentStore?.matches.first(where: { $0.round == initialRoundObject.id && $0.index == bracketPosition / 2 }) } func toggleSummonConfirmation() { @@ -587,7 +595,7 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { } func insertOnServer() { - self.tournamentStore.teamRegistrations.writeChangeAndInsertOnServer(instance: self) + self.tournamentStore?.teamRegistrations.writeChangeAndInsertOnServer(instance: self) for playerRegistration in self.unsortedPlayers() { playerRegistration.insertOnServer() } diff --git a/PadelClub/Data/TeamScore.swift b/PadelClub/Data/TeamScore.swift index c3fedde..cc54a41 100644 --- a/PadelClub/Data/TeamScore.swift +++ b/PadelClub/Data/TeamScore.swift @@ -50,7 +50,7 @@ final class TeamScore: BaseTeamScore, SideStorable { try super.init(from: decoder) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { guard let storeId else { fatalError("missing store id for \(String(describing: type(of: self)))") } @@ -65,14 +65,14 @@ final class TeamScore: BaseTeamScore, SideStorable { // MARK: - Computed dependencies func matchObject() -> Match? { - return self.tournamentStore.matches.findById(self.match) + return self.tournamentStore?.matches.findById(self.match) } var team: TeamRegistration? { guard let teamRegistration else { return nil } - return self.tournamentStore.teamRegistrations.findById(teamRegistration) + return self.tournamentStore?.teamRegistrations.findById(teamRegistration) } // MARK: - @@ -107,7 +107,7 @@ final class TeamScore: BaseTeamScore, SideStorable { // } func insertOnServer() { - self.tournamentStore.teamScores.writeChangeAndInsertOnServer(instance: self) + self.tournamentStore?.teamScores.writeChangeAndInsertOnServer(instance: self) } } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index fdb5b4f..32b397c 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -77,31 +77,31 @@ final class Tournament: BaseTournament { try super.init(from: decoder) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return TournamentLibrary.shared.store(tournamentId: self.id) } override func deleteDependencies() { - let store = self.tournamentStore - let drawLogs = Array(self.tournamentStore.drawLogs) + guard let store = self.tournamentStore else { return } + let drawLogs = Array(store.drawLogs) for drawLog in drawLogs { drawLog.deleteDependencies() } store.drawLogs.deleteDependencies(drawLogs) - let teams = self.tournamentStore.teamRegistrations + let teams = store.teamRegistrations for team in Array(teams) { team.deleteDependencies() } store.teamRegistrations.deleteDependencies(teams) - let groups = Array(self.tournamentStore.groupStages) + let groups = Array(store.groupStages) for group in groups { group.deleteDependencies() } store.groupStages.deleteDependencies(groups) - let rounds = Array(self.tournamentStore.rounds) + let rounds = Array(store.rounds) for round in rounds { round.deleteDependencies() } @@ -120,20 +120,24 @@ final class Tournament: BaseTournament { // MARK: - Computed Dependencies func unsortedTeams() -> [TeamRegistration] { - return Array(self.tournamentStore.teamRegistrations) + guard let tournamentStore = self.tournamentStore else { return [] } + return Array(tournamentStore.teamRegistrations) } func groupStages(atStep step: Int = 0) -> [GroupStage] { - let groupStages: [GroupStage] = self.tournamentStore.groupStages.filter { $0.tournament == self.id && $0.step == step } + guard let tournamentStore = self.tournamentStore else { return [] } + let groupStages: [GroupStage] = tournamentStore.groupStages.filter { $0.tournament == self.id && $0.step == step } return groupStages.sorted(by: \.index) } func allGroupStages() -> [GroupStage] { - return self.tournamentStore.groupStages.sorted(by: \GroupStage.computedOrder) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.groupStages.sorted(by: \GroupStage.computedOrder) } func allRounds() -> [Round] { - return Array(self.tournamentStore.rounds) + guard let tournamentStore = self.tournamentStore else { return [] } + return Array(tournamentStore.rounds) } // MARK: - @@ -354,7 +358,7 @@ defer { } func getRound(atRoundIndex roundIndex: Int) -> Round? { - return self.tournamentStore.rounds.first(where: { $0.index == roundIndex }) + return self.tournamentStore?.rounds.first(where: { $0.index == roundIndex }) // return Store.main.filter(isIncluded: { $0.tournament == id && $0.index == roundIndex }).first } @@ -546,15 +550,18 @@ defer { } func allMatches() -> [Match] { - return self.tournamentStore.matches.filter { $0.disabled == false } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matches.filter { $0.disabled == false } } func _allMatchesIncludingDisabled() -> [Match] { - return Array(self.tournamentStore.matches) + guard let tournamentStore = self.tournamentStore else { return [] } + return Array(tournamentStore.matches) } func rounds() -> [Round] { - let rounds: [Round] = self.tournamentStore.rounds.filter { $0.isUpperBracket() } + guard let tournamentStore = self.tournamentStore else { return [] } + let rounds: [Round] = tournamentStore.rounds.filter { $0.isUpperBracket() } return rounds.sorted(by: \.index).reversed() } @@ -677,12 +684,14 @@ defer { } func unsortedTeamsWithoutWO() -> [TeamRegistration] { - return self.tournamentStore.teamRegistrations.filter { $0.walkOut == false } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.teamRegistrations.filter { $0.walkOut == false } // return Store.main.filter { $0.tournament == self.id && $0.walkOut == false } } func walkoutTeams() -> [TeamRegistration] { - return self.tournamentStore.teamRegistrations.filter { $0.walkOut == true } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.teamRegistrations.filter { $0.walkOut == true } // return Store.main.filter { $0.tournament == self.id && $0.walkOut == true } } @@ -702,7 +711,8 @@ defer { } func unsortedPlayers() -> [PlayerRegistration] { - return Array(self.tournamentStore.playerRegistrations) + guard let tournamentStore = self.tournamentStore else { return [] } + return Array(tournamentStore.playerRegistrations) } func selectedPlayers() -> [PlayerRegistration] { @@ -718,7 +728,8 @@ defer { } func players() -> [PlayerRegistration] { - return self.tournamentStore.playerRegistrations.sorted(by: \.computedRank) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.playerRegistrations.sorted(by: \.computedRank) } func unrankValue(for malePlayer: Bool) -> Int? { @@ -838,8 +849,10 @@ defer { } } - self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teamsToImport) - self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: teams.flatMap { $0.players }) + if let tournamentStore = self.tournamentStore { + tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teamsToImport) + tournamentStore.playerRegistrations.addOrUpdate(contentOfs: teams.flatMap { $0.players }) + } if state() == .build && groupStageCount > 0 && groupStageTeams().isEmpty { setGroupStage(randomize: groupStageSortMode == .random) @@ -1020,7 +1033,7 @@ defer { _removeStrings(from: &teams, stringsToRemove: disabledIds) teams[interval.last] = disabledIds let teamNames : [String] = disabledIds.compactMap { - let t : TeamRegistration? = tournamentStore.teamRegistrations.findById($0) + let t : TeamRegistration? = self.tournamentStore?.teamRegistrations.findById($0) return t }.map { $0.canonicalName } print("winners.isEmpty", "\(interval.last) : ", teamNames) @@ -1033,7 +1046,7 @@ defer { _removeStrings(from: &teams, stringsToRemove: winners) teams[interval.first + winners.count - 1] = winners let teamNames : [String] = winners.compactMap { - let t: TeamRegistration? = tournamentStore.teamRegistrations.findById($0) + let t: TeamRegistration? = tournamentStore?.teamRegistrations.findById($0) return t }.map { $0.canonicalName } print("winners", "\(interval.last + winners.count - 1) : ", teamNames) @@ -1044,7 +1057,7 @@ defer { _removeStrings(from: &teams, stringsToRemove: losers) teams[interval.first + winners.count] = losers let loserTeamNames : [String] = losers.compactMap { - let t: TeamRegistration? = tournamentStore.teamRegistrations.findById($0) + let t: TeamRegistration? = tournamentStore?.teamRegistrations.findById($0) return t }.map { $0.canonicalName } print("losers", "\(interval.first + winners.count) : ", loserTeamNames) @@ -1088,11 +1101,13 @@ defer { } func setRankings(finalRanks: [Int: [String]]) async -> [Int: [TeamRegistration]] { + guard let tournamentStore = self.tournamentStore else { return [:] } + var rankings: [Int: [TeamRegistration]] = [:] finalRanks.keys.sorted().forEach { rank in if let rankedTeamIds = finalRanks[rank] { - let teams: [TeamRegistration] = rankedTeamIds.compactMap { self.tournamentStore.teamRegistrations.findById($0) } + let teams: [TeamRegistration] = rankedTeamIds.compactMap { tournamentStore.teamRegistrations.findById($0) } rankings[rank] = teams } } @@ -1106,7 +1121,7 @@ defer { } } - self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) + tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) return rankings } @@ -1121,7 +1136,7 @@ defer { teams.forEach { team in team.lockedWeight = team.weight } - self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } func unlockRegistration() { @@ -1130,7 +1145,7 @@ defer { teams.forEach { team in team.lockedWeight = nil } - self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } func updateWeights() { @@ -1139,9 +1154,9 @@ defer { let players = team.unsortedPlayers() players.forEach { $0.setComputedRank(in: self) } team.setWeight(from: players, inTournamentCategory: tournamentCategory) - self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) } - self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } func updateRank(to newDate: Date?) async throws { @@ -1472,27 +1487,30 @@ defer { } do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: wcs) + try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: wcs) } catch { Logger.error(error) } } func addEmptyTeamRegistration(_ count: Int) { + + guard let tournamentStore = self.tournamentStore else { return } + let teams = (0.. Bool { @@ -1792,7 +1810,8 @@ defer { private let _currentSelectionSorting : [MySortDescriptor] = [.keyPath(\.weight), .keyPath(\.id)] private func _matchSchedulers() -> [MatchScheduler] { - return self.tournamentStore.matchSchedulers.filter { $0.tournament == self.id } + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.matchSchedulers.filter { $0.tournament == self.id } // DataStore.shared.matchSchedulers.filter(isIncluded: { $0.tournament == self.id }) } @@ -1827,7 +1846,7 @@ defer { } func tournamentWinner() -> TeamRegistration? { - let finals: Round? = self.tournamentStore.rounds.first(where: { $0.index == 0 && $0.isUpperBracket() }) + let finals: Round? = self.tournamentStore?.rounds.first(where: { $0.index == 0 && $0.isUpperBracket() }) return finals?.playedMatches().first?.winner() } @@ -1890,7 +1909,7 @@ defer { } func groupStageLoserBracket() -> Round? { - tournamentStore.rounds.first(where: { $0.groupStageLoserBracket }) + self.tournamentStore?.rounds.first(where: { $0.groupStageLoserBracket }) } func groupStageLoserBracketsInitialPlace() -> Int { @@ -1901,14 +1920,14 @@ defer { let lastStep = lastStep() + 1 for i in 0.. Int { - self.tournamentStore.groupStages.sorted(by: \.step).last?.step ?? 0 + self.tournamentStore?.groupStages.sorted(by: \.step).last?.step ?? 0 } func generateSmartLoserGroupStageBracket() { @@ -1919,7 +1938,7 @@ defer { let match = Match(round: groupStageLoserBracket.id, index: placeCount, format: groupStageLoserBracket.matchFormat) match.setMatchName("\(placeCount)\(placeCount.ordinalFormattedSuffix(feminine: true)) place") do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } @@ -1958,7 +1977,8 @@ defer { } func drawLogs() -> [DrawLog] { - self.tournamentStore.drawLogs.sorted(by: \.drawDate) + guard let tournamentStore = self.tournamentStore else { return [] } + return tournamentStore.drawLogs.sorted(by: \.drawDate) } func seedSpotsLeft() -> Bool { @@ -1989,7 +2009,7 @@ defer { } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: seeds) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: seeds) } catch { Logger.error(error) } @@ -2004,12 +2024,12 @@ defer { } do { - try tournamentStore.teamScores.delete(contentOfs: ts) + try tournamentStore?.teamScores.delete(contentOfs: ts) } catch { Logger.error(error) } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) } catch { Logger.error(error) } @@ -2028,7 +2048,7 @@ defer { let matches = (0.. TournamentStore { + func store(tournamentId: String) -> TournamentStore? { + guard let tournament = DataStore.shared.tournaments.first(where: { $0.id == tournamentId }) else { return nil } + if let store = self._stores[tournamentId] { return store } diff --git a/PadelClub/Utils/Patcher.swift b/PadelClub/Utils/Patcher.swift index 885348f..4b76f79 100644 --- a/PadelClub/Utils/Patcher.swift +++ b/PadelClub/Utils/Patcher.swift @@ -56,7 +56,7 @@ class Patcher { for tournament in DataStore.shared.tournaments { let id = tournament.id - let store = TournamentLibrary.shared.store(tournamentId: tournament.id) + guard let store = TournamentLibrary.shared.store(tournamentId: tournament.id) else { continue } for round in store.rounds { round.storeId = id diff --git a/PadelClub/Views/Calling/CallSettingsView.swift b/PadelClub/Views/Calling/CallSettingsView.swift index 7834e0e..334cd41 100644 --- a/PadelClub/Views/Calling/CallSettingsView.swift +++ b/PadelClub/Views/Calling/CallSettingsView.swift @@ -15,7 +15,7 @@ struct CallSettingsView: View { @State private var showSendToAllView: Bool = false @State private var addLink: Bool = false - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -64,7 +64,7 @@ struct CallSettingsView: View { team.callDate = nil } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } catch { Logger.error(error) } @@ -82,7 +82,7 @@ struct CallSettingsView: View { } } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index 1be6236..47ba573 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -104,7 +104,7 @@ struct CallView: View { } } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -130,7 +130,7 @@ struct CallView: View { } } do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: calledTeams) + try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: calledTeams) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Calling/SendToAllView.swift b/PadelClub/Views/Calling/SendToAllView.swift index d261f35..3c06250 100644 --- a/PadelClub/Views/Calling/SendToAllView.swift +++ b/PadelClub/Views/Calling/SendToAllView.swift @@ -32,7 +32,7 @@ struct SendToAllView: View { @State var summonParamByMessage: Bool = false @State var summonParamReSummon: Bool = false - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -233,12 +233,14 @@ struct SendToAllView: View { } func _roundTeams() -> [TeamRegistration] { - let rounds: [Round] = contactRecipients.compactMap { self.tournamentStore.rounds.findById($0) } + guard let tournamentStore = self.tournamentStore else { return [] } + let rounds: [Round] = contactRecipients.compactMap { tournamentStore.rounds.findById($0) } return rounds.flatMap { $0.teams() } } func _groupStagesTeams() -> [TeamRegistration] { - let groupStages : [GroupStage] = contactRecipients.compactMap { self.tournamentStore.groupStages.findById($0) } + guard let tournamentStore = self.tournamentStore else { return [] } + let groupStages : [GroupStage] = contactRecipients.compactMap { tournamentStore.groupStages.findById($0) } return groupStages.flatMap { $0.teams() } } diff --git a/PadelClub/Views/Calling/TeamsCallingView.swift b/PadelClub/Views/Calling/TeamsCallingView.swift index 8a5d1e9..677fb2b 100644 --- a/PadelClub/Views/Calling/TeamsCallingView.swift +++ b/PadelClub/Views/Calling/TeamsCallingView.swift @@ -117,7 +117,7 @@ struct CallMenuOptionsView: View { } set: { _ in team.toggleSummonConfirmation() do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -152,7 +152,7 @@ struct CallMenuOptionsView: View { RowButtonView("Effacer la date de convocation", role: .destructive) { team.callDate = nil do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -165,7 +165,7 @@ struct CallMenuOptionsView: View { RowButtonView("Indiquer comme convoquée", role: .destructive) { team.callDate = team.initialMatch()?.startDate ?? tournament.startDate do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Cashier/CashierSettingsView.swift b/PadelClub/Views/Cashier/CashierSettingsView.swift index fe93c84..2090e0d 100644 --- a/PadelClub/Views/Cashier/CashierSettingsView.swift +++ b/PadelClub/Views/Cashier/CashierSettingsView.swift @@ -137,7 +137,7 @@ struct CashierSettingsView: View { } private func _save(players: [PlayerRegistration]) { - tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + tournament.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) } private func _save() { diff --git a/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift b/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift index 521b328..50e5155 100644 --- a/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift +++ b/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift @@ -29,7 +29,7 @@ struct GroupStageSettingsView: View { _courtIndex = .init(wrappedValue: groupStage._matches().first?.courtIndex ?? 0) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -70,7 +70,7 @@ struct GroupStageSettingsView: View { } do { - try tournamentStore.matches.addOrUpdate(contentOfs: groupStage._matches()) + try tournamentStore?.matches.addOrUpdate(contentOfs: groupStage._matches()) } catch { Logger.error(error) } @@ -93,7 +93,7 @@ struct GroupStageSettingsView: View { groupStage._matches().forEach({ $0.updateTeamScores() }) } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } catch { Logger.error(error) } @@ -116,7 +116,7 @@ struct GroupStageSettingsView: View { } do { - try tournamentStore.matches.addOrUpdate(contentOfs: groupStage._matches()) + try tournamentStore?.matches.addOrUpdate(contentOfs: groupStage._matches()) } catch { Logger.error(error) } @@ -132,7 +132,7 @@ struct GroupStageSettingsView: View { groupStage._matches().forEach({ $0.updateTeamScores() }) } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } catch { Logger.error(error) } @@ -243,7 +243,7 @@ struct GroupStageSettingsView: View { private func _save() { do { - try tournamentStore.groupStages.addOrUpdate(instance: groupStage) + try tournamentStore?.groupStages.addOrUpdate(instance: groupStage) } catch { Logger.error(error) } diff --git a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift index 18a3786..74a181f 100644 --- a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift +++ b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift @@ -23,7 +23,7 @@ struct GroupStageTeamView: View { let groupStage: GroupStage var team: TeamRegistration - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -189,7 +189,7 @@ struct GroupStageTeamView: View { private func _save() { do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } diff --git a/PadelClub/Views/GroupStage/GroupStageView.swift b/PadelClub/Views/GroupStage/GroupStageView.swift index 9fc489f..1473da7 100644 --- a/PadelClub/Views/GroupStage/GroupStageView.swift +++ b/PadelClub/Views/GroupStage/GroupStageView.swift @@ -22,7 +22,7 @@ struct GroupStageView: View { _sortingMode = .init(wrappedValue: groupStage.hasEnded() ? .score : .weight) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -118,7 +118,7 @@ struct GroupStageView: View { let scores: [GroupStage.TeamGroupStageScore]? let teams: [TeamRegistration] - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -207,7 +207,7 @@ struct GroupStageView: View { Button { player.hasArrived.toggle() do { - try self.tournamentStore.playerRegistrations.addOrUpdate(instance: player) + try self.tournamentStore?.playerRegistrations.addOrUpdate(instance: player) } catch { Logger.error(error) } @@ -236,7 +236,7 @@ struct GroupStageView: View { team.groupStagePosition = index groupStage._matches().forEach({ $0.updateTeamScores() }) do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -266,7 +266,7 @@ struct GroupStageView: View { private func _save() { do { - try tournamentStore.groupStages.addOrUpdate(instance: groupStage) + try tournamentStore?.groupStages.addOrUpdate(instance: groupStage) } catch { Logger.error(error) } diff --git a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift index 6961bae..c037ca9 100644 --- a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift @@ -15,7 +15,7 @@ struct GroupStagesSettingsView: View { @State private var generationDoneMessage: String? let step: Int - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -26,7 +26,7 @@ struct GroupStagesSettingsView: View { let issues = tournament.groupStageTeamPlacementIssue() DisclosureGroup { ForEach(issues.shouldBeInIt, id: \.self) { id in - if let team: TeamRegistration = self.tournamentStore.teamRegistrations.findById(id) { + if let team: TeamRegistration = self.tournamentStore?.teamRegistrations.findById(id) { TeamRowView(team: team) } } @@ -39,12 +39,12 @@ struct GroupStagesSettingsView: View { } DisclosureGroup { ForEach(issues.shouldNotBeInIt, id: \.self) { id in - if let team = self.tournamentStore.teamRegistrations.findById(id) { + if let team = self.tournamentStore?.teamRegistrations.findById(id) { Menu { Button("Retirer de sa poule") { team.resetGroupeStagePosition() do { - try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -80,7 +80,7 @@ struct GroupStagesSettingsView: View { let round = Round(tournament: tournament.id, index: 0, matchFormat: tournament.loserRoundFormat, groupStageLoserBracket: true) do { - try tournamentStore.rounds.addOrUpdate(instance: round) + try tournamentStore?.rounds.addOrUpdate(instance: round) } catch { Logger.error(error) } @@ -89,7 +89,7 @@ struct GroupStagesSettingsView: View { } else if let groupStageLoserBracket = tournament.groupStageLoserBracket() { RowButtonView("Supprimer les matchs de classements", role: .destructive) { do { - try tournamentStore.rounds.delete(instance: groupStageLoserBracket) + try tournamentStore?.rounds.delete(instance: groupStageLoserBracket) } catch { Logger.error(error) } @@ -110,7 +110,7 @@ struct GroupStagesSettingsView: View { RowButtonView("Supprimer cette phase de poule", role: .destructive) { let groupStages = tournament.groupStages(atStep: tournament.lastStep()) do { - try tournament.tournamentStore.groupStages.delete(contentOfs: groupStages) + try self.tournamentStore?.groupStages.delete(contentOfs: groupStages) } catch { Logger.error(error) } @@ -138,7 +138,7 @@ struct GroupStagesSettingsView: View { } do { - try tournamentStore.matches.addOrUpdate(contentOfs: matches) + try tournamentStore?.matches.addOrUpdate(contentOfs: matches) } catch { Logger.error(error) } @@ -211,7 +211,7 @@ struct GroupStagesSettingsView: View { } } do { - try self.tournament.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages) + try self.tournament.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages) } catch { Logger.error(error) } @@ -225,7 +225,7 @@ struct GroupStagesSettingsView: View { groupStage.name = nil } do { - try self.tournament.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages) + try self.tournament.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages) } catch { Logger.error(error) } @@ -242,7 +242,7 @@ struct GroupStagesSettingsView: View { groupStage._matches().forEach({ $0.updateTeamScores() }) } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } catch { Logger.error(error) } diff --git a/PadelClub/Views/GroupStage/GroupStagesView.swift b/PadelClub/Views/GroupStage/GroupStagesView.swift index 13cd9e2..05446a2 100644 --- a/PadelClub/Views/GroupStage/GroupStagesView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesView.swift @@ -124,7 +124,7 @@ struct GroupStagesView: View { results.forEach { drawResult in missingQualifiedFromGroupStages[drawResult.drawIndex].qualified = true do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: missingQualifiedFromGroupStages[drawResult.drawIndex]) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: missingQualifiedFromGroupStages[drawResult.drawIndex]) } catch { Logger.error(error) } diff --git a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift index 5c42a24..b5e2096 100644 --- a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift +++ b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift @@ -21,7 +21,7 @@ struct LoserBracketFromGroupStageView: View { _isEditingLoserBracketGroupStage = .init(wrappedValue: loserBracket._matches().isEmpty) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -110,13 +110,13 @@ struct LoserBracketFromGroupStageView: View { let match = Match(round: loserBracket.id, index: placeCount, format: loserBracket.matchFormat) match.setMatchName("\(placeCount)\(placeCount.ordinalFormattedSuffix()) place") - tournamentStore.matches.addOrUpdate(instance: match) + tournamentStore?.matches.addOrUpdate(instance: match) } private func _deleteAllMatches() { let displayableMatches = loserBracket.playedMatches().sorted(by: \.index) - tournamentStore.matches.delete(contentOfs: displayableMatches) + tournamentStore?.matches.delete(contentOfs: displayableMatches) } @@ -164,7 +164,7 @@ struct GroupStageLoserBracketMatchFooterView: View { Spacer() FooterButtonView("Effacer", role: .destructive) { do { - try match.tournamentStore.matches.delete(instance: match) + try match.tournamentStore?.matches.delete(instance: match) } catch { Logger.error(error) } @@ -199,7 +199,7 @@ struct GroupStageLoserBracketMatchFooterView: View { match.setMatchName("\(newIndexValidated)\(newIndexValidated.ordinalFormattedSuffix()) place") - match.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) - match.tournamentStore.matches.addOrUpdate(instance: match) + match.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores) + match.tournamentStore?.matches.addOrUpdate(instance: match) } } diff --git a/PadelClub/Views/Match/Components/MatchDateView.swift b/PadelClub/Views/Match/Components/MatchDateView.swift index 51ccc19..0c7ec65 100644 --- a/PadelClub/Views/Match/Components/MatchDateView.swift +++ b/PadelClub/Views/Match/Components/MatchDateView.swift @@ -191,7 +191,7 @@ struct MatchDateView: View { } do { - try self.match.tournamentStore.matches.addOrUpdate(instance: match) + try self.match.tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 45f0444..a81ea94 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -38,7 +38,7 @@ struct MatchDetailView: View { @State private var presentRanking: Bool = false @State private var confirmScoreEdition: Bool = false - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return match.tournamentStore } @@ -121,8 +121,10 @@ struct MatchDetailView: View { DisclosureGroup { ForEach(unpaid) { player in LabeledContent { - PlayerPayView(player: player) - .environmentObject(tournamentStore) + if let store = self.tournamentStore { + PlayerPayView(player: player) + .environmentObject(store) + } } label: { Text(player.playerLabel()) } @@ -140,8 +142,12 @@ struct MatchDetailView: View { menuView } .sheet(isPresented: $showDetails) { - MatchTeamDetailView(match: match).tint(.master) - .environmentObject(tournamentStore) + if let store = self.tournamentStore { + MatchTeamDetailView(match: match).tint(.master) + .environmentObject(store) + } else { + Text("no store") + } } .sheet(isPresented: self.$showSubscriptionView, content: { NavigationStack { @@ -364,7 +370,7 @@ struct MatchDetailView: View { ForEach(match.teamScores) { teamScore in Button(role: .destructive) { do { - try tournamentStore.teamScores.delete(instance: teamScore) + try tournamentStore?.teamScores.delete(instance: teamScore) } catch { Logger.error(error) } @@ -566,7 +572,7 @@ struct MatchDetailView: View { if match.isReady() == false && match.teams().count == 2 { let teamsScores = match.getOrCreateTeamScores() do { - try tournamentStore.teamScores.addOrUpdate(contentOfs: teamsScores) + try tournamentStore?.teamScores.addOrUpdate(contentOfs: teamsScores) } catch { Logger.error(error) } @@ -615,7 +621,7 @@ struct MatchDetailView: View { private func _saveMatch() { do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Match/MatchRowView.swift b/PadelClub/Views/Match/MatchRowView.swift index 643078e..e74bd44 100644 --- a/PadelClub/Views/Match/MatchRowView.swift +++ b/PadelClub/Views/Match/MatchRowView.swift @@ -69,7 +69,7 @@ struct MatchRowView: View { Button { player.hasArrived.toggle() do { - try player.tournamentStore.playerRegistrations.addOrUpdate(instance: player) + try player.tournamentStore?.playerRegistrations.addOrUpdate(instance: player) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Match/MatchSetupView.swift b/PadelClub/Views/Match/MatchSetupView.swift index dc932bd..29c47a8 100644 --- a/PadelClub/Views/Match/MatchSetupView.swift +++ b/PadelClub/Views/Match/MatchSetupView.swift @@ -16,7 +16,7 @@ struct MatchSetupView: View { @State var match: Match @State private var seedGroup: SeedInterval? - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return match.tournamentStore } @@ -68,19 +68,19 @@ struct MatchSetupView: View { if walkOutSpot || team.bracketPosition != nil || matchTypeContext == .loserBracket { match.setLuckyLoser(team: team, teamPosition: teamPosition) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } } else if team.bracketPosition == nil { team.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -97,7 +97,7 @@ struct MatchSetupView: View { if let randomTeam = luckyLosers.randomElement() { match.setLuckyLoser(team: randomTeam, teamPosition: teamPosition) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } @@ -112,12 +112,12 @@ struct MatchSetupView: View { if let randomTeam = availableQualifiedTeams.randomElement() { randomTeam.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: randomTeam) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: randomTeam) } catch { Logger.error(error) } @@ -132,12 +132,12 @@ struct MatchSetupView: View { if let randomTeam = tournament.randomSeed(fromSeedGroup: seedGroup) { randomTeam.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: randomTeam) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: randomTeam) } catch { Logger.error(error) } @@ -158,7 +158,7 @@ struct MatchSetupView: View { Button { match.unlockSeedPosition(atTeamPosition: teamPosition) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } @@ -170,7 +170,7 @@ struct MatchSetupView: View { ConfirmButtonView(shouldConfirm: shouldConfirm, message: MatchSetupView.confirmationMessage) { _ = match.lockAndGetSeedPosition(atTeamPosition: teamPosition) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } @@ -193,7 +193,7 @@ struct MatchSetupView: View { if match.isSeededBy(team: team, inTeamPosition: teamPosition) { if let score = match.teamScore(ofTeam: team) { do { - try tournamentStore.teamScores.delete(instance: score) + try tournamentStore?.teamScores.delete(instance: score) } catch { Logger.error(error) } @@ -201,7 +201,7 @@ struct MatchSetupView: View { team.bracketPosition = nil do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -209,7 +209,7 @@ struct MatchSetupView: View { if previousMatch.disabled { previousMatch.enableMatch() do { - try tournamentStore.matches.addOrUpdate(instance: previousMatch) + try tournamentStore?.matches.addOrUpdate(instance: previousMatch) } catch { Logger.error(error) } @@ -217,14 +217,14 @@ struct MatchSetupView: View { } do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } } else if match.isLoserBracket { if let score = match.teamScore(ofTeam: team) { do { - try tournamentStore.teamScores.delete(instance: score) + try tournamentStore?.teamScores.delete(instance: score) } catch { Logger.error(error) } @@ -232,7 +232,7 @@ struct MatchSetupView: View { } else { match.teamWillBeWalkOut(team) do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 487f80c..c660ffe 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -73,17 +73,18 @@ struct ToolboxView: View { for tournament in dataStore.tournaments { - let store = tournament.tournamentStore - let playerRegistrations = store.playerRegistrations - playerRegistrations.forEach { player in - player.firstName = player.firstName.trimmed.capitalized - player.lastName = player.lastName.trimmed.uppercased() - } - - do { - try store.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations) - } catch { - Logger.error(error) + if let store = tournament.tournamentStore { + let playerRegistrations = store.playerRegistrations + playerRegistrations.forEach { player in + player.firstName = player.firstName.trimmed.capitalized + player.lastName = player.lastName.trimmed.uppercased() + } + + do { + try store.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations) + } catch { + Logger.error(error) + } } } @@ -95,13 +96,14 @@ struct ToolboxView: View { for tournament in DataStore.shared.tournaments { - let store: TournamentStore = tournament.tournamentStore - - let teamRegistrations = store.teamRegistrations.filter({ $0.tournamentObject() == nil }) - do { - try store.teamRegistrations.delete(contentOfs: teamRegistrations) - } catch { - Logger.error(error) + if let store: TournamentStore = tournament.tournamentStore { + + let teamRegistrations = store.teamRegistrations.filter({ $0.tournamentObject() == nil }) + do { + try store.teamRegistrations.delete(contentOfs: teamRegistrations) + } catch { + Logger.error(error) + } } } } @@ -112,13 +114,14 @@ struct ToolboxView: View { RowButtonView("Delete players") { for tournament in DataStore.shared.tournaments { - let store: TournamentStore = tournament.tournamentStore - - let playersRegistrations = store.playerRegistrations.filter({ $0.team() == nil }) - do { - try store.playerRegistrations.delete(contentOfs: playersRegistrations) - } catch { - Logger.error(error) + if let store: TournamentStore = tournament.tournamentStore { + + let playersRegistrations = store.playerRegistrations.filter({ $0.team() == nil }) + do { + try store.playerRegistrations.delete(contentOfs: playersRegistrations) + } catch { + Logger.error(error) + } } } diff --git a/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift b/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift index ec29423..a0e6866 100644 --- a/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift +++ b/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift @@ -17,7 +17,7 @@ struct GroupStageScheduleEditorView: View { @State private var startDate: Date @State private var currentDate: Date? - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -42,7 +42,7 @@ struct GroupStageScheduleEditorView: View { private func _save() { do { - try tournamentStore.groupStages.addOrUpdate(instance: groupStage) + try tournamentStore?.groupStages.addOrUpdate(instance: groupStage) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift b/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift index e14ad4b..0d5eafb 100644 --- a/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift +++ b/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift @@ -19,7 +19,7 @@ struct LoserRoundScheduleEditorView: View { @State private var matchFormat: MatchFormat @State private var currentDate: Date? - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -92,7 +92,7 @@ struct LoserRoundScheduleEditorView: View { private func _save() { do { - try self.tournamentStore.rounds.addOrUpdate(contentOfs: upperRound.loserRounds()) + try self.tournamentStore?.rounds.addOrUpdate(contentOfs: upperRound.loserRounds()) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift b/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift index 6ee09ec..567c182 100644 --- a/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift +++ b/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift @@ -20,7 +20,7 @@ struct LoserRoundStepScheduleEditorView: View { @State private var startDate: Date //@State private var matchFormat: MatchFormat - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -79,7 +79,7 @@ struct LoserRoundStepScheduleEditorView: View { private func _save() { do { - try tournamentStore.rounds.addOrUpdate(contentOfs: upperRound.loserRounds(forRoundIndex: round.index)) + try tournamentStore?.rounds.addOrUpdate(contentOfs: upperRound.loserRounds(forRoundIndex: round.index)) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Planning/MatchScheduleEditorView.swift b/PadelClub/Views/Planning/MatchScheduleEditorView.swift index 592a4ac..24f534a 100644 --- a/PadelClub/Views/Planning/MatchScheduleEditorView.swift +++ b/PadelClub/Views/Planning/MatchScheduleEditorView.swift @@ -14,7 +14,7 @@ struct MatchScheduleEditorView: View { @State private var startDate: Date @State private var currentDate: Date? - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -54,7 +54,7 @@ struct MatchScheduleEditorView: View { private func _save() { do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index 8a0d080..22312e4 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -24,7 +24,7 @@ struct PlanningSettingsView: View { @State private var deletingDateMatchesDone: Bool = false @State private var deletingDone: Bool = false - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -148,7 +148,7 @@ struct PlanningSettingsView: View { .headerProminence(.increased) .onAppear { do { - try self.tournamentStore.matchSchedulers.addOrUpdate(instance: matchScheduler) + try self.tournamentStore?.matchSchedulers.addOrUpdate(instance: matchScheduler) } catch { Logger.error(error) } @@ -228,7 +228,7 @@ struct PlanningSettingsView: View { $0.startDate = nil $0.confirmed = false }) - try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) + try self.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches) deletingDateMatchesDone = true } catch { Logger.error(error) @@ -245,7 +245,7 @@ struct PlanningSettingsView: View { do { deletingDone = false allGroupStages.forEach({ $0.startDate = nil }) - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages) + try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: allGroupStages) deletingDone = true } catch { @@ -261,7 +261,7 @@ struct PlanningSettingsView: View { do { deletingDone = false allRounds.forEach({ $0.startDate = nil }) - try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) + try self.tournamentStore?.rounds.addOrUpdate(contentOfs: allRounds) deletingDone = true } catch { Logger.error(error) @@ -281,13 +281,13 @@ struct PlanningSettingsView: View { $0.startDate = nil $0.confirmed = false }) - try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) + try self.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches) allGroupStages.forEach({ $0.startDate = nil }) - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages) + try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: allGroupStages) allRounds.forEach({ $0.startDate = nil }) - try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) + try self.tournamentStore?.rounds.addOrUpdate(contentOfs: allRounds) deletingDone = true } catch { Logger.error(error) @@ -308,13 +308,13 @@ struct PlanningSettingsView: View { $0.endDate = nil $0.confirmed = false }) - try self.tournamentStore.matches.addOrUpdate(contentOfs: tournament.allMatches()) + try self.tournamentStore?.matches.addOrUpdate(contentOfs: tournament.allMatches()) allGroupStages.forEach({ $0.startDate = nil }) - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages) + try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: allGroupStages) allRounds.forEach({ $0.startDate = nil }) - try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) + try self.tournamentStore?.rounds.addOrUpdate(contentOfs: allRounds) deletingDone = true } catch { Logger.error(error) @@ -485,7 +485,7 @@ struct PlanningSettingsView: View { private func _save() { do { - try self.tournamentStore.matchSchedulers.addOrUpdate(instance: matchScheduler) + try self.tournamentStore?.matchSchedulers.addOrUpdate(instance: matchScheduler) try dataStore.tournaments.addOrUpdate(instance: tournament) } catch { Logger.error(error) diff --git a/PadelClub/Views/Planning/PlanningView.swift b/PadelClub/Views/Planning/PlanningView.swift index 0f3193c..7e28e6a 100644 --- a/PadelClub/Views/Planning/PlanningView.swift +++ b/PadelClub/Views/Planning/PlanningView.swift @@ -97,7 +97,7 @@ struct PlanningView: View { ToolbarItem(placement: .topBarTrailing) { Button("Sauver") { do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) + try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches) } catch { Logger.error(error) } @@ -503,7 +503,7 @@ struct PlanningView: View { // } // } // -// try? self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: matches) +// try? self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: matches) // } // // diff --git a/PadelClub/Views/Planning/RoundScheduleEditorView.swift b/PadelClub/Views/Planning/RoundScheduleEditorView.swift index 679b306..2c42ac7 100644 --- a/PadelClub/Views/Planning/RoundScheduleEditorView.swift +++ b/PadelClub/Views/Planning/RoundScheduleEditorView.swift @@ -16,7 +16,7 @@ struct RoundScheduleEditorView: View { @State private var startDate: Date @State private var currentDate: Date? - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -71,7 +71,7 @@ struct RoundScheduleEditorView: View { private func _save() { do { - try tournamentStore.rounds.addOrUpdate(instance: round) + try tournamentStore?.rounds.addOrUpdate(instance: round) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Planning/SchedulerView.swift b/PadelClub/Views/Planning/SchedulerView.swift index 333357e..a4e83b6 100644 --- a/PadelClub/Views/Planning/SchedulerView.swift +++ b/PadelClub/Views/Planning/SchedulerView.swift @@ -26,7 +26,7 @@ struct SchedulerView: View { var tournament: Tournament var destination: ScheduleDestination - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -49,7 +49,7 @@ struct SchedulerView: View { } do { try dataStore.tournaments.addOrUpdate(instance: tournament) - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages) + try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages) } catch { Logger.error(error) } @@ -90,7 +90,7 @@ struct SchedulerView: View { do { try dataStore.tournaments.addOrUpdate(instance: tournament) - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages) + try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages) } catch { Logger.error(error) } @@ -131,7 +131,7 @@ struct SchedulerView: View { Button { round.updateMatchFormatAndAllMatches(federalFormat) do { - try self.tournamentStore.rounds.addOrUpdate(instance: round) + try self.tournamentStore?.rounds.addOrUpdate(instance: round) } catch { Logger.error(error) } @@ -176,7 +176,7 @@ struct SchedulerView: View { Button { round.updateMatchFormatAndAllMatches(federalFormat) do { - try self.tournamentStore.rounds.addOrUpdate(instance: round) + try self.tournamentStore?.rounds.addOrUpdate(instance: round) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift index f3cf674..022e037 100644 --- a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift +++ b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift @@ -14,7 +14,7 @@ struct PlayerSexPickerView: View { @Environment(Tournament.self) var tournament: Tournament @Bindable var player: PlayerRegistration - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -38,10 +38,10 @@ struct PlayerSexPickerView: View { private func _save() { player.setComputedRank(in: tournament) - tournamentStore.playerRegistrations.addOrUpdate(instance: player) + tournamentStore?.playerRegistrations.addOrUpdate(instance: player) if let team = player.team() { team.updateWeight(inTournamentCategory: tournament.tournamentCategory) - tournamentStore.teamRegistrations.addOrUpdate(instance: team) + tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } } diff --git a/PadelClub/Views/Player/PlayerDetailView.swift b/PadelClub/Views/Player/PlayerDetailView.swift index 88fe914..5ae6220 100644 --- a/PadelClub/Views/Player/PlayerDetailView.swift +++ b/PadelClub/Views/Player/PlayerDetailView.swift @@ -19,7 +19,7 @@ struct PlayerDetailView: View { @State private var email: String @FocusState var focusedField: PlayerRegistration.CodingKeys? - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -257,13 +257,13 @@ struct PlayerDetailView: View { private func _save() { do { - try self.tournamentStore.playerRegistrations.addOrUpdate(instance: player) + try self.tournamentStore?.playerRegistrations.addOrUpdate(instance: player) } catch { Logger.error(error) } if let team = player.team() { do { - try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Player/PlayerView.swift b/PadelClub/Views/Player/PlayerView.swift index 755ac6d..c604fe9 100644 --- a/PadelClub/Views/Player/PlayerView.swift +++ b/PadelClub/Views/Player/PlayerView.swift @@ -15,7 +15,7 @@ struct PlayerView: View { let player: PlayerRegistration - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -24,7 +24,7 @@ struct PlayerView: View { .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button(role: .destructive) { do { - try self.tournamentStore.playerRegistrations.delete(instance: player) + try self.tournamentStore?.playerRegistrations.delete(instance: player) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Round/DrawLogsView.swift b/PadelClub/Views/Round/DrawLogsView.swift index 6459955..cd16e96 100644 --- a/PadelClub/Views/Round/DrawLogsView.swift +++ b/PadelClub/Views/Round/DrawLogsView.swift @@ -52,7 +52,7 @@ struct DrawLogsView: View { Button("Tout effacer", role: .destructive) { do { - try tournament.tournamentStore.drawLogs.deleteAll() + try tournament.tournamentStore?.drawLogs.deleteAll() } catch { Logger.error(error) } diff --git a/PadelClub/Views/Round/LoserRoundSettingsView.swift b/PadelClub/Views/Round/LoserRoundSettingsView.swift index f39455f..5026317 100644 --- a/PadelClub/Views/Round/LoserRoundSettingsView.swift +++ b/PadelClub/Views/Round/LoserRoundSettingsView.swift @@ -64,7 +64,7 @@ struct LoserRoundSettingsView: View { }) allRoundMatches.forEach({ $0.setMatchName($0.roundTitle()) }) do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches) + try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: allRoundMatches) } catch { Logger.error(error) } @@ -121,13 +121,13 @@ struct LoserRoundSettingsView: View { } do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: matches) + try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: matches) } catch { Logger.error(error) } do { - try self.tournament.tournamentStore.rounds.addOrUpdate(instance: upperBracketRound.round) + try self.tournament.tournamentStore?.rounds.addOrUpdate(instance: upperBracketRound.round) } catch { Logger.error(error) } @@ -145,7 +145,7 @@ struct LoserRoundSettingsView: View { match._toggleLoserMatchDisableState(true) } do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: upperBracketRound.round.allLoserRoundMatches()) + try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: upperBracketRound.round.allLoserRoundMatches()) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Round/LoserRoundView.swift b/PadelClub/Views/Round/LoserRoundView.swift index fba3563..dd4cd71 100644 --- a/PadelClub/Views/Round/LoserRoundView.swift +++ b/PadelClub/Views/Round/LoserRoundView.swift @@ -133,7 +133,7 @@ struct LoserRoundView: View { let allRoundMatches = loserBracket.allMatches.filter({ $0.name == nil }) allRoundMatches.forEach({ $0.setMatchName($0.roundTitle()) }) do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches) + try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: allRoundMatches) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 9bac2be..58ac05e 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -14,7 +14,7 @@ struct RoundSettingsView: View { @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @Environment(Tournament.self) var tournament: Tournament - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -25,7 +25,7 @@ struct RoundSettingsView: View { let issues = tournament.bracketTeamPlacementIssue() DisclosureGroup { ForEach(issues.shouldBeInIt, id: \.self) { id in - if let team: TeamRegistration = self.tournamentStore.teamRegistrations.findById(id) { + if let team: TeamRegistration = self.tournamentStore?.teamRegistrations.findById(id) { TeamRowView(team: team) } } @@ -38,11 +38,11 @@ struct RoundSettingsView: View { } DisclosureGroup { ForEach(issues.shouldNotBeInIt, id: \.self) { id in - if let team = self.tournamentStore.teamRegistrations.findById(id) { + if let team = self.tournamentStore?.teamRegistrations.findById(id) { Menu { Button("Retirer du tableau") { team.resetBracketPosition() - self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } } label: { TeamRowView(team: team) @@ -128,7 +128,7 @@ struct RoundSettingsView: View { let allRoundMatches = tournament.allLoserRoundMatches() allRoundMatches.forEach({ $0.setMatchName($0.roundTitle()) }) do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches) + try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: allRoundMatches) } catch { Logger.error(error) } @@ -164,8 +164,8 @@ struct RoundSettingsView: View { teams.forEach { team in team.resetBracketPosition() } - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) - try tournamentStore.rounds.delete(instance: lastRound) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) + try tournamentStore?.rounds.delete(instance: lastRound) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index 23bf3d5..bd96ee7 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -30,7 +30,7 @@ struct RoundView: View { BracketEditTip.matchesHidden = upperRound.round.getDisabledMatches().count } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -242,7 +242,7 @@ struct RoundView: View { FooterButtonView("Désactiver", role: .destructive) { match.disableMatch() do { - try tournamentStore.matches.addOrUpdate(instance: match) + try tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } @@ -327,7 +327,7 @@ struct RoundView: View { private func _save(seeds: [TeamRegistration]) { do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: seeds) + try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: seeds) } catch { Logger.error(error) } @@ -365,7 +365,7 @@ struct RoundView: View { } do { - try tournament.tournamentStore.matches.addOrUpdate(contentOfs: matchesToUpdate + loserMatches) + try tournament.tournamentStore?.matches.addOrUpdate(contentOfs: matchesToUpdate + loserMatches) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Score/EditScoreView.swift b/PadelClub/Views/Score/EditScoreView.swift index 52e3225..d04bf4a 100644 --- a/PadelClub/Views/Score/EditScoreView.swift +++ b/PadelClub/Views/Score/EditScoreView.swift @@ -207,7 +207,7 @@ struct EditScoreView: View { self.confirmScoreEdition = true if let match = matchDescriptor.match { do { - try match.tournamentStore.matches.addOrUpdate(instance: match) + try match.tournamentStore?.matches.addOrUpdate(instance: match) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Team/CoachListView.swift b/PadelClub/Views/Team/CoachListView.swift index 7a980d6..f5a771e 100644 --- a/PadelClub/Views/Team/CoachListView.swift +++ b/PadelClub/Views/Team/CoachListView.swift @@ -50,7 +50,7 @@ struct CoachListView: View { private func _save() { do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 1e6bad3..8eaf36e 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -33,7 +33,7 @@ struct EditingTeamView: View { } } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -100,7 +100,7 @@ struct EditingTeamView: View { team.walkOut = false team.wildCardBracket = value do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -116,7 +116,7 @@ struct EditingTeamView: View { team.walkOut = false team.wildCardGroupStage = value do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -132,7 +132,7 @@ struct EditingTeamView: View { team.wildCardGroupStage = false team.walkOut = value do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -195,7 +195,7 @@ struct EditingTeamView: View { RowButtonView("Effacer l'équipe", role: .destructive, systemImage: "trash") { team.deleteTeamScores() do { - try tournamentStore.teamRegistrations.delete(instance: team) + try tournamentStore?.teamRegistrations.delete(instance: team) } catch { Logger.error(error) } @@ -303,7 +303,7 @@ struct EditingTeamView: View { } set: { confirmed in team.confirmationDate = confirmed ? Date() : nil do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -318,7 +318,7 @@ struct EditingTeamView: View { $0.hasArrived = hasArrived } do { - try tournamentStore.playerRegistrations.addOrUpdate(contentOfs: team.unsortedPlayers()) + try tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: team.unsortedPlayers()) } catch { Logger.error(error) } @@ -327,7 +327,7 @@ struct EditingTeamView: View { } private func _save() { do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index a25219b..46cea98 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -105,7 +105,7 @@ struct FileImportView: View { _fileProvider = .init(wrappedValue: defaultFileProvider) } - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -123,7 +123,7 @@ struct FileImportView: View { private func _deleteTeams() async { await MainActor.run { do { - try tournamentStore.teamRegistrations.delete(contentOfs: tournament.unsortedTeams()) + try tournamentStore?.teamRegistrations.delete(contentOfs: tournament.unsortedTeams()) } catch { Logger.error(error) } @@ -509,7 +509,7 @@ struct FileImportView: View { } do { - try tournament.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unfound) + try tournament.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unfound) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Tournament/Screen/AddTeamView.swift b/PadelClub/Views/Tournament/Screen/AddTeamView.swift index bf8b9ec..784a928 100644 --- a/PadelClub/Views/Tournament/Screen/AddTeamView.swift +++ b/PadelClub/Views/Tournament/Screen/AddTeamView.swift @@ -46,7 +46,7 @@ struct AddTeamView: View { @State private var testMessageIndex: Int = 0 @State private var presentLocalMultiplayerSearch: Bool = false - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -361,8 +361,8 @@ struct AddTeamView: View { } let team = tournament.addTeam(players) - self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) - self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) + self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) pasteString = nil editableTextField = "" @@ -389,8 +389,8 @@ struct AddTeamView: View { let players = _currentSelection() editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory) - self.tournamentStore.teamRegistrations.addOrUpdate(instance: editedTeam) - self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + self.tournamentStore?.teamRegistrations.addOrUpdate(instance: editedTeam) + self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) pasteString = nil editableTextField = "" diff --git a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift index d39e5e0..c63eba2 100644 --- a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift @@ -224,13 +224,15 @@ struct InscriptionInfoView: View { Section { DisclosureGroup { ForEach(playersWithoutValidLicense) { - EditablePlayerView(player: $0, editingOptions: [.licenceId]) - .environmentObject(tournament.tournamentStore) - .onChange(of: $0.licenceId) { - players = tournament.unsortedPlayers() - let isImported = players.anySatisfy({ $0.isImported() }) - playersWithoutValidLicense = tournament.playersWithoutValidLicense(in: players, isImported: isImported) - } + if let store = tournament.tournamentStore { + EditablePlayerView(player: $0, editingOptions: [.licenceId]) + .environmentObject(store) + .onChange(of: $0.licenceId) { + players = tournament.unsortedPlayers() + let isImported = players.anySatisfy({ $0.isImported() }) + playersWithoutValidLicense = tournament.playersWithoutValidLicense(in: players, isImported: isImported) + } + } } } label: { LabeledContent { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 7b2f6fe..e9511b4 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -225,14 +225,14 @@ struct TournamentGeneralSettingsView: View { } do { - try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: matches) + try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: matches) } catch { Logger.error(error) } } do { - try self.tournament.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) + try self.tournament.tournamentStore?.rounds.addOrUpdate(contentOfs: rounds) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift index ebf4bde..c89afd4 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift @@ -17,7 +17,7 @@ struct TournamentMatchFormatsSettingsView: View { @State private var confirmUpdate: Bool = false @State private var updateCompleted: Bool = false - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -99,12 +99,12 @@ struct TournamentMatchFormatsSettingsView: View { } } do { - try tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages) + try tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages) } catch { Logger.error(error) } do { - try tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) + try tournamentStore?.rounds.addOrUpdate(contentOfs: allRounds) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift index 07f95d5..7527c1e 100644 --- a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift @@ -17,7 +17,7 @@ struct UpdateSourceRankDateView: View { @State private var updatingRank = false var tournament: Tournament - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -48,7 +48,7 @@ struct UpdateSourceRankDateView: View { player.setComputedRank(in: tournament) } - try tournamentStore.playerRegistrations.addOrUpdate(contentOfs: tournament.unsortedPlayers()) + try tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: tournament.unsortedPlayers()) tournament.unsortedTeams().forEach { team in team.setWeight(from: team.players(), inTournamentCategory: tournament.tournamentCategory) @@ -57,7 +57,7 @@ struct UpdateSourceRankDateView: View { } } - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) try dataStore.tournaments.addOrUpdate(instance: tournament) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 775bdc2..3f4b2c5 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -50,7 +50,7 @@ struct InscriptionManagerView: View { @State private var pasteString: String? @State private var registrationIssues: Int? = nil - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -195,7 +195,7 @@ struct InscriptionManagerView: View { } do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: waitingList) + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: waitingList) try dataStore.tournaments.addOrUpdate(instance: tournament) } catch { Logger.error(error) @@ -979,7 +979,7 @@ struct InscriptionManagerView: View { Button(role: .destructive) { team.deleteTeamScores() do { - try tournamentStore.teamRegistrations.delete(instance: team) + try tournamentStore?.teamRegistrations.delete(instance: team) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index b89c2b5..35936f8 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -388,7 +388,7 @@ struct TableStructureView: View { team.groupStage = nil } do { - try tournament.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + try tournament.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) } catch { Logger.error(error) } diff --git a/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift b/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift index 438ade9..c4c79ef 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift @@ -137,33 +137,37 @@ struct TournamentCashierView: View { } var body: some View { - VStack(spacing: 0) { - GenericDestinationPickerView(selectedDestination: $selectedDestination, destinations: allDestinations, nilDestinationIsValid: true) - switch selectedDestination { - case .none: - CashierSettingsView(tournament: tournament) - case .some(let selectedCall): - switch selectedCall { - case .summary: - CashierDetailView(tournament: tournament).id(selectedCall.id) - case .groupStage(let groupStage): - CashierView(tournament: tournament, teams: groupStage.teams()).id(selectedCall.id) - .searchable(text: $cashierViewModel.searchText, isPresented: $cashierViewModel.isSearching, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher un joueur")) - case .bracket(let round): - CashierView(tournament: tournament, teams: round.seeds()).id(selectedCall.id) - .searchable(text: $cashierViewModel.searchText, isPresented: $cashierViewModel.isSearching, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher un joueur")) - case .all(let tournament): - CashierView(tournament: tournament, teams: tournament.selectedSortedTeams()).id(selectedCall.id) - .searchable(text: $cashierViewModel.searchText, isPresented: $cashierViewModel.isSearching, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher un joueur")) + if let store = self.tournament.tournamentStore { + VStack(spacing: 0) { + GenericDestinationPickerView(selectedDestination: $selectedDestination, destinations: allDestinations, nilDestinationIsValid: true) + switch selectedDestination { + case .none: + CashierSettingsView(tournament: tournament) + case .some(let selectedCall): + switch selectedCall { + case .summary: + CashierDetailView(tournament: tournament).id(selectedCall.id) + case .groupStage(let groupStage): + CashierView(tournament: tournament, teams: groupStage.teams()).id(selectedCall.id) + .searchable(text: $cashierViewModel.searchText, isPresented: $cashierViewModel.isSearching, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher un joueur")) + case .bracket(let round): + CashierView(tournament: tournament, teams: round.seeds()).id(selectedCall.id) + .searchable(text: $cashierViewModel.searchText, isPresented: $cashierViewModel.isSearching, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher un joueur")) + case .all(let tournament): + CashierView(tournament: tournament, teams: tournament.selectedSortedTeams()).id(selectedCall.id) + .searchable(text: $cashierViewModel.searchText, isPresented: $cashierViewModel.isSearching, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher un joueur")) + } } } + .environment(tournament) + .environmentObject(store) + .environmentObject(cashierViewModel) + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + .navigationTitle(tournament.isFree() ? "Présence" : "Encaissement") + } else { + Text("no store") } - .environment(tournament) - .environmentObject(tournament.tournamentStore) - .environmentObject(cashierViewModel) - .navigationBarTitleDisplayMode(.inline) - .toolbarBackground(.visible, for: .navigationBar) - .navigationTitle(tournament.isFree() ? "Présence" : "Encaissement") } } diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index 62c5352..e79b11d 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -17,7 +17,7 @@ struct TournamentRankView: View { @State private var calculating = false @State private var selectedTeam: TeamRegistration? - var tournamentStore: TournamentStore { + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -118,7 +118,7 @@ struct TournamentRankView: View { Button("Valider") { selectedTeam.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: selectedTeam.finalRanking! - 1, count: tournament.teamCount) do { - try self.tournamentStore.teamRegistrations.addOrUpdate(instance: selectedTeam) + try self.tournamentStore?.teamRegistrations.addOrUpdate(instance: selectedTeam) } catch { Logger.error(error) } @@ -171,7 +171,7 @@ struct TournamentRankView: View { team.finalRanking = key team.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: key - 1, count: tournament.teamCount) do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -273,7 +273,7 @@ struct TournamentRankView: View { team.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: key - 1, count: tournament.teamCount) do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -289,7 +289,7 @@ struct TournamentRankView: View { Button("Valider") { team.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: key - 1, count: tournament.teamCount) do { - try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team) + try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) } catch { Logger.error(error) } @@ -317,7 +317,7 @@ struct TournamentRankView: View { private func _save() { do { - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) + try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) } catch { Logger.error(error) } From 74375dec5070e4e93f44b77e664b71789b3176e9 Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 7 Feb 2025 17:25:05 +0100 Subject: [PATCH 26/43] fix crash --- PadelClub/Data/Tournament.swift | 4 ++-- PadelClub/Data/TournamentStore.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 32b397c..70f69b6 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -89,8 +89,8 @@ final class Tournament: BaseTournament { } store.drawLogs.deleteDependencies(drawLogs) - let teams = store.teamRegistrations - for team in Array(teams) { + let teams = Array(store.teamRegistrations) + for team in teams { team.deleteDependencies() } store.teamRegistrations.deleteDependencies(teams) diff --git a/PadelClub/Data/TournamentStore.swift b/PadelClub/Data/TournamentStore.swift index 5439e30..3168ccd 100644 --- a/PadelClub/Data/TournamentStore.swift +++ b/PadelClub/Data/TournamentStore.swift @@ -65,7 +65,7 @@ class TournamentStore: ObservableObject { } @objc func _leStorageDidSynchronize(notification: Notification) { - Logger.log("SYNCED > Match count = \(self.matches.count)") +// Logger.log("SYNCED > teamRegistrations count = \(self.teamRegistrations.count)") } deinit { From 982520683cb4c6b2138b3948bce05f2d235c15e1 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 11 Feb 2025 14:18:35 +0100 Subject: [PATCH 27/43] Adds last sync date in the debug view --- PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift index 246c3a7..0fb469c 100644 --- a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift @@ -16,6 +16,7 @@ struct DebugSettingsView: View { LabeledContent("Has Websocket Manager", value: self._hasWebSocketManager) LabeledContent("Websocket ping", value: self._wsPingStatus) LabeledContent("Websocket failure", value: self._wsFailure) + LabeledContent("Last sync date", value: self._lastSyncDate) } Section("Settings") { @@ -66,6 +67,9 @@ struct DebugSettingsView: View { fileprivate var _hasWebSocketManager: String { return "\(StoreCenter.main.hasWebSocketManager)" } + fileprivate var _lastSyncDate: String { + return "\(StoreCenter.main.lastSyncDate)" + } } //#Preview { From 9352975bdd420b542a5f84d02b4cb011ba8215fa Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 12 Feb 2025 15:41:40 +0100 Subject: [PATCH 28/43] Fixes tests --- PadelClubTests/ServerDataTests.swift | 449 ++++++++++++---------- PadelClubTests/SynchronizationTests.swift | 2 +- PadelClubTests/TokenExemptionTests.swift | 7 +- 3 files changed, 247 insertions(+), 211 deletions(-) diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 5a0d348..3614a8b 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -48,25 +48,27 @@ final class ServerDataTests: XCTestCase { club.phone = "061234567890" club.courtCount = 3 - let inserted_club: Club = try await StoreCenter.main.service().post(club) - assert(inserted_club.lastUpdate == club.lastUpdate) - assert(inserted_club.name == club.name) - assert(inserted_club.acronym == club.acronym) - assert(inserted_club.zipCode == club.zipCode) - assert(inserted_club.address == club.address) - assert(inserted_club.latitude == club.latitude) - assert(inserted_club.longitude == club.longitude) - assert(inserted_club.phone == club.phone) - assert(inserted_club.courtCount == club.courtCount) - assert(inserted_club.broadcastCode != nil) - assert(inserted_club.timezone != club.timezone) - - inserted_club.phone = "123456" - inserted_club.lastUpdate = Date() - - let updated_club: Club = try await StoreCenter.main.service().put(inserted_club) - assert(updated_club.phone == inserted_club.phone) - + if let inserted_club: Club = try await StoreCenter.main.service().post(club) { + assert(inserted_club.lastUpdate == club.lastUpdate) + assert(inserted_club.name == club.name) + assert(inserted_club.acronym == club.acronym) + assert(inserted_club.zipCode == club.zipCode) + assert(inserted_club.address == club.address) + assert(inserted_club.latitude == club.latitude) + assert(inserted_club.longitude == club.longitude) + assert(inserted_club.phone == club.phone) + assert(inserted_club.courtCount == club.courtCount) + assert(inserted_club.broadcastCode != nil) + assert(inserted_club.timezone != club.timezone) + + inserted_club.phone = "123456" + inserted_club.lastUpdate = Date() + + let updated_club: Club = try await StoreCenter.main.service().put(inserted_club) + assert(updated_club.phone == inserted_club.phone) + } else { + XCTFail("missing data") + } } func testLogin() async throws { @@ -88,12 +90,14 @@ final class ServerDataTests: XCTestCase { } let event = Event(creator: userId, club: clubId, name: "Roland Garros", tenupId: "abc") - let e = try await StoreCenter.main.service().post(event) - - assert(e.name == event.name) - assert(e.lastUpdate == event.lastUpdate) - assert(e.tenupId == event.tenupId) - + if let e = try await StoreCenter.main.service().post(event){ + + assert(e.name == event.name) + assert(e.lastUpdate == event.lastUpdate) + assert(e.tenupId == event.tenupId) + } else { + XCTFail("missing data") + } } func testTournament() async throws { @@ -105,54 +109,59 @@ final class ServerDataTests: XCTestCase { } let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: true, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual, initialSeedRound: 8, initialSeedCount: 4, accountIsRequired: false, licenseIsRequired: false, minimumPlayerPerTeam: 3, maximumPlayerPerTeam: 5, information: "Super") - let t = try await StoreCenter.main.service().post(tournament) - assert(t.lastUpdate.formatted() == tournament.lastUpdate.formatted()) - assert(t.event == tournament.event) - assert(t.name == tournament.name) - assert(t.startDate.formatted() == tournament.startDate.formatted()) - assert(t.endDate?.formatted() == tournament.endDate?.formatted()) - assert(t.creationDate.formatted() == tournament.creationDate.formatted()) - assert(t.isPrivate == tournament.isPrivate) - assert(t.groupStageFormat == tournament.groupStageFormat) - assert(t.roundFormat == tournament.roundFormat) - assert(t.loserRoundFormat == tournament.loserRoundFormat) - assert(t.groupStageSortMode == tournament.groupStageSortMode) - assert(t.groupStageCount == tournament.groupStageCount) - assert(t.rankSourceDate?.formatted() == tournament.rankSourceDate?.formatted()) - assert(t.dayDuration == tournament.dayDuration) - assert(t.teamCount == tournament.teamCount) - assert(t.teamSorting == tournament.teamSorting) - assert(t.federalCategory == tournament.federalCategory) - assert(t.federalLevelCategory == tournament.federalLevelCategory) - assert(t.federalAgeCategory == tournament.federalAgeCategory) - assert(t.closedRegistrationDate?.formatted() == tournament.closedRegistrationDate?.formatted()) - assert(t.groupStageAdditionalQualified == tournament.groupStageAdditionalQualified) - assert(t.courtCount == tournament.courtCount) - assert(t.prioritizeClubMembers == tournament.prioritizeClubMembers) - assert(t.qualifiedPerGroupStage == tournament.qualifiedPerGroupStage) - assert(t.teamsPerGroupStage == tournament.teamsPerGroupStage) - assert(t.entryFee == tournament.entryFee) - assert(t.additionalEstimationDuration == tournament.additionalEstimationDuration) - assert(t.isDeleted == tournament.isDeleted) - assert(t.publishTeams == tournament.publishTeams) - assert(t.publishSummons == tournament.publishSummons) - assert(t.publishGroupStages == tournament.publishGroupStages) - assert(t.publishBrackets == tournament.publishBrackets) - assert(t.shouldVerifyBracket == tournament.shouldVerifyBracket) - assert(t.shouldVerifyGroupStage == tournament.shouldVerifyGroupStage) - assert(t.hideTeamsWeight == tournament.hideTeamsWeight) - assert(t.publishTournament == tournament.publishTournament) - assert(t.hidePointsEarned == tournament.hidePointsEarned) - assert(t.publishRankings == tournament.publishRankings) - assert(t.loserBracketMode == tournament.loserBracketMode) - assert(t.initialSeedCount == tournament.initialSeedCount) - assert(t.initialSeedRound == tournament.initialSeedRound) - assert(t.accountIsRequired == tournament.accountIsRequired) - assert(t.licenseIsRequired == tournament.licenseIsRequired) - assert(t.minimumPlayerPerTeam == tournament.minimumPlayerPerTeam) - assert(t.maximumPlayerPerTeam == tournament.maximumPlayerPerTeam) - assert(t.information == tournament.information) + if let t = try await StoreCenter.main.service().post(tournament) { + + assert(t.lastUpdate.formatted() == tournament.lastUpdate.formatted()) + assert(t.event == tournament.event) + assert(t.name == tournament.name) + assert(t.startDate.formatted() == tournament.startDate.formatted()) + assert(t.endDate?.formatted() == tournament.endDate?.formatted()) + assert(t.creationDate.formatted() == tournament.creationDate.formatted()) + assert(t.isPrivate == tournament.isPrivate) + assert(t.groupStageFormat == tournament.groupStageFormat) + assert(t.roundFormat == tournament.roundFormat) + assert(t.loserRoundFormat == tournament.loserRoundFormat) + assert(t.groupStageSortMode == tournament.groupStageSortMode) + assert(t.groupStageCount == tournament.groupStageCount) + assert(t.rankSourceDate?.formatted() == tournament.rankSourceDate?.formatted()) + assert(t.dayDuration == tournament.dayDuration) + assert(t.teamCount == tournament.teamCount) + assert(t.teamSorting == tournament.teamSorting) + assert(t.federalCategory == tournament.federalCategory) + assert(t.federalLevelCategory == tournament.federalLevelCategory) + assert(t.federalAgeCategory == tournament.federalAgeCategory) + assert(t.closedRegistrationDate?.formatted() == tournament.closedRegistrationDate?.formatted()) + assert(t.groupStageAdditionalQualified == tournament.groupStageAdditionalQualified) + assert(t.courtCount == tournament.courtCount) + assert(t.prioritizeClubMembers == tournament.prioritizeClubMembers) + assert(t.qualifiedPerGroupStage == tournament.qualifiedPerGroupStage) + assert(t.teamsPerGroupStage == tournament.teamsPerGroupStage) + assert(t.entryFee == tournament.entryFee) + assert(t.additionalEstimationDuration == tournament.additionalEstimationDuration) + assert(t.isDeleted == tournament.isDeleted) + assert(t.publishTeams == tournament.publishTeams) + assert(t.publishSummons == tournament.publishSummons) + assert(t.publishGroupStages == tournament.publishGroupStages) + assert(t.publishBrackets == tournament.publishBrackets) + assert(t.shouldVerifyBracket == tournament.shouldVerifyBracket) + assert(t.shouldVerifyGroupStage == tournament.shouldVerifyGroupStage) + assert(t.hideTeamsWeight == tournament.hideTeamsWeight) + assert(t.publishTournament == tournament.publishTournament) + assert(t.hidePointsEarned == tournament.hidePointsEarned) + assert(t.publishRankings == tournament.publishRankings) + assert(t.loserBracketMode == tournament.loserBracketMode) + assert(t.initialSeedCount == tournament.initialSeedCount) + assert(t.initialSeedRound == tournament.initialSeedRound) + assert(t.accountIsRequired == tournament.accountIsRequired) + assert(t.licenseIsRequired == tournament.licenseIsRequired) + assert(t.minimumPlayerPerTeam == tournament.minimumPlayerPerTeam) + assert(t.maximumPlayerPerTeam == tournament.maximumPlayerPerTeam) + assert(t.information == tournament.information) + + } else { + XCTFail("missing data") + } } func testGroupStage() async throws { @@ -165,18 +174,20 @@ final class ServerDataTests: XCTestCase { let groupStage = GroupStage(tournament: tournamentId, index: 2, size: 3, format: MatchFormat.nineGames, startDate: Date(), name: "Yeah!", step: 1) groupStage.storeId = "123" - let gs: GroupStage = try await StoreCenter.main.service().post(groupStage) - - assert(gs.tournament == groupStage.tournament) - assert(gs.storeId == groupStage.storeId) - assert(gs.lastUpdate == groupStage.lastUpdate) - assert(gs.name == groupStage.name) - assert(gs.index == groupStage.index) - assert(gs.size == groupStage.size) - assert(gs.matchFormat == groupStage.matchFormat) - assert(gs.startDate != nil) - assert(gs.step == groupStage.step) - + if let gs: GroupStage = try await StoreCenter.main.service().post(groupStage) { + + assert(gs.tournament == groupStage.tournament) + assert(gs.storeId == groupStage.storeId) + assert(gs.lastUpdate == groupStage.lastUpdate) + assert(gs.name == groupStage.name) + assert(gs.index == groupStage.index) + assert(gs.size == groupStage.size) + assert(gs.matchFormat == groupStage.matchFormat) + assert(gs.startDate != nil) + assert(gs.step == groupStage.step) + } else { + XCTFail("missing data") + } } @@ -192,18 +203,20 @@ final class ServerDataTests: XCTestCase { let round = Round(tournament: tournamentId, index: 1, parent: parentRoundId, matchFormat: MatchFormat.nineGames, startDate: Date(), groupStageLoserBracket: false, loserBracketMode: .manual) round.storeId = "abc" - let r: Round = try await StoreCenter.main.service().post(round) - - assert(r.storeId == round.storeId) - assert(r.tournament == round.tournament) - assert(r.lastUpdate == round.lastUpdate) - assert(r.index == round.index) - assert(r.parent == round.parent) - assert(r.matchFormat == round.matchFormat) - assert(r.startDate != nil) - assert(r.groupStageLoserBracket == round.groupStageLoserBracket) - assert(r.loserBracketMode == round.loserBracketMode) - + if let r: Round = try await StoreCenter.main.service().post(round) { + + assert(r.storeId == round.storeId) + assert(r.tournament == round.tournament) + assert(r.lastUpdate == round.lastUpdate) + assert(r.index == round.index) + assert(r.parent == round.parent) + assert(r.matchFormat == round.matchFormat) + assert(r.startDate != nil) + assert(r.groupStageLoserBracket == round.groupStageLoserBracket) + assert(r.loserBracketMode == round.loserBracketMode) + } else { + XCTFail("missing data") + } } func testTeamRegistration() async throws { @@ -222,30 +235,33 @@ final class ServerDataTests: XCTestCase { let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true) teamRegistration.storeId = "123" - let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration) - - assert(tr.storeId == teamRegistration.storeId) - assert(tr.tournament == teamRegistration.tournament) - assert(tr.lastUpdate == teamRegistration.lastUpdate) - assert(tr.groupStage == teamRegistration.groupStage) - assert(tr.registrationDate != nil) - assert(tr.callDate != nil) - assert(tr.bracketPosition == teamRegistration.bracketPosition) - assert(tr.groupStagePosition == teamRegistration.groupStagePosition) - assert(tr.comment == teamRegistration.comment) - assert(tr.source == teamRegistration.source) - assert(tr.sourceValue == teamRegistration.sourceValue) - assert(tr.logo == teamRegistration.logo) - assert(tr.name == teamRegistration.name) - assert(tr.walkOut == teamRegistration.walkOut) - assert(tr.wildCardBracket == teamRegistration.wildCardBracket) - assert(tr.wildCardGroupStage == teamRegistration.wildCardGroupStage) - assert(tr.weight == teamRegistration.weight) - assert(tr.lockedWeight == teamRegistration.lockedWeight) - assert(tr.confirmationDate?.formatted() == teamRegistration.confirmationDate?.formatted()) - assert(tr.qualified == teamRegistration.qualified) - assert(tr.finalRanking == teamRegistration.finalRanking) - assert(tr.pointsEarned == teamRegistration.pointsEarned) + if let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration) { + + assert(tr.storeId == teamRegistration.storeId) + assert(tr.tournament == teamRegistration.tournament) + assert(tr.lastUpdate == teamRegistration.lastUpdate) + assert(tr.groupStage == teamRegistration.groupStage) + assert(tr.registrationDate != nil) + assert(tr.callDate != nil) + assert(tr.bracketPosition == teamRegistration.bracketPosition) + assert(tr.groupStagePosition == teamRegistration.groupStagePosition) + assert(tr.comment == teamRegistration.comment) + assert(tr.source == teamRegistration.source) + assert(tr.sourceValue == teamRegistration.sourceValue) + assert(tr.logo == teamRegistration.logo) + assert(tr.name == teamRegistration.name) + assert(tr.walkOut == teamRegistration.walkOut) + assert(tr.wildCardBracket == teamRegistration.wildCardBracket) + assert(tr.wildCardGroupStage == teamRegistration.wildCardGroupStage) + assert(tr.weight == teamRegistration.weight) + assert(tr.lockedWeight == teamRegistration.lockedWeight) + assert(tr.confirmationDate?.formatted() == teamRegistration.confirmationDate?.formatted()) + assert(tr.qualified == teamRegistration.qualified) + assert(tr.finalRanking == teamRegistration.finalRanking) + assert(tr.pointsEarned == teamRegistration.pointsEarned) + } else { + XCTFail("missing data") + } } func testPlayerRegistration() async throws { @@ -259,31 +275,33 @@ final class ServerDataTests: XCTestCase { let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerPaymentType.cash, sex: PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true) playerRegistration.storeId = "123" - let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration) - - assert(pr.storeId == playerRegistration.storeId) - assert(pr.lastName == playerRegistration.lastName) - assert(pr.teamRegistration == playerRegistration.teamRegistration) - assert(pr.firstName == playerRegistration.firstName) - assert(pr.lastName == playerRegistration.lastName) - assert(pr.licenceId == playerRegistration.licenceId) - assert(pr.rank == playerRegistration.rank) - assert(pr.paymentType == playerRegistration.paymentType) - assert(pr.sex == playerRegistration.sex) - assert(pr.tournamentPlayed == playerRegistration.tournamentPlayed) - assert(pr.points == playerRegistration.points) - assert(pr.clubName == playerRegistration.clubName) - assert(pr.ligueName == playerRegistration.ligueName) - assert(pr.assimilation == playerRegistration.assimilation) - assert(pr.phoneNumber == playerRegistration.phoneNumber) - assert(pr.birthdate == playerRegistration.birthdate) - assert(pr.computedRank == playerRegistration.computedRank) - assert(pr.source == playerRegistration.source) - assert(pr.hasArrived == playerRegistration.hasArrived) - assert(pr.captain == playerRegistration.captain) - assert(pr.coach == playerRegistration.coach) - assert(pr.registeredOnline == playerRegistration.registeredOnline) - + if let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration) { + + assert(pr.storeId == playerRegistration.storeId) + assert(pr.lastName == playerRegistration.lastName) + assert(pr.teamRegistration == playerRegistration.teamRegistration) + assert(pr.firstName == playerRegistration.firstName) + assert(pr.lastName == playerRegistration.lastName) + assert(pr.licenceId == playerRegistration.licenceId) + assert(pr.rank == playerRegistration.rank) + assert(pr.paymentType == playerRegistration.paymentType) + assert(pr.sex == playerRegistration.sex) + assert(pr.tournamentPlayed == playerRegistration.tournamentPlayed) + assert(pr.points == playerRegistration.points) + assert(pr.clubName == playerRegistration.clubName) + assert(pr.ligueName == playerRegistration.ligueName) + assert(pr.assimilation == playerRegistration.assimilation) + assert(pr.phoneNumber == playerRegistration.phoneNumber) + assert(pr.birthdate == playerRegistration.birthdate) + assert(pr.computedRank == playerRegistration.computedRank) + assert(pr.source == playerRegistration.source) + assert(pr.hasArrived == playerRegistration.hasArrived) + assert(pr.captain == playerRegistration.captain) + assert(pr.coach == playerRegistration.coach) + assert(pr.registeredOnline == playerRegistration.registeredOnline) + } else { + XCTFail("missing data") + } } func testMatch() async throws { @@ -298,23 +316,25 @@ final class ServerDataTests: XCTestCase { let match: Match = Match(round: parentRoundId, groupStage: nil, startDate: Date(), endDate: Date(), index: 2, format: MatchFormat.twoSets, servingTeamId: teamRegistrationId, winningTeamId: teamRegistrationId, losingTeamId: teamRegistrationId, disabled: true, courtIndex: 1, confirmed: true) match.storeId = "123" - let m: Match = try await StoreCenter.main.service().post(match) - - assert(m.storeId == match.storeId) - assert(m.lastUpdate == match.lastUpdate) - assert(m.round == match.round) - assert(m.groupStage == match.groupStage) - assert(m.startDate != nil) - assert(m.endDate != nil) - assert(m.index == match.index) - assert(m.matchFormat == match.matchFormat) - assert(m.servingTeamId == match.servingTeamId) - assert(m.winningTeamId == match.winningTeamId) - assert(m.losingTeamId == match.losingTeamId) - assert(m.disabled == match.disabled) - assert(m.courtIndex == match.courtIndex) - assert(m.confirmed == match.confirmed) - + if let m: Match = try await StoreCenter.main.service().post(match) { + + assert(m.storeId == match.storeId) + assert(m.lastUpdate == match.lastUpdate) + assert(m.round == match.round) + assert(m.groupStage == match.groupStage) + assert(m.startDate != nil) + assert(m.endDate != nil) + assert(m.index == match.index) + assert(m.matchFormat == match.matchFormat) + assert(m.servingTeamId == match.servingTeamId) + assert(m.winningTeamId == match.winningTeamId) + assert(m.losingTeamId == match.losingTeamId) + assert(m.disabled == match.disabled) + assert(m.courtIndex == match.courtIndex) + assert(m.confirmed == match.confirmed) + } else { + XCTFail("missing data") + } } func testTeamScore() async throws { @@ -331,16 +351,18 @@ final class ServerDataTests: XCTestCase { } let teamScore = TeamScore(match: matchId, teamRegistration: teamRegistrationId, score: "6/6", walkOut: 1, luckyLoser: 1) teamScore.storeId = "!23" - let ts: TeamScore = try await StoreCenter.main.service().post(teamScore) - - assert(ts.storeId == teamScore.storeId) - assert(ts.lastUpdate == teamScore.lastUpdate) - assert(ts.match == teamScore.match) - assert(ts.teamRegistration == teamScore.teamRegistration) - assert(ts.score == teamScore.score) - assert(ts.walkOut == teamScore.walkOut) - assert(ts.luckyLoser == teamScore.luckyLoser) - + if let ts: TeamScore = try await StoreCenter.main.service().post(teamScore) { + + assert(ts.storeId == teamScore.storeId) + assert(ts.lastUpdate == teamScore.lastUpdate) + assert(ts.match == teamScore.match) + assert(ts.teamRegistration == teamScore.teamRegistration) + assert(ts.score == teamScore.score) + assert(ts.walkOut == teamScore.walkOut) + assert(ts.luckyLoser == teamScore.luckyLoser) + } else { + XCTFail("missing data") + } } func testCourt() async throws { @@ -352,15 +374,17 @@ final class ServerDataTests: XCTestCase { } let court = Court(index: 1, club: clubId, name: "Philippe Chatrier", exitAllowed: true, indoor: true) - let c: Court = try await StoreCenter.main.service().post(court) - - assert(c.lastUpdate == court.lastUpdate) - assert(c.club == court.club) - assert(c.name == court.name) - assert(c.index == court.index) - assert(c.exitAllowed == court.exitAllowed) - assert(c.indoor == court.indoor) - + if let c: Court = try await StoreCenter.main.service().post(court) { + + assert(c.lastUpdate == court.lastUpdate) + assert(c.club == court.club) + assert(c.name == court.name) + assert(c.index == court.index) + assert(c.exitAllowed == court.exitAllowed) + assert(c.indoor == court.indoor) + } else { + XCTFail("missing data") + } } func testDateInterval() async throws { @@ -372,14 +396,16 @@ final class ServerDataTests: XCTestCase { } let dateInterval = DateInterval(event: eventId, courtIndex: 1, startDate: Date(), endDate: Date()) - let di: PadelClub.DateInterval = try await StoreCenter.main.service().post(dateInterval) - - assert(di.lastUpdate == dateInterval.lastUpdate) - assert(di.event == dateInterval.event) - assert(di.courtIndex == dateInterval.courtIndex) - assert(di.startDate.formatted() == dateInterval.startDate.formatted()) - assert(di.endDate.formatted() == dateInterval.endDate.formatted()) - + if let di: PadelClub.DateInterval = try await StoreCenter.main.service().post(dateInterval) { + + assert(di.lastUpdate == dateInterval.lastUpdate) + assert(di.event == dateInterval.event) + assert(di.courtIndex == dateInterval.courtIndex) + assert(di.startDate.formatted() == dateInterval.startDate.formatted()) + assert(di.endDate.formatted() == dateInterval.endDate.formatted()) + } else { + XCTFail("missing data") + } } func testPurchase() async throws { @@ -394,17 +420,19 @@ final class ServerDataTests: XCTestCase { let purchase: Purchase = Purchase(user: userId, transactionId: transactionId, purchaseDate: Date(), productId: "app.padelclub.productId", quantity: quantity, revocationDate: Date(), expirationDate: Date()) - let p: Purchase = try await StoreCenter.main.service().post(purchase) - - assert(p.id == purchase.id) - assert(p.lastUpdate == purchase.lastUpdate) - assert(p.user == purchase.user) - assert(p.productId == purchase.productId) - assert(p.purchaseDate.formatted() == purchase.purchaseDate.formatted()) - assert(p.quantity == purchase.quantity) - assert(p.revocationDate?.formatted() == purchase.revocationDate?.formatted()) - assert(p.expirationDate?.formatted() == purchase.expirationDate?.formatted()) - + if let p: Purchase = try await StoreCenter.main.service().post(purchase) { + + assert(p.id == purchase.id) + assert(p.lastUpdate == purchase.lastUpdate) + assert(p.user == purchase.user) + assert(p.productId == purchase.productId) + assert(p.purchaseDate.formatted() == purchase.purchaseDate.formatted()) + assert(p.quantity == purchase.quantity) + assert(p.revocationDate?.formatted() == purchase.revocationDate?.formatted()) + assert(p.expirationDate?.formatted() == purchase.expirationDate?.formatted()) + } else { + XCTFail("missing data") + } } func testDrawLog() async throws { @@ -416,14 +444,19 @@ final class ServerDataTests: XCTestCase { } let drawLog = DrawLog(tournament: tournamentId, drawSeed: 1, drawMatchIndex: 1, drawTeamPosition: .two, drawType: .court) - let d: DrawLog = try await StoreCenter.main.service().post(drawLog) - - assert(d.tournament == drawLog.tournament) - assert(d.drawDate.formatted() == drawLog.drawDate.formatted()) - assert(d.drawSeed == drawLog.drawSeed) - assert(d.drawTeamPosition == drawLog.drawTeamPosition) - assert(d.drawMatchIndex == drawLog.drawMatchIndex) - assert(d.drawType == drawLog.drawType) + drawLog.storeId = "!23" + + if let d: DrawLog = try await StoreCenter.main.service().post(drawLog) { + + assert(d.tournament == drawLog.tournament) + assert(d.drawDate.formatted() == drawLog.drawDate.formatted()) + assert(d.drawSeed == drawLog.drawSeed) + assert(d.drawTeamPosition == drawLog.drawTeamPosition) + assert(d.drawMatchIndex == drawLog.drawMatchIndex) + assert(d.drawType == drawLog.drawType) + } else { + XCTFail("missing data") + } } } diff --git a/PadelClubTests/SynchronizationTests.swift b/PadelClubTests/SynchronizationTests.swift index 1312527..8d35757 100644 --- a/PadelClubTests/SynchronizationTests.swift +++ b/PadelClubTests/SynchronizationTests.swift @@ -15,7 +15,7 @@ struct SynchronizationTests { let password: String = "StaxKikoo12" init() { - StoreCenter.main.configureURLs(httpScheme: "http://", domain: "127.0.0.1:8000") + StoreCenter.main.configureURLs(secureScheme: false, domain: "127.0.0.1:8000") } @Test func synchronizationTest() async throws { diff --git a/PadelClubTests/TokenExemptionTests.swift b/PadelClubTests/TokenExemptionTests.swift index 7160028..458e48b 100644 --- a/PadelClubTests/TokenExemptionTests.swift +++ b/PadelClubTests/TokenExemptionTests.swift @@ -31,8 +31,11 @@ final class TokenExemptionTests: XCTestCase { let club: Club = Club(name: "mon club 2", acronym: "MC", phone: "132", code: "456", address: "l'adresse", city: "la ville", zipCode: "13131", latitude: 13.11111, longitude: 1.121212) - let c = try await StoreCenter.main.service().post(club) - assert(c.id == club.id) + if let c = try await StoreCenter.main.service().post(club) { + assert(c.id == club.id) + } else { + XCTFail("missing data") + } do { _ = try await StoreCenter.main.service().put(club) From eaabafb07e68ce1da5f060a408b675b54a54b05a Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 11 Mar 2025 11:04:19 +0100 Subject: [PATCH 29/43] Improve API list view --- .../Navigation/Toolbox/APICallsListView.swift | 185 +++++++++++++++++- .../Navigation/Toolbox/ToolboxView.swift | 162 ++++++++------- 2 files changed, 262 insertions(+), 85 deletions(-) diff --git a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift index 467b105..c99abd0 100644 --- a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift +++ b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift @@ -10,32 +10,91 @@ import LeStorage struct APICallsListView: View { - @State var collections: [String] = [] + @State var descriptors: [CollectionDescriptor] = [] var body: some View { List { - ForEach(self.collections) { name in - NavigationLink(name) { - APICallsView(name: name) + ForEach(self.descriptors) { descriptor in + + NavigationLink { + if let syncedType = descriptor.type as? any SyncedStorable.Type { + APICallsView(name: descriptor.name, type: syncedType) + } + } label: { + LabeledContent(descriptor.name, value: descriptor.count.string) } + } }.onAppear { - self.collections = Store.main.collectionNames() + self.load() } - } + + func load() { + self.descriptors = Store.main.collectionNames().map { + CollectionDescriptor(name: $0.0, type: $0.1) + }.sorted(by: \.name, order: .ascending) + Task { + + for descriptor in self.descriptors { + if let type = descriptor.type as? any SyncedStorable.Type { + await self.loadCount(type, descriptor) + } + } + } + } + + func loadCount(_ type: T.Type, _ descriptor: CollectionDescriptor) async { + let calls = await StoreCenter.main.apiCalls(type: type) + descriptor.count = calls.count +// Logger.log("\(descriptor.name), count = \(calls.count)") + } + +} + +@Observable +class CollectionDescriptor: Identifiable, Hashable { + + var id: UUID = UUID() + var name: String + var type: any Storable.Type + var count: Int = 0 + + init(name: String, type: any Storable.Type) { + self.name = name + self.type = type + } + + static func == (lhs: CollectionDescriptor, rhs: CollectionDescriptor) -> Bool { + return lhs.id == rhs.id + } + func hash(into hasher: inout Hasher) { + hasher.combine(self.id) + } + } struct APICallsView: View { var name: String - @State var text: String = "" + var type: any SyncedStorable.Type + +// @State var text: String = "" + @State var apiCalls: [any SomeCall] = [] var body: some View { List { - Text(self.text).lineLimit(nil) - }.onAppear { + ForEach(self.apiCalls, id: \.id) { apiCall in + NavigationLink { + APICallView(apiCall: apiCall, type: self.type) + } label: { + LabeledContent(apiCall.method.rawValue, value: apiCall.attemptsCount.string) + } + } +// Text(self.text).lineLimit(nil) + }.navigationTitle(self.name) + .onAppear { self._load() } } @@ -43,10 +102,116 @@ struct APICallsView: View { @MainActor fileprivate func _load() { Task { - self.text = await StoreCenter.main.apiCallsFileContent(resourceName: self.name) + await self.load(self.type) +// self.text = await StoreCenter.main.apiCallsFileContent(resourceName: self.name) } } + func load(_ type: T.Type) async { + let calls: [ApiCall] = await StoreCenter.main.apiCalls(type: type) + self.apiCalls = calls + } + +} + +struct APICallView: View { + + var apiCall: any SomeCall + var type: any SyncedStorable.Type + @State private var errorMessage: String? = nil + @State private var isLoading: Bool = false + @State private var showSuccessPopover: Bool = false + + var body: some View { + Form { + Section { + LabeledContent("Method", value: apiCall.method.rawValue) + LabeledContent("Data id", value: apiCall.stringId) + LabeledContent("Attempts", value: apiCall.attemptsCount.string) + } + + Section { + if isLoading { + ProgressView() + .padding() + } else { + + Button("Execute") { + self.execute() + // Clear previous error when attempting again + errorMessage = nil + } + .disabled(isLoading) + } + if let errorMessage = errorMessage { + LabeledContent("Error", value: errorMessage) + // VStack(alignment: .leading) { + // Text("Error:") + // .fontWeight(.bold) + // .foregroundColor(.red) + // Text(errorMessage) + // .foregroundColor(.red) + // } + } + if showSuccessPopover { + VStack { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + .font(.title) + Text("Success") + .foregroundColor(.green) + } + .padding() + .background( + RoundedRectangle(cornerRadius: 8) + .fill(Color.white) + .shadow(radius: 2) + ) + .transition(.scale.combined(with: .opacity)) + } + + } + Section { + Text(apiCall.dataContent ?? "none").monospaced().font(.caption) + } + + } + .animation(.easeInOut, value: showSuccessPopover) + .navigationTitle(String(describing: self.type)) + } + + func execute() { + self.execute(type: self.type) + } + + func execute(type: T.Type) { + if let call = self.apiCall as? ApiCall { + Task { + await MainActor.run { + isLoading = true + } + + do { + let result = try await StoreCenter.main.execute(apiCalls: [call]) + print(result) + // Update UI on the main thread + await MainActor.run { + errorMessage = nil + isLoading = false + + // Show success popover + showSuccessPopover = true + } + } catch { + // Update UI on the main thread + await MainActor.run { + errorMessage = error.localizedDescription + isLoading = false + } + } + } + } + } } //#Preview { diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index c660ffe..439d714 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -52,82 +52,11 @@ struct ToolboxView: View { } #if DEBUG + DebugView() + #endif - Section { - NavigationLink("Settings") { - DebugSettingsView() - } - NavigationLink("API calls") { - APICallsListView() - } - } - - Section { - RowButtonView("Reset ALL API Calls") { - StoreCenter.main.resetApiCalls() - Logger.log("Api calls reset") - } - } - Section { - RowButtonView("Fix Names") { - - for tournament in dataStore.tournaments { - - if let store = tournament.tournamentStore { - let playerRegistrations = store.playerRegistrations - playerRegistrations.forEach { player in - player.firstName = player.firstName.trimmed.capitalized - player.lastName = player.lastName.trimmed.uppercased() - } - - do { - try store.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations) - } catch { - Logger.error(error) - } - } - } - - } - } - - Section { - RowButtonView("Delete teams") { - - for tournament in DataStore.shared.tournaments { - - if let store: TournamentStore = tournament.tournamentStore { - - let teamRegistrations = store.teamRegistrations.filter({ $0.tournamentObject() == nil }) - do { - try store.teamRegistrations.delete(contentOfs: teamRegistrations) - } catch { - Logger.error(error) - } - } - } - } - } - - Section { - // TODO - RowButtonView("Delete players") { - - for tournament in DataStore.shared.tournaments { - if let store: TournamentStore = tournament.tournamentStore { - - let playersRegistrations = store.playerRegistrations.filter({ $0.team() == nil }) - do { - try store.playerRegistrations.delete(contentOfs: playersRegistrations) - } catch { - Logger.error(error) - } - } - } - - } - - } + #if TESTFLIGHT + DebugView() #endif Section { @@ -248,6 +177,89 @@ struct ToolboxView: View { // ToolboxView() //} +struct DebugView: View { + + @EnvironmentObject var dataStore: DataStore + + var body: some View { + + Section { + NavigationLink("Settings") { + DebugSettingsView() + } + NavigationLink("API calls") { + APICallsListView() + } + } + + Section { + RowButtonView("Reset ALL API Calls") { + StoreCenter.main.resetApiCalls() + Logger.log("Api calls reset") + } + } + Section { + RowButtonView("Fix Names") { + + for tournament in dataStore.tournaments { + + if let store = tournament.tournamentStore { + let playerRegistrations = store.playerRegistrations + playerRegistrations.forEach { player in + player.firstName = player.firstName.trimmed.capitalized + player.lastName = player.lastName.trimmed.uppercased() + } + + do { + try store.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations) + } catch { + Logger.error(error) + } + } + } + + } + } + + Section { + RowButtonView("Delete teams") { + + for tournament in DataStore.shared.tournaments { + + if let store: TournamentStore = tournament.tournamentStore { + + let teamRegistrations = store.teamRegistrations.filter({ $0.tournamentObject() == nil }) + do { + try store.teamRegistrations.delete(contentOfs: teamRegistrations) + } catch { + Logger.error(error) + } + } + } + } + } + + Section { + // TODO + RowButtonView("Delete players") { + + for tournament in DataStore.shared.tournaments { + if let store: TournamentStore = tournament.tournamentStore { + + let playersRegistrations = store.playerRegistrations.filter({ $0.team() == nil }) + do { + try store.playerRegistrations.delete(contentOfs: playersRegistrations) + } catch { + Logger.error(error) + } + } + } + + } + + } + } +} struct ZipLog: Transferable { private func _getZip() -> URL? { From 602755fe2a79c091a381f99711d534345bf747fa Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 11 Mar 2025 15:28:06 +0100 Subject: [PATCH 30/43] Improve API call execution and add developer mode --- .../Navigation/Toolbox/APICallsListView.swift | 24 +++++-- .../Navigation/Toolbox/ToolboxView.swift | 70 +++++++++++++++---- PadelClubTests/ServerDataTests.swift | 8 ++- PadelClubTests/TokenExemptionTests.swift | 8 ++- PadelClubTests/UserDataTests.swift | 29 ++++---- 5 files changed, 100 insertions(+), 39 deletions(-) diff --git a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift index c99abd0..4c904d6 100644 --- a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift +++ b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift @@ -192,16 +192,26 @@ struct APICallView: View { } do { - let result = try await StoreCenter.main.execute(apiCalls: [call]) - print(result) - // Update UI on the main thread + let results = try await StoreCenter.main.execute(apiCalls: [call]) + await MainActor.run { - errorMessage = nil isLoading = false - - // Show success popover - showSuccessPopover = true + if let result = results.first { + errorMessage = result.message + showSuccessPopover = (200..<300).contains(result.status) + } else { + errorMessage = "no results" + } } + + // Update UI on the main thread +// await MainActor.run { +// errorMessage = nil +// isLoading = false +// +// // Show success popover +// showSuccessPopover = true +// } } catch { // Update UI on the main thread await MainActor.run { diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 439d714..3e9836c 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -15,6 +15,12 @@ struct ToolboxView: View { @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel @State private var didResetApiCalls: Bool = false + @State var showDebugViews: Bool = false + + @State private var tapCount = 0 + @State private var lastTapTime: Date? = nil + private let tapTimeThreshold: TimeInterval = 1.0 + var lastDataSource: String? { dataStore.appSettings.lastDataSource } @@ -39,25 +45,21 @@ struct ToolboxView: View { .contextMenu { ShareLink(item: URLs.main.url) } - + SupportButtonView(contentIsUnavailable: false) Link(destination: URLs.appReview.url) { Text("Partagez vos impressions !") } - + Link(destination: URLs.instagram.url) { Text("Compte Instagram PadelClub.app") } } - #if DEBUG - DebugView() - #endif - - #if TESTFLIGHT - DebugView() - #endif + if showDebugViews { + DebugView() + } Section { NavigationLink { @@ -65,7 +67,7 @@ struct ToolboxView: View { } label: { Label("Rechercher un joueur", systemImage: "person.fill.viewfinder") } - + NavigationLink { RankCalculatorView() } label: { @@ -113,13 +115,13 @@ struct ToolboxView: View { didResetApiCalls = true } } - + Section { Link(destination: URLs.appDescription.url) { Text("Page de présentation de Padel Club") } } - + Section { Link(destination: URLs.privacy.url) { Text("Politique de confidentialité") @@ -136,6 +138,16 @@ struct ToolboxView: View { } } } + .onAppear { +//#if DEBUG +// self.showDebugViews = true +//#endif +// +//#if TESTFLIGHT +// self.showDebugViews = true +//#endif + + } .overlay(alignment: .bottom) { if didResetApiCalls { Label("logs effacés", systemImage: "checkmark") @@ -148,8 +160,16 @@ struct ToolboxView: View { } } } - .navigationTitle(TabDestination.toolbox.title) + .navigationBarTitleDisplayMode(.large) + // .navigationTitle(TabDestination.toolbox.title) .toolbar { + ToolbarItem(placement: .principal) { + Text(TabDestination.toolbox.title) + .font(.headline) + .onTapGesture { + _handleTitleTap() + } + } ToolbarItem(placement: .topBarLeading) { Link(destination: URLs.appStore.url) { Text("v\(PadelClubApp.appVersion)") @@ -171,6 +191,30 @@ struct ToolboxView: View { } } } + + private func _handleTitleTap() { + // Reset counter if too much time elapsed since last tap + if let lastTime = lastTapTime, Date().timeIntervalSince(lastTime) > tapTimeThreshold { + tapCount = 0 + } + + // Update tap count and time + tapCount += 1 + lastTapTime = Date() + + print("Tap count: \(tapCount)") + + // Check if we've reached 4 taps + if tapCount == 4 { + _secretFeatureActivated() + tapCount = 0 + } + } + + private func _secretFeatureActivated() { + self.showDebugViews = true + } + } //#Preview { diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 3614a8b..9fe95a4 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -64,8 +64,12 @@ final class ServerDataTests: XCTestCase { inserted_club.phone = "123456" inserted_club.lastUpdate = Date() - let updated_club: Club = try await StoreCenter.main.service().put(inserted_club) - assert(updated_club.phone == inserted_club.phone) + if let updated_club: Club = try await StoreCenter.main.service().put(inserted_club) { + assert(updated_club.phone == inserted_club.phone) + } else { + XCTFail("missing data") + } + } else { XCTFail("missing data") } diff --git a/PadelClubTests/TokenExemptionTests.swift b/PadelClubTests/TokenExemptionTests.swift index 458e48b..e257ddf 100644 --- a/PadelClubTests/TokenExemptionTests.swift +++ b/PadelClubTests/TokenExemptionTests.swift @@ -47,9 +47,11 @@ final class TokenExemptionTests: XCTestCase { let _ = try await self.login() club.creator = user.id - let uc = try await StoreCenter.main.service().put(club) - assert(uc.creator == user.id) - + if let uc = try await StoreCenter.main.service().put(club) { + assert(uc.creator == user.id) + } else { + XCTFail("missing data") + } } func login() async throws -> CustomUser { diff --git a/PadelClubTests/UserDataTests.swift b/PadelClubTests/UserDataTests.swift index 4d98ac1..c5a9f63 100644 --- a/PadelClubTests/UserDataTests.swift +++ b/PadelClubTests/UserDataTests.swift @@ -65,20 +65,21 @@ final class UserDataTests: XCTestCase { user.loserBracketMatchFormatPreference = MatchFormat.twoSetsOfFourGames user.loserBracketMode = .manual - let uu = try await StoreCenter.main.service().put(user) - - assert(uu.summonsMessageBody == user.summonsMessageBody) - assert(uu.summonsMessageSignature == user.summonsMessageSignature) - assert(uu.summonsAvailablePaymentMethods == user.summonsAvailablePaymentMethods) - assert(uu.summonsDisplayFormat == user.summonsDisplayFormat) - assert(uu.summonsDisplayEntryFee == user.summonsDisplayEntryFee) - assert(uu.summonsUseFullCustomMessage == user.summonsUseFullCustomMessage) - assert(uu.matchFormatsDefaultDuration == user.matchFormatsDefaultDuration) - assert(uu.bracketMatchFormatPreference == user.bracketMatchFormatPreference) - assert(uu.groupStageMatchFormatPreference == user.groupStageMatchFormatPreference) - assert(uu.loserBracketMatchFormatPreference == user.loserBracketMatchFormatPreference) - assert(uu.loserBracketMode == user.loserBracketMode) - + if let uu = try await StoreCenter.main.service().put(user) { + assert(uu.summonsMessageBody == user.summonsMessageBody) + assert(uu.summonsMessageSignature == user.summonsMessageSignature) + assert(uu.summonsAvailablePaymentMethods == user.summonsAvailablePaymentMethods) + assert(uu.summonsDisplayFormat == user.summonsDisplayFormat) + assert(uu.summonsDisplayEntryFee == user.summonsDisplayEntryFee) + assert(uu.summonsUseFullCustomMessage == user.summonsUseFullCustomMessage) + assert(uu.matchFormatsDefaultDuration == user.matchFormatsDefaultDuration) + assert(uu.bracketMatchFormatPreference == user.bracketMatchFormatPreference) + assert(uu.groupStageMatchFormatPreference == user.groupStageMatchFormatPreference) + assert(uu.loserBracketMatchFormatPreference == user.loserBracketMatchFormatPreference) + assert(uu.loserBracketMode == user.loserBracketMode) + } else { + XCTFail("missing data") + } } } From 4dc72c0aafc85cfe491b21cbc49fbccb271a08af Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 11 Mar 2025 17:27:17 +0100 Subject: [PATCH 31/43] Fix issue where tournament deletes kept tournament file directory --- PadelClub/Data/DataStore.swift | 10 ++++++++++ PadelClub/Views/Navigation/Agenda/EventListView.swift | 11 +---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 9e21f9b..d0f910a 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -212,6 +212,16 @@ class DataStore: ObservableObject { } + func deleteTournament(_ tournament: Tournament) { + let event = tournament.eventObject() + let isLastTournament = event?.tournaments.count == 1 + self.tournaments.delete(instance: tournament) + if let event, isLastTournament { + self.events.delete(instance: event) + } + StoreCenter.main.destroyStore(identifier: tournament.id) + } + fileprivate func _localDisconnect() { StoreCenter.main.collectionsCanSynchronize = false diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 56feb26..6998a05 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -285,16 +285,7 @@ struct EventListView: View { #if DEBUG .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button(role: .destructive) { - do { - let event = tournament.eventObject() - let isLastTournament = event?.tournaments.count == 1 - try dataStore.tournaments.delete(instance: tournament) - if let event, isLastTournament { - try dataStore.events.delete(instance: event) - } - } catch { - Logger.error(error) - } + dataStore.deleteTournament(tournament) } label: { LabelDelete() } From 2b856fcde4def9f155ac3c5522a9a7e50666a7de Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 12 Mar 2025 11:26:04 +0100 Subject: [PATCH 32/43] Fix issue where data was not deleted --- PadelClub/Data/DataStore.swift | 1 - PadelClub/Data/TeamRegistration.swift | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index d0f910a..d98db75 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -329,7 +329,6 @@ class DataStore: ObservableObject { return runningMatches } - func runningAndNextMatches() -> [Match] { let dateNow : Date = Date() let lastTournaments = self.tournaments.filter { $0.isDeleted == false && $0.startDate <= dateNow && $0.hasEnded() == false }.sorted(by: \Tournament.startDate, order: .descending).prefix(10) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 850e504..45c36a7 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -498,7 +498,7 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable { } let playersToRemove = previousPlayers.subtracting(players) - self.tournamentStore?.playerRegistrations.delete(contentOfs: playersToRemove) + self.tournamentStore?.playerRegistrations.delete(contentOfs: Array(playersToRemove)) setWeight(from: Array(players), inTournamentCategory: tournamentCategory) players.forEach { player in From 6334648efd0a5596d52d503ba9ae2b0ca9c108a3 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 12 Mar 2025 15:19:47 +0100 Subject: [PATCH 33/43] Fix memory issue when going in federal players search --- .../Views/Tournament/Screen/AddTeamView.swift | 456 ++++++------------ 1 file changed, 153 insertions(+), 303 deletions(-) diff --git a/PadelClub/Views/Tournament/Screen/AddTeamView.swift b/PadelClub/Views/Tournament/Screen/AddTeamView.swift index 8c71827..5ebb60e 100644 --- a/PadelClub/Views/Tournament/Screen/AddTeamView.swift +++ b/PadelClub/Views/Tournament/Screen/AddTeamView.swift @@ -10,7 +10,7 @@ import LeStorage import CoreData struct AddTeamView: View { - + @Environment(\.dismiss) var dismiss private var fetchRequest: FetchRequest @@ -45,7 +45,7 @@ struct AddTeamView: View { @State private var displayWarningNotEnoughCharacter: Bool = false @State private var testMessageIndex: Int = 0 @State private var presentLocalMultiplayerSearch: Bool = false - + var tournamentStore: TournamentStore? { return self.tournament.tournamentStore } @@ -61,11 +61,11 @@ struct AddTeamView: View { createdPlayers.insert(player) createdPlayerIds.insert(player.id) } - + _createdPlayers = .init(wrappedValue: createdPlayers) _createdPlayerIds = .init(wrappedValue: createdPlayerIds) } - + let request: NSFetchRequest = ImportedPlayer.fetchRequest() request.sortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)] request.fetchLimit = 1000 @@ -77,7 +77,7 @@ struct AddTeamView: View { _textHeight = .init(wrappedValue: Self._calculateHeight(text: pasteString)) cancelShouldDismiss = true } - + fetchRequest = FetchRequest(fetchRequest: request, animation: .default) } @@ -97,7 +97,7 @@ struct AddTeamView: View { computedBody } } - + var computedBody: some View { List(selection: $createdPlayerIds) { _buildingTeamView() @@ -127,7 +127,7 @@ struct AddTeamView: View { Button("Créer l'équipe quand même") { _createTeam(checkDuplicates: false, checkHomonym: false) } - + Button("Annuler", role: .cancel) { confirmHomonym = false } @@ -140,7 +140,7 @@ struct AddTeamView: View { Button("Créer l'équipe quand même") { _createTeam(checkDuplicates: false, checkHomonym: true) } - + Button("Annuler", role: .cancel) { confirmDuplicate = false } @@ -221,11 +221,11 @@ struct AddTeamView: View { .buttonBorderShape(.capsule) } } - + ToolbarItem(placement: .topBarTrailing) { Button { let generalString = UIPasteboard.general.string ?? "" - + #if targetEnvironment(simulator) let s = testMessages[testMessageIndex % testMessages.count] handlePasteString(s) @@ -249,15 +249,15 @@ struct AddTeamView: View { .navigationTitle(editedTeam == nil ? "Ajouter une équipe" : "Modifier l'équipe") .environment(\.editMode, Binding.constant(EditMode.active)) } - + private func _isEditingTeam() -> Bool { createdPlayerIds.isEmpty == false || editedTeam != nil || pasteString != nil } - + var unsortedPlayers: [PlayerRegistration] { tournament.unsortedPlayers() } - + @ViewBuilder private func _managementView() -> some View { Section { @@ -269,7 +269,7 @@ struct AddTeamView: View { Text("Cherchez dans la base fédérale de \(rankSourceDate.monthYearFormatted), vous y trouverez tous les joueurs ayant participé à au moins un tournoi dans les 12 derniers mois.") } } - + if tournament.isAnimation(), createdPlayers.isEmpty == true { Section { RowButtonView("Ajouter plusieurs joueurs du club") { @@ -279,7 +279,7 @@ struct AddTeamView: View { Text("Crée une équipe par joueur sélectionné") } } - + Section { RowButtonView("Créer un non classé / non licencié") { if let pasteString, pasteString.isEmpty == false { @@ -292,7 +292,7 @@ struct AddTeamView: View { Text("Si le joueur n'a pas encore de licence ou n'a pas encore participé à une compétition, vous pouvez le créer vous-même.") } } - + private func _addPlayerSex() -> Int { switch tournament.tournamentCategory { case .men, .unlisted: @@ -304,11 +304,11 @@ struct AddTeamView: View { } } - + private func _filterOption() -> PlayerFilterOption { return tournament.tournamentCategory.playerFilterOption } - + private func _currentSelection() -> Set { var currentSelection = Set() createdPlayerIds.compactMap { id in @@ -318,7 +318,7 @@ struct AddTeamView: View { player.setComputedRank(in: tournament) currentSelection.insert(player) } - + createdPlayerIds.compactMap { id in createdPlayers.first(where: { id == $0.id }) }.forEach { @@ -334,7 +334,7 @@ struct AddTeamView: View { }.forEach { player in currentSelection.append(player.license) } - + createdPlayerIds.compactMap { id in createdPlayers.first(where: { id == $0.id }) }.forEach { @@ -342,7 +342,7 @@ struct AddTeamView: View { } return currentSelection } - + private func _isDuplicate() -> Bool { if tournament.isAnimation() { return false } let ids : [String?] = _currentSelectionIds() @@ -351,15 +351,15 @@ struct AddTeamView: View { } return false } - + private func _createTeam(checkDuplicates: Bool, checkHomonym: Bool) { if checkDuplicates && _isDuplicate() { confirmDuplicate = true return } - + let players = _currentSelection() - + if checkHomonym { homonyms = players.filter({ $0.hasHomonym() }) if homonyms.isEmpty == false { @@ -367,27 +367,27 @@ struct AddTeamView: View { return } } - + let team = tournament.addTeam(players) self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team) self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) - - pasteString = nil - editableTextField = "" - createdPlayers.removeAll() - createdPlayerIds.removeAll() - - if team.players().count > 1 { - dismiss() - } else { - editedTeam = team - team.unsortedPlayers().forEach { player in - createdPlayers.insert(player) - createdPlayerIds.insert(player.id) - } - } + + pasteString = nil + editableTextField = "" + createdPlayers.removeAll() + createdPlayerIds.removeAll() + + if team.players().count > 1 { + dismiss() + } else { + editedTeam = team + team.unsortedPlayers().forEach { player in + createdPlayers.insert(player) + createdPlayerIds.insert(player.id) + } + } } - + private func _updateTeam(checkDuplicates: Bool) { guard let editedTeam else { return } if checkDuplicates && _isDuplicate() { @@ -399,7 +399,7 @@ struct AddTeamView: View { editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory) self.tournamentStore?.teamRegistrations.addOrUpdate(instance: editedTeam) self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) - + pasteString = nil editableTextField = "" @@ -407,7 +407,7 @@ struct AddTeamView: View { dismiss() } } - + // Calculating the height based on the content of the TextEditor static private func _calculateHeight(text: String) -> CGFloat { let size = CGSize(width: UIScreen.main.bounds.width - 32, height: .infinity) @@ -420,23 +420,15 @@ struct AddTeamView: View { ) return max(boundingRect.height + 20, 40) // Add some padding and set a minimum height } - - struct PasteStringSection: View { - - let pasteString: String? - @Binding var editableTextField: String - @Binding var textHeight: CGFloat - @FocusState var focusedField: AddTeamView.FocusField? - var handlePasteString: (String) -> Void - @Binding var displayWarningNotEnoughCharacter: Bool - - var body: some View { + + @ViewBuilder + private func _buildingTeamView() -> some View { if let pasteString { Section { TextEditor(text: $editableTextField) .frame(height: textHeight) .onChange(of: editableTextField) { - textHeight = AddTeamView._calculateHeight(text: pasteString) + textHeight = Self._calculateHeight(text: pasteString) } .focused($focusedField, equals: .pasteField) .toolbar { @@ -468,104 +460,121 @@ struct AddTeamView: View { FooterButtonView("effacer le texte") { self.focusedField = nil self.editableTextField = "" - self.handlePasteString("") + self.pasteString = nil } } } } - } - } - - - @ViewBuilder - private func _buildingTeamView() -> some View { - - PasteStringSection( - pasteString: pasteString, - editableTextField: $editableTextField, - textHeight: $textHeight, - focusedField: _focusedField, - handlePasteString: handlePasteString, - displayWarningNotEnoughCharacter: $displayWarningNotEnoughCharacter - ) + + Section { + ForEach(createdPlayerIds.sorted(), id: \.self) { id in + if let p = createdPlayers.first(where: { $0.id == id }) { + VStack(alignment: .leading, spacing: 0) { + if let player = unsortedPlayers.first(where: { ($0.licenceId == p.licenceId && $0.licenceId != nil) }), editedTeam?.includes(player: player) == false { + Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerAgeInadequate(player: p) { + Text("Âge invalide !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerRankInadequate(player: p) { + Text("Trop bien classé !").foregroundStyle(.logoRed).bold() + } + PlayerView(player: p).tag(p.id) + .environment(tournament) + } + } + if let p = fetchPlayers.first(where: { $0.license == id }) { + VStack(alignment: .leading, spacing: 0) { + if let pasteString, pasteString.isEmpty == false, unsortedPlayers.first(where: { $0.licenceId == p.license }) != nil { + Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerAgeInadequate(player: p) { + Text("Âge invalide !").foregroundStyle(.logoRed).bold() + } + if tournament.isPlayerRankInadequate(player: p) { + Text("Trop bien classé !").foregroundStyle(.logoRed).bold() + } + ImportedPlayerView(player: p).tag(p.license!) + } + } + } + if editedTeam == nil { + if createdPlayerIds.isEmpty { + RowButtonView("Bloquer une place") { + _createTeam(checkDuplicates: false, checkHomonym: false) + } + } else { + RowButtonView("Ajouter l'équipe") { + _createTeam(checkDuplicates: true, checkHomonym: true) + } + } + } else { + RowButtonView("Confirmer") { + _updateTeam(checkDuplicates: false) + dismiss() + } + } + } header: { + let _currentSelection = _currentSelection() + let selectedSortedTeams = tournament.selectedSortedTeams() + let rank = _currentSelection.map { + $0.computedRank + }.reduce(0, +) + let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count + if _currentSelection.isEmpty == false, tournament.hideWeight() == false, rank > 0 { + HStack(spacing: 16.0) { + VStack(alignment: .leading, spacing: 0) { + Text("Rang").font(.caption) + Text("#" + (teamIndex + 1).formatted()) + } - TeamSelectionSection( - createdPlayerIds: createdPlayerIds, - createdPlayers: createdPlayers, - unsortedPlayers: unsortedPlayers, - fetchPlayers: fetchPlayers, - editedTeam: editedTeam, - pasteString: pasteString, - tournament: tournament, - _createTeam: _createTeam, - _updateTeam: _updateTeam, - dismiss: dismiss, - _currentSelection: _currentSelection - ) + VStack(alignment: .leading, spacing: 0) { + Text("Poids").font(.caption) + Text(rank.formatted()) + } + Spacer() + VStack(alignment: .trailing, spacing: 0) { + Text("").font(.caption) + Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count)) + } + } +// } else { +// Text("Préparation de l'équipe") + } + } + if let pasteString, pasteString.isEmpty == false { - let sortedPlayers = _searchFilteredPlayers() - - if sortedPlayers.isEmpty { - ContentUnavailableView { - Label("Aucun résultat", systemImage: "person.2.slash") - } description: { - Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.") - } actions: { - RowButtonView("Créer un joueur non classé") { - selectionSearchField = pasteString - } + let sortedPlayers = _searchFilteredPlayers() + + if sortedPlayers.isEmpty { + ContentUnavailableView { + Label("Aucun résultat", systemImage: "person.2.slash") + } description: { + Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.") + } actions: { + RowButtonView("Créer un joueur non classé") { + selectionSearchField = pasteString + } + + RowButtonView("Chercher dans la base") { + presentPlayerSearch = true + } - RowButtonView("Chercher dans la base") { - presentPlayerSearch = true + RowButtonView("Effacer cette recherche") { + self.pasteString = nil + self.editableTextField = "" + } } - RowButtonView("Effacer cette recherche") { - self.pasteString = nil - self.editableTextField = "" - } + } else { + _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString) } - } else { - _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString) + _managementView() } - } else { - _managementView() - } } - -// -// if let pasteString, pasteString.isEmpty == false { -// let sortedPlayers = _searchFilteredPlayers() -// -// if sortedPlayers.isEmpty { -// ContentUnavailableView { -// Label("Aucun résultat", systemImage: "person.2.slash") -// } description: { -// Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.") -// } actions: { -// RowButtonView("Créer un joueur non classé") { -// selectionSearchField = pasteString -// } -// -// RowButtonView("Chercher dans la base") { -// presentPlayerSearch = true -// } -// -// RowButtonView("Effacer cette recherche") { -// self.pasteString = nil -// self.editableTextField = "" -// } -// } -// -// } else { -// _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString) -// } -// } else { -// _managementView() -// } -// } - + @MainActor func hitForSearch(_ ip: ImportedPlayer, _ pasteString: String?) -> Int { guard let pasteString else { return 0 } @@ -602,7 +611,7 @@ struct AddTeamView: View { } return 1 } - + private func handlePasteString(_ first: String) { if first.isEmpty == false { fetchPlayers.nsPredicate = SearchViewModel.pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption()) @@ -617,7 +626,7 @@ struct AddTeamView: View { @ViewBuilder private func _listOfPlayers(searchFilteredPlayers: [ImportedPlayer], pasteString: String) -> some View { let sortedPlayers = _sortedPlayers(searchFilteredPlayers: searchFilteredPlayers, pasteString: pasteString) - + Section { ForEach(sortedPlayers) { player in ImportedPlayerView(player: player).tag(player.license!) @@ -628,7 +637,7 @@ struct AddTeamView: View { } } - + private func _searchFilteredPlayers() -> [ImportedPlayer] { if searchField.isEmpty { return Array(fetchPlayers) @@ -636,171 +645,12 @@ struct AddTeamView: View { return fetchPlayers.filter({ $0.contains(searchField) }) } } - + private func _sortedPlayers(searchFilteredPlayers: [ImportedPlayer], pasteString: String) -> [ImportedPlayer] { return searchFilteredPlayers.sorted(by: { hitForSearch($0, pasteString) > hitForSearch($1, pasteString) }) } } -struct TeamSelectionSection: View { - let createdPlayerIds: Set - let createdPlayers: Set - let unsortedPlayers: [PlayerRegistration] - let fetchPlayers: FetchedResults - let editedTeam: TeamRegistration? - let pasteString: String? - let tournament: Tournament - let _createTeam: (Bool, Bool) -> Void - let _updateTeam: (Bool) -> Void - let dismiss: DismissAction - let _currentSelection: () -> Set - - var body: some View { - Section { - PlayerListView(createdPlayerIds: createdPlayerIds, - createdPlayers: createdPlayers, - unsortedPlayers: unsortedPlayers, - fetchPlayers: fetchPlayers, - editedTeam: editedTeam, - pasteString: pasteString, - tournament: tournament) - - ActionButton(editedTeam: editedTeam, - createdPlayerIds: createdPlayerIds, - _createTeam: _createTeam, - _updateTeam: _updateTeam, - dismiss: dismiss) - } header: { - TeamHeader(tournament: tournament, - _currentSelection: _currentSelection) - } - } -} - -struct PlayerListView: View { - let createdPlayerIds: Set - let createdPlayers: Set - let unsortedPlayers: [PlayerRegistration] - let fetchPlayers: FetchedResults - let editedTeam: TeamRegistration? - let pasteString: String? - let tournament: Tournament - - var body: some View { - ForEach(createdPlayerIds.sorted(), id: \.self) { id in - if let p = createdPlayers.first(where: { $0.id == id }) { - CreatedPlayerView(player: p, unsortedPlayers: unsortedPlayers, editedTeam: editedTeam, tournament: tournament) - } - if let p = fetchPlayers.first(where: { $0.license == id }) { - FetchedPlayerView(player: p, unsortedPlayers: unsortedPlayers, pasteString: pasteString, tournament: tournament) - } - } - } -} - -struct CreatedPlayerView: View { - let player: PlayerRegistration - let unsortedPlayers: [PlayerRegistration] - let editedTeam: TeamRegistration? - let tournament: Tournament - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - if let existingPlayer = unsortedPlayers.first(where: { ($0.licenceId == player.licenceId && $0.licenceId != nil) }), editedTeam?.includes(player: existingPlayer) == false { - Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerAgeInadequate(player: player) { - Text("Âge invalide !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerRankInadequate(player: player) { - Text("Trop bien classé !").foregroundStyle(.logoRed).bold() - } - PlayerView(player: player).tag(player.id) - .environment(tournament) - } - } -} - -struct FetchedPlayerView: View { - let player: ImportedPlayer - let unsortedPlayers: [PlayerRegistration] - let pasteString: String? - let tournament: Tournament - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - if let pasteString, pasteString.isEmpty == false, unsortedPlayers.first(where: { $0.licenceId == player.license }) != nil { - Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerAgeInadequate(player: player) { - Text("Âge invalide !").foregroundStyle(.logoRed).bold() - } - if tournament.isPlayerRankInadequate(player: player) { - Text("Trop bien classé !").foregroundStyle(.logoRed).bold() - } - ImportedPlayerView(player: player).tag(player.license!) - } - } -} - -struct ActionButton: View { - let editedTeam: TeamRegistration? - let createdPlayerIds: Set - let _createTeam: (Bool, Bool) -> Void - let _updateTeam: (Bool) -> Void - let dismiss: DismissAction - - var body: some View { - if editedTeam == nil { - if createdPlayerIds.isEmpty { - RowButtonView("Bloquer une place") { - _createTeam(false, false) - } - } else { - RowButtonView("Ajouter l'équipe") { - _createTeam(true, true) - } - } - } else { - RowButtonView("Confirmer") { - _updateTeam(false) - dismiss() - } - } - } -} - -struct TeamHeader: View { - let tournament: Tournament - let _currentSelection: () -> Set - - var body: some View { - let currentSelection = _currentSelection() - let selectedSortedTeams = tournament.selectedSortedTeams() - let rank = currentSelection.map { $0.computedRank }.reduce(0, +) - let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count - - if !currentSelection.isEmpty, !tournament.hideWeight(), rank > 0 { - HStack(spacing: 16.0) { - VStack(alignment: .leading, spacing: 0) { - Text("Rang").font(.caption) - Text("#" + (teamIndex + 1).formatted()) - } - - VStack(alignment: .leading, spacing: 0) { - Text("Poids").font(.caption) - Text(rank.formatted()) - } - Spacer() - VStack(alignment: .trailing, spacing: 0) { - Text("").font(.caption) - Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count)) - } - } - } - } -} - let testMessages = [ "Anthony dovetta ( 3620578 K )et christophe capeau ( 4666443v)", """ @@ -827,6 +677,6 @@ Tullou Benjamin 8990867f """, """ Sms Julien La Croix +33622886688 -Salut Raz, c'est ! Ju Lacroix J'espère que tu vas bien depuis le temps! Est-ce que tu peux nous inscrire au 1000 de Bandol avec Derek Gerson stp? +Salut Raz, c'est ! Ju Lacroix J'espère que tu vas bien depuis le temps! Est-ce que tu peux nous inscrire au 1000 de Bandol avec Derek Gerson stp? """ ] From 006e407ee605d9ab3c5cc480f31bd311963f553c Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 12 Mar 2025 15:31:42 +0100 Subject: [PATCH 34/43] Fix missing dismiss after disconnect + UI improvement --- PadelClub/Views/Components/RowButtonView.swift | 2 +- PadelClub/Views/User/AccountView.swift | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/PadelClub/Views/Components/RowButtonView.swift b/PadelClub/Views/Components/RowButtonView.swift index 0dce2d5..d3aa97e 100644 --- a/PadelClub/Views/Components/RowButtonView.swift +++ b/PadelClub/Views/Components/RowButtonView.swift @@ -97,7 +97,7 @@ struct RowButtonView: View { .confirmationDialog("Confirmation", isPresented: $askConfirmation, titleVisibility: .visible) { - Button("OK") { + Button("OK", role: self.role) { if let action { action() } else if let asyncAction { diff --git a/PadelClub/Views/User/AccountView.swift b/PadelClub/Views/User/AccountView.swift index f0d5e5f..539a2d4 100644 --- a/PadelClub/Views/User/AccountView.swift +++ b/PadelClub/Views/User/AccountView.swift @@ -9,7 +9,9 @@ import SwiftUI import LeStorage struct AccountView: View { - + + @Environment(\.dismiss) private var dismiss + var user: CustomUser var handler: () -> () @@ -28,13 +30,14 @@ struct AccountView: View { Section { RowButtonView("Déconnexion", role: .destructive) { DataStore.shared.disconnect() - handler() + dismiss() +// handler() } } Section { RowButtonView("Supprimer mon compte", role: .destructive, confirmationMessage: "Voulez-vous vraiment supprimer définitivement votre compte et ses données associées ?") { DataStore.shared.deleteAccount() - handler() +// handler() } } }.navigationTitle(user.username) From e56c61449edeaa6f7d90428ad84d2320cb9ef690 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 12 Mar 2025 16:39:32 +0100 Subject: [PATCH 35/43] minor improvements --- PadelClub/Views/Navigation/Umpire/PadelClubView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift index 5263260..5c66ff9 100644 --- a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift +++ b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift @@ -40,10 +40,10 @@ struct PadelClubView: View { if let currentMonth = monthData.first, currentMonth.incompleteMode { Section { - Text("Attention, depuis Août 2024, les données fédérales publiques des joueurs (messieurs) récupérables sont incomplètes car limité au 80.000 premiers joueurs.") + Text("Attention, depuis Août 2024, les données fédérales publiques des joueurs (messieurs) récupérables sont incomplètes car limitées aux 80 000 premiers joueurs.") if currentMonth.maleUnrankedValue == nil { - Text("Le rang d'un joueur non-classé n'est donc pas calculable pour le moment, Padel Club utilisera une valeur par défaut de de 90.000.") + Text("Le rang d'un joueur non-classé n'est donc pas calculable pour le moment, Padel Club utilisera une valeur par défaut de 90 000.") } Text("Un classement souligné comme ci-dessous indiquera que l'information provient d'un mois précédent.") From 459a90253ab09a39858562636e77a343a1709915 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 12 Mar 2025 17:23:45 +0100 Subject: [PATCH 36/43] minor improvements #2 --- .../Navigation/Toolbox/ToolboxView.swift | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 3e9836c..a9b8865 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -57,7 +57,7 @@ struct ToolboxView: View { } } - if showDebugViews { + if self.showDebugViews { DebugView() } @@ -139,13 +139,13 @@ struct ToolboxView: View { } } .onAppear { -//#if DEBUG -// self.showDebugViews = true -//#endif -// -//#if TESTFLIGHT -// self.showDebugViews = true -//#endif +#if DEBUG + self.showDebugViews = true +#endif + +#if TESTFLIGHT + self.showDebugViews = true +#endif } .overlay(alignment: .bottom) { @@ -160,7 +160,7 @@ struct ToolboxView: View { } } } - .navigationBarTitleDisplayMode(.large) +// .navigationBarTitleDisplayMode(.large) // .navigationTitle(TabDestination.toolbox.title) .toolbar { ToolbarItem(placement: .principal) { @@ -206,12 +206,12 @@ struct ToolboxView: View { // Check if we've reached 4 taps if tapCount == 4 { - _secretFeatureActivated() + _showDebugView() tapCount = 0 } } - private func _secretFeatureActivated() { + private func _showDebugView() { self.showDebugViews = true } From 36504eec51e4247c476f0b6d34ff9725e053c79f Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 13 Mar 2025 14:44:27 +0100 Subject: [PATCH 37/43] Bumps testflight target version to 1.1.22 --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index d761ec0..57d5523 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3902,7 +3902,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.1; + MARKETING_VERSION = 1.1.22; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3944,7 +3944,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.1; + MARKETING_VERSION = 1.1.22; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 3a17267c83130503dd8d5f97ab60a8bbbaab19f1 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 17 Mar 2025 11:08:26 +0100 Subject: [PATCH 38/43] add infos --- .../Navigation/Toolbox/APICallsListView.swift | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift index 4c904d6..6706d47 100644 --- a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift +++ b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift @@ -32,11 +32,11 @@ struct APICallsListView: View { } func load() { - self.descriptors = Store.main.collectionNames().map { - CollectionDescriptor(name: $0.0, type: $0.1) - }.sorted(by: \.name, order: .ascending) Task { - + self.descriptors = await StoreCenter.main.apiCollectionDescriptors().map { + CollectionDescriptor(name: $0.0, type: $0.1) + }.sorted(by: \.name, order: .ascending) + for descriptor in self.descriptors { if let type = descriptor.type as? any SyncedStorable.Type { await self.loadCount(type, descriptor) @@ -85,14 +85,19 @@ struct APICallsView: View { var body: some View { List { - ForEach(self.apiCalls, id: \.id) { apiCall in - NavigationLink { - APICallView(apiCall: apiCall, type: self.type) - } label: { - LabeledContent(apiCall.method.rawValue, value: apiCall.attemptsCount.string) + Section(header: + Text("\(apiCalls.count) API calls") + .font(.headline) + .foregroundColor(.primary) + ) { + ForEach(self.apiCalls, id: \.id) { apiCall in + NavigationLink { + APICallView(apiCall: apiCall, type: self.type) + } label: { + LabeledContent(apiCall.method.rawValue, value: apiCall.attemptsCount.string) + } } } -// Text(self.text).lineLimit(nil) }.navigationTitle(self.name) .onAppear { self._load() From 8c5536c99b4e7b5191b111ea4b950172d0190d98 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 19 Mar 2025 11:00:34 +0100 Subject: [PATCH 39/43] remove print --- PadelClub/Data/GroupStage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index 27fe45b..cd2d346 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -90,7 +90,7 @@ final class GroupStage: BaseGroupStage, SideStorable { format: self.matchFormat, name: self.localizedMatchUpLabel(for: index)) match.store = self.store - print("_createMatch(index)", index) +// print("_createMatch(index)", index) return match } From e9aba1bd5063f6e6f9d6cc7bf1f7bb72e18090b2 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 19 Mar 2025 13:58:55 +0100 Subject: [PATCH 40/43] Refactor hasToken with isAuthenticated --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- PadelClub/AppDelegate.swift | 2 +- PadelClub/Data/Tournament.swift | 2 +- PadelClub/Views/Navigation/MainView.swift | 8 ++------ PadelClub/Views/Navigation/Umpire/UmpireView.swift | 12 ++++-------- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index a9d7428..85e8fa6 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3879,7 +3879,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3922,7 +3922,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/AppDelegate.swift b/PadelClub/AppDelegate.swift index c57e143..a678151 100644 --- a/PadelClub/AppDelegate.swift +++ b/PadelClub/AppDelegate.swift @@ -27,7 +27,7 @@ class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDel func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - if StoreCenter.main.hasToken() { + if StoreCenter.main.isAuthenticated { Task { do { let services = try StoreCenter.main.service() diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index a576d5e..07feb15 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2320,7 +2320,7 @@ defer { } func refreshTeamList() async { - guard StoreCenter.main.hasToken() else { return } + guard StoreCenter.main.isAuthenticated else { return } guard shouldRefreshTeams(), refreshInProgress == false, enableOnlineRegistration, hasEnded() == false else { return } await MainActor.run { refreshInProgress = true diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index c92b57e..b306c5c 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -47,12 +47,8 @@ struct MainView: View { } )} - private func _isConnected() -> Bool { - return StoreCenter.main.hasToken() && StoreCenter.main.userId != nil - } - var badgeText: Text? { - return (dataStore.appSettings.didCreateAccount && _isConnected() == false) ? Text("!").font(.headline) : nil + return (dataStore.appSettings.didCreateAccount && StoreCenter.main.isAuthenticated == false) ? Text("!").font(.headline) : nil } var body: some View { @@ -105,7 +101,7 @@ struct MainView: View { .environmentObject(dataStore) .task { //await self._checkSourceFileAvailability() - if StoreCenter.main.hasToken() { + if StoreCenter.main.isAuthenticated { do { try await dataStore.clubs.loadDataFromServerIfAllowed() } catch { diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index d72ba88..872be35 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -55,7 +55,7 @@ struct UmpireView: View { } } - if self._isConnected() { + if StoreCenter.main.isAuthenticated { NavigationLink { AccountView(user: dataStore.user) { } } label: { @@ -242,10 +242,6 @@ struct UmpireView: View { } } - fileprivate func _isConnected() -> Bool { - return dataStore.user.username.count > 0 && StoreCenter.main.hasToken() - } - } struct AccountRowView: View { @@ -254,9 +250,9 @@ struct AccountRowView: View { var userName: String var body: some View { - let hasToken = StoreCenter.main.hasToken() + let isAuthenticated = StoreCenter.main.isAuthenticated LabeledContent { - if hasToken { + if isAuthenticated { Text(self.userName) } else if StoreCenter.main.userName() != nil { Image(systemName: "xmark.circle.fill") @@ -264,7 +260,7 @@ struct AccountRowView: View { } } label: { Label("Mon compte", systemImage: "person.fill") - if hasToken && dataStore.user.email.isEmpty == false { + if isAuthenticated && dataStore.user.email.isEmpty == false { Text(dataStore.user.email) } } From 1fb273e69691b00812031eea4b121902647764d0 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 19 Mar 2025 16:10:19 +0100 Subject: [PATCH 41/43] LeStorage refactoring --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- .../Views/Navigation/Toolbox/DebugSettingsView.swift | 5 +++-- PadelClub/Views/Navigation/Umpire/UmpireView.swift | 2 +- PadelClub/Views/Shared/SupportButtonView.swift | 3 ++- PadelClubTests/ServerDataTests.swift | 3 ++- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 85e8fa6..9083e8b 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3879,7 +3879,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3902,7 +3902,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.22; + MARKETING_VERSION = 1.1.23; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3922,7 +3922,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3944,7 +3944,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.22; + MARKETING_VERSION = 1.1.23; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift index 0fb469c..76716fb 100644 --- a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift @@ -35,11 +35,12 @@ struct DebugSettingsView: View { } fileprivate var _userName: String { - return StoreCenter.main.userName() ?? "" + return StoreCenter.main.userName ?? "" } fileprivate var _token: String { - return StoreCenter.main.token() ?? "" + let token = try? StoreCenter.main.token() + return token ?? "" } fileprivate var _apiURL: String { diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index 872be35..78adfb1 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -254,7 +254,7 @@ struct AccountRowView: View { LabeledContent { if isAuthenticated { Text(self.userName) - } else if StoreCenter.main.userName() != nil { + } else if StoreCenter.main.userName != nil { Image(systemName: "xmark.circle.fill") .foregroundStyle(.logoRed) } diff --git a/PadelClub/Views/Shared/SupportButtonView.swift b/PadelClub/Views/Shared/SupportButtonView.swift index 0586c88..b715356 100644 --- a/PadelClub/Views/Shared/SupportButtonView.swift +++ b/PadelClub/Views/Shared/SupportButtonView.swift @@ -79,7 +79,8 @@ struct SupportButtonView: View { private func _getBody() -> String { let separator = "---------------------------------------------" - return ["Décrivez votre problème", "\n\n\n", separator, "token", StoreCenter.main.token(), separator, "userId", StoreCenter.main.userId, separator, "dataStore userId", DataStore.shared.user.id].compacted().joined(separator: "\n") + let token = try? StoreCenter.main.token() + return ["Décrivez votre problème", "\n\n\n", separator, "token", token ?? "", separator, "userId", StoreCenter.main.userId, separator, "dataStore userId", DataStore.shared.user.id].compacted().joined(separator: "\n") } private func _getDeviceIdentifier() -> String { diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 9fe95a4..0ef72d8 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -76,7 +76,8 @@ final class ServerDataTests: XCTestCase { } func testLogin() async throws { - let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password) + let service = try StoreCenter.main.service() + let user: CustomUser = try await service.login(username: self.username, password: self.password) assert(user.username == self.username) } From a6c7fddf0c6eea96d643ea20b6035c406fc6f78e Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 21 Mar 2025 14:55:32 +0100 Subject: [PATCH 42/43] hide tournament sharing buttons --- PadelClub/Views/Navigation/Agenda/EventListView.swift | 10 +++++----- PadelClub/Views/Tournament/TournamentView.swift | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 01e0376..364aa90 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -296,11 +296,11 @@ struct EventListView: View { } label: { LabelDelete() } - Button() { - self.showUserSearch = true - } label: { - ShareLabel().tint(.orange) - } +// Button() { +// self.showUserSearch = true +// } label: { +// ShareLabel().tint(.orange) +// } } #endif diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 1efed36..9aa834c 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -268,9 +268,9 @@ struct TournamentView: View { Label("Imprimer", systemImage: "printer") } - NavigationLink(value: Screen.share) { - Label("Partager", systemImage: "square.and.arrow.up") - } +// NavigationLink(value: Screen.share) { +// Label("Partager", systemImage: "square.and.arrow.up") +// } Divider() From 5f4c0640ca19d3b4e858134feebc2b476eeaf6ae Mon Sep 17 00:00:00 2001 From: Laurent Date: Sun, 23 Mar 2025 11:32:28 +0100 Subject: [PATCH 43/43] Fix issue, the related user becomes the creator --- PadelClub/Data/Club.swift | 6 ++++-- PadelClub/Data/Event.swift | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index 8c64362..f52bc18 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -89,7 +89,7 @@ extension Club { } func hasBeenCreated(by creatorId: String?) -> Bool { - return creatorId == creator || creator == nil + return creatorId == creator || creator == nil || self.relatedUser == creatorId } func isFavorite() -> Bool { @@ -108,7 +108,9 @@ extension Club { if let club { return club } else { - return Club(creator: StoreCenter.main.userId, name: name, code: code, city: city, zipCode: zipCode) + let club = Club(creator: StoreCenter.main.userId, name: name, code: code, city: city, zipCode: zipCode) + club.relatedUser = StoreCenter.main.userId + return club } } diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index d45ff18..c2e9709 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -14,7 +14,7 @@ final class Event: BaseEvent { internal init(creator: String? = nil, club: String? = nil, name: String? = nil, tenupId: String? = nil) { super.init(creator: creator, club: club, name: name, tenupId: tenupId) - + self.relatedUser = creator } required init(from decoder: Decoder) throws {