From 2c99a323b9a559bf938066401879230cb6591229 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 1 May 2025 14:26:23 +0200 Subject: [PATCH] Split project with PadelClubData --- PadelClub.xcodeproj/project.pbxproj | 820 +---- .../contents.xcworkspacedata | 3 + PadelClub/AppDelegate.swift | 32 + PadelClub/Data/AppSettings.swift | 111 - PadelClub/Data/Club.swift | 115 - .../Coredata/ImportedPlayer+Extensions.swift | 1 + PadelClub/Data/Court.swift | 58 - PadelClub/Data/CustomUser.swift | 316 -- PadelClub/Data/DataStore.swift | 403 --- PadelClub/Data/DateInterval.swift | 76 - PadelClub/Data/DrawLog.swift | 97 - PadelClub/Data/Event.swift | 100 - .../Data/Federal/FederalTournament.swift | 19 +- .../Federal/FederalTournamentHolder.swift | 1 + PadelClub/Data/Gen/BaseClub.swift | 150 - PadelClub/Data/Gen/BaseCourt.swift | 93 - PadelClub/Data/Gen/BaseCustomUser.swift | 262 -- PadelClub/Data/Gen/BaseDateInterval.swift | 80 - PadelClub/Data/Gen/BaseDrawLog.swift | 100 - PadelClub/Data/Gen/BaseEvent.swift | 100 - PadelClub/Data/Gen/BaseGroupStage.swift | 107 - PadelClub/Data/Gen/BaseMatch.swift | 156 - PadelClub/Data/Gen/BaseMatchScheduler.swift | 163 - PadelClub/Data/Gen/BaseMonthData.swift | 122 - .../Data/Gen/BasePlayerRegistration.swift | 227 -- PadelClub/Data/Gen/BasePurchase.swift | 99 - PadelClub/Data/Gen/BaseRound.swift | 107 - PadelClub/Data/Gen/BaseTeamRegistration.swift | 199 -- PadelClub/Data/Gen/BaseTeamScore.swift | 99 - PadelClub/Data/Gen/BaseTournament.swift | 590 ---- PadelClub/Data/Gen/Club.json | 93 - PadelClub/Data/Gen/Court.json | 42 - PadelClub/Data/Gen/CustomUser.json | 177 -- PadelClub/Data/Gen/DateInterval.json | 33 - PadelClub/Data/Gen/Drawlog.json | 47 - PadelClub/Data/Gen/Event.json | 48 - PadelClub/Data/Gen/GroupStage.json | 53 - PadelClub/Data/Gen/Match.json | 83 - PadelClub/Data/Gen/MatchScheduler.json | 91 - PadelClub/Data/Gen/MonthData.json | 71 - PadelClub/Data/Gen/PlayerRegistration.json | 138 - PadelClub/Data/Gen/Purchase.json | 51 - PadelClub/Data/Gen/Round.json | 53 - PadelClub/Data/Gen/TeamRegistration.json | 118 - PadelClub/Data/Gen/TeamScore.json | 44 - PadelClub/Data/Gen/Tournament.json | 355 --- PadelClub/Data/Gen/generator.py | 593 ---- PadelClub/Data/GroupStage.swift | 684 ----- PadelClub/Data/Match.swift | 1088 ------- PadelClub/Data/MatchScheduler.swift | 888 ------ PadelClub/Data/MockData.swift | 66 - PadelClub/Data/PlayerPaymentType.swift | 53 - PadelClub/Data/PlayerRegistration.swift | 510 ---- PadelClub/Data/README.md | 35 - PadelClub/Data/Round.swift | 1049 ------- PadelClub/Data/TeamRegistration.swift | 779 ----- PadelClub/Data/TeamScore.swift | 117 - PadelClub/Data/Tournament.swift | 2688 ----------------- PadelClub/Data/TournamentLibrary.swift | 33 - PadelClub/Data/TournamentStore.swift | 68 - PadelClub/Extensions/Array+Extensions.swift | 96 - PadelClub/Extensions/Badge+Extensions.swift | 25 + .../Extensions/Calendar+Extensions.swift | 71 - .../CodingContainer+Extensions.swift | 45 - .../Extensions/CustomUser+Extensions.swift | 22 + PadelClub/Extensions/Date+Extensions.swift | 266 -- .../FixedWidthInteger+Extensions.swift | 43 - PadelClub/Extensions/Locale+Extensions.swift | 28 - .../MonthData+Extensions.swift} | 28 +- PadelClub/Extensions/MySortDescriptor.swift | 27 - .../NumberFormatter+Extensions.swift | 20 - .../PlayerRegistration+Extensions.swift | 229 ++ PadelClub/Extensions/Round+Extensions.swift | 33 + .../Extensions/Sequence+Extensions.swift | 103 - .../SourceFileManager+Extensions.swift | 34 + .../Extensions/SpinDrawable+Extensions.swift | 42 + PadelClub/Extensions/String+Crypto.swift | 47 - PadelClub/Extensions/String+Extensions.swift | 313 -- .../TeamRegistration+Extensions.swift | 67 + .../Extensions/Tournament+Extensions.swift | 445 +++ PadelClub/Extensions/URL+Extensions.swift | 182 -- PadelClub/PadelClubApp.swift | 1 + PadelClub/Utils/ContactManager.swift | 254 -- PadelClub/Utils/CryptoKey.swift | 12 - PadelClub/Utils/DisplayContext.swift | 64 - PadelClub/Utils/ExportFormat.swift | 37 - PadelClub/Utils/FileImportManager.swift | 1 + PadelClub/Utils/HtmlGenerator.swift | 1 + PadelClub/Utils/HtmlService.swift | 1 + .../Utils/Network/NetworkFederalService.swift | 1 + PadelClub/Utils/Network/NetworkManager.swift | 1 + .../Utils/Network/NetworkManagerError.swift | 28 - PadelClub/Utils/Network/RefundService.swift | 1 + PadelClub/Utils/PListReader.swift | 47 - PadelClub/Utils/PadelRule.swift | 2116 ------------- PadelClub/Utils/Patcher.swift | 143 - PadelClub/Utils/SourceFileManager.swift | 270 -- PadelClub/Utils/SwiftParser.swift | 1 + PadelClub/Utils/Tips.swift | 1 + PadelClub/Utils/URLs.swift | 95 - PadelClub/ViewModel/AgendaDestination.swift | 1 + .../ViewModel/FederalDataViewModel.swift | 1 + PadelClub/ViewModel/MatchDescriptor.swift | 154 - PadelClub/ViewModel/MatchSpot.swift | 29 - PadelClub/ViewModel/NavigationViewModel.swift | 1 + PadelClub/ViewModel/Screen.swift | 26 - PadelClub/ViewModel/SearchViewModel.swift | 118 +- PadelClub/ViewModel/SeedInterval.swift | 66 - PadelClub/ViewModel/Selectable.swift | 89 - PadelClub/ViewModel/SetDescriptor.swift | 66 - .../Views/Calling/BracketCallingView.swift | 1 + .../CallMessageCustomizationView.swift | 1 + .../Views/Calling/CallSettingsView.swift | 1 + PadelClub/Views/Calling/CallView.swift | 1 + .../Calling/Components/MenuWarningView.swift | 1 + .../PlayersWithoutContactView.swift | 1 + .../Views/Calling/GroupStageCallingView.swift | 1 + .../Views/Calling/SeedsCallingView.swift | 1 + PadelClub/Views/Calling/SendToAllView.swift | 1 + .../Views/Calling/TeamsCallingView.swift | 1 + .../Views/Cashier/CashierDetailView.swift | 1 + .../Views/Cashier/CashierSettingsView.swift | 1 + PadelClub/Views/Cashier/CashierView.swift | 18 +- .../Cashier/Event/EventCreationView.swift | 1 + .../Views/Cashier/Event/EventLinksView.swift | 1 + .../Cashier/Event/EventSettingsView.swift | 1 + .../Cashier/Event/EventTournamentsView.swift | 1 + PadelClub/Views/Cashier/Event/EventView.swift | 1 + .../Event/TournamentConfiguratorView.swift | 1 + PadelClub/Views/Club/ClubDetailView.swift | 1 + PadelClub/Views/Club/ClubImportView.swift | 1 + PadelClub/Views/Club/ClubRowView.swift | 1 + PadelClub/Views/Club/ClubSearchView.swift | 1 + PadelClub/Views/Club/ClubsView.swift | 1 + PadelClub/Views/Club/CourtView.swift | 1 + PadelClub/Views/Club/CreateClubView.swift | 1 + .../Club/Shared/ClubCourtSetupView.swift | 1 + PadelClub/Views/Components/ComposeViews.swift | 123 + .../Views/Components/FortuneWheelView.swift | 38 +- .../GenericDestinationPickerView.swift | 1 + .../Views/Components/MatchListView.swift | 1 + .../Components/GroupStageSettingsView.swift | 1 + .../Components/GroupStageTeamView.swift | 1 + .../Views/GroupStage/GroupStageView.swift | 1 + .../GroupStage/GroupStagesSettingsView.swift | 1 + .../Views/GroupStage/GroupStagesView.swift | 1 + .../LoserBracketFromGroupStageView.swift | 1 + .../GroupStageTeamReplacementView.swift | 1 + .../Match/Components/MatchDateView.swift | 1 + .../Components/MatchTeamDetailView.swift | 1 + .../Match/Components/PlayerBlockView.swift | 1 + PadelClub/Views/Match/EditSharingView.swift | 1 + PadelClub/Views/Match/MatchDetailView.swift | 10 +- PadelClub/Views/Match/MatchRowView.swift | 1 + PadelClub/Views/Match/MatchSetupView.swift | 1 + PadelClub/Views/Match/MatchSummaryView.swift | 1 + .../Navigation/Agenda/ActivityView.swift | 1 + .../Navigation/Agenda/CalendarView.swift | 1 + .../Navigation/Agenda/EventListView.swift | 1 + .../Agenda/TournamentLookUpView.swift | 1 + .../Agenda/TournamentSubscriptionView.swift | 1 + PadelClub/Views/Navigation/MainView.swift | 1 + .../Ongoing/OngoingContainerView.swift | 1 + .../Ongoing/OngoingDestination.swift | 1 + .../Navigation/Ongoing/OngoingView.swift | 1 + .../Organizer/OrganizedTournamentView.swift | 1 + .../Organizer/TournamentButtonView.swift | 1 + .../Organizer/TournamentOrganizerView.swift | 1 + .../Navigation/Toolbox/APICallsListView.swift | 1 + .../Toolbox/DebugSettingsView.swift | 1 + .../Toolbox/DurationSettingsView.swift | 1 + .../Toolbox/GlobalSettingsView.swift | 3 +- .../Toolbox/MatchFormatStorageView.swift | 1 + .../Toolbox/RankCalculatorView.swift | 1 + .../Navigation/Toolbox/ToolboxView.swift | 3 +- .../Navigation/Umpire/NetworkStatusView.swift | 1 + .../Navigation/Umpire/PadelClubView.swift | 1 + .../Umpire/UmpireStatisticView.swift | 1 + .../Views/Navigation/Umpire/UmpireView.swift | 1 + .../Planning/Components/DatePickingView.swift | 1 + .../DatePickingViewWithFormat.swift | 1 + .../GroupStageDatePickingView.swift | 1 + .../Components/MatchFormatPickingView.swift | 1 + .../Components/MultiCourtPickerView.swift | 1 + .../CourtAvailabilitySettingsView.swift | 9 +- .../GroupStageScheduleEditorView.swift | 1 + .../LoserRoundScheduleEditorView.swift | 1 + .../LoserRoundStepScheduleEditorView.swift | 1 + .../Views/Planning/MatchFormatGuideView.swift | 1 + .../Planning/MatchScheduleEditorView.swift | 1 + .../Views/Planning/PlanningByCourtView.swift | 1 + .../Views/Planning/PlanningSettingsView.swift | 1 + PadelClub/Views/Planning/PlanningView.swift | 1 + .../Planning/RoundScheduleEditorView.swift | 1 + PadelClub/Views/Planning/SchedulerView.swift | 1 + .../Components/EditablePlayerView.swift | 1 + .../Player/Components/PlayerPayView.swift | 1 + .../Player/Components/PlayerPopoverView.swift | 1 + .../Components/PlayerSexPickerView.swift | 1 + PadelClub/Views/Player/PlayerDetailView.swift | 1 + .../Views/Player/PlayerStatisticView.swift | 1 + PadelClub/Views/Player/PlayerView.swift | 1 + PadelClub/Views/Round/DrawLogsView.swift | 1 + .../Views/Round/LoserRoundSettingsView.swift | 1 + PadelClub/Views/Round/LoserRoundView.swift | 1 + PadelClub/Views/Round/LoserRoundsView.swift | 1 + .../Round/PreviewBracketPositionView.swift | 1 + PadelClub/Views/Round/RoundSettingsView.swift | 1 + PadelClub/Views/Round/RoundView.swift | 1 + PadelClub/Views/Round/RoundsView.swift | 1 + PadelClub/Views/Score/EditScoreView.swift | 1 + PadelClub/Views/Score/FollowUpMatchView.swift | 1 + .../Views/Score/PointSelectionView.swift | 1 + PadelClub/Views/Score/PointView.swift | 1 + PadelClub/Views/Score/SetInputView.swift | 1 + PadelClub/Views/Score/SetLabelView.swift | 1 + PadelClub/Views/Shared/DateMenuView.swift | 1 + .../Views/Shared/ImportedPlayerView.swift | 1 + .../Views/Shared/InscriptionLegendView.swift | 1 + .../Views/Shared/LearnMoreSheetView.swift | 1 + .../Views/Shared/MatchFormatRowView.swift | 1 + .../Shared/MatchFormatSelectionView.swift | 1 + .../Views/Shared/MatchTypeSelectionView.swift | 1 + .../OnlineWaitingListFaqSheetView.swift | 1 + .../Views/Shared/PaymentInfoSheetView.swift | 1 + .../Shared/RegistrationInfoSheetView.swift | 1 + .../Shared/SelectablePlayerListView.swift | 26 + .../Views/Shared/SupportButtonView.swift | 3 +- .../Views/Shared/TournamentFilterView.swift | 1 + PadelClub/Views/Team/CoachListView.swift | 1 + .../Team/Components/TeamHeaderView.swift | 1 + .../Team/Components/TeamWeightView.swift | 1 + PadelClub/Views/Team/EditingTeamView.swift | 1 + PadelClub/Views/Team/TeamDetailView.swift | 1 + PadelClub/Views/Team/TeamPickerView.swift | 3 +- PadelClub/Views/Team/TeamRestingView.swift | 1 + PadelClub/Views/Team/TeamRowView.swift | 1 + .../ConsolationTournamentImportView.swift | 1 + .../Views/Tournament/FileImportView.swift | 1 + .../Views/Tournament/Screen/AddTeamView.swift | 1 + .../Tournament/Screen/BroadcastView.swift | 1 + .../Screen/Components/CloseDatePicker.swift | 1 + .../Components/EventClubSettingsView.swift | 1 + .../Components/InscriptionInfoView.swift | 1 + .../Screen/Components/RefundResultsView.swift | 1 + .../TournamentCategorySettingsView.swift | 1 + .../TournamentClubSettingsView.swift | 1 + .../Components/TournamentDatePickerView.swift | 1 + .../TournamentDurationManagerView.swift | 1 + .../TournamentFieldsManagerView.swift | 1 + .../TournamentFormatSelectionView.swift | 1 + .../TournamentGeneralSettingsView.swift | 1 + .../TournamentLevelPickerView.swift | 1 + .../TournamentMatchFormatsSettingsView.swift | 1 + .../Components/TournamentStatusView.swift | 1 + .../Components/UpdateSourceRankDateView.swift | 1 + .../Screen/InscriptionManagerView.swift | 1 + .../Tournament/Screen/PrintSettingsView.swift | 1 + .../Screen/RegistrationSetupView.swift | 54 +- .../Screen/TableStructureView.swift | 1 + .../Screen/TournamentCallView.swift | 1 + .../Screen/TournamentCashierView.swift | 1 + .../Screen/TournamentRankView.swift | 1 + .../Screen/TournamentScheduleView.swift | 1 + .../Screen/TournamentSettingsView.swift | 1 + .../Shared/TournamentBroadcastRowView.swift | 1 + .../Shared/TournamentCellView.swift | 1 + .../Views/Tournament/Subscription/Guard.swift | 336 --- .../Subscription/PaymentStatusView.swift | 1 + .../Tournament/Subscription/Purchase.swift | 95 - .../Subscription/PurchaseListView.swift | 1 + .../Tournament/Subscription/StoreItem.swift | 38 - .../Subscription/StoreManager.swift | 140 - .../Subscription/SubscriptionInfoView.swift | 1 + .../Subscription/SubscriptionView.swift | 1 + .../Tournament/TournamentBuildView.swift | 1 + .../Views/Tournament/TournamentInitView.swift | 1 + .../TournamentInscriptionView.swift | 1 + .../Tournament/TournamentRunningView.swift | 1 + .../Views/Tournament/TournamentView.swift | 1 + PadelClub/Views/User/AccountView.swift | 1 + PadelClub/Views/User/ChangePasswordView.swift | 1 + PadelClub/Views/User/LoginView.swift | 2 + PadelClub/Views/User/ShareModelView.swift | 1 + PadelClub/Views/User/UserCreationView.swift | 1 + 285 files changed, 1453 insertions(+), 20353 deletions(-) delete mode 100644 PadelClub/Data/AppSettings.swift delete mode 100644 PadelClub/Data/Club.swift delete mode 100644 PadelClub/Data/Court.swift delete mode 100644 PadelClub/Data/CustomUser.swift delete mode 100644 PadelClub/Data/DataStore.swift delete mode 100644 PadelClub/Data/DateInterval.swift delete mode 100644 PadelClub/Data/DrawLog.swift delete mode 100644 PadelClub/Data/Event.swift delete mode 100644 PadelClub/Data/Gen/BaseClub.swift delete mode 100644 PadelClub/Data/Gen/BaseCourt.swift delete mode 100644 PadelClub/Data/Gen/BaseCustomUser.swift delete mode 100644 PadelClub/Data/Gen/BaseDateInterval.swift delete mode 100644 PadelClub/Data/Gen/BaseDrawLog.swift delete mode 100644 PadelClub/Data/Gen/BaseEvent.swift delete mode 100644 PadelClub/Data/Gen/BaseGroupStage.swift delete mode 100644 PadelClub/Data/Gen/BaseMatch.swift delete mode 100644 PadelClub/Data/Gen/BaseMatchScheduler.swift delete mode 100644 PadelClub/Data/Gen/BaseMonthData.swift delete mode 100644 PadelClub/Data/Gen/BasePlayerRegistration.swift delete mode 100644 PadelClub/Data/Gen/BasePurchase.swift delete mode 100644 PadelClub/Data/Gen/BaseRound.swift delete mode 100644 PadelClub/Data/Gen/BaseTeamRegistration.swift delete mode 100644 PadelClub/Data/Gen/BaseTeamScore.swift delete mode 100644 PadelClub/Data/Gen/BaseTournament.swift delete mode 100644 PadelClub/Data/Gen/Club.json delete mode 100644 PadelClub/Data/Gen/Court.json delete mode 100644 PadelClub/Data/Gen/CustomUser.json delete mode 100644 PadelClub/Data/Gen/DateInterval.json delete mode 100644 PadelClub/Data/Gen/Drawlog.json delete mode 100644 PadelClub/Data/Gen/Event.json delete mode 100644 PadelClub/Data/Gen/GroupStage.json delete mode 100644 PadelClub/Data/Gen/Match.json delete mode 100644 PadelClub/Data/Gen/MatchScheduler.json delete mode 100644 PadelClub/Data/Gen/MonthData.json delete mode 100644 PadelClub/Data/Gen/PlayerRegistration.json delete mode 100644 PadelClub/Data/Gen/Purchase.json delete mode 100644 PadelClub/Data/Gen/Round.json delete mode 100644 PadelClub/Data/Gen/TeamRegistration.json delete mode 100644 PadelClub/Data/Gen/TeamScore.json delete mode 100644 PadelClub/Data/Gen/Tournament.json delete mode 100644 PadelClub/Data/Gen/generator.py delete mode 100644 PadelClub/Data/GroupStage.swift delete mode 100644 PadelClub/Data/Match.swift delete mode 100644 PadelClub/Data/MatchScheduler.swift delete mode 100644 PadelClub/Data/MockData.swift delete mode 100644 PadelClub/Data/PlayerPaymentType.swift delete mode 100644 PadelClub/Data/PlayerRegistration.swift delete mode 100644 PadelClub/Data/README.md delete mode 100644 PadelClub/Data/Round.swift delete mode 100644 PadelClub/Data/TeamRegistration.swift delete mode 100644 PadelClub/Data/TeamScore.swift delete mode 100644 PadelClub/Data/Tournament.swift delete mode 100644 PadelClub/Data/TournamentLibrary.swift delete mode 100644 PadelClub/Data/TournamentStore.swift delete mode 100644 PadelClub/Extensions/Array+Extensions.swift create mode 100644 PadelClub/Extensions/Badge+Extensions.swift delete mode 100644 PadelClub/Extensions/Calendar+Extensions.swift delete mode 100644 PadelClub/Extensions/CodingContainer+Extensions.swift create mode 100644 PadelClub/Extensions/CustomUser+Extensions.swift delete mode 100644 PadelClub/Extensions/Date+Extensions.swift delete mode 100644 PadelClub/Extensions/FixedWidthInteger+Extensions.swift delete mode 100644 PadelClub/Extensions/Locale+Extensions.swift rename PadelClub/{Data/MonthData.swift => Extensions/MonthData+Extensions.swift} (78%) delete mode 100644 PadelClub/Extensions/MySortDescriptor.swift delete mode 100644 PadelClub/Extensions/NumberFormatter+Extensions.swift create mode 100644 PadelClub/Extensions/PlayerRegistration+Extensions.swift create mode 100644 PadelClub/Extensions/Round+Extensions.swift delete mode 100644 PadelClub/Extensions/Sequence+Extensions.swift create mode 100644 PadelClub/Extensions/SourceFileManager+Extensions.swift create mode 100644 PadelClub/Extensions/SpinDrawable+Extensions.swift delete mode 100644 PadelClub/Extensions/String+Crypto.swift delete mode 100644 PadelClub/Extensions/String+Extensions.swift create mode 100644 PadelClub/Extensions/TeamRegistration+Extensions.swift create mode 100644 PadelClub/Extensions/Tournament+Extensions.swift delete mode 100644 PadelClub/Extensions/URL+Extensions.swift delete mode 100644 PadelClub/Utils/ContactManager.swift delete mode 100644 PadelClub/Utils/CryptoKey.swift delete mode 100644 PadelClub/Utils/DisplayContext.swift delete mode 100644 PadelClub/Utils/ExportFormat.swift delete mode 100644 PadelClub/Utils/Network/NetworkManagerError.swift delete mode 100644 PadelClub/Utils/PListReader.swift delete mode 100644 PadelClub/Utils/PadelRule.swift delete mode 100644 PadelClub/Utils/Patcher.swift delete mode 100644 PadelClub/Utils/SourceFileManager.swift delete mode 100644 PadelClub/Utils/URLs.swift delete mode 100644 PadelClub/ViewModel/MatchDescriptor.swift delete mode 100644 PadelClub/ViewModel/MatchSpot.swift delete mode 100644 PadelClub/ViewModel/Screen.swift delete mode 100644 PadelClub/ViewModel/SeedInterval.swift delete mode 100644 PadelClub/ViewModel/Selectable.swift delete mode 100644 PadelClub/ViewModel/SetDescriptor.swift create mode 100644 PadelClub/Views/Components/ComposeViews.swift delete mode 100644 PadelClub/Views/Tournament/Subscription/Guard.swift delete mode 100644 PadelClub/Views/Tournament/Subscription/Purchase.swift delete mode 100644 PadelClub/Views/Tournament/Subscription/StoreItem.swift delete mode 100644 PadelClub/Views/Tournament/Subscription/StoreManager.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 51f606e..c4ce7b7 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -21,155 +21,72 @@ 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 */; }; - C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; C4607A7D2C04DDE2004CB781 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */; }; - C471D1542D0C8FED0068091F /* Drawlog.json in Resources */ = {isa = PBXBuildFile; fileRef = C471D1532D0C8FE80068091F /* Drawlog.json */; }; - C471D1552D0C8FED0068091F /* Drawlog.json in Resources */ = {isa = PBXBuildFile; fileRef = C471D1532D0C8FE80068091F /* Drawlog.json */; }; - C471D1562D0C8FED0068091F /* Drawlog.json in Resources */ = {isa = PBXBuildFile; fileRef = C471D1532D0C8FE80068091F /* Drawlog.json */; }; - C471D1582D0C91FE0068091F /* BaseDrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471D1572D0C91FE0068091F /* BaseDrawLog.swift */; }; - C471D1592D0C91FE0068091F /* BaseDrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471D1572D0C91FE0068091F /* BaseDrawLog.swift */; }; - C471D15A2D0C91FF0068091F /* BaseDrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471D1572D0C91FE0068091F /* BaseDrawLog.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 */; }; 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 */; }; - C49C73142D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */; }; - C49C73152D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */; }; - C49C73162D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */; }; + C49770212DC25A23005CD239 /* PadelClubData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49770202DC25A23005CD239 /* PadelClubData.framework */; }; + C49770222DC25A24005CD239 /* PadelClubData.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C49770202DC25A23005CD239 /* PadelClubData.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C49771E12DC25F04005CD239 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DD2DC25F04005CD239 /* Color+Extensions.swift */; }; + C49771E22DC25F04005CD239 /* Badge+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DE2DC25F04005CD239 /* Badge+Extensions.swift */; }; + C49771E32DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DF2DC25F04005CD239 /* SpinDrawable+Extensions.swift */; }; + C49771E42DC25F04005CD239 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DD2DC25F04005CD239 /* Color+Extensions.swift */; }; + C49771E52DC25F04005CD239 /* Badge+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DE2DC25F04005CD239 /* Badge+Extensions.swift */; }; + C49771E62DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DF2DC25F04005CD239 /* SpinDrawable+Extensions.swift */; }; + C49771E72DC25F04005CD239 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DD2DC25F04005CD239 /* Color+Extensions.swift */; }; + C49771E82DC25F04005CD239 /* Badge+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DE2DC25F04005CD239 /* Badge+Extensions.swift */; }; + C49771E92DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771DF2DC25F04005CD239 /* SpinDrawable+Extensions.swift */; }; + C49771EB2DC25F8D005CD239 /* SourceFileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EA2DC25F8D005CD239 /* SourceFileManager+Extensions.swift */; }; + C49771EC2DC25F8D005CD239 /* SourceFileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EA2DC25F8D005CD239 /* SourceFileManager+Extensions.swift */; }; + C49771ED2DC25F8D005CD239 /* SourceFileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EA2DC25F8D005CD239 /* SourceFileManager+Extensions.swift */; }; + C49771F32DC25F93005CD239 /* CustomUser+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EE2DC25F93005CD239 /* CustomUser+Extensions.swift */; }; + C49771F42DC25F93005CD239 /* PlayerRegistration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EF2DC25F93005CD239 /* PlayerRegistration+Extensions.swift */; }; + C49771F52DC25F93005CD239 /* TeamRegistration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F02DC25F93005CD239 /* TeamRegistration+Extensions.swift */; }; + C49771F62DC25F93005CD239 /* Tournament+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F12DC25F93005CD239 /* Tournament+Extensions.swift */; }; + C49771F72DC25F93005CD239 /* MonthData+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F22DC25F93005CD239 /* MonthData+Extensions.swift */; }; + C49771F82DC25F93005CD239 /* CustomUser+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EE2DC25F93005CD239 /* CustomUser+Extensions.swift */; }; + C49771F92DC25F93005CD239 /* PlayerRegistration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EF2DC25F93005CD239 /* PlayerRegistration+Extensions.swift */; }; + C49771FA2DC25F93005CD239 /* TeamRegistration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F02DC25F93005CD239 /* TeamRegistration+Extensions.swift */; }; + C49771FB2DC25F93005CD239 /* Tournament+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F12DC25F93005CD239 /* Tournament+Extensions.swift */; }; + C49771FC2DC25F93005CD239 /* MonthData+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F22DC25F93005CD239 /* MonthData+Extensions.swift */; }; + C49771FD2DC25F93005CD239 /* CustomUser+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EE2DC25F93005CD239 /* CustomUser+Extensions.swift */; }; + C49771FE2DC25F93005CD239 /* PlayerRegistration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771EF2DC25F93005CD239 /* PlayerRegistration+Extensions.swift */; }; + C49771FF2DC25F93005CD239 /* TeamRegistration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F02DC25F93005CD239 /* TeamRegistration+Extensions.swift */; }; + C49772002DC25F93005CD239 /* Tournament+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F12DC25F93005CD239 /* Tournament+Extensions.swift */; }; + C49772012DC25F93005CD239 /* MonthData+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49771F22DC25F93005CD239 /* MonthData+Extensions.swift */; }; + C49772032DC260D3005CD239 /* Round+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49772022DC260D3005CD239 /* Round+Extensions.swift */; }; + C49772042DC260D3005CD239 /* Round+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49772022DC260D3005CD239 /* Round+Extensions.swift */; }; + C49772052DC260D3005CD239 /* Round+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49772022DC260D3005CD239 /* Round+Extensions.swift */; }; + C49772392DC28A92005CD239 /* ComposeViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49772382DC28A92005CD239 /* ComposeViews.swift */; }; + C497723A2DC28A92005CD239 /* ComposeViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49772382DC28A92005CD239 /* ComposeViews.swift */; }; + C497723B2DC28A92005CD239 /* ComposeViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49772382DC28A92005CD239 /* ComposeViews.swift */; }; + C49774BB2DC37C10005CD239 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49774BA2DC37C10005CD239 /* NetworkMonitor.swift */; }; + C49774BC2DC37C10005CD239 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49774BA2DC37C10005CD239 /* NetworkMonitor.swift */; }; + C49774BD2DC37C10005CD239 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49774BA2DC37C10005CD239 /* NetworkMonitor.swift */; }; C49C731E2D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; }; C49C731F2D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; }; C49C73202D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; }; C49EF0192BD694290077B5AA /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; - C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; C49EF0262BD80AE80077B5AA /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */; }; C49EF0392BDFF4600077B5AA /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; }; C49EF03A2BDFF4600077B5AA /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 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 */; }; C4A47D872B7BA36D00ADC637 /* UserCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D862B7BA36D00ADC637 /* UserCreationView.swift */; }; C4A47D8A2B7BBB6500ADC637 /* SubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D892B7BBB6500ADC637 /* SubscriptionView.swift */; }; - C4A47D902B7BBBEC00ADC637 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */; }; - C4A47D912B7BBBEC00ADC637 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */; }; - C4A47D922B7BBBEC00ADC637 /* StoreItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */; }; 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 /* 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 */; }; C4D05D472DC10AE5009B053C /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4D05D462DC10AE5009B053C /* WebKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; C4D05D492DC10CBE009B053C /* PaymentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D05D482DC10CBE009B053C /* PaymentStatusView.swift */; }; C4D05D4A2DC10CBE009B053C /* PaymentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D05D482DC10CBE009B053C /* PaymentStatusView.swift */; }; C4D05D4B2DC10CBE009B053C /* PaymentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D05D482DC10CBE009B053C /* PaymentStatusView.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 */; }; - C4FC2E2B2C2C0E4D0021F3BF /* TournamentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */; }; FF025AD82BD0C10F00A86CF8 /* TeamHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */; }; FF025ADB2BD0C2D000A86CF8 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; }; FF025ADD2BD0C94300A86CF8 /* FooterButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */; }; @@ -178,7 +95,6 @@ FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; }; FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; }; FF025AE72BD1111000A86CF8 /* GlobalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */; }; - FF025AE92BD1307F00A86CF8 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE82BD1307E00A86CF8 /* MonthData.swift */; }; FF025AED2BD1513700A86CF8 /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEC2BD1513700A86CF8 /* AppScreen.swift */; }; FF025AEF2BD1AE9400A86CF8 /* DurationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */; }; FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; }; @@ -212,16 +128,13 @@ FF17CA582CC02FEB003C7323 /* CoachListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA562CC02FEA003C7323 /* CoachListView.swift */; }; FF17CA592CC02FEB003C7323 /* CoachListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA562CC02FEA003C7323 /* CoachListView.swift */; }; FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; }; - FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; }; FF1CBC1F2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; }; FF1CBC222BB53E590036DAAB /* FederalTournamentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC212BB53E590036DAAB /* FederalTournamentHolder.swift */; }; FF1CBC232BB53E590036DAAB /* ClubHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC202BB53E590036DAAB /* ClubHolder.swift */; }; FF1DC5512BAB351300FD8220 /* ClubDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5502BAB351300FD8220 /* ClubDetailView.swift */; }; - FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; }; FF1DC5552BAB36DD00FD8220 /* CreateClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5542BAB36DD00FD8220 /* CreateClubView.swift */; }; FF1DC5572BAB3AED00FD8220 /* ClubsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5562BAB3AED00FD8220 /* ClubsView.swift */; }; FF1DC5592BAB767000FD8220 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5582BAB767000FD8220 /* Tips.swift */; }; - FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */; }; FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; }; FF1F4B6D2BF9E60B000B4573 /* TournamentBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B6C2BF9E60B000B4573 /* TournamentBuildView.swift */; }; FF1F4B712BF9EFE9000B4573 /* TournamentInscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B702BF9EFE9000B4573 /* TournamentInscriptionView.swift */; }; @@ -250,7 +163,6 @@ FF3A74322D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; FF3A74332D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; FF3A74342D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; - FF3B60A32BC49BBC008C2E66 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; }; FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; }; FF44421C2BE39FA2008BBF0B /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */; }; @@ -265,11 +177,9 @@ FF4CBF442C996C0600151637 /* UserCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D862B7BA36D00ADC637 /* UserCreationView.swift */; }; FF4CBF452C996C0600151637 /* TabDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091652B90F0B000AB08DA /* TabDestination.swift */; }; FF4CBF462C996C0600151637 /* CashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F72BCE78C70080F940 /* CashierView.swift */; }; - FF4CBF472C996C0600151637 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263E2BAD7D5C00650388 /* Event.swift */; }; FF4CBF482C996C0600151637 /* PlayerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */; }; FF4CBF492C996C0600151637 /* LoserRoundStepScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */; }; FF4CBF4A2C996C0600151637 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; }; - FF4CBF4B2C996C0600151637 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; }; FF4CBF4C2C996C0600151637 /* FileImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */; }; FF4CBF4D2C996C0600151637 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; }; FF4CBF4E2C996C0600151637 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; }; @@ -283,7 +193,6 @@ FF4CBF562C996C0600151637 /* MatchSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D052BAF3C4200A9A3BD /* MatchSetupView.swift */; }; FF4CBF572C996C0600151637 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6B42B9248200002987F /* NetworkManager.swift */; }; FF4CBF582C996C0600151637 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; }; - FF4CBF592C996C0600151637 /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; FF4CBF5A2C996C0600151637 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; }; FF4CBF5B2C996C0600151637 /* GroupStageTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */; }; FF4CBF5C2C996C0600151637 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; }; @@ -292,22 +201,17 @@ FF4CBF5F2C996C0600151637 /* TeamWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */; }; FF4CBF602C996C0600151637 /* SeedsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268002BCE94920080F940 /* SeedsCallingView.swift */; }; FF4CBF612C996C0600151637 /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268082BCEDC2C0080F940 /* CallView.swift */; }; - FF4CBF622C996C0600151637 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */; }; FF4CBF632C996C0600151637 /* EditSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE1030F2C366DCD00684FC9 /* EditSharingView.swift */; }; FF4CBF642C996C0600151637 /* TournamentCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091672B90F79F00AB08DA /* TournamentCellView.swift */; }; - FF4CBF652C996C0600151637 /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */; }; FF4CBF662C996C0600151637 /* CashierDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F92BCE78EB0080F940 /* CashierDetailView.swift */; }; FF4CBF672C996C0600151637 /* EventClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103072C353B7600684FC9 /* EventClubSettingsView.swift */; }; FF4CBF682C996C0600151637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; }; FF4CBF692C996C0600151637 /* PlayersWithoutContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCEDA4B2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift */; }; FF4CBF6A2C996C0600151637 /* TeamsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCD16B22C3E5E590092707B /* TeamsCallingView.swift */; }; - FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; }; - FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */; }; FF4CBF6D2C996C0600151637 /* EditablePlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162822BCFBE4E000C4809 /* EditablePlayerView.swift */; }; FF4CBF6E2C996C0600151637 /* PlayerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162842BD00279000C4809 /* PlayerDetailView.swift */; }; FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */; }; FF4CBF702C996C0600151637 /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */; }; - FF4CBF712C996C0600151637 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; }; FF4CBF722C996C0600151637 /* SwiftParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */; }; FF4CBF732C996C0600151637 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; }; FF4CBF742C996C0600151637 /* TournamentSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */; }; @@ -331,40 +235,28 @@ FF4CBF862C996C0600151637 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.swift */; }; FF4CBF872C996C0600151637 /* PointSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */; }; FF4CBF882C996C0600151637 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; }; - FF4CBF892C996C0600151637 /* ContactManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680A2BCEE3E10080F940 /* ContactManager.swift */; }; FF4CBF8A2C996C0600151637 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD2F22C412681000DBD9A /* AppDelegate.swift */; }; FF4CBF8B2C996C0600151637 /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */; }; FF4CBF8C2C996C0600151637 /* EditScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0012BBC39A600B82851 /* EditScoreView.swift */; }; FF4CBF8D2C996C0600151637 /* TournamentOrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091612B90F04300AB08DA /* TournamentOrganizerView.swift */; }; - FF4CBF8E2C996C0600151637 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */; }; FF4CBF8F2C996C0600151637 /* TournamentRunningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF52BAED51600A9A3BD /* TournamentRunningView.swift */; }; FF4CBF902C996C0600151637 /* TournamentDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */; }; FF4CBF912C996C0600151637 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; }; FF4CBF922C996C0600151637 /* ConfirmButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE8C2BF2C7601E80046B243 /* ConfirmButtonView.swift */; }; FF4CBF932C996C0600151637 /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; }; FF4CBF942C996C0600151637 /* MatchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFF2BAEEF6400A9A3BD /* MatchRowView.swift */; }; - FF4CBF952C996C0600151637 /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B79102BBDA63A00906534 /* Locale+Extensions.swift */; }; FF4CBF962C996C0600151637 /* HtmlService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B732BFA00FC000B4573 /* HtmlService.swift */; }; - FF4CBF972C996C0600151637 /* GroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CE72BAEC70100A9A3BD /* GroupStage.swift */; }; FF4CBF982C996C0600151637 /* TournamentCashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162802BCF945C000C4809 /* TournamentCashierView.swift */; }; - FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */; }; FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */; }; - FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */; }; FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; }; FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162862BD004AD000C4809 /* EditingTeamView.swift */; }; - FF4CBF9E2C996C0600151637 /* NetworkManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */; }; - FF4CBF9F2C996C0600151637 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; }; - FF4CBFA02C996C0600151637 /* TournamentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */; }; FF4CBFA12C996C0600151637 /* LoserRoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */; }; FF4CBFA22C996C0600151637 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; }; FF4CBFA32C996C0600151637 /* CloseDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */; }; FF4CBFA42C996C0600151637 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; }; FF4CBFA52C996C0600151637 /* PlanningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9644F2BC25E3700EEF017 /* PlanningView.swift */; }; - FF4CBFA62C996C0600151637 /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEB2BAECB9900A9A3BD /* Match.swift */; }; FF4CBFA72C996C0600151637 /* TournamentLevelPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */; }; FF4CBFA82C996C0600151637 /* FederalTournamentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC212BB53E590036DAAB /* FederalTournamentHolder.swift */; }; - FF4CBFA92C996C0600151637 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; }; - FF4CBFAA2C996C0600151637 /* SetDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */; }; FF4CBFAB2C996C0600151637 /* TeamHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */; }; FF4CBFAC2C996C0600151637 /* OrganizedTournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */; }; FF4CBFAD2C996C0600151637 /* RoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964562BC26B3400EEF017 /* RoundScheduleEditorView.swift */; }; @@ -372,16 +264,11 @@ FF4CBFAF2C996C0600151637 /* TournamentConfiguratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */; }; FF4CBFB02C996C0600151637 /* ClubImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */; }; FF4CBFB12C996C0600151637 /* UnderlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF558C622C6CDD020071F9AE /* UnderlineView.swift */; }; - FF4CBFB22C996C0600151637 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; }; FF4CBFB32C996C0600151637 /* CallMessageCustomizationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */; }; FF4CBFB42C996C0600151637 /* TournamentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087E92BE25EF1004E1E47 /* TournamentStatusView.swift */; }; FF4CBFB52C996C0600151637 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; }; FF4CBFB62C996C0600151637 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; }; - FF4CBFB72C996C0600151637 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; }; FF4CBFB82C996C0600151637 /* TableStructureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26532BAE1E4400650388 /* TableStructureView.swift */; }; - FF4CBFB92C996C0600151637 /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; - FF4CBFBA2C996C0600151637 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; }; - FF4CBFBB2C996C0600151637 /* Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CED2BAECBD700A9A3BD /* Round.swift */; }; FF4CBFBC2C996C0600151637 /* FederalDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */; }; FF4CBFBD2C996C0600151637 /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; }; FF4CBFBE2C996C0600151637 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; @@ -390,14 +277,10 @@ FF4CBFC12C996C0600151637 /* ClubRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D882BB4935C005CB568 /* ClubRowView.swift */; }; FF4CBFC22C996C0600151637 /* ClubDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5502BAB351300FD8220 /* ClubDetailView.swift */; }; FF4CBFC32C996C0600151637 /* GroupStageCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */; }; - FF4CBFC42C996C0600151637 /* CryptoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0432BE286780077B5AA /* CryptoKey.swift */; }; FF4CBFC52C996C0600151637 /* CashierSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11627C2BCF941A000C4809 /* CashierSettingsView.swift */; }; FF4CBFC62C996C0600151637 /* LoserRoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFCDE0D2BCC833600317DEF /* LoserRoundScheduleEditorView.swift */; }; - FF4CBFC72C996C0600151637 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; }; - FF4CBFC82C996C0600151637 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */; }; FF4CBFC92C996C0600151637 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; }; FF4CBFCA2C996C0600151637 /* Alphabet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8E1CE52C006E0200184680 /* Alphabet.swift */; }; - FF4CBFCB2C996C0600151637 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD82B923F3C008466FA /* String+Extensions.swift */; }; FF4CBFCC2C996C0600151637 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */; }; FF4CBFCD2C996C0600151637 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; }; FF4CBFCE2C996C0600151637 /* LoserRoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */; }; @@ -408,14 +291,12 @@ FF4CBFD32C996C0600151637 /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; }; FF4CBFD42C996C0600151637 /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; }; FF4CBFD52C996C0600151637 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */; }; - FF4CBFD62C996C0600151637 /* FixedWidthInteger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */; }; FF4CBFD72C996C0600151637 /* LoserBracketFromGroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */; }; FF4CBFD82C996C0600151637 /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; }; FF4CBFD92C996C0600151637 /* ClubSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */; }; FF4CBFDA2C996C0600151637 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; }; FF4CBFDB2C996C0600151637 /* InscriptionManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */; }; FF4CBFDC2C996C0600151637 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; }; - FF4CBFDD2C996C0600151637 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */; }; FF4CBFDE2C996C0600151637 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */; }; FF4CBFDF2C996C0600151637 /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; }; FF4CBFE02C996C0600151637 /* TournamentFieldsManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */; }; @@ -423,40 +304,30 @@ FF4CBFE22C996C0600151637 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; }; FF4CBFE32C996C0600151637 /* DatePickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162892BD05247000C4809 /* DatePickingView.swift */; }; FF4CBFE42C996C0600151637 /* MatchFormatRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0192BBC5A8500B82851 /* MatchFormatRowView.swift */; }; - FF4CBFE52C996C0600151637 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE82BD1307E00A86CF8 /* MonthData.swift */; }; FF4CBFE62C996C0600151637 /* MenuWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */; }; FF4CBFE72C996C0600151637 /* TournamentBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B6C2BF9E60B000B4573 /* TournamentBuildView.swift */; }; FF4CBFE82C996C0600151637 /* TeamPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0A2BAF3D4C00A9A3BD /* TeamPickerView.swift */; }; FF4CBFEA2C996C0600151637 /* EventTournamentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */; }; - FF4CBFEB2C996C0600151637 /* DisplayContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */; }; FF4CBFEC2C996C0600151637 /* TournamentCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268062BCE94D90080F940 /* TournamentCallView.swift */; }; FF4CBFED2C996C0600151637 /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; }; FF4CBFEE2C996C0600151637 /* GroupStagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */; }; FF4CBFEF2C996C0600151637 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; }; - FF4CBFF02C996C0600151637 /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; - FF4CBFF12C996C0600151637 /* MatchDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */; }; FF4CBFF22C996C0600151637 /* TournamentFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */; }; FF4CBFF32C996C0600151637 /* MatchListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065D2BBD8040009D6715 /* MatchListView.swift */; }; FF4CBFF42C996C0600151637 /* PadelClubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4002B6D249D002A7B48 /* PadelClubApp.swift */; }; FF4CBFF52C996C0600151637 /* TournamentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */; }; - FF4CBFF62C996C0600151637 /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */; }; FF4CBFF72C996C0600151637 /* GroupStageTeamReplacementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9AC3942BE3627B00C2E883 /* GroupStageTeamReplacementView.swift */; }; FF4CBFF82C996C0600151637 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; }; FF4CBFF92C996C0600151637 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; }; 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 /* 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 */; }; FF4CC0012C996C0600151637 /* TeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D082BAF3D4000A9A3BD /* TeamDetailView.swift */; }; FF4CC0022C996C0600151637 /* GroupStagesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */; }; FF4CC0032C996C0600151637 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; }; FF4CC0042C996C0600151637 /* HtmlGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B722BFA00FB000B4573 /* HtmlGenerator.swift */; }; - FF4CC0052C996C0600151637 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26352BAD523300650388 /* PadelRule.swift */; }; - FF4CC0062C996C0600151637 /* TeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */; }; - FF4CC0072C996C0600151637 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */; }; FF4CC0082C996C0600151637 /* GroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFA2BAEE13800A9A3BD /* GroupStageView.swift */; }; FF4CC0092C996C0600151637 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5582BAB767000FD8220 /* Tips.swift */; }; FF4CC00A2C996C0600151637 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; }; @@ -464,14 +335,10 @@ FF4CC00C2C996C0600151637 /* PlanningSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964522BC262B000EEF017 /* PlanningSettingsView.swift */; }; FF4CC00D2C996C0600151637 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; }; FF4CC00E2C996C0600151637 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; }; - FF4CC00F2C996C0600151637 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD52B923960008466FA /* URL+Extensions.swift */; }; FF4CC0102C996C0600151637 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; }; FF4CC0112C996C0600151637 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */; }; FF4CC0122C996C0600151637 /* MatchDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D022BAEF0C000A9A3BD /* MatchDetailView.swift */; }; - FF4CC0132C996C0600151637 /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */; }; FF4CC0142C996C0600151637 /* PlayerBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0E2BAF63B000A9A3BD /* PlayerBlockView.swift */; }; - FF4CC0152C996C0600151637 /* StoreItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */; }; - FF4CC0162C996C0600151637 /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; FF4CC0172C996C0600151637 /* PointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0112BBC3E1A00B82851 /* PointView.swift */; }; FF4CC0182C996C0600151637 /* ClubHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC202BB53E590036DAAB /* ClubHolder.swift */; }; FF4CC0192C996C0600151637 /* EventSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */; }; @@ -479,13 +346,11 @@ FF4CC01B2C996C0600151637 /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; }; FF4CC01C2C996C0600151637 /* MatchFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26502BAE0BAD00650388 /* MatchFormatSelectionView.swift */; }; FF4CC01D2C996C0600151637 /* TournamentRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF712BE19274008B4B7E /* TournamentRankView.swift */; }; - FF4CC01E2C996C0600151637 /* NumberFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */; }; FF4CC01F2C996C0600151637 /* SetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */; }; FF4CC0202C996C0600151637 /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */; }; FF4CC0212C996C0600151637 /* GroupStageScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */; }; FF4CC0222C996C0600151637 /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41812BF73EB3001B24CB /* EventView.swift */; }; FF4CC0232C996C0600151637 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; }; - FF4CC0242C996C0600151637 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; }; FF4CC0252C996C0600151637 /* Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF72BAEDF0000A9A3BD /* Labels.swift */; }; FF4CC0262C996C0600151637 /* CopyPasteButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */; }; FF4CC0272C996C0600151637 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; }; @@ -495,11 +360,8 @@ FF4CC02B2C996C0600151637 /* RankCalculatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */; }; FF4CC02C2C996C0600151637 /* DateBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091692B90F95E00AB08DA /* DateBoxView.swift */; }; FF4CC02D2C996C0600151637 /* LearnMoreSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */; }; - FF4CC02E2C996C0600151637 /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; }; - FF4CC02F2C996C0600151637 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; }; FF4CC0302C996C0600151637 /* UpdateSourceRankDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */; }; FF4CC0312C996C0600151637 /* GlobalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */; }; - FF4CC0322C996C0600151637 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */; }; FF4CC0332C996C0600151637 /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; FF4CC0352C996C0600151637 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = FF4CBF3F2C996C0600151637 /* Algorithms */; }; FF4CC0362C996C0600151637 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = FF4CBF412C996C0600151637 /* Zip */; }; @@ -532,11 +394,9 @@ FF5BAF6E2BE0B3C8008B4B7E /* FederalDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */; }; FF5BAF722BE19274008B4B7E /* TournamentRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF712BE19274008B4B7E /* TournamentRankView.swift */; }; FF5D0D722BB3EFA5005CB568 /* LearnMoreSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */; }; - FF5D0D742BB41DF8005CB568 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */; }; FF5D0D762BB428B2005CB568 /* ListRowViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */; }; FF5D0D782BB42C5B005CB568 /* InscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D772BB42C5B005CB568 /* InscriptionInfoView.swift */; }; FF5D0D852BB48997005CB568 /* RankCalculatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */; }; - FF5D0D872BB48AFD005CB568 /* NumberFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */; }; FF5D0D892BB4935C005CB568 /* ClubRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D882BB4935C005CB568 /* ClubRowView.swift */; }; FF5D0D8B2BB4D1E3005CB568 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */; }; FF5D30512BD94E1000F2B93D /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; }; @@ -550,9 +410,6 @@ FF6087EC2BE26A2F004E1E47 /* BroadcastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087EB2BE26A2F004E1E47 /* BroadcastView.swift */; }; FF6525C32C8C61B400B9498E /* LoserBracketFromGroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */; }; FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; }; - FF6761532CC77D2100CC9BF2 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; }; - FF6761542CC77D2100CC9BF2 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; }; - FF6761552CC77D2100CC9BF2 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; }; FF6761572CC7803600CC9BF2 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; }; FF6761582CC7803600CC9BF2 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; }; FF6761592CC7803600CC9BF2 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; }; @@ -561,12 +418,7 @@ FF67615D2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF67615A2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift */; }; FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */; }; FF6EC8FB2B94788600EA7F5A /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; }; - FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; }; FF6EC9002B94794700EA7F5A /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */; }; - FF6EC9042B9479F500EA7F5A /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */; }; - FF6EC9062B947A1000EA7F5A /* NetworkManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */; }; - FF6EC9092B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */; }; - FF6EC90B2B947AC000EA7F5A /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */; }; FF7091622B90F04300AB08DA /* TournamentOrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091612B90F04300AB08DA /* TournamentOrganizerView.swift */; }; FF7091662B90F0B000AB08DA /* TabDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091652B90F0B000AB08DA /* TabDestination.swift */; }; FF7091682B90F79F00AB08DA /* TournamentCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091672B90F79F00AB08DA /* TournamentCellView.swift */; }; @@ -576,11 +428,9 @@ FF70FAC32C90584900129CC2 /* UserCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D862B7BA36D00ADC637 /* UserCreationView.swift */; }; FF70FAC42C90584900129CC2 /* TabDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091652B90F0B000AB08DA /* TabDestination.swift */; }; FF70FAC52C90584900129CC2 /* CashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F72BCE78C70080F940 /* CashierView.swift */; }; - FF70FAC62C90584900129CC2 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263E2BAD7D5C00650388 /* Event.swift */; }; FF70FAC72C90584900129CC2 /* PlayerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */; }; FF70FAC82C90584900129CC2 /* LoserRoundStepScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */; }; FF70FAC92C90584900129CC2 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; }; - FF70FACA2C90584900129CC2 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; }; FF70FACB2C90584900129CC2 /* FileImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */; }; FF70FACC2C90584900129CC2 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; }; FF70FACD2C90584900129CC2 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; }; @@ -594,7 +444,6 @@ FF70FAD52C90584900129CC2 /* MatchSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D052BAF3C4200A9A3BD /* MatchSetupView.swift */; }; FF70FAD62C90584900129CC2 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6B42B9248200002987F /* NetworkManager.swift */; }; FF70FAD72C90584900129CC2 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; }; - FF70FAD82C90584900129CC2 /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; FF70FAD92C90584900129CC2 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; }; FF70FADA2C90584900129CC2 /* GroupStageTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */; }; FF70FADB2C90584900129CC2 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; }; @@ -603,22 +452,17 @@ FF70FADE2C90584900129CC2 /* TeamWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */; }; FF70FADF2C90584900129CC2 /* SeedsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268002BCE94920080F940 /* SeedsCallingView.swift */; }; FF70FAE02C90584900129CC2 /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268082BCEDC2C0080F940 /* CallView.swift */; }; - FF70FAE12C90584900129CC2 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */; }; FF70FAE22C90584900129CC2 /* EditSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE1030F2C366DCD00684FC9 /* EditSharingView.swift */; }; FF70FAE32C90584900129CC2 /* TournamentCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091672B90F79F00AB08DA /* TournamentCellView.swift */; }; - FF70FAE42C90584900129CC2 /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */; }; FF70FAE52C90584900129CC2 /* CashierDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F92BCE78EB0080F940 /* CashierDetailView.swift */; }; FF70FAE62C90584900129CC2 /* EventClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103072C353B7600684FC9 /* EventClubSettingsView.swift */; }; FF70FAE72C90584900129CC2 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; }; FF70FAE82C90584900129CC2 /* PlayersWithoutContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCEDA4B2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift */; }; FF70FAE92C90584900129CC2 /* TeamsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCD16B22C3E5E590092707B /* TeamsCallingView.swift */; }; - FF70FAEA2C90584900129CC2 /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; }; - FF70FAEB2C90584900129CC2 /* TeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */; }; FF70FAEC2C90584900129CC2 /* EditablePlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162822BCFBE4E000C4809 /* EditablePlayerView.swift */; }; FF70FAED2C90584900129CC2 /* PlayerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162842BD00279000C4809 /* PlayerDetailView.swift */; }; FF70FAEE2C90584900129CC2 /* ListRowViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */; }; FF70FAEF2C90584900129CC2 /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */; }; - FF70FAF02C90584900129CC2 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; }; FF70FAF12C90584900129CC2 /* SwiftParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */; }; FF70FAF22C90584900129CC2 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; }; FF70FAF32C90584900129CC2 /* TournamentSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */; }; @@ -642,40 +486,28 @@ FF70FB052C90584900129CC2 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.swift */; }; FF70FB062C90584900129CC2 /* PointSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */; }; FF70FB072C90584900129CC2 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; }; - FF70FB082C90584900129CC2 /* ContactManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680A2BCEE3E10080F940 /* ContactManager.swift */; }; FF70FB092C90584900129CC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD2F22C412681000DBD9A /* AppDelegate.swift */; }; FF70FB0A2C90584900129CC2 /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */; }; FF70FB0B2C90584900129CC2 /* EditScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0012BBC39A600B82851 /* EditScoreView.swift */; }; FF70FB0C2C90584900129CC2 /* TournamentOrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091612B90F04300AB08DA /* TournamentOrganizerView.swift */; }; - FF70FB0D2C90584900129CC2 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */; }; FF70FB0E2C90584900129CC2 /* TournamentRunningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF52BAED51600A9A3BD /* TournamentRunningView.swift */; }; FF70FB0F2C90584900129CC2 /* TournamentDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */; }; FF70FB102C90584900129CC2 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; }; FF70FB112C90584900129CC2 /* ConfirmButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE8C2BF2C7601E80046B243 /* ConfirmButtonView.swift */; }; FF70FB122C90584900129CC2 /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; }; FF70FB132C90584900129CC2 /* MatchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFF2BAEEF6400A9A3BD /* MatchRowView.swift */; }; - FF70FB142C90584900129CC2 /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B79102BBDA63A00906534 /* Locale+Extensions.swift */; }; FF70FB152C90584900129CC2 /* HtmlService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B732BFA00FC000B4573 /* HtmlService.swift */; }; - FF70FB162C90584900129CC2 /* GroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CE72BAEC70100A9A3BD /* GroupStage.swift */; }; FF70FB172C90584900129CC2 /* TournamentCashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162802BCF945C000C4809 /* TournamentCashierView.swift */; }; - FF70FB182C90584900129CC2 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */; }; FF70FB192C90584900129CC2 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */; }; - FF70FB1A2C90584900129CC2 /* PlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */; }; FF70FB1B2C90584900129CC2 /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; }; FF70FB1C2C90584900129CC2 /* EditingTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162862BD004AD000C4809 /* EditingTeamView.swift */; }; - FF70FB1D2C90584900129CC2 /* NetworkManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */; }; - FF70FB1E2C90584900129CC2 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; }; - FF70FB1F2C90584900129CC2 /* TournamentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */; }; FF70FB202C90584900129CC2 /* LoserRoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */; }; FF70FB212C90584900129CC2 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; }; FF70FB222C90584900129CC2 /* CloseDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */; }; FF70FB232C90584900129CC2 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; }; FF70FB242C90584900129CC2 /* PlanningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9644F2BC25E3700EEF017 /* PlanningView.swift */; }; - FF70FB252C90584900129CC2 /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEB2BAECB9900A9A3BD /* Match.swift */; }; FF70FB262C90584900129CC2 /* TournamentLevelPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */; }; FF70FB272C90584900129CC2 /* FederalTournamentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC212BB53E590036DAAB /* FederalTournamentHolder.swift */; }; - FF70FB282C90584900129CC2 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; }; - FF70FB292C90584900129CC2 /* SetDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */; }; FF70FB2A2C90584900129CC2 /* TeamHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */; }; FF70FB2B2C90584900129CC2 /* OrganizedTournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */; }; FF70FB2C2C90584900129CC2 /* RoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964562BC26B3400EEF017 /* RoundScheduleEditorView.swift */; }; @@ -683,16 +515,11 @@ FF70FB2E2C90584900129CC2 /* TournamentConfiguratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */; }; FF70FB2F2C90584900129CC2 /* ClubImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */; }; FF70FB302C90584900129CC2 /* UnderlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF558C622C6CDD020071F9AE /* UnderlineView.swift */; }; - FF70FB312C90584900129CC2 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; }; FF70FB322C90584900129CC2 /* CallMessageCustomizationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */; }; FF70FB332C90584900129CC2 /* TournamentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087E92BE25EF1004E1E47 /* TournamentStatusView.swift */; }; FF70FB342C90584900129CC2 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; }; FF70FB352C90584900129CC2 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; }; - FF70FB362C90584900129CC2 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; }; FF70FB372C90584900129CC2 /* TableStructureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26532BAE1E4400650388 /* TableStructureView.swift */; }; - FF70FB382C90584900129CC2 /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; - FF70FB392C90584900129CC2 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; }; - FF70FB3A2C90584900129CC2 /* Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CED2BAECBD700A9A3BD /* Round.swift */; }; FF70FB3B2C90584900129CC2 /* FederalDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */; }; FF70FB3C2C90584900129CC2 /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; }; FF70FB3D2C90584900129CC2 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; @@ -701,14 +528,10 @@ FF70FB402C90584900129CC2 /* ClubRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D882BB4935C005CB568 /* ClubRowView.swift */; }; FF70FB412C90584900129CC2 /* ClubDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5502BAB351300FD8220 /* ClubDetailView.swift */; }; FF70FB422C90584900129CC2 /* GroupStageCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */; }; - FF70FB432C90584900129CC2 /* CryptoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0432BE286780077B5AA /* CryptoKey.swift */; }; FF70FB442C90584900129CC2 /* CashierSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11627C2BCF941A000C4809 /* CashierSettingsView.swift */; }; FF70FB452C90584900129CC2 /* LoserRoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFCDE0D2BCC833600317DEF /* LoserRoundScheduleEditorView.swift */; }; - FF70FB462C90584900129CC2 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; }; - FF70FB472C90584900129CC2 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */; }; FF70FB482C90584900129CC2 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; }; FF70FB492C90584900129CC2 /* Alphabet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8E1CE52C006E0200184680 /* Alphabet.swift */; }; - FF70FB4A2C90584900129CC2 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD82B923F3C008466FA /* String+Extensions.swift */; }; FF70FB4B2C90584900129CC2 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */; }; FF70FB4C2C90584900129CC2 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; }; FF70FB4D2C90584900129CC2 /* LoserRoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */; }; @@ -719,14 +542,12 @@ FF70FB522C90584900129CC2 /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; }; FF70FB532C90584900129CC2 /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; }; FF70FB542C90584900129CC2 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */; }; - FF70FB552C90584900129CC2 /* FixedWidthInteger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */; }; FF70FB562C90584900129CC2 /* LoserBracketFromGroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */; }; FF70FB572C90584900129CC2 /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; }; FF70FB582C90584900129CC2 /* ClubSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */; }; FF70FB592C90584900129CC2 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; }; FF70FB5A2C90584900129CC2 /* InscriptionManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */; }; FF70FB5B2C90584900129CC2 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; }; - FF70FB5C2C90584900129CC2 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */; }; FF70FB5D2C90584900129CC2 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */; }; FF70FB5E2C90584900129CC2 /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; }; FF70FB5F2C90584900129CC2 /* TournamentFieldsManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */; }; @@ -734,40 +555,30 @@ FF70FB612C90584900129CC2 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; }; FF70FB622C90584900129CC2 /* DatePickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162892BD05247000C4809 /* DatePickingView.swift */; }; FF70FB632C90584900129CC2 /* MatchFormatRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0192BBC5A8500B82851 /* MatchFormatRowView.swift */; }; - FF70FB642C90584900129CC2 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE82BD1307E00A86CF8 /* MonthData.swift */; }; FF70FB652C90584900129CC2 /* MenuWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */; }; FF70FB662C90584900129CC2 /* TournamentBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B6C2BF9E60B000B4573 /* TournamentBuildView.swift */; }; FF70FB672C90584900129CC2 /* TeamPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0A2BAF3D4C00A9A3BD /* TeamPickerView.swift */; }; FF70FB692C90584900129CC2 /* EventTournamentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */; }; - FF70FB6A2C90584900129CC2 /* DisplayContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */; }; FF70FB6B2C90584900129CC2 /* TournamentCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268062BCE94D90080F940 /* TournamentCallView.swift */; }; FF70FB6C2C90584900129CC2 /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; }; FF70FB6D2C90584900129CC2 /* GroupStagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */; }; FF70FB6E2C90584900129CC2 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; }; - FF70FB6F2C90584900129CC2 /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; - FF70FB702C90584900129CC2 /* MatchDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */; }; FF70FB712C90584900129CC2 /* TournamentFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */; }; FF70FB722C90584900129CC2 /* MatchListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065D2BBD8040009D6715 /* MatchListView.swift */; }; FF70FB732C90584900129CC2 /* PadelClubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4002B6D249D002A7B48 /* PadelClubApp.swift */; }; FF70FB742C90584900129CC2 /* TournamentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */; }; - FF70FB752C90584900129CC2 /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */; }; FF70FB762C90584900129CC2 /* GroupStageTeamReplacementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9AC3942BE3627B00C2E883 /* GroupStageTeamReplacementView.swift */; }; FF70FB772C90584900129CC2 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; }; FF70FB782C90584900129CC2 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; }; 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 /* 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 */; }; FF70FB802C90584900129CC2 /* TeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D082BAF3D4000A9A3BD /* TeamDetailView.swift */; }; FF70FB812C90584900129CC2 /* GroupStagesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */; }; FF70FB822C90584900129CC2 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; }; FF70FB832C90584900129CC2 /* HtmlGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B722BFA00FB000B4573 /* HtmlGenerator.swift */; }; - FF70FB842C90584900129CC2 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26352BAD523300650388 /* PadelRule.swift */; }; - FF70FB852C90584900129CC2 /* TeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */; }; - FF70FB862C90584900129CC2 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */; }; FF70FB872C90584900129CC2 /* GroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFA2BAEE13800A9A3BD /* GroupStageView.swift */; }; FF70FB882C90584900129CC2 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5582BAB767000FD8220 /* Tips.swift */; }; FF70FB892C90584900129CC2 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; }; @@ -775,14 +586,10 @@ FF70FB8B2C90584900129CC2 /* PlanningSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964522BC262B000EEF017 /* PlanningSettingsView.swift */; }; FF70FB8C2C90584900129CC2 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; }; FF70FB8D2C90584900129CC2 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; }; - FF70FB8E2C90584900129CC2 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD52B923960008466FA /* URL+Extensions.swift */; }; FF70FB8F2C90584900129CC2 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; }; FF70FB902C90584900129CC2 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */; }; FF70FB912C90584900129CC2 /* MatchDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D022BAEF0C000A9A3BD /* MatchDetailView.swift */; }; - FF70FB922C90584900129CC2 /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */; }; FF70FB932C90584900129CC2 /* PlayerBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0E2BAF63B000A9A3BD /* PlayerBlockView.swift */; }; - FF70FB942C90584900129CC2 /* StoreItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */; }; - FF70FB952C90584900129CC2 /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; FF70FB962C90584900129CC2 /* PointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0112BBC3E1A00B82851 /* PointView.swift */; }; FF70FB972C90584900129CC2 /* ClubHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC202BB53E590036DAAB /* ClubHolder.swift */; }; FF70FB982C90584900129CC2 /* EventSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */; }; @@ -790,13 +597,11 @@ FF70FB9A2C90584900129CC2 /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; }; FF70FB9B2C90584900129CC2 /* MatchFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26502BAE0BAD00650388 /* MatchFormatSelectionView.swift */; }; FF70FB9C2C90584900129CC2 /* TournamentRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF712BE19274008B4B7E /* TournamentRankView.swift */; }; - FF70FB9D2C90584900129CC2 /* NumberFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */; }; FF70FB9E2C90584900129CC2 /* SetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */; }; FF70FB9F2C90584900129CC2 /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */; }; FF70FBA02C90584900129CC2 /* GroupStageScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */; }; FF70FBA12C90584900129CC2 /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41812BF73EB3001B24CB /* EventView.swift */; }; FF70FBA22C90584900129CC2 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; }; - FF70FBA32C90584900129CC2 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; }; FF70FBA42C90584900129CC2 /* Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF72BAEDF0000A9A3BD /* Labels.swift */; }; FF70FBA52C90584900129CC2 /* CopyPasteButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */; }; FF70FBA62C90584900129CC2 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; }; @@ -806,11 +611,8 @@ FF70FBAA2C90584900129CC2 /* RankCalculatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */; }; FF70FBAB2C90584900129CC2 /* DateBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091692B90F95E00AB08DA /* DateBoxView.swift */; }; FF70FBAC2C90584900129CC2 /* LearnMoreSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */; }; - FF70FBAD2C90584900129CC2 /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; }; - FF70FBAE2C90584900129CC2 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; }; FF70FBAF2C90584900129CC2 /* UpdateSourceRankDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */; }; FF70FBB02C90584900129CC2 /* GlobalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */; }; - FF70FBB12C90584900129CC2 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */; }; FF70FBB22C90584900129CC2 /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; FF70FBB42C90584900129CC2 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = FF70FABE2C90584900129CC2 /* Algorithms */; }; FF70FBB52C90584900129CC2 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = FF70FAC02C90584900129CC2 /* Zip */; }; @@ -850,10 +652,8 @@ FF82CFC52B911F5B00B0CAF2 /* OrganizedTournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */; }; FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; }; FF8E1CE62C006E0200184680 /* Alphabet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8E1CE52C006E0200184680 /* Alphabet.swift */; }; - FF8F26382BAD523300650388 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26352BAD523300650388 /* PadelRule.swift */; }; FF8F263B2BAD528600650388 /* EventCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263A2BAD528600650388 /* EventCreationView.swift */; }; FF8F263D2BAD627A00650388 /* TournamentConfiguratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */; }; - FF8F263F2BAD7D5C00650388 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263E2BAD7D5C00650388 /* Event.swift */; }; FF8F26412BADFC8700650388 /* TournamentInitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26402BADFC8700650388 /* TournamentInitView.swift */; }; FF8F26432BADFE5B00650388 /* TournamentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */; }; FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; }; @@ -874,14 +674,6 @@ FF9268032BCE94A30080F940 /* GroupStageCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */; }; FF9268072BCE94D90080F940 /* TournamentCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268062BCE94D90080F940 /* TournamentCallView.swift */; }; FF9268092BCEDC2C0080F940 /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268082BCEDC2C0080F940 /* CallView.swift */; }; - FF92680B2BCEE3E10080F940 /* ContactManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680A2BCEE3E10080F940 /* ContactManager.swift */; }; - FF92680D2BCEE5EA0080F940 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */; }; - FF967CEA2BAEC70100A9A3BD /* GroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CE72BAEC70100A9A3BD /* GroupStage.swift */; }; - FF967CEC2BAECB9900A9A3BD /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEB2BAECB9900A9A3BD /* Match.swift */; }; - FF967CEE2BAECBD700A9A3BD /* Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CED2BAECBD700A9A3BD /* Round.swift */; }; - FF967CF22BAECC0B00A9A3BD /* TeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */; }; - FF967CF32BAECC0B00A9A3BD /* PlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */; }; - FF967CF42BAECC0B00A9A3BD /* TeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */; }; FF967CF62BAED51600A9A3BD /* TournamentRunningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF52BAED51600A9A3BD /* TournamentRunningView.swift */; }; FF967CF82BAEDF0000A9A3BD /* Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF72BAEDF0000A9A3BD /* Labels.swift */; }; FF967CFC2BAEE52E00A9A3BD /* GroupStagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */; }; @@ -916,12 +708,6 @@ FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; FFB378362D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; - FFB39B342D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; }; - FFB39B352D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; }; - FFB39B362D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; }; - FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; - FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; - FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; FFBE62052CE9DA0900815D33 /* MatchViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */; }; FFBE62062CE9DA0900815D33 /* MatchViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */; }; FFBE62072CE9DA0900815D33 /* MatchViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */; }; @@ -946,7 +732,6 @@ FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; }; FFC83D512BB8087E00750834 /* RoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D502BB8087E00750834 /* RoundView.swift */; }; FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; }; - FFC91B012BD85C2F00B29808 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; }; FFC91B032BD85E2400B29808 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.swift */; }; FFCB74132C4625BB008384D0 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */; }; FFCB74172C480411008384D0 /* CopyPasteButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */; }; @@ -957,15 +742,11 @@ FFCFC00C2BBC3D1E00B82851 /* EditScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0012BBC39A600B82851 /* EditScoreView.swift */; }; FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */; }; FFCFC0122BBC3E1A00B82851 /* PointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0112BBC3E1A00B82851 /* PointView.swift */; }; - FFCFC0142BBC59FC00B82851 /* MatchDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */; }; FFCFC0162BBC5A4C00B82851 /* SetInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */; }; FFCFC0182BBC5A6800B82851 /* SetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */; }; FFCFC01A2BBC5A8500B82851 /* MatchFormatRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0192BBC5A8500B82851 /* MatchFormatRowView.swift */; }; - FFCFC01C2BBC5AAA00B82851 /* SetDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */; }; FFD655D82C8DE27400E5B35E /* TournamentLookUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD655D72C8DE27400E5B35E /* TournamentLookUpView.swift */; }; FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; }; - FFDB1C6D2BB2A02000F1E467 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; }; - FFDB1C732BB2CFE900F1E467 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */; }; FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; }; FFE103082C353B7600684FC9 /* EventClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103072C353B7600684FC9 /* EventClubSettingsView.swift */; }; FFE103102C366DCD00684FC9 /* EditSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE1030F2C366DCD00684FC9 /* EditSharingView.swift */; }; @@ -996,15 +777,9 @@ FFEF7F4E2BDE69130033D0F0 /* MenuWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */; }; FFF0241E2BF48B15001F14B4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = FFF0241D2BF48B15001F14B4 /* Localizable.strings */; }; FFF03C942BD91D0C00B516FC /* ButtonValidateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */; }; - FFF116E12BD2A9B600A33B06 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; }; FFF116E32BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; }; - FFF1D2CB2C4A22B200C8D33D /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */; }; FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; }; FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; }; - FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; }; - FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD52B923960008466FA /* URL+Extensions.swift */; }; - FFF8ACD92B923F3C008466FA /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD82B923F3C008466FA /* String+Extensions.swift */; }; - FFF8ACDB2B923F48008466FA /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */; }; FFF964502BC25E3700EEF017 /* PlanningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9644F2BC25E3700EEF017 /* PlanningView.swift */; }; FFF964532BC262B000EEF017 /* PlanningSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964522BC262B000EEF017 /* PlanningSettingsView.swift */; }; FFF964552BC266CF00EEF017 /* SchedulerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964542BC266CF00EEF017 /* SchedulerView.swift */; }; @@ -1038,6 +813,7 @@ dstSubfolderSpec = 10; files = ( C49EF03A2BDFF4600077B5AA /* LeStorage.framework in Embed Frameworks */, + C49770222DC25A24005CD239 /* PadelClubData.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -1070,7 +846,6 @@ C40CD2F22C412681000DBD9A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C411C9C22BEBA453003017AD /* ServerDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDataTests.swift; sourceTree = ""; }; C411C9C82BF219CB003017AD /* UserDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDataTests.swift; sourceTree = ""; }; - C411C9CC2BF21DAF003017AD /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; C411C9CF2BF38F41003017AD /* TokenExemptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenExemptionTests.swift; sourceTree = ""; }; C425D3FD2B6D249D002A7B48 /* PadelClub.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PadelClub.app; sourceTree = BUILT_PRODUCTS_DIR; }; C425D4002B6D249D002A7B48 /* PadelClubApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubApp.swift; sourceTree = ""; }; @@ -1083,78 +858,40 @@ C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITestsLaunchTests.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 = ""; }; - 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 = ""; }; - C471D1532D0C8FE80068091F /* Drawlog.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Drawlog.json; sourceTree = ""; }; - C471D1572D0C91FE0068091F /* BaseDrawLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDrawLog.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 = ""; }; C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkStatusView.swift; sourceTree = ""; }; C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewModifier.swift; sourceTree = ""; }; - C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPaymentType.swift; sourceTree = ""; }; + C49770202DC25A23005CD239 /* PadelClubData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PadelClubData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C49771DD2DC25F04005CD239 /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = ""; }; + C49771DE2DC25F04005CD239 /* Badge+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Badge+Extensions.swift"; sourceTree = ""; }; + C49771DF2DC25F04005CD239 /* SpinDrawable+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SpinDrawable+Extensions.swift"; sourceTree = ""; }; + C49771EA2DC25F8D005CD239 /* SourceFileManager+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceFileManager+Extensions.swift"; sourceTree = ""; }; + C49771EE2DC25F93005CD239 /* CustomUser+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CustomUser+Extensions.swift"; sourceTree = ""; }; + C49771EF2DC25F93005CD239 /* PlayerRegistration+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlayerRegistration+Extensions.swift"; sourceTree = ""; }; + C49771F02DC25F93005CD239 /* TeamRegistration+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TeamRegistration+Extensions.swift"; sourceTree = ""; }; + C49771F12DC25F93005CD239 /* Tournament+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tournament+Extensions.swift"; sourceTree = ""; }; + C49771F22DC25F93005CD239 /* MonthData+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MonthData+Extensions.swift"; sourceTree = ""; }; + C49772022DC260D3005CD239 /* Round+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Round+Extensions.swift"; sourceTree = ""; }; + C49772382DC28A92005CD239 /* ComposeViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViews.swift; sourceTree = ""; }; + C49774BA2DC37C10005CD239 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = ""; }; C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionComparator.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 = ""; }; C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionInfoView.swift; sourceTree = ""; }; C49EF0372BDFF3000077B5AA /* LeStorage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LeStorage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 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 = ""; }; C4A47D862B7BA36D00ADC637 /* UserCreationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserCreationView.swift; sourceTree = ""; }; C4A47D892B7BBB6500ADC637 /* SubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionView.swift; sourceTree = ""; }; - C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreManager.swift; sourceTree = ""; }; - C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Guard.swift; sourceTree = ""; }; - C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreItem.swift; sourceTree = ""; }; 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 /* 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 = ""; }; C4D05D462DC10AE5009B053C /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; C4D05D482DC10CBE009B053C /* PaymentStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentStatusView.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 = ""; }; - C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentStore.swift; sourceTree = ""; }; FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamHeaderView.swift; sourceTree = ""; }; FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchTeamDetailView.swift; sourceTree = ""; }; FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterButtonView.swift; sourceTree = ""; }; @@ -1163,7 +900,6 @@ FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentMatchFormatsSettingsView.swift; sourceTree = ""; }; FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentGeneralSettingsView.swift; sourceTree = ""; }; FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSettingsView.swift; sourceTree = ""; }; - FF025AE82BD1307E00A86CF8 /* MonthData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthData.swift; sourceTree = ""; }; FF025AEC2BD1513700A86CF8 /* AppScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppScreen.swift; sourceTree = ""; }; FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationSettingsView.swift; sourceTree = ""; }; FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatStorageView.swift; sourceTree = ""; }; @@ -1231,16 +967,13 @@ FF17CA522CBE4788003C7323 /* BracketCallingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BracketCallingView.swift; sourceTree = ""; }; FF17CA562CC02FEA003C7323 /* CoachListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoachListView.swift; sourceTree = ""; }; FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournament.swift; sourceTree = ""; }; - FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Extensions.swift"; sourceTree = ""; }; FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournamentSearchScope.swift; sourceTree = ""; }; FF1CBC202BB53E590036DAAB /* ClubHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubHolder.swift; sourceTree = ""; }; FF1CBC212BB53E590036DAAB /* FederalTournamentHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournamentHolder.swift; sourceTree = ""; }; FF1DC5502BAB351300FD8220 /* ClubDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubDetailView.swift; sourceTree = ""; }; - FF1DC5522BAB354A00FD8220 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = ""; }; FF1DC5542BAB36DD00FD8220 /* CreateClubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateClubView.swift; sourceTree = ""; }; FF1DC5562BAB3AED00FD8220 /* ClubsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubsView.swift; sourceTree = ""; }; FF1DC5582BAB767000FD8220 /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = ""; }; - FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayContext.swift; sourceTree = ""; }; FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonView.swift; sourceTree = ""; }; FF1F4B6C2BF9E60B000B4573 /* TournamentBuildView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBuildView.swift; sourceTree = ""; }; FF1F4B702BF9EFE9000B4573 /* TournamentInscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentInscriptionView.swift; sourceTree = ""; }; @@ -1266,7 +999,6 @@ FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationInfoSheetView.swift; sourceTree = ""; }; FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InscriptionLegendView.swift; sourceTree = ""; }; - FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduler.swift; sourceTree = ""; }; FF3F74F52B919E45004CFE0E /* UmpireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UmpireView.swift; sourceTree = ""; }; FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgendaDestination.swift; sourceTree = ""; }; FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentCategorySettingsView.swift; sourceTree = ""; }; @@ -1285,11 +1017,9 @@ FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalDataViewModel.swift; sourceTree = ""; }; FF5BAF712BE19274008B4B7E /* TournamentRankView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentRankView.swift; sourceTree = ""; }; FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LearnMoreSheetView.swift; sourceTree = ""; }; - FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = ""; }; FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRowViewModifier.swift; sourceTree = ""; }; FF5D0D772BB42C5B005CB568 /* InscriptionInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InscriptionInfoView.swift; sourceTree = ""; }; FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankCalculatorView.swift; sourceTree = ""; }; - FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+Extensions.swift"; sourceTree = ""; }; FF5D0D882BB4935C005CB568 /* ClubRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubRowView.swift; sourceTree = ""; }; FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImportedPlayer+Extensions.swift"; sourceTree = ""; }; @@ -1303,17 +1033,11 @@ FF6087EB2BE26A2F004E1E47 /* BroadcastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BroadcastView.swift; sourceTree = ""; }; FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserBracketFromGroupStageView.swift; sourceTree = ""; }; FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentFilterView.swift; sourceTree = ""; }; - FF6761522CC77D1900CC9BF2 /* DrawLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawLog.swift; sourceTree = ""; }; FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawLogsView.swift; sourceTree = ""; }; FF67615A2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewBracketPositionView.swift; sourceTree = ""; }; FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowButtonView.swift; sourceTree = ""; }; FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentButtonView.swift; sourceTree = ""; }; - FF6EC8FD2B94792300EA7F5A /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = ""; }; FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationContext.swift; sourceTree = ""; }; - FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Extensions.swift"; sourceTree = ""; }; - FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManagerError.swift; sourceTree = ""; }; - FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FixedWidthInteger+Extensions.swift"; sourceTree = ""; }; - FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = ""; }; FF7091612B90F04300AB08DA /* TournamentOrganizerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentOrganizerView.swift; sourceTree = ""; }; FF7091652B90F0B000AB08DA /* TabDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabDestination.swift; sourceTree = ""; }; FF7091672B90F79F00AB08DA /* TournamentCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentCellView.swift; sourceTree = ""; }; @@ -1329,10 +1053,8 @@ FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganizedTournamentView.swift; sourceTree = ""; }; FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = ""; }; FF8E1CE52C006E0200184680 /* Alphabet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alphabet.swift; sourceTree = ""; }; - FF8F26352BAD523300650388 /* PadelRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelRule.swift; sourceTree = ""; }; FF8F263A2BAD528600650388 /* EventCreationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventCreationView.swift; sourceTree = ""; }; FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentConfiguratorView.swift; sourceTree = ""; }; - FF8F263E2BAD7D5C00650388 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; FF8F26402BADFC8700650388 /* TournamentInitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentInitView.swift; sourceTree = ""; }; FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentSettingsView.swift; sourceTree = ""; }; FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentDurationManagerView.swift; sourceTree = ""; }; @@ -1353,14 +1075,6 @@ FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageCallingView.swift; sourceTree = ""; }; FF9268062BCE94D90080F940 /* TournamentCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentCallView.swift; sourceTree = ""; }; FF9268082BCEDC2C0080F940 /* CallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallView.swift; sourceTree = ""; }; - FF92680A2BCEE3E10080F940 /* ContactManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactManager.swift; sourceTree = ""; }; - FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = ""; }; - FF967CE72BAEC70100A9A3BD /* GroupStage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStage.swift; sourceTree = ""; }; - FF967CEB2BAECB9900A9A3BD /* Match.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Match.swift; sourceTree = ""; }; - FF967CED2BAECBD700A9A3BD /* Round.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Round.swift; sourceTree = ""; }; - FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamScore.swift; sourceTree = ""; }; - FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRegistration.swift; sourceTree = ""; }; - FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerRegistration.swift; sourceTree = ""; }; FF967CF52BAED51600A9A3BD /* TournamentRunningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentRunningView.swift; sourceTree = ""; }; FF967CF72BAEDF0000A9A3BD /* Labels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Labels.swift; sourceTree = ""; }; FF967CFA2BAEE13800A9A3BD /* GroupStageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageView.swift; sourceTree = ""; }; @@ -1384,9 +1098,6 @@ FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = ""; }; FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatGuideView.swift; sourceTree = ""; }; - FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchSpot.swift; sourceTree = ""; }; - FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = ""; }; - FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = ""; }; FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchViewStyle.swift; sourceTree = ""; }; FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageTeamView.swift; sourceTree = ""; }; FFBF065D2BBD8040009D6715 /* MatchListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchListView.swift; sourceTree = ""; }; @@ -1405,7 +1116,6 @@ FFC83D4E2BB807D100750834 /* RoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundsView.swift; sourceTree = ""; }; FFC83D502BB8087E00750834 /* RoundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundView.swift; sourceTree = ""; }; FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FortuneWheelView.swift; sourceTree = ""; }; - FFC91B002BD85C2F00B29808 /* Court.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Court.swift; sourceTree = ""; }; FFC91B022BD85E2400B29808 /* CourtView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourtView.swift; sourceTree = ""; }; FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageSettingsView.swift; sourceTree = ""; }; FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyPasteButtonView.swift; sourceTree = ""; }; @@ -1415,16 +1125,12 @@ FFCFC0012BBC39A600B82851 /* EditScoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditScoreView.swift; sourceTree = ""; }; FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointSelectionView.swift; sourceTree = ""; }; FFCFC0112BBC3E1A00B82851 /* PointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointView.swift; sourceTree = ""; }; - FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchDescriptor.swift; sourceTree = ""; }; FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetInputView.swift; sourceTree = ""; }; FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetLabelView.swift; sourceTree = ""; }; FFCFC0192BBC5A8500B82851 /* MatchFormatRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatRowView.swift; sourceTree = ""; }; - FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDescriptor.swift; sourceTree = ""; }; FFD655D72C8DE27400E5B35E /* TournamentLookUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentLookUpView.swift; sourceTree = ""; }; FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubView.swift; sourceTree = ""; }; FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; - FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; - FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySortDescriptor.swift; sourceTree = ""; }; FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeferredViewModifier.swift; sourceTree = ""; }; FFE103072C353B7600684FC9 /* EventClubSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventClubSettingsView.swift; path = PadelClub/Views/Tournament/Screen/Components/EventClubSettingsView.swift; sourceTree = SOURCE_ROOT; }; FFE1030F2C366DCD00684FC9 /* EditSharingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSharingView.swift; sourceTree = ""; }; @@ -1442,15 +1148,9 @@ FFF0241C2BF48B15001F14B4 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; FFF0241F2BF48B1A001F14B4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonValidateView.swift; sourceTree = ""; }; - FFF116E02BD2A9B600A33B06 /* DateInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateInterval.swift; sourceTree = ""; }; FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourtAvailabilitySettingsView.swift; sourceTree = ""; }; - FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportFormat.swift; sourceTree = ""; }; FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduleEditorView.swift; sourceTree = ""; }; FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalPlayer.swift; sourceTree = ""; }; - FFF8ACD32B92392C008466FA /* SourceFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceFileManager.swift; sourceTree = ""; }; - FFF8ACD52B923960008466FA /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; - FFF8ACD82B923F3C008466FA /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; - FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; FFF9644F2BC25E3700EEF017 /* PlanningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanningView.swift; sourceTree = ""; }; FFF964522BC262B000EEF017 /* PlanningSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanningSettingsView.swift; sourceTree = ""; }; FFF964542BC266CF00EEF017 /* SchedulerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchedulerView.swift; sourceTree = ""; }; @@ -1464,6 +1164,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C49770212DC25A23005CD239 /* PadelClubData.framework in Frameworks */, C4D05D472DC10AE5009B053C /* WebKit.framework in Frameworks */, FFCFBFFE2BBBE86600B82851 /* Algorithms in Frameworks */, FF92660D2C241CE0002361A4 /* Zip in Frameworks */, @@ -1547,7 +1248,7 @@ FF3F74FD2B91A087004CFE0E /* ViewModel */, C4A47D5F2B6D3B2D00ADC637 /* Data */, FFF8ACD02B9238A2008466FA /* Utils */, - FFF8ACD72B923F26008466FA /* Extensions */, + C49771E02DC25F04005CD239 /* Extensions */, C425D4042B6D249E002A7B48 /* Assets.xcassets */, FFF024192BF48AEE001F14B4 /* Localization */, FF0EC54D2BB195CA0056B6D1 /* CSV */, @@ -1590,78 +1291,33 @@ C425D4592B6D255B002A7B48 /* Frameworks */ = { isa = PBXGroup; children = ( + C49770202DC25A23005CD239 /* PadelClubData.framework */, C4D05D462DC10AE5009B053C /* WebKit.framework */, C49EF0372BDFF3000077B5AA /* LeStorage.framework */, ); name = Frameworks; sourceTree = ""; }; - C488C7FD2CC7D6CD0082001F /* Gen */ = { + C49771E02DC25F04005CD239 /* Extensions */ = { isa = PBXGroup; children = ( - C488C7E52CC7D1660082001F /* generator.py */, - C488C7EC2CC7D2290082001F /* Club.json */, - C488C8132CC7E4240082001F /* Court.json */, - C488C8142CC7E4240082001F /* CustomUser.json */, - C488C8152CC7E4240082001F /* DateInterval.json */, - C471D1532D0C8FE80068091F /* Drawlog.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 */, - C471D1572D0C91FE0068091F /* BaseDrawLog.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 */, + C49771EE2DC25F93005CD239 /* CustomUser+Extensions.swift */, + C49771EF2DC25F93005CD239 /* PlayerRegistration+Extensions.swift */, + C49771F02DC25F93005CD239 /* TeamRegistration+Extensions.swift */, + C49771F12DC25F93005CD239 /* Tournament+Extensions.swift */, + C49771F22DC25F93005CD239 /* MonthData+Extensions.swift */, + C49771EA2DC25F8D005CD239 /* SourceFileManager+Extensions.swift */, + C49771DD2DC25F04005CD239 /* Color+Extensions.swift */, + C49771DE2DC25F04005CD239 /* Badge+Extensions.swift */, + C49771DF2DC25F04005CD239 /* SpinDrawable+Extensions.swift */, + C49772022DC260D3005CD239 /* Round+Extensions.swift */, ); - path = Gen; + path = Extensions; sourceTree = ""; }; C4A47D5F2B6D3B2D00ADC637 /* Data */ = { isa = PBXGroup; children = ( - C488C7FD2CC7D6CD0082001F /* Gen */, - C411C9CC2BF21DAF003017AD /* README.md */, - C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */, - C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */, - C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */, - C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */, - C4A47D592B6D383C00ADC637 /* Tournament.swift */, - FF967CE72BAEC70100A9A3BD /* GroupStage.swift */, - FF967CED2BAECBD700A9A3BD /* Round.swift */, - FF967CEB2BAECB9900A9A3BD /* Match.swift */, - FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */, - C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */, - FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */, - FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */, - C4A47D622B6D3D6500ADC637 /* Club.swift */, - FF8F263E2BAD7D5C00650388 /* Event.swift */, - FF025AE82BD1307E00A86CF8 /* MonthData.swift */, - FF1DC5522BAB354A00FD8220 /* MockData.swift */, - FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */, - FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */, - FFC91B002BD85C2F00B29808 /* Court.swift */, - FFF116E02BD2A9B600A33B06 /* DateInterval.swift */, - FF6761522CC77D1900CC9BF2 /* DrawLog.swift */, FF6EC9012B94799200EA7F5A /* Coredata */, FF6EC9022B9479B900EA7F5A /* Federal */, ); @@ -1707,11 +1363,7 @@ C4A47D882B7BBB5000ADC637 /* Subscription */ = { isa = PBXGroup; children = ( - C45BAE432BCA753E002EEC8A /* Purchase.swift */, C4A47D892B7BBB6500ADC637 /* SubscriptionView.swift */, - C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */, - C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */, - C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */, C49EF0182BD694290077B5AA /* PurchaseListView.swift */, C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */, C4D05D482DC10CBE009B053C /* PaymentStatusView.swift */, @@ -1735,6 +1387,7 @@ FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */, FF558C622C6CDD020071F9AE /* UnderlineView.swift */, FFE8C2BF2C7601E80046B243 /* ConfirmButtonView.swift */, + C49772382DC28A92005CD239 /* ComposeViews.swift */, ); path = Components; sourceTree = ""; @@ -1989,8 +1642,6 @@ FF3F74FD2B91A087004CFE0E /* ViewModel */ = { isa = PBXGroup; children = ( - FF6EC8FD2B94792300EA7F5A /* Screen.swift */, - FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */, FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */, FF7091652B90F0B000AB08DA /* TabDestination.swift */, FF025AEC2BD1513700A86CF8 /* AppScreen.swift */, @@ -1998,10 +1649,6 @@ FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */, FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */, FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */, - FFB9C8702BBADDE200A0EF4F /* Selectable.swift */, - FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */, - FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */, - FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */, FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */, FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */, FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */, @@ -2075,7 +1722,6 @@ children = ( FF4AB6B42B9248200002987F /* NetworkManager.swift */, FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */, - FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */, FFE8B5BA2DA9896800BDE966 /* RefundService.swift */, FFE8B5C62DAA390000BDE966 /* StripeValidationService.swift */, FFE8B5CA2DAA429E00BDE966 /* XlsToCsvService.swift */, @@ -2267,47 +1913,18 @@ children = ( FF6EC9072B947A1E00EA7F5A /* Network */, FF8E1CE52C006E0200184680 /* Alphabet.swift */, - FF92680A2BCEE3E10080F940 /* ContactManager.swift */, - FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */, FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */, FF1F4B722BFA00FB000B4573 /* HtmlGenerator.swift */, FF1F4B732BFA00FC000B4573 /* HtmlService.swift */, - C49EF0432BE286780077B5AA /* CryptoKey.swift */, FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */, - FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */, - FF8F26352BAD523300650388 /* PadelRule.swift */, - C4B3A1542C2581DA0078EAA8 /* Patcher.swift */, - C4EC6F582BE92D88000CEAB4 /* PListReader.swift */, - FFF8ACD32B92392C008466FA /* SourceFileManager.swift */, + C49774BA2DC37C10005CD239 /* NetworkMonitor.swift */, FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */, FF1DC5582BAB767000FD8220 /* Tips.swift */, - C49EF01A2BD6A1E80077B5AA /* URLs.swift */, C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */, - FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */, ); path = Utils; sourceTree = ""; }; - FFF8ACD72B923F26008466FA /* Extensions */ = { - isa = PBXGroup; - children = ( - FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */, - FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */, - C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */, - FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */, - FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */, - FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */, - C44B79102BBDA63A00906534 /* Locale+Extensions.swift */, - FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */, - FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */, - FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */, - C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */, - FFF8ACD82B923F3C008466FA /* String+Extensions.swift */, - FFF8ACD52B923960008466FA /* URL+Extensions.swift */, - ); - path = Extensions; - sourceTree = ""; - }; FFF964512BC2628600EEF017 /* Planning */ = { isa = PBXGroup; children = ( @@ -2494,24 +2111,8 @@ 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 */, - C471D1552D0C8FED0068091F /* Drawlog.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 */, @@ -2549,24 +2150,8 @@ 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 */, - C471D1562D0C8FED0068091F /* Drawlog.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 */, @@ -2590,24 +2175,8 @@ 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 */, - C471D1542D0C8FED0068091F /* Drawlog.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 */, @@ -2630,12 +2199,10 @@ C4A47D872B7BA36D00ADC637 /* UserCreationView.swift in Sources */, FF7091662B90F0B000AB08DA /* TabDestination.swift in Sources */, FF9267F82BCE78C70080F940 /* CashierView.swift in Sources */, - FF8F263F2BAD7D5C00650388 /* Event.swift in Sources */, FF77CE562CCCD1EB00CBCBB4 /* DatePickingViewWithFormat.swift in Sources */, FF5D30532BD94E2E00F2B93D /* PlayerHolder.swift in Sources */, FF11628C2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift in Sources */, FF53FBB82BFB302B0051D4C3 /* ClubCourtSetupView.swift in Sources */, - C4B3A1552C2581DA0078EAA8 /* Patcher.swift in Sources */, FF089EBF2BB0B14600F0AEC7 /* FileImportView.swift in Sources */, C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */, FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */, @@ -2650,7 +2217,6 @@ FF967D062BAF3C4200A9A3BD /* MatchSetupView.swift in Sources */, FF4AB6B52B9248200002987F /* NetworkManager.swift in Sources */, FF2B6F5E2C036A1500835EE7 /* EventLinksView.swift in Sources */, - FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */, FF025AE12BD0EB9000A86CF8 /* TournamentClubSettingsView.swift in Sources */, FFBF065C2BBD2657009D6715 /* GroupStageTeamView.swift in Sources */, FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */, @@ -2661,25 +2227,25 @@ FFE8B5C12DAA325400BDE966 /* RefundResultsView.swift in Sources */, FF9268012BCE94920080F940 /* SeedsCallingView.swift in Sources */, FF9268092BCEDC2C0080F940 /* CallView.swift in Sources */, - FF5D0D742BB41DF8005CB568 /* Color+Extensions.swift in Sources */, FFE103102C366DCD00684FC9 /* EditSharingView.swift in Sources */, FF7091682B90F79F00AB08DA /* TournamentCellView.swift in Sources */, - FF6EC9042B9479F500EA7F5A /* Sequence+Extensions.swift in Sources */, FF9267FA2BCE78EC0080F940 /* CashierDetailView.swift in Sources */, - C488C8722CC816410082001F /* BasePurchase.swift in Sources */, + C49771F32DC25F93005CD239 /* CustomUser+Extensions.swift in Sources */, + C49771F42DC25F93005CD239 /* PlayerRegistration+Extensions.swift in Sources */, + C49771F52DC25F93005CD239 /* TeamRegistration+Extensions.swift in Sources */, + C49771F62DC25F93005CD239 /* Tournament+Extensions.swift in Sources */, + C49771F72DC25F93005CD239 /* MonthData+Extensions.swift in Sources */, FFE103082C353B7600684FC9 /* EventClubSettingsView.swift in Sources */, C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */, FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */, FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */, FFCD16B32C3E5E590092707B /* TeamsCallingView.swift in Sources */, - FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */, - FF967CF22BAECC0B00A9A3BD /* TeamScore.swift in Sources */, FF1162832BCFBE4E000C4809 /* EditablePlayerView.swift in Sources */, FF1162852BD00279000C4809 /* PlayerDetailView.swift in Sources */, FFA252AE2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */, + C49771EB2DC25F8D005CD239 /* SourceFileManager+Extensions.swift in Sources */, FF5D0D762BB428B2005CB568 /* ListRowViewModifier.swift in Sources */, FF6EC9002B94794700EA7F5A /* PresentationContext.swift in Sources */, - FFDB1C6D2BB2A02000F1E467 /* AppSettings.swift in Sources */, FF0EC5202BB16F680056B6D1 /* SwiftParser.swift in Sources */, C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */, FF8044AC2C8F676D00A49A52 /* TournamentSubscriptionView.swift in Sources */, @@ -2689,21 +2255,9 @@ 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 */, - C471D1592D0C91FE0068091F /* BaseDrawLog.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 */, C4D05D492DC10CBE009B053C /* PaymentStatusView.swift in Sources */, - C488C8382CC7E4240082001F /* BaseTeamScore.swift in Sources */, FFE8B5BB2DA9896800BDE966 /* RefundService.swift in Sources */, - C488C8392CC7E4240082001F /* BaseTournament.swift in Sources */, + C49774BD2DC37C10005CD239 /* NetworkMonitor.swift in Sources */, FF6087EC2BE26A2F004E1E47 /* BroadcastView.swift in Sources */, FFF964552BC266CF00EEF017 /* SchedulerView.swift in Sources */, FFA1B1292BB71773006CE248 /* PadelClubButtonView.swift in Sources */, @@ -2720,44 +2274,29 @@ FFC91B032BD85E2400B29808 /* CourtView.swift in Sources */, FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */, FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */, - FF92680B2BCEE3E10080F940 /* ContactManager.swift in Sources */, C40CD2F32C412681000DBD9A /* AppDelegate.swift in Sources */, C49EF0262BD80AE80077B5AA /* SubscriptionInfoView.swift in Sources */, FFCFC00C2BBC3D1E00B82851 /* EditScoreView.swift in Sources */, FF7091622B90F04300AB08DA /* TournamentOrganizerView.swift in Sources */, - FF92680D2BCEE5EA0080F940 /* NetworkMonitor.swift in Sources */, FF967CF62BAED51600A9A3BD /* TournamentRunningView.swift in Sources */, FF8F264D2BAE0B4100650388 /* TournamentDatePickerView.swift in Sources */, FFF116E32BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift in Sources */, FFE8C2C02C7601E80046B243 /* ConfirmButtonView.swift in Sources */, C4FC2E272C2AABC90021F3BF /* PasswordField.swift in Sources */, FF967D042BAEF1C300A9A3BD /* MatchRowView.swift in Sources */, - C44B79112BBDA63A00906534 /* Locale+Extensions.swift in Sources */, FF1F4B742BFA00FC000B4573 /* HtmlService.swift in Sources */, - FF967CEA2BAEC70100A9A3BD /* GroupStage.swift in Sources */, - FF6761542CC77D2100CC9BF2 /* DrawLog.swift in Sources */, FF1162812BCF945C000C4809 /* TournamentCashierView.swift in Sources */, - C4A47D902B7BBBEC00ADC637 /* StoreManager.swift in Sources */, 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 */, FF3A74332D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */, - FF6EC9062B947A1000EA7F5A /* NetworkManagerError.swift in Sources */, - C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */, - C4FC2E2B2C2C0E4D0021F3BF /* TournamentStore.swift in Sources */, FF5647132C0B6F390081F995 /* LoserRoundSettingsView.swift in Sources */, 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 */, FF1CBC222BB53E590036DAAB /* FederalTournamentHolder.swift in Sources */, - C4A47D5E2B6D38EC00ADC637 /* DataStore.swift in Sources */, - FFCFC01C2BBC5AAA00B82851 /* SetDescriptor.swift in Sources */, FF025AD82BD0C10F00A86CF8 /* TeamHeaderView.swift in Sources */, FF82CFC52B911F5B00B0CAF2 /* OrganizedTournamentView.swift in Sources */, FFF964572BC26B3400EEF017 /* RoundScheduleEditorView.swift in Sources */, @@ -2767,16 +2306,11 @@ FFC1E10C2BAC7FB0008D6F59 /* ClubImportView.swift in Sources */, FF558C632C6CDD020071F9AE /* UnderlineView.swift in Sources */, FFE8B5CD2DAA42A000BDE966 /* XlsToCsvService.swift in Sources */, - FF3B60A32BC49BBC008C2E66 /* MatchScheduler.swift in Sources */, FF11627A2BCF8109000C4809 /* CallMessageCustomizationView.swift in Sources */, FF6087EA2BE25EF1004E1E47 /* TournamentStatusView.swift in Sources */, FF025ADB2BD0C2D000A86CF8 /* MatchTeamDetailView.swift in Sources */, FF5DA1952BB927E800A33061 /* GenericDestinationPickerView.swift in Sources */, - FFF116E12BD2A9B600A33B06 /* DateInterval.swift in Sources */, FF8F26542BAE1E4400650388 /* TableStructureView.swift in Sources */, - C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */, - FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */, - FF967CEE2BAECBD700A9A3BD /* Round.swift in Sources */, FFBFC3922CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */, FF5BAF6E2BE0B3C8008B4B7E /* FederalDataViewModel.swift in Sources */, FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */, @@ -2786,27 +2320,25 @@ FF5D0D892BB4935C005CB568 /* ClubRowView.swift in Sources */, FF1DC5512BAB351300FD8220 /* ClubDetailView.swift in Sources */, FF9268032BCE94A30080F940 /* GroupStageCallingView.swift in Sources */, - C49EF0442BE286780077B5AA /* CryptoKey.swift in Sources */, FF11627D2BCF941A000C4809 /* CashierSettingsView.swift in Sources */, FFFCDE0E2BCC833600317DEF /* LoserRoundScheduleEditorView.swift in Sources */, - C4A47D632B6D3D6500ADC637 /* Club.swift in Sources */, - FF6EC90B2B947AC000EA7F5A /* Array+Extensions.swift in Sources */, FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */, FF8E1CE62C006E0200184680 /* Alphabet.swift in Sources */, - FFF8ACD92B923F3C008466FA /* String+Extensions.swift in Sources */, FFCB74132C4625BB008384D0 /* GroupStageSettingsView.swift in Sources */, FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */, FFC2DCB22BBE75D40046DB9F /* LoserRoundView.swift in Sources */, FF90FC1D2C44FB3E009339B2 /* AddTeamView.swift in Sources */, FF9267FC2BCE84870080F940 /* PlayerPayView.swift in Sources */, FF2B51552C7A4DAF00FFF126 /* PlanningByCourtView.swift in Sources */, + C49771E12DC25F04005CD239 /* Color+Extensions.swift in Sources */, + C49771E22DC25F04005CD239 /* Badge+Extensions.swift in Sources */, + C49771E32DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */, FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */, FFA252B22CDD2C080074E63F /* OngoingContainerView.swift in Sources */, FF6EC8FB2B94788600EA7F5A /* TournamentButtonView.swift in Sources */, FF6761582CC7803600CC9BF2 /* DrawLogsView.swift in Sources */, FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */, FFBF06602BBD9F6D009D6715 /* NavigationViewModel.swift in Sources */, - FF6EC9092B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift in Sources */, FF6525C32C8C61B400B9498E /* LoserBracketFromGroupStageView.swift in Sources */, FF5D30512BD94E1000F2B93D /* ImportedPlayer+Extensions.swift in Sources */, FFC1E1042BAC28C6008D6F59 /* ClubSearchView.swift in Sources */, @@ -2817,7 +2349,6 @@ FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */, FF77CE542CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */, - FFDB1C732BB2CFE900F1E467 /* MySortDescriptor.swift in Sources */, FF5D0D8B2BB4D1E3005CB568 /* CalendarView.swift in Sources */, FF1CBC1F2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift in Sources */, FF8F26472BAE0ACB00650388 /* TournamentFieldsManagerView.swift in Sources */, @@ -2825,41 +2356,33 @@ FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */, FF11628A2BD05247000C4809 /* DatePickingView.swift in Sources */, FFCFC01A2BBC5A8500B82851 /* MatchFormatRowView.swift in Sources */, - FF025AE92BD1307F00A86CF8 /* MonthData.swift in Sources */, FFEF7F4E2BDE69130033D0F0 /* MenuWarningView.swift in Sources */, FF1F4B6D2BF9E60B000B4573 /* TournamentBuildView.swift in Sources */, FF967D0B2BAF3D4C00A9A3BD /* TeamPickerView.swift in Sources */, FFBF41842BF75ED7001B24CB /* EventTournamentsView.swift in Sources */, - FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */, - FFB39B352D8E8B05008E0C89 /* MatchSpot.swift in Sources */, FF17CA4A2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */, FF9268072BCE94D90080F940 /* TournamentCallView.swift in Sources */, FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */, FF967CFC2BAEE52E00A9A3BD /* GroupStagesView.swift in Sources */, FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */, + C497723A2DC28A92005CD239 /* ComposeViews.swift in Sources */, FF3A73F32D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, - C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */, - FFCFC0142BBC59FC00B82851 /* MatchDescriptor.swift in Sources */, - C49C73142D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */, FF8F264C2BAE0B4100650388 /* TournamentFormatSelectionView.swift in Sources */, FF17CA572CC02FEA003C7323 /* CoachListView.swift in Sources */, FFBF065E2BBD8040009D6715 /* MatchListView.swift in Sources */, C425D4012B6D249D002A7B48 /* PadelClubApp.swift in Sources */, FF8F26432BADFE5B00650388 /* TournamentSettingsView.swift in Sources */, - C49EF03C2BE15AF80077B5AA /* String+Crypto.swift in Sources */, FF9AC3952BE3627B00C2E883 /* GroupStageTeamReplacementView.swift in Sources */, FF4C7F022BBBD7150031B6A3 /* TabItemModifier.swift in Sources */, + C49772042DC260D3005CD239 /* Round+Extensions.swift in Sources */, FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */, FFE8B63C2DACEAED00BDE966 /* ConfigurationService.swift in Sources */, FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */, FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */, FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */, - C4A47DAD2B85FCCD00ADC637 /* CustomUser.swift in Sources */, - C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */, FF967D012BAEF0B400A9A3BD /* MatchSummaryView.swift in Sources */, FFA252B62CDD2C6C0074E63F /* OngoingDestination.swift in Sources */, FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */, - FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */, FF967D092BAF3D4000A9A3BD /* TeamDetailView.swift in Sources */, C488C8822CCBE8FC0082001F /* NetworkStatusView.swift in Sources */, FFA252A92CDB70520074E63F /* PlayerStatisticView.swift in Sources */, @@ -2868,9 +2391,6 @@ FF67615D2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift in Sources */, FF1F4B752BFA00FC000B4573 /* HtmlGenerator.swift in Sources */, FF17CA532CBE4788003C7323 /* BracketCallingView.swift in Sources */, - FF8F26382BAD523300650388 /* PadelRule.swift in Sources */, - FF967CF42BAECC0B00A9A3BD /* TeamRegistration.swift in Sources */, - FFF8ACDB2B923F48008466FA /* Date+Extensions.swift in Sources */, FF967CFD2BAEE5F500A9A3BD /* GroupStageView.swift in Sources */, FF1DC5592BAB767000FD8220 /* Tips.swift in Sources */, FF59FFB72B90EFBF0061EFF9 /* MainView.swift in Sources */, @@ -2879,16 +2399,11 @@ FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */, FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */, FFE8B5C82DAA390900BDE966 /* StripeValidationService.swift in Sources */, - FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */, C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */, FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */, FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */, FFE8B5B32DA848D300BDE966 /* OnlineWaitingListFaqSheetView.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 */, FFCFC0122BBC3E1A00B82851 /* PointView.swift in Sources */, FF1CBC232BB53E590036DAAB /* ClubHolder.swift in Sources */, FFBF41862BF75FDA001B24CB /* EventSettingsView.swift in Sources */, @@ -2897,13 +2412,11 @@ FF8F26512BAE0BAD00650388 /* MatchFormatSelectionView.swift in Sources */, FF5BAF722BE19274008B4B7E /* TournamentRankView.swift in Sources */, FF77CE5B2CCCD1FF00CBCBB4 /* GroupStageDatePickingView.swift in Sources */, - FF5D0D872BB48AFD005CB568 /* NumberFormatter+Extensions.swift in Sources */, FFCFC0182BBC5A6800B82851 /* SetLabelView.swift in Sources */, C4489BE22C05BF5000043F3D /* DebugSettingsView.swift in Sources */, FFF9645B2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift in Sources */, FFBF41822BF73EB3001B24CB /* EventView.swift in Sources */, C4A47DA62B83948E00ADC637 /* LoginView.swift in Sources */, - FFC91B012BD85C2F00B29808 /* Court.swift in Sources */, FF967CF82BAEDF0000A9A3BD /* Labels.swift in Sources */, FFCB74172C480411008384D0 /* CopyPasteButtonView.swift in Sources */, FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */, @@ -2913,12 +2426,9 @@ FF5D0D852BB48997005CB568 /* RankCalculatorView.swift in Sources */, FF70916A2B90F95E00AB08DA /* DateBoxView.swift in Sources */, FF5D0D722BB3EFA5005CB568 /* LearnMoreSheetView.swift in Sources */, - FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */, FF4623CC2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */, - C4EC6F592BE92D88000CEAB4 /* PListReader.swift in Sources */, FF0EC5222BB173E70056B6D1 /* UpdateSourceRankDateView.swift in Sources */, FF025AE72BD1111000A86CF8 /* GlobalSettingsView.swift in Sources */, - C4A47D912B7BBBEC00ADC637 /* Guard.swift in Sources */, C49EF0192BD694290077B5AA /* PurchaseListView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2952,12 +2462,10 @@ FF4CBF442C996C0600151637 /* UserCreationView.swift in Sources */, FF4CBF452C996C0600151637 /* TabDestination.swift in Sources */, FF4CBF462C996C0600151637 /* CashierView.swift in Sources */, - FF4CBF472C996C0600151637 /* Event.swift in Sources */, FF77CE582CCCD1EB00CBCBB4 /* DatePickingViewWithFormat.swift in Sources */, FF4CBF482C996C0600151637 /* PlayerHolder.swift in Sources */, FF4CBF492C996C0600151637 /* LoserRoundStepScheduleEditorView.swift in Sources */, FF4CBF4A2C996C0600151637 /* ClubCourtSetupView.swift in Sources */, - FF4CBF4B2C996C0600151637 /* Patcher.swift in Sources */, FF4CBF4C2C996C0600151637 /* FileImportView.swift in Sources */, FF4CBF4D2C996C0600151637 /* StepperView.swift in Sources */, FF9B442C2D950E48002448C8 /* ConsolationTournamentImportView.swift in Sources */, @@ -2972,7 +2480,6 @@ FF4CBF562C996C0600151637 /* MatchSetupView.swift in Sources */, FF4CBF572C996C0600151637 /* NetworkManager.swift in Sources */, FF4CBF582C996C0600151637 /* EventLinksView.swift in Sources */, - FF4CBF592C996C0600151637 /* SeedInterval.swift in Sources */, FF4CBF5A2C996C0600151637 /* TournamentClubSettingsView.swift in Sources */, FF4CBF5B2C996C0600151637 /* GroupStageTeamView.swift in Sources */, FF4CBF5C2C996C0600151637 /* RoundSettingsView.swift in Sources */, @@ -2982,25 +2489,19 @@ FF4CBF5F2C996C0600151637 /* TeamWeightView.swift in Sources */, FF4CBF602C996C0600151637 /* SeedsCallingView.swift in Sources */, FF4CBF612C996C0600151637 /* CallView.swift in Sources */, - FF4CBF622C996C0600151637 /* Color+Extensions.swift in Sources */, FF4CBF632C996C0600151637 /* EditSharingView.swift in Sources */, FF4CBF642C996C0600151637 /* TournamentCellView.swift in Sources */, - FF4CBF652C996C0600151637 /* Sequence+Extensions.swift in Sources */, FF4CBF662C996C0600151637 /* CashierDetailView.swift in Sources */, FF4CBF672C996C0600151637 /* EventClubSettingsView.swift in Sources */, FF4CBF682C996C0600151637 /* AccountView.swift in Sources */, FF4CBF692C996C0600151637 /* PlayersWithoutContactView.swift in Sources */, FF17CA4D2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */, FF4CBF6A2C996C0600151637 /* TeamsCallingView.swift in Sources */, - 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 */, FFA252AF2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */, FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */, FF4CBF702C996C0600151637 /* PresentationContext.swift in Sources */, - FF4CBF712C996C0600151637 /* AppSettings.swift in Sources */, FF4CBF722C996C0600151637 /* SwiftParser.swift in Sources */, FF4CBF732C996C0600151637 /* ChangePasswordView.swift in Sources */, FF4CBF742C996C0600151637 /* TournamentSubscriptionView.swift in Sources */, @@ -3010,23 +2511,16 @@ FF4CBF782C996C0600151637 /* EventCreationView.swift in Sources */, 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 */, - 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 */, - C49C73152D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */, - C488C8512CC7E4240082001F /* BasePlayerRegistration.swift in Sources */, - C488C8522CC7E4240082001F /* BaseTeamScore.swift in Sources */, - C488C8532CC7E4240082001F /* BaseTournament.swift in Sources */, FF4CBF7B2C996C0600151637 /* BroadcastView.swift in Sources */, + C49771FD2DC25F93005CD239 /* CustomUser+Extensions.swift in Sources */, + C49771FE2DC25F93005CD239 /* PlayerRegistration+Extensions.swift in Sources */, + C49771FF2DC25F93005CD239 /* TeamRegistration+Extensions.swift in Sources */, + C49772002DC25F93005CD239 /* Tournament+Extensions.swift in Sources */, + C49772012DC25F93005CD239 /* MonthData+Extensions.swift in Sources */, FF4CBF7C2C996C0600151637 /* SchedulerView.swift in Sources */, FF4CBF7D2C996C0600151637 /* PadelClubButtonView.swift in Sources */, + C49774BC2DC37C10005CD239 /* NetworkMonitor.swift in Sources */, FF4CBF7E2C996C0600151637 /* TournamentSeedEditing.swift in Sources */, FF4CBF7F2C996C0600151637 /* TournamentView.swift in Sources */, FF4CBF802C996C0600151637 /* OngoingView.swift in Sources */, @@ -3040,45 +2534,30 @@ FF4CBF862C996C0600151637 /* CourtView.swift in Sources */, FF4CBF872C996C0600151637 /* PointSelectionView.swift in Sources */, 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 */, - FF4CBF8E2C996C0600151637 /* NetworkMonitor.swift in Sources */, FF4CBF8F2C996C0600151637 /* TournamentRunningView.swift in Sources */, FF4CBF902C996C0600151637 /* TournamentDatePickerView.swift in Sources */, FF4CBF912C996C0600151637 /* CourtAvailabilitySettingsView.swift in Sources */, FF4CBF922C996C0600151637 /* ConfirmButtonView.swift in Sources */, FF4CBF932C996C0600151637 /* PasswordField.swift in Sources */, FF4CBF942C996C0600151637 /* MatchRowView.swift in Sources */, - FF4CBF952C996C0600151637 /* Locale+Extensions.swift in Sources */, FF4CBF962C996C0600151637 /* HtmlService.swift in Sources */, - FF4CBF972C996C0600151637 /* GroupStage.swift in Sources */, - FF6761532CC77D2100CC9BF2 /* DrawLog.swift in Sources */, FF4CBF982C996C0600151637 /* TournamentCashierView.swift in Sources */, - FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */, FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */, - FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */, C4339BFD2CFF7D68004E5F09 /* ShareModelView.swift in Sources */, FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */, FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */, FF3A74322D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */, - FF4CBF9E2C996C0600151637 /* NetworkManagerError.swift in Sources */, - FF4CBF9F2C996C0600151637 /* Tournament.swift in Sources */, - FF4CBFA02C996C0600151637 /* TournamentStore.swift in Sources */, FF4CBFA12C996C0600151637 /* LoserRoundSettingsView.swift in Sources */, 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 */, FF4CBFA82C996C0600151637 /* FederalTournamentHolder.swift in Sources */, - FF4CBFA92C996C0600151637 /* DataStore.swift in Sources */, - FF4CBFAA2C996C0600151637 /* SetDescriptor.swift in Sources */, FF4CBFAB2C996C0600151637 /* TeamHeaderView.swift in Sources */, FF4CBFAC2C996C0600151637 /* OrganizedTournamentView.swift in Sources */, FF4CBFAD2C996C0600151637 /* RoundScheduleEditorView.swift in Sources */, @@ -3087,48 +2566,38 @@ FFE8B5C72DAA390900BDE966 /* StripeValidationService.swift in Sources */, FF4CBFB02C996C0600151637 /* ClubImportView.swift in Sources */, FF4CBFB12C996C0600151637 /* UnderlineView.swift in Sources */, - FF4CBFB22C996C0600151637 /* MatchScheduler.swift in Sources */, FF4CBFB32C996C0600151637 /* CallMessageCustomizationView.swift in Sources */, FF4CBFB42C996C0600151637 /* TournamentStatusView.swift in Sources */, FF4CBFB52C996C0600151637 /* MatchTeamDetailView.swift in Sources */, FF4CBFB62C996C0600151637 /* GenericDestinationPickerView.swift in Sources */, - FF4CBFB72C996C0600151637 /* DateInterval.swift in Sources */, FF4CBFB82C996C0600151637 /* TableStructureView.swift in Sources */, - FF4CBFB92C996C0600151637 /* Purchase.swift in Sources */, - FF4CBFBA2C996C0600151637 /* Screen.swift in Sources */, - FF4CBFBB2C996C0600151637 /* Round.swift in Sources */, FFBFC3912CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */, FF4CBFBC2C996C0600151637 /* FederalDataViewModel.swift in Sources */, FF4CBFBD2C996C0600151637 /* AgendaDestination.swift in Sources */, FF4CBFBE2C996C0600151637 /* PadelClubApp.xcdatamodeld in Sources */, FF4CBFBF2C996C0600151637 /* SetInputView.swift in Sources */, - C471D15A2D0C91FF0068091F /* BaseDrawLog.swift in Sources */, FF4CBFC02C996C0600151637 /* ButtonValidateView.swift in Sources */, FF4CBFC12C996C0600151637 /* ClubRowView.swift in Sources */, FF4CBFC22C996C0600151637 /* ClubDetailView.swift in Sources */, FF4CBFC32C996C0600151637 /* GroupStageCallingView.swift in Sources */, - FF4CBFC42C996C0600151637 /* CryptoKey.swift in Sources */, FF4CBFC52C996C0600151637 /* CashierSettingsView.swift in Sources */, FF4CBFC62C996C0600151637 /* LoserRoundScheduleEditorView.swift in Sources */, - FF4CBFC72C996C0600151637 /* Club.swift in Sources */, FFE8B5B82DA8763800BDE966 /* PaymentInfoSheetView.swift in Sources */, - FF4CBFC82C996C0600151637 /* Array+Extensions.swift in Sources */, FF4CBFC92C996C0600151637 /* ToolboxView.swift in Sources */, FF4CBFCA2C996C0600151637 /* Alphabet.swift in Sources */, - FF4CBFCB2C996C0600151637 /* String+Extensions.swift in Sources */, FF4CBFCC2C996C0600151637 /* GroupStageSettingsView.swift in Sources */, FF4CBFCD2C996C0600151637 /* TournamentGeneralSettingsView.swift in Sources */, FF4CBFCE2C996C0600151637 /* LoserRoundView.swift in Sources */, FF4CBFCF2C996C0600151637 /* AddTeamView.swift in Sources */, FF4CBFD02C996C0600151637 /* PlayerPayView.swift in Sources */, FF4CBFD12C996C0600151637 /* PlanningByCourtView.swift in Sources */, + C49772052DC260D3005CD239 /* Round+Extensions.swift in Sources */, FF4CBFD22C996C0600151637 /* FileImportManager.swift in Sources */, FFA252B12CDD2C080074E63F /* OngoingContainerView.swift in Sources */, FF4CBFD32C996C0600151637 /* TournamentButtonView.swift in Sources */, FF6761592CC7803600CC9BF2 /* DrawLogsView.swift in Sources */, FF4CBFD42C996C0600151637 /* FederalPlayer.swift in Sources */, FF4CBFD52C996C0600151637 /* NavigationViewModel.swift in Sources */, - FF4CBFD62C996C0600151637 /* FixedWidthInteger+Extensions.swift in Sources */, FF4CBFD72C996C0600151637 /* LoserBracketFromGroupStageView.swift in Sources */, FF4CBFD82C996C0600151637 /* ImportedPlayer+Extensions.swift in Sources */, FF4CBFD92C996C0600151637 /* ClubSearchView.swift in Sources */, @@ -3138,7 +2607,6 @@ FFB378362D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */, FF77CE522CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF4CBFDC2C996C0600151637 /* ActivityView.swift in Sources */, - FF4CBFDD2C996C0600151637 /* MySortDescriptor.swift in Sources */, FF4CBFDE2C996C0600151637 /* CalendarView.swift in Sources */, FF4CBFDF2C996C0600151637 /* FederalTournamentSearchScope.swift in Sources */, FF4CBFE02C996C0600151637 /* TournamentFieldsManagerView.swift in Sources */, @@ -3147,44 +2615,36 @@ FF4CBFE32C996C0600151637 /* DatePickingView.swift in Sources */, FFE8B63B2DACEAED00BDE966 /* ConfigurationService.swift in Sources */, FF4CBFE42C996C0600151637 /* MatchFormatRowView.swift in Sources */, - FF4CBFE52C996C0600151637 /* MonthData.swift in Sources */, FF4CBFE62C996C0600151637 /* MenuWarningView.swift in Sources */, FF4CBFE72C996C0600151637 /* TournamentBuildView.swift in Sources */, FF4CBFE82C996C0600151637 /* TeamPickerView.swift in Sources */, FF4CBFEA2C996C0600151637 /* EventTournamentsView.swift in Sources */, - FF4CBFEB2C996C0600151637 /* DisplayContext.swift in Sources */, - FFB39B342D8E8B05008E0C89 /* MatchSpot.swift in Sources */, FF17CA4B2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */, + C49771EC2DC25F8D005CD239 /* SourceFileManager+Extensions.swift in Sources */, FF4CBFEC2C996C0600151637 /* TournamentCallView.swift in Sources */, FF4CBFED2C996C0600151637 /* LoserRoundsView.swift in Sources */, FF4CBFEE2C996C0600151637 /* GroupStagesView.swift in Sources */, FF4CBFEF2C996C0600151637 /* PadelClubView.swift in Sources */, FFE8B5CC2DAA42A000BDE966 /* XlsToCsvService.swift in Sources */, FF3A73F52D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, - FF4CBFF02C996C0600151637 /* URLs.swift in Sources */, C4D05D4A2DC10CBE009B053C /* PaymentStatusView.swift in Sources */, - FF4CBFF12C996C0600151637 /* MatchDescriptor.swift in Sources */, + C49772392DC28A92005CD239 /* ComposeViews.swift in Sources */, FF4CBFF22C996C0600151637 /* TournamentFormatSelectionView.swift in Sources */, FF17CA592CC02FEB003C7323 /* CoachListView.swift in Sources */, FF4CBFF32C996C0600151637 /* MatchListView.swift in Sources */, FFE8B5BD2DA9896800BDE966 /* RefundService.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 */, FF4CBFF92C996C0600151637 /* DeferredViewModifier.swift in Sources */, FF4CBFFA2C996C0600151637 /* TournamentScheduleView.swift in Sources */, - FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */, FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */, FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */, - FF4CBFFD2C996C0600151637 /* CustomUser.swift in Sources */, FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */, FFE8B5B52DA848D400BDE966 /* OnlineWaitingListFaqSheetView.swift in Sources */, FFA252B52CDD2C6C0074E63F /* OngoingDestination.swift in Sources */, FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */, - FF4CC0002C996C0600151637 /* MockData.swift in Sources */, FF4CC0012C996C0600151637 /* TeamDetailView.swift in Sources */, FFA252AA2CDB70520074E63F /* PlayerStatisticView.swift in Sources */, FF4CC0022C996C0600151637 /* GroupStagesSettingsView.swift in Sources */, @@ -3192,9 +2652,6 @@ FF67615C2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift in Sources */, FF4CC0042C996C0600151637 /* HtmlGenerator.swift in Sources */, FF17CA542CBE4788003C7323 /* BracketCallingView.swift in Sources */, - FF4CC0052C996C0600151637 /* PadelRule.swift in Sources */, - FF4CC0062C996C0600151637 /* TeamRegistration.swift in Sources */, - FF4CC0072C996C0600151637 /* Date+Extensions.swift in Sources */, FF4CC0082C996C0600151637 /* GroupStageView.swift in Sources */, FF4CC0092C996C0600151637 /* Tips.swift in Sources */, FF4CC00A2C996C0600151637 /* MainView.swift in Sources */, @@ -3202,31 +2659,27 @@ FF4CC00C2C996C0600151637 /* PlanningSettingsView.swift in Sources */, FF4CC00D2C996C0600151637 /* MatchScheduleEditorView.swift in Sources */, FF4CC00E2C996C0600151637 /* FortuneWheelView.swift in Sources */, - FF4CC00F2C996C0600151637 /* URL+Extensions.swift in Sources */, FF4CC0102C996C0600151637 /* LoadingViewModifier.swift in Sources */, 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 */, FF4CC0172C996C0600151637 /* PointView.swift in Sources */, FF4CC0182C996C0600151637 /* ClubHolder.swift in Sources */, FF4CC0192C996C0600151637 /* EventSettingsView.swift in Sources */, + C49771E72DC25F04005CD239 /* Color+Extensions.swift in Sources */, + C49771E82DC25F04005CD239 /* Badge+Extensions.swift in Sources */, + C49771E92DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */, FF4CC01A2C996C0600151637 /* InscriptionInfoView.swift in Sources */, FF4CC01B2C996C0600151637 /* SelectablePlayerListView.swift in Sources */, FF4CC01C2C996C0600151637 /* MatchFormatSelectionView.swift in Sources */, FF4CC01D2C996C0600151637 /* TournamentRankView.swift in Sources */, FF77CE5C2CCCD1FF00CBCBB4 /* GroupStageDatePickingView.swift in Sources */, - FF4CC01E2C996C0600151637 /* NumberFormatter+Extensions.swift in Sources */, FF4CC01F2C996C0600151637 /* SetLabelView.swift in Sources */, FF4CC0202C996C0600151637 /* DebugSettingsView.swift in Sources */, FF4CC0212C996C0600151637 /* GroupStageScheduleEditorView.swift in Sources */, FF4CC0222C996C0600151637 /* EventView.swift in Sources */, FF4CC0232C996C0600151637 /* LoginView.swift in Sources */, FFE8B5BF2DAA325400BDE966 /* RefundResultsView.swift in Sources */, - FF4CC0242C996C0600151637 /* Court.swift in Sources */, FF4CC0252C996C0600151637 /* Labels.swift in Sources */, FF4CC0262C996C0600151637 /* CopyPasteButtonView.swift in Sources */, FF4CC0272C996C0600151637 /* PlayerSexPickerView.swift in Sources */, @@ -3236,12 +2689,9 @@ FF4CC02B2C996C0600151637 /* RankCalculatorView.swift in Sources */, FF4CC02C2C996C0600151637 /* DateBoxView.swift in Sources */, FF4CC02D2C996C0600151637 /* LearnMoreSheetView.swift in Sources */, - FF4CC02E2C996C0600151637 /* SourceFileManager.swift in Sources */, FF4623CB2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */, - FF4CC02F2C996C0600151637 /* PListReader.swift in Sources */, FF4CC0302C996C0600151637 /* UpdateSourceRankDateView.swift in Sources */, FF4CC0312C996C0600151637 /* GlobalSettingsView.swift in Sources */, - FF4CC0322C996C0600151637 /* Guard.swift in Sources */, FF4CC0332C996C0600151637 /* PurchaseListView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3253,12 +2703,10 @@ FF70FAC32C90584900129CC2 /* UserCreationView.swift in Sources */, FF70FAC42C90584900129CC2 /* TabDestination.swift in Sources */, FF70FAC52C90584900129CC2 /* CashierView.swift in Sources */, - FF70FAC62C90584900129CC2 /* Event.swift in Sources */, FF77CE572CCCD1EB00CBCBB4 /* DatePickingViewWithFormat.swift in Sources */, FF70FAC72C90584900129CC2 /* PlayerHolder.swift in Sources */, FF70FAC82C90584900129CC2 /* LoserRoundStepScheduleEditorView.swift in Sources */, FF70FAC92C90584900129CC2 /* ClubCourtSetupView.swift in Sources */, - FF70FACA2C90584900129CC2 /* Patcher.swift in Sources */, FF70FACB2C90584900129CC2 /* FileImportView.swift in Sources */, FF70FACC2C90584900129CC2 /* StepperView.swift in Sources */, FF9B442B2D950E48002448C8 /* ConsolationTournamentImportView.swift in Sources */, @@ -3273,7 +2721,6 @@ FF70FAD52C90584900129CC2 /* MatchSetupView.swift in Sources */, FF70FAD62C90584900129CC2 /* NetworkManager.swift in Sources */, FF70FAD72C90584900129CC2 /* EventLinksView.swift in Sources */, - FF70FAD82C90584900129CC2 /* SeedInterval.swift in Sources */, FF70FAD92C90584900129CC2 /* TournamentClubSettingsView.swift in Sources */, FF70FADA2C90584900129CC2 /* GroupStageTeamView.swift in Sources */, FF70FADB2C90584900129CC2 /* RoundSettingsView.swift in Sources */, @@ -3283,25 +2730,19 @@ FF70FADE2C90584900129CC2 /* TeamWeightView.swift in Sources */, FF70FADF2C90584900129CC2 /* SeedsCallingView.swift in Sources */, FF70FAE02C90584900129CC2 /* CallView.swift in Sources */, - FF70FAE12C90584900129CC2 /* Color+Extensions.swift in Sources */, FF70FAE22C90584900129CC2 /* EditSharingView.swift in Sources */, FF70FAE32C90584900129CC2 /* TournamentCellView.swift in Sources */, - FF70FAE42C90584900129CC2 /* Sequence+Extensions.swift in Sources */, FF70FAE52C90584900129CC2 /* CashierDetailView.swift in Sources */, FF70FAE62C90584900129CC2 /* EventClubSettingsView.swift in Sources */, FF70FAE72C90584900129CC2 /* AccountView.swift in Sources */, FF70FAE82C90584900129CC2 /* PlayersWithoutContactView.swift in Sources */, FF17CA4E2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */, FF70FAE92C90584900129CC2 /* TeamsCallingView.swift in Sources */, - 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 */, FFA252AD2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */, FF70FAEE2C90584900129CC2 /* ListRowViewModifier.swift in Sources */, FF70FAEF2C90584900129CC2 /* PresentationContext.swift in Sources */, - FF70FAF02C90584900129CC2 /* AppSettings.swift in Sources */, FF70FAF12C90584900129CC2 /* SwiftParser.swift in Sources */, FF70FAF22C90584900129CC2 /* ChangePasswordView.swift in Sources */, FF70FAF32C90584900129CC2 /* TournamentSubscriptionView.swift in Sources */, @@ -3311,23 +2752,16 @@ FF70FAF72C90584900129CC2 /* EventCreationView.swift in Sources */, 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 */, - 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 */, - C49C73162D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */, - C488C85E2CC7E4240082001F /* BasePlayerRegistration.swift in Sources */, - C488C85F2CC7E4240082001F /* BaseTeamScore.swift in Sources */, - C488C8602CC7E4240082001F /* BaseTournament.swift in Sources */, FF70FAFA2C90584900129CC2 /* BroadcastView.swift in Sources */, + C49771F82DC25F93005CD239 /* CustomUser+Extensions.swift in Sources */, + C49771F92DC25F93005CD239 /* PlayerRegistration+Extensions.swift in Sources */, + C49771FA2DC25F93005CD239 /* TeamRegistration+Extensions.swift in Sources */, + C49771FB2DC25F93005CD239 /* Tournament+Extensions.swift in Sources */, + C49771FC2DC25F93005CD239 /* MonthData+Extensions.swift in Sources */, FF70FAFB2C90584900129CC2 /* SchedulerView.swift in Sources */, FF70FAFC2C90584900129CC2 /* PadelClubButtonView.swift in Sources */, + C49774BB2DC37C10005CD239 /* NetworkMonitor.swift in Sources */, FF70FAFD2C90584900129CC2 /* TournamentSeedEditing.swift in Sources */, FF70FAFE2C90584900129CC2 /* TournamentView.swift in Sources */, FF70FAFF2C90584900129CC2 /* OngoingView.swift in Sources */, @@ -3341,45 +2775,30 @@ FF70FB052C90584900129CC2 /* CourtView.swift in Sources */, FF70FB062C90584900129CC2 /* PointSelectionView.swift in Sources */, 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 */, - FF70FB0D2C90584900129CC2 /* NetworkMonitor.swift in Sources */, FF70FB0E2C90584900129CC2 /* TournamentRunningView.swift in Sources */, FF70FB0F2C90584900129CC2 /* TournamentDatePickerView.swift in Sources */, FF70FB102C90584900129CC2 /* CourtAvailabilitySettingsView.swift in Sources */, FF70FB112C90584900129CC2 /* ConfirmButtonView.swift in Sources */, FF70FB122C90584900129CC2 /* PasswordField.swift in Sources */, FF70FB132C90584900129CC2 /* MatchRowView.swift in Sources */, - FF70FB142C90584900129CC2 /* Locale+Extensions.swift in Sources */, FF70FB152C90584900129CC2 /* HtmlService.swift in Sources */, - FF70FB162C90584900129CC2 /* GroupStage.swift in Sources */, - FF6761552CC77D2100CC9BF2 /* DrawLog.swift in Sources */, FF70FB172C90584900129CC2 /* TournamentCashierView.swift in Sources */, - FF70FB182C90584900129CC2 /* StoreManager.swift in Sources */, FF70FB192C90584900129CC2 /* SearchViewModel.swift in Sources */, - FF70FB1A2C90584900129CC2 /* PlayerRegistration.swift in Sources */, C4339BFC2CFF7D68004E5F09 /* ShareModelView.swift in Sources */, FF70FB1B2C90584900129CC2 /* ImportedPlayerView.swift in Sources */, FF70FB1C2C90584900129CC2 /* EditingTeamView.swift in Sources */, FF3A74342D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */, - FF70FB1D2C90584900129CC2 /* NetworkManagerError.swift in Sources */, - FF70FB1E2C90584900129CC2 /* Tournament.swift in Sources */, - FF70FB1F2C90584900129CC2 /* TournamentStore.swift in Sources */, FF70FB202C90584900129CC2 /* LoserRoundSettingsView.swift in Sources */, 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 */, FF70FB272C90584900129CC2 /* FederalTournamentHolder.swift in Sources */, - FF70FB282C90584900129CC2 /* DataStore.swift in Sources */, - FF70FB292C90584900129CC2 /* SetDescriptor.swift in Sources */, FF70FB2A2C90584900129CC2 /* TeamHeaderView.swift in Sources */, FF70FB2B2C90584900129CC2 /* OrganizedTournamentView.swift in Sources */, FF70FB2C2C90584900129CC2 /* RoundScheduleEditorView.swift in Sources */, @@ -3388,48 +2807,38 @@ FFE8B5C92DAA390900BDE966 /* StripeValidationService.swift in Sources */, FF70FB2F2C90584900129CC2 /* ClubImportView.swift in Sources */, FF70FB302C90584900129CC2 /* UnderlineView.swift in Sources */, - FF70FB312C90584900129CC2 /* MatchScheduler.swift in Sources */, FF70FB322C90584900129CC2 /* CallMessageCustomizationView.swift in Sources */, FF70FB332C90584900129CC2 /* TournamentStatusView.swift in Sources */, FF70FB342C90584900129CC2 /* MatchTeamDetailView.swift in Sources */, FF70FB352C90584900129CC2 /* GenericDestinationPickerView.swift in Sources */, - FF70FB362C90584900129CC2 /* DateInterval.swift in Sources */, FF70FB372C90584900129CC2 /* TableStructureView.swift in Sources */, - FF70FB382C90584900129CC2 /* Purchase.swift in Sources */, - FF70FB392C90584900129CC2 /* Screen.swift in Sources */, - FF70FB3A2C90584900129CC2 /* Round.swift in Sources */, FFBFC3932CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */, FF70FB3B2C90584900129CC2 /* FederalDataViewModel.swift in Sources */, FF70FB3C2C90584900129CC2 /* AgendaDestination.swift in Sources */, FF70FB3D2C90584900129CC2 /* PadelClubApp.xcdatamodeld in Sources */, FF70FB3E2C90584900129CC2 /* SetInputView.swift in Sources */, - C471D1582D0C91FE0068091F /* BaseDrawLog.swift in Sources */, FF70FB3F2C90584900129CC2 /* ButtonValidateView.swift in Sources */, FF70FB402C90584900129CC2 /* ClubRowView.swift in Sources */, FF70FB412C90584900129CC2 /* ClubDetailView.swift in Sources */, FF70FB422C90584900129CC2 /* GroupStageCallingView.swift in Sources */, - FF70FB432C90584900129CC2 /* CryptoKey.swift in Sources */, FF70FB442C90584900129CC2 /* CashierSettingsView.swift in Sources */, FF70FB452C90584900129CC2 /* LoserRoundScheduleEditorView.swift in Sources */, - FF70FB462C90584900129CC2 /* Club.swift in Sources */, FFE8B5B92DA8763800BDE966 /* PaymentInfoSheetView.swift in Sources */, - FF70FB472C90584900129CC2 /* Array+Extensions.swift in Sources */, FF70FB482C90584900129CC2 /* ToolboxView.swift in Sources */, FF70FB492C90584900129CC2 /* Alphabet.swift in Sources */, - FF70FB4A2C90584900129CC2 /* String+Extensions.swift in Sources */, FF70FB4B2C90584900129CC2 /* GroupStageSettingsView.swift in Sources */, FF70FB4C2C90584900129CC2 /* TournamentGeneralSettingsView.swift in Sources */, FF70FB4D2C90584900129CC2 /* LoserRoundView.swift in Sources */, FF70FB4E2C90584900129CC2 /* AddTeamView.swift in Sources */, FF70FB4F2C90584900129CC2 /* PlayerPayView.swift in Sources */, FF70FB502C90584900129CC2 /* PlanningByCourtView.swift in Sources */, + C49772032DC260D3005CD239 /* Round+Extensions.swift in Sources */, FF70FB512C90584900129CC2 /* FileImportManager.swift in Sources */, FFA252B32CDD2C080074E63F /* OngoingContainerView.swift in Sources */, FF70FB522C90584900129CC2 /* TournamentButtonView.swift in Sources */, FF6761572CC7803600CC9BF2 /* DrawLogsView.swift in Sources */, FF70FB532C90584900129CC2 /* FederalPlayer.swift in Sources */, FF70FB542C90584900129CC2 /* NavigationViewModel.swift in Sources */, - FF70FB552C90584900129CC2 /* FixedWidthInteger+Extensions.swift in Sources */, FF70FB562C90584900129CC2 /* LoserBracketFromGroupStageView.swift in Sources */, FF70FB572C90584900129CC2 /* ImportedPlayer+Extensions.swift in Sources */, FF70FB582C90584900129CC2 /* ClubSearchView.swift in Sources */, @@ -3439,7 +2848,6 @@ FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */, FF77CE532CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF70FB5B2C90584900129CC2 /* ActivityView.swift in Sources */, - FF70FB5C2C90584900129CC2 /* MySortDescriptor.swift in Sources */, FF70FB5D2C90584900129CC2 /* CalendarView.swift in Sources */, FF70FB5E2C90584900129CC2 /* FederalTournamentSearchScope.swift in Sources */, FF70FB5F2C90584900129CC2 /* TournamentFieldsManagerView.swift in Sources */, @@ -3448,44 +2856,36 @@ FF70FB622C90584900129CC2 /* DatePickingView.swift in Sources */, FFE8B63A2DACEAED00BDE966 /* ConfigurationService.swift in Sources */, FF70FB632C90584900129CC2 /* MatchFormatRowView.swift in Sources */, - FF70FB642C90584900129CC2 /* MonthData.swift in Sources */, FF70FB652C90584900129CC2 /* MenuWarningView.swift in Sources */, FF70FB662C90584900129CC2 /* TournamentBuildView.swift in Sources */, FF70FB672C90584900129CC2 /* TeamPickerView.swift in Sources */, FF70FB692C90584900129CC2 /* EventTournamentsView.swift in Sources */, - FF70FB6A2C90584900129CC2 /* DisplayContext.swift in Sources */, - FFB39B362D8E8B05008E0C89 /* MatchSpot.swift in Sources */, FF17CA492CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */, + C49771ED2DC25F8D005CD239 /* SourceFileManager+Extensions.swift in Sources */, FF70FB6B2C90584900129CC2 /* TournamentCallView.swift in Sources */, FF70FB6C2C90584900129CC2 /* LoserRoundsView.swift in Sources */, FF70FB6D2C90584900129CC2 /* GroupStagesView.swift in Sources */, FF70FB6E2C90584900129CC2 /* PadelClubView.swift in Sources */, FFE8B5CB2DAA42A000BDE966 /* XlsToCsvService.swift in Sources */, FF3A73F42D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, - FF70FB6F2C90584900129CC2 /* URLs.swift in Sources */, C4D05D4B2DC10CBE009B053C /* PaymentStatusView.swift in Sources */, - FF70FB702C90584900129CC2 /* MatchDescriptor.swift in Sources */, + C497723B2DC28A92005CD239 /* ComposeViews.swift in Sources */, FF70FB712C90584900129CC2 /* TournamentFormatSelectionView.swift in Sources */, FF17CA582CC02FEB003C7323 /* CoachListView.swift in Sources */, FF70FB722C90584900129CC2 /* MatchListView.swift in Sources */, FFE8B5BC2DA9896800BDE966 /* RefundService.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 */, FF70FB782C90584900129CC2 /* DeferredViewModifier.swift in Sources */, FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */, FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */, FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */, - FF70FB7C2C90584900129CC2 /* CustomUser.swift in Sources */, - C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */, FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */, FFE8B5B42DA848D400BDE966 /* OnlineWaitingListFaqSheetView.swift in Sources */, FFA252B72CDD2C6C0074E63F /* OngoingDestination.swift in Sources */, FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */, - FF70FB7F2C90584900129CC2 /* MockData.swift in Sources */, FF70FB802C90584900129CC2 /* TeamDetailView.swift in Sources */, FFA252AB2CDB70520074E63F /* PlayerStatisticView.swift in Sources */, FF70FB812C90584900129CC2 /* GroupStagesSettingsView.swift in Sources */, @@ -3493,9 +2893,6 @@ FF67615B2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift in Sources */, FF70FB832C90584900129CC2 /* HtmlGenerator.swift in Sources */, FF17CA552CBE4788003C7323 /* BracketCallingView.swift in Sources */, - FF70FB842C90584900129CC2 /* PadelRule.swift in Sources */, - FF70FB852C90584900129CC2 /* TeamRegistration.swift in Sources */, - FF70FB862C90584900129CC2 /* Date+Extensions.swift in Sources */, FF70FB872C90584900129CC2 /* GroupStageView.swift in Sources */, FF70FB882C90584900129CC2 /* Tips.swift in Sources */, FF70FB892C90584900129CC2 /* MainView.swift in Sources */, @@ -3503,31 +2900,27 @@ FF70FB8B2C90584900129CC2 /* PlanningSettingsView.swift in Sources */, FF70FB8C2C90584900129CC2 /* MatchScheduleEditorView.swift in Sources */, FF70FB8D2C90584900129CC2 /* FortuneWheelView.swift in Sources */, - FF70FB8E2C90584900129CC2 /* URL+Extensions.swift in Sources */, FF70FB8F2C90584900129CC2 /* LoadingViewModifier.swift in Sources */, 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 */, FF70FB962C90584900129CC2 /* PointView.swift in Sources */, FF70FB972C90584900129CC2 /* ClubHolder.swift in Sources */, FF70FB982C90584900129CC2 /* EventSettingsView.swift in Sources */, + C49771E42DC25F04005CD239 /* Color+Extensions.swift in Sources */, + C49771E52DC25F04005CD239 /* Badge+Extensions.swift in Sources */, + C49771E62DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */, FF70FB992C90584900129CC2 /* InscriptionInfoView.swift in Sources */, FF70FB9A2C90584900129CC2 /* SelectablePlayerListView.swift in Sources */, FF70FB9B2C90584900129CC2 /* MatchFormatSelectionView.swift in Sources */, FF70FB9C2C90584900129CC2 /* TournamentRankView.swift in Sources */, FF77CE5A2CCCD1FF00CBCBB4 /* GroupStageDatePickingView.swift in Sources */, - FF70FB9D2C90584900129CC2 /* NumberFormatter+Extensions.swift in Sources */, FF70FB9E2C90584900129CC2 /* SetLabelView.swift in Sources */, FF70FB9F2C90584900129CC2 /* DebugSettingsView.swift in Sources */, FF70FBA02C90584900129CC2 /* GroupStageScheduleEditorView.swift in Sources */, FF70FBA12C90584900129CC2 /* EventView.swift in Sources */, FF70FBA22C90584900129CC2 /* LoginView.swift in Sources */, FFE8B5C02DAA325400BDE966 /* RefundResultsView.swift in Sources */, - FF70FBA32C90584900129CC2 /* Court.swift in Sources */, FF70FBA42C90584900129CC2 /* Labels.swift in Sources */, FF70FBA52C90584900129CC2 /* CopyPasteButtonView.swift in Sources */, FF70FBA62C90584900129CC2 /* PlayerSexPickerView.swift in Sources */, @@ -3537,12 +2930,9 @@ FF70FBAA2C90584900129CC2 /* RankCalculatorView.swift in Sources */, FF70FBAB2C90584900129CC2 /* DateBoxView.swift in Sources */, FF70FBAC2C90584900129CC2 /* LearnMoreSheetView.swift in Sources */, - FF70FBAD2C90584900129CC2 /* SourceFileManager.swift in Sources */, FF4623CD2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */, - FF70FBAE2C90584900129CC2 /* PListReader.swift in Sources */, FF70FBAF2C90584900129CC2 /* UpdateSourceRankDateView.swift in Sources */, FF70FBB02C90584900129CC2 /* GlobalSettingsView.swift in Sources */, - FF70FBB12C90584900129CC2 /* Guard.swift in Sources */, FF70FBB22C90584900129CC2 /* PurchaseListView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/PadelClub.xcworkspace/contents.xcworkspacedata b/PadelClub.xcworkspace/contents.xcworkspacedata index d6ac0a1..d698402 100644 --- a/PadelClub.xcworkspace/contents.xcworkspacedata +++ b/PadelClub.xcworkspace/contents.xcworkspacedata @@ -4,6 +4,9 @@ + + diff --git a/PadelClub/AppDelegate.swift b/PadelClub/AppDelegate.swift index 6381247..dd39d25 100644 --- a/PadelClub/AppDelegate.swift +++ b/PadelClub/AppDelegate.swift @@ -9,6 +9,7 @@ import Foundation import UIKit import LeStorage import UserNotifications +import PadelClubData class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { @@ -16,6 +17,8 @@ class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDel // Override point for customization after application launch. _ = Guard.main // init guard + + self._configureLeStorage() UIApplication.shared.registerForRemoteNotifications() UNUserNotificationCenter.current().delegate = self @@ -23,6 +26,35 @@ class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDel return true } + fileprivate func _configureLeStorage() { + StoreCenter.main.blackListUserName("apple-test") + +// let secureScheme = true + let domain: String = URLs.activationHost.rawValue + + #if DEBUG + 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(secureScheme: true, domain: domain) + } + #else + StoreCenter.main.configureURLs(secureScheme: true, domain: domain) + #endif + + StoreCenter.main.logsFailedAPICalls() + + var synchronized: Bool = true + + #if DEBUG + if let sync = PListReader.readBool(plist: "local", key: "synchronized") { + synchronized = sync + } + #endif + + StoreCenter.main.forceNoSynchronization = !synchronized + } func applicationWillEnterForeground(_ application: UIApplication) { Task { try await Guard.main.refreshPurchasedAppleProducts() diff --git a/PadelClub/Data/AppSettings.swift b/PadelClub/Data/AppSettings.swift deleted file mode 100644 index 4dbc2db..0000000 --- a/PadelClub/Data/AppSettings.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// AppSettings.swift -// PadelClub -// -// Created by Razmig Sarkissian on 26/03/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -@Observable -final class AppSettings: MicroStorable { - - var lastDataSource: String? = nil - var didCreateAccount: Bool = false - var didRegisterAccount: Bool = false - - //search tournament stuff - var tournamentCategories: Set - var tournamentLevels: Set - var tournamentAges: Set - var tournamentTypes: Set - var startDate: Date - var endDate: Date - var city: String - var distance: Double - var sortingOption: String - var nationalCup: Bool - var dayDuration: Int? - var dayPeriod: DayPeriod - - func lastDataSourceDate() -> Date? { - guard let lastDataSource else { return nil } - return URL.importDateFormatter.date(from: lastDataSource) - } - - func localizedLastDataSource() -> String? { - guard let lastDataSource else { return nil } - guard let date = URL.importDateFormatter.date(from: lastDataSource) else { return nil } - - return date.monthYearFormatted - } - - func resetSearch() { - tournamentAges = Set() - tournamentTypes = Set() - tournamentLevels = Set() - tournamentCategories = Set() - city = "" - distance = 30 - startDate = Date() - endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())! - sortingOption = "dateDebut+asc" - nationalCup = false - dayDuration = nil - dayPeriod = .all - } - - required init() { - tournamentAges = Set() - tournamentTypes = Set() - tournamentLevels = Set() - tournamentCategories = Set() - city = "" - distance = 30 - startDate = Date() - endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())! - sortingOption = "dateDebut+asc" - nationalCup = false - dayDuration = nil - dayPeriod = .all - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - lastDataSource = try container.decodeIfPresent(String.self, forKey: ._lastDataSource) - didCreateAccount = try container.decodeIfPresent(Bool.self, forKey: ._didCreateAccount) ?? false - didRegisterAccount = try container.decodeIfPresent(Bool.self, forKey: ._didRegisterAccount) ?? false - tournamentCategories = try container.decodeIfPresent(Set.self, forKey: ._tournamentCategories) ?? Set() - tournamentLevels = try container.decodeIfPresent(Set.self, forKey: ._tournamentLevels) ?? Set() - tournamentAges = try container.decodeIfPresent(Set.self, forKey: ._tournamentAges) ?? Set() - tournamentTypes = try container.decodeIfPresent(Set.self, forKey: ._tournamentTypes) ?? Set() - startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date() - endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? Calendar.current.date(byAdding: .month, value: 3, to: Date())! - city = try container.decodeIfPresent(String.self, forKey: ._city) ?? "" - distance = try container.decodeIfPresent(Double.self, forKey: ._distance) ?? 30 - sortingOption = try container.decodeIfPresent(String.self, forKey: ._sortingOption) ?? "dateDebut+asc" - nationalCup = try container.decodeIfPresent(Bool.self, forKey: ._nationalCup) ?? false - dayDuration = try container.decodeIfPresent(Int.self, forKey: ._dayDuration) - dayPeriod = try container.decodeIfPresent(DayPeriod.self, forKey: ._dayPeriod) ?? .all - } - - enum CodingKeys: String, CodingKey { - case _lastDataSource = "lastDataSource" - case _didCreateAccount = "didCreateAccount" - case _didRegisterAccount = "didRegisterAccount" - case _tournamentCategories = "tournamentCategories" - case _tournamentLevels = "tournamentLevels" - case _tournamentAges = "tournamentAges" - case _tournamentTypes = "tournamentTypes" - case _startDate = "startDate" - case _endDate = "endDate" - case _city = "city" - case _distance = "distance" - case _sortingOption = "sortingOption" - case _nationalCup = "nationalCup" - case _dayDuration = "dayDuration" - case _dayPeriod = "dayPeriod" - } -} diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift deleted file mode 100644 index a3b61f5..0000000 --- a/PadelClub/Data/Club.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// Club.swift -// PadelClub -// -// Created by Laurent Morvillier on 02/02/2024. -// - -import Foundation -import SwiftUI -import LeStorage - -@Observable -final class Club: BaseClub { - - func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String { - switch displayStyle { - case .wide, .title: - return name - case .short: - return acronym - } - } - - func shareURL() -> URL? { - return URL(string: URLs.main.url.appending(path: "?club=\(id)").absoluteString.removingPercentEncoding!) - } - - var customizedCourts: [Court] { - DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index) - } - - override func deleteDependencies() { - let customizedCourts = self.customizedCourts - for customizedCourt in customizedCourts { - customizedCourt.deleteDependencies() - } - DataStore.shared.courts.deleteDependencies(customizedCourts) - } - -} - -extension Club { - var isValid: Bool { - name.isEmpty == false && name.count > 3 - } - - func automaticShortName() -> String { - name.acronym() - } - - enum AcronymMode: String, CaseIterable { - case automatic = "Automatique" - case custom = "Personalisée" - } - - func shortNameMode() -> AcronymMode { - (acronym.isEmpty || acronym == automaticShortName()) ? .automatic : .custom - } - - func hasTenupId() -> Bool { - code != nil - } - - func federalLink() -> URL? { - guard let code else { return nil } - return URL(string: "https://tenup.fft.fr/club/\(code)") - } - - func courtName(atIndex courtIndex: Int) -> String { - courtNameIfAvailable(atIndex: courtIndex) ?? Court.courtIndexedTitle(atIndex: courtIndex) - } - - func courtNameIfAvailable(atIndex courtIndex: Int) -> String? { - customizedCourts.first(where: { $0.index == courtIndex })?.name - } - - func update(fromClub club: Club) { - self.acronym = club.acronym - self.name = club.name - 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 - } - - func hasBeenCreated(by creatorId: String?) -> Bool { - return creatorId == creator || creator == nil || self.relatedUser == creatorId - } - - func isFavorite() -> Bool { - return DataStore.shared.user.clubs.contains(where: { $0 == self.id }) - } - - static func findOrCreate(name: String, code: String?, city: String? = nil, zipCode: String? = nil) -> Club { - - /* - - identify a club : code, name, ?? - - */ - let club: Club? = DataStore.shared.clubs.first(where: { (code == nil && $0.name == name && $0.city == city && $0.zipCode == zipCode) || code != nil && $0.code == code }) - - if let club { - return club - } else { - let club = Club(creator: StoreCenter.main.userId, name: name, acronym: name.acronym(), code: code, city: city, zipCode: zipCode) - club.relatedUser = StoreCenter.main.userId - return club - } - } - -} diff --git a/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift b/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift index d08eaa6..d65f340 100644 --- a/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift +++ b/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift @@ -6,6 +6,7 @@ // import Foundation +import PadelClubData extension ImportedPlayer: PlayerHolder { func getAssimilatedAsMaleRank() -> Int? { diff --git a/PadelClub/Data/Court.swift b/PadelClub/Data/Court.swift deleted file mode 100644 index a1a7a7b..0000000 --- a/PadelClub/Data/Court.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// Court.swift -// PadelClub -// -// Created by Razmig Sarkissian on 23/04/2024. -// - -import Foundation -import SwiftUI -import LeStorage - -@Observable -final class Court: BaseCourt { - - static func == (lhs: Court, rhs: Court) -> Bool { - lhs.id == rhs.id - } - - 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 - self.name = name - self.exitAllowed = exitAllowed - self.indoor = indoor - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - - func courtTitle() -> String { - self.name ?? courtIndexTitle() - } - - func courtIndexTitle() -> String { - Self.courtIndexedTitle(atIndex: index) - } - - static func courtIndexedTitle(atIndex index: Int) -> String { - ("Terrain #" + (index + 1).formatted()) - } - - func clubObject() -> Club? { - Store.main.findById(club) - } - - override func deleteDependencies() { - } - -} diff --git a/PadelClub/Data/CustomUser.swift b/PadelClub/Data/CustomUser.swift deleted file mode 100644 index 6b27a25..0000000 --- a/PadelClub/Data/CustomUser.swift +++ /dev/null @@ -1,316 +0,0 @@ -// -// User.swift -// PadelClub -// -// Created by Laurent Morvillier on 21/02/2024. -// - -import Foundation -import LeStorage - -enum UserRight: Int, Codable { - case none = 0 - case edition = 1 - case creation = 2 -} - -enum RegistrationPaymentMode: Int, Codable { - case disabled = 0 - case corporate = 1 - case noFee = 2 - case stripe = 3 - - static let stripeFixedFee = 0.25 // Fixed fee in euros - static let stripePercentageFee = 0.014 // 1.4% - - func canEnableOnlinePayment() -> Bool { - switch self { - case .disabled: - return false - case .corporate: - return true - case .noFee: - return true - case .stripe: - return true - } - } - - func requiresStripe() -> Bool { - switch self { - case .disabled: - return false - case .corporate: - return true - case .noFee: - return true - case .stripe: - return true - } - } -} - -@Observable -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? - - 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) - - -// 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) - } - - required public init() { - super.init() - } - - public func uuid() throws -> UUID { - if let uuid = UUID(uuidString: self.id) { - return uuid - } - throw UUIDError.cantConvertString(string: self.id) - } - - func currentPlayerData() -> ImportedPlayer? { - guard let licenceId = self.licenceId?.strippedLicense else { return nil } - let federalContext = PersistenceController.shared.localContainer.viewContext - let fetchRequest = ImportedPlayer.fetchRequest() - let predicate = NSPredicate(format: "license == %@", licenceId) - fetchRequest.predicate = predicate - return try? federalContext.fetch(fetchRequest).first - } - - func defaultSignature(_ tournament: Tournament?) -> String { - let fullName = tournament?.umpireCustomContact ?? fullName() - return "Sportivement,\n\(fullName), votre JAP." - } - - func fullName() -> String { - [firstName, lastName].joined(separator: " ") - } - - func hasTenupClubs() -> Bool { - self.clubsObjects().filter({ $0.code != nil }).isEmpty == false - } - - func hasFavoriteClubsAndCreatedClubs() -> Bool { - clubsObjects(includeCreated: true).isEmpty == false - } - - func setUserClub(_ userClub: Club) { - self.clubs.insert(userClub.id, at: 0) - } - - func clubsObjects(includeCreated: Bool = false) -> [Club] { - return DataStore.shared.clubs.filter({ (includeCreated && $0.creator == id) || clubs.contains($0.id) }) - } - - func createdClubsObjectsNotFavorite() -> [Club] { - return DataStore.shared.clubs.filter({ ($0.creator == id) && clubs.contains($0.id) == false }) - } - - func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) { - if estimatedDuration == matchFormat.defaultEstimatedDuration { - matchFormatsDefaultDuration?.removeValue(forKey: matchFormat) - } else { - matchFormatsDefaultDuration = matchFormatsDefaultDuration ?? [MatchFormat: Int]() - matchFormatsDefaultDuration?[matchFormat] = estimatedDuration - } - } - - func addClub(_ club: Club) { - if !self.clubs.contains(where: { $0.id == club.id }) { - self.clubs.append(club.id) - } - } - - func getSummonsMessageSignature() -> String? { - if let summonsMessageSignature, summonsMessageSignature.isEmpty == false { - return summonsMessageSignature - } else { - return nil - } - } - - func canEnableOnlinePayment() -> Bool { - registrationPaymentMode.canEnableOnlinePayment() - } -// 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) - } - -} - -class UserCreationForm: CustomUser, UserPasswordBase { - - 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) - - self.summonsMessageBody = user.summonsMessageBody - self.summonsMessageSignature = user.summonsMessageSignature - self.summonsAvailablePaymentMethods = user.summonsAvailablePaymentMethods - self.summonsDisplayFormat = user.summonsDisplayFormat - self.summonsDisplayEntryFee = user.summonsDisplayEntryFee - self.summonsUseFullCustomMessage = user.summonsUseFullCustomMessage - self.matchFormatsDefaultDuration = user.matchFormatsDefaultDuration - self.bracketMatchFormatPreference = user.bracketMatchFormatPreference - self.groupStageMatchFormatPreference = user.groupStageMatchFormatPreference - self.loserBracketMatchFormatPreference = user.loserBracketMatchFormatPreference - - } - - required init(from decoder: Decoder) throws { - fatalError("init(from:) has not been implemented") - } - - required public init() { - self.password = "" - super.init() - } - - public var password: String - - private enum CodingKeys: String, CodingKey { - case password - } - - override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.password, forKey: .password) - } -} diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift deleted file mode 100644 index 655733c..0000000 --- a/PadelClub/Data/DataStore.swift +++ /dev/null @@ -1,403 +0,0 @@ -// -// DataStore.swift -// PadelClub -// -// Created by Laurent Morvillier on 02/02/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -class DataStore: ObservableObject { - - static let shared = DataStore() - - @Published var user: CustomUser = CustomUser.placeHolder() { - didSet { - let loggedUser = StoreCenter.main.isAuthenticated - - if loggedUser { - if self.user.id != self.userStorage.item()?.id { - self.userStorage.setItemNoSync(self.user) - StoreCenter.main.initialSynchronization(clear: false) - self._fixMissingClubCreatorIfNecessary(self.clubs) - self._fixMissingEventCreatorIfNecessary(self.events) - } - } else { - self._temporaryLocalUser.item = self.user - } - } - } - - fileprivate(set) var tournaments: SyncedCollection - fileprivate(set) var clubs: SyncedCollection - fileprivate(set) var courts: SyncedCollection - fileprivate(set) var events: SyncedCollection - fileprivate(set) var monthData: StoredCollection - fileprivate(set) var dateIntervals: SyncedCollection - fileprivate(set) var purchases: SyncedCollection - - var userStorage: StoredSingleton - - fileprivate var _temporaryLocalUser: OptionalStorage = OptionalStorage(fileName: "tmp_local_user.json") - fileprivate(set) var appSettingsStorage: MicroStorage = MicroStorage(fileName: "appsettings.json") - - var appSettings: AppSettings { - appSettingsStorage.item - } - - init() { - let store = Store.main - StoreCenter.main.blackListUserName("apple-test") - -// let secureScheme = true - let domain: String = URLs.activationHost.rawValue - - #if DEBUG - 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(secureScheme: true, domain: domain) - } - #else - StoreCenter.main.configureURLs(secureScheme: true, domain: domain) - #endif - - StoreCenter.main.logsFailedAPICalls() - - var synchronized: Bool = true - - #if DEBUG - if let sync = PListReader.readBool(plist: "local", key: "synchronized") { - synchronized = sync - } - #endif - - StoreCenter.main.forceNoSynchronization = !synchronized - - - let indexed: Bool = true - 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.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) - StoreCenter.main.loadApiCallCollection(type: Round.self) - StoreCenter.main.loadApiCallCollection(type: PlayerRegistration.self) - 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) - NotificationCenter.default.addObserver( - self, - selector: #selector(_willEnterForegroundNotification), - name: UIScene.willEnterForegroundNotification, - object: nil) - - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - func saveUser() { - if user.username.count > 0 { - self.userStorage.update() - } else { - self._temporaryLocalUser.item = self.user - } - } - - @objc func collectionDidLoad(notification: Notification) { - - if let userSingleton: StoredSingleton = notification.object as? StoredSingleton { - self.user = userSingleton.item() ?? self._temporaryLocalUser.item ?? CustomUser.placeHolder() - } else if let clubsCollection: SyncedCollection = notification.object as? SyncedCollection { - self._fixMissingClubCreatorIfNecessary(clubsCollection) - } else if let eventsCollection: SyncedCollection = notification.object as? SyncedCollection { - self._fixMissingEventCreatorIfNecessary(eventsCollection) - } - - if Store.main.fileCollectionsAllLoaded() { - AutomaticPatcher.applyAllWhenApplicable() - } - - } - - fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: SyncedCollection) { - 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) - } - } - } - - fileprivate func _fixMissingEventCreatorIfNecessary(_ eventsCollection: SyncedCollection) { - for event in eventsCollection { - if let userId = StoreCenter.main.userId, event.creator == nil { - event.creator = userId - do { - try event.insertOnServer() - } catch { - Logger.error(error) - } - } - } - } - - @objc func collectionDidUpdate(notification: Notification) { - self.objectWillChange.send() - } - - @objc func _willEnterForegroundNotification() { - Task { - try await self.purchases.loadDataFromServerIfAllowed(clear: true) - } - } - - func disconnect() { - - Task { - if await StoreCenter.main.hasPendingAPICalls() { - // todo qu'est ce qu'on fait des API Call ? - } - - do { - let services = try StoreCenter.main.service() - try await services.logout() - } catch { - Logger.error(error) - } - - DispatchQueue.main.async { - self._localDisconnect() - } - } - - } - - func deleteAccount() { - - Task { - do { - let services = try StoreCenter.main.service() - try await services.deleteAccount() - } catch { - Logger.error(error) - } - - DispatchQueue.main.async { - self._localDisconnect() - } - - } - - } - - 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() { - - let tournamendIds: [String] = self.tournaments.map { $0.id } - - TournamentLibrary.shared.reset() - - self.tournaments.reset() - self.clubs.reset() - self.courts.reset() - self.events.reset() - self.dateIntervals.reset() - self.userStorage.reset() - self.purchases.reset() - - Guard.main.disconnect() - - StoreCenter.main.disconnect() - - for tournament in tournamendIds { - StoreCenter.main.destroyStore(identifier: tournament.id) - } - - self.user = self._temporaryLocalUser.item ?? CustomUser.placeHolder() - self.user.clubs.removeAll() - - } - - func tryUserUpdate(userCopy: CustomUser) async throws { - try await self.userStorage.tryPutBeforeUpdating(userCopy) - } - -// 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 - - private var _lastRunningCheckDate: Date? = nil - private var _cachedRunningMatches: [Match]? = nil - - func runningMatches() -> [Match] { - let dateNow : Date = Date() - if let lastCheck = _lastRunningCheckDate, - let cachedMatches = _cachedRunningMatches, - dateNow.timeIntervalSince(lastCheck) < 5 { - return cachedMatches - } - let lastTournaments = self.tournaments.filter { $0.isDeleted == false && $0.startDate <= dateNow && $0.hasEnded() == false }.sorted(by: \Tournament.startDate, order: .descending).prefix(10) - - var runningMatches: [Match] = [] - for tournament in lastTournaments { - if let store = tournament.tournamentStore { - let matches = store.matches.filter { match in - match.disabled == false && match.isRunning() - } - runningMatches.append(contentsOf: matches) - } - } - _lastRunningCheckDate = dateNow - _cachedRunningMatches = runningMatches - return _cachedRunningMatches! - } - - private var _lastRunningAndNextCheckDate: Date? = nil - private var _cachedRunningAndNextMatches: [Match]? = nil - - func runningAndNextMatches() -> [Match] { - let dateNow : Date = Date() - if let lastCheck = _lastRunningAndNextCheckDate, - let cachedMatches = _cachedRunningAndNextMatches, - dateNow.timeIntervalSince(lastCheck) < 5 { - return cachedMatches - } - - let lastTournaments = self.tournaments.filter { $0.isDeleted == false && $0.startDate <= dateNow && $0.hasEnded() == false }.sorted(by: \Tournament.startDate, order: .descending).prefix(10) - - var runningMatches: [Match] = [] - for tournament in lastTournaments { - 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) - } - } - _lastRunningAndNextCheckDate = dateNow - _cachedRunningAndNextMatches = runningMatches - return _cachedRunningAndNextMatches! - } - - private var _lastEndCheckDate: Date? = nil - private var _cachedEndMatches: [Match]? = nil - - func endMatches() -> [Match] { - let dateNow : Date = Date() - - if let lastCheck = _lastEndCheckDate, - let cachedMatches = _cachedEndMatches, - dateNow.timeIntervalSince(lastCheck) < 5 { - return cachedMatches - } - - let lastTournaments = self.tournaments.filter { $0.isDeleted == false && $0.startDate <= dateNow && $0.hasEnded() == false }.sorted(by: \Tournament.startDate, order: .descending).prefix(10) - - var runningMatches: [Match] = [] - for tournament in lastTournaments { - if let store = tournament.tournamentStore { - let matches = store.matches.filter { match in - match.disabled == false && match.hasEnded() } - runningMatches.append(contentsOf: matches) - } - } - - _lastEndCheckDate = dateNow - _cachedEndMatches = runningMatches.sorted(by: \.endDate!, order: .descending) - return _cachedEndMatches! - } - - func subscriptionUnitlyPayedTournaments(after date: Date) -> Int { - return DataStore.shared.tournaments.count(where: { tournament in - tournament.creationDate > date && - tournament.payment == .subscriptionUnit && - tournament.isCanceled == false && - tournament.isDeleted == false }) - } - -} diff --git a/PadelClub/Data/DateInterval.swift b/PadelClub/Data/DateInterval.swift deleted file mode 100644 index 94db947..0000000 --- a/PadelClub/Data/DateInterval.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// DateInterval.swift -// PadelClub -// -// Created by Razmig Sarkissian on 19/04/2024. -// - -import Foundation -import SwiftUI -import LeStorage - -@Observable -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 - - internal init(event: String, courtIndex: Int, startDate: Date, endDate: Date) { - 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) - } - - required public init() { - super.init() - } - - var range: Range { - startDate.. Bool { - Calendar.current.isDate(startDate, inSameDayAs: endDate) - } - - func isDateInside(_ date: Date) -> Bool { - date >= startDate && date <= endDate - } - - func isDateOutside(_ date: Date) -> Bool { - date <= startDate && date <= endDate && date >= startDate && date >= endDate - } - - 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" -// } - - func insertOnServer() throws { - DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self) - } - -} diff --git a/PadelClub/Data/DrawLog.swift b/PadelClub/Data/DrawLog.swift deleted file mode 100644 index a0be6e3..0000000 --- a/PadelClub/Data/DrawLog.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// DrawLog.swift -// PadelClub -// -// Created by razmig on 22/10/2024. -// - -import Foundation -import SwiftUI -import LeStorage - -@Observable -final class DrawLog: BaseDrawLog, SideStorable { - - func tournamentObject() -> Tournament? { - Store.main.findById(self.tournament) - } - - func computedBracketPosition() -> Int { - drawMatchIndex * 2 + drawTeamPosition.rawValue - } - - func updateTeamBracketPosition(_ team: TeamRegistration) { - guard let match = drawMatch() else { return } - let seedPosition: Int = match.lockAndGetSeedPosition(atTeamPosition: drawTeamPosition) - team.bracketPosition = seedPosition - tournamentObject()?.updateTeamScores(in: seedPosition) - } - - func exportedDrawLog() -> String { - [drawType.localizedDrawType(), drawDate.localizedDate(), localizedDrawLogLabel(), localizedDrawBranch()].filter({ $0.isEmpty == false }).joined(separator: " ") - } - - func localizedDrawSeedLabel() -> String { - return "\(drawType.localizedDrawType()) #\(drawSeed + 1)" - } - - func localizedDrawLogLabel() -> String { - return [localizedDrawSeedLabel(), positionLabel()].filter({ $0.isEmpty == false }).joined(separator: " -> ") - } - - func localizedDrawBranch() -> String { - switch drawType { - case .seed: - return drawTeamPosition.localizedBranchLabel() - default: - return "" - } - } - - func drawMatch() -> Match? { - 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 }) - default: - return nil - } - } - - func positionLabel() -> String { - return drawMatch()?.roundAndMatchTitle() ?? "" - } - - func roundLabel() -> String { - return drawMatch()?.roundTitle() ?? "" - } - - func matchLabel() -> String { - return drawMatch()?.matchTitle() ?? "" - } - - var tournamentStore: TournamentStore? { - return TournamentLibrary.shared.store(tournamentId: self.tournament) - } - - override func deleteDependencies() { - } - -} - -enum DrawType: Int, Codable { - case seed - case groupStage - case court - - func localizedDrawType() -> String { - switch self { - case .seed: - return "Tête de série" - case .groupStage: - return "Poule" - case .court: - return "Terrain" - } - } -} diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift deleted file mode 100644 index ed3d6e4..0000000 --- a/PadelClub/Data/Event.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// Event_v2.swift -// Padel Tournament -// -// Created by razmig on 10/03/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -@Observable -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 { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - - override func deleteDependencies() { - let tournaments = self.tournaments - for tournament in tournaments { - tournament.deleteDependencies() - } - - DataStore.shared.tournaments.deleteDependencies(tournaments) - - let courtsUnavailabilities = self.courtsUnavailability - for courtsUnavailability in courtsUnavailabilities { - courtsUnavailability.deleteDependencies() - } - DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities) - } - - // MARK: - Computed dependencies - - var tournaments: [Tournament] { - DataStore.shared.tournaments.filter { $0.event == self.id && $0.isDeleted == false } - } - - func clubObject() -> Club? { - guard let club else { return nil } - return Store.main.findById(club) - } - - var courtsUnavailability: [DateInterval] { - DataStore.shared.dateIntervals.filter({ $0.event == id }) - } - - // MARK: - - - func eventCourtCount() -> Int { - tournaments.map { $0.courtCount }.max() ?? 2 - } - - func eventStartDate() -> Date { - tournaments.map { $0.startDate }.min() ?? Date() - } - - func eventDayDuration() -> Int { - tournaments.map { $0.dayDuration }.max() ?? 1 - } - - func eventTitle() -> String { - if let name, name.isEmpty == false { - return name - } else { - return "Événement" - } - } - - func existingBuild(_ build: any TournamentBuildHolder) -> Tournament? { - tournaments.first(where: { $0.isSameBuild(build) }) - } - - func tournamentsCourtsUsed(exluding tournamentId: String) -> [DateInterval] { - tournaments.filter { $0.id != tournamentId }.flatMap({ tournament in - tournament.getPlayedMatchDateIntervals(in: self) - }) - } - - func insertOnServer() throws { - DataStore.shared.events.writeChangeAndInsertOnServer(instance: self) - for tournament in self.tournaments { - try tournament.insertOnServer() - } - for dataInterval in self.courtsUnavailability { - try dataInterval.insertOnServer() - } - } - -} diff --git a/PadelClub/Data/Federal/FederalTournament.swift b/PadelClub/Data/Federal/FederalTournament.swift index d8d07f7..debddba 100644 --- a/PadelClub/Data/Federal/FederalTournament.swift +++ b/PadelClub/Data/Federal/FederalTournament.swift @@ -6,25 +6,8 @@ import Foundation import CoreLocation import LeStorage +import PadelClubData -enum DayPeriod: Int, CaseIterable, Identifiable, Codable { - var id: Int { self.rawValue } - - case all - case weekend - case week - - func localizedDayPeriodLabel() -> String { - switch self { - case .all: - return "n'importe" - case .week: - return "la semaine" - case .weekend: - return "le week-end" - } - } -} // MARK: - FederalTournament struct FederalTournament: Identifiable, Codable { diff --git a/PadelClub/Data/Federal/FederalTournamentHolder.swift b/PadelClub/Data/Federal/FederalTournamentHolder.swift index 594e98b..edb617e 100644 --- a/PadelClub/Data/Federal/FederalTournamentHolder.swift +++ b/PadelClub/Data/Federal/FederalTournamentHolder.swift @@ -6,6 +6,7 @@ // import Foundation +import PadelClubData protocol FederalTournamentHolder { var holderId: String { get } diff --git a/PadelClub/Data/Gen/BaseClub.swift b/PadelClub/Data/Gen/BaseClub.swift deleted file mode 100644 index c55acf3..0000000 --- a/PadelClub/Data/Gen/BaseClub.swift +++ /dev/null @@ -1,150 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseClub: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "clubs" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = true - - var id: String = Store.randomId() - 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 - var timezone: String? = TimeZone.current.identifier - - init( - id: String = Store.randomId(), - 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, - timezone: String? = TimeZone.current.identifier - ) { - super.init() - self.id = id - 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 - self.timezone = timezone - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - 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 _timezone = "timezone" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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 - self.timezone = try container.decodeIfPresent(String.self, forKey: ._timezone) ?? TimeZone.current.identifier - try super.init(from: decoder) - } - - 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.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) - try container.encode(self.timezone, forKey: ._timezone) - try super.encode(to: encoder) - } - - 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.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 - self.timezone = club.timezone - } - - 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 deleted file mode 100644 index eb57668..0000000 --- a/PadelClub/Data/Gen/BaseCourt.swift +++ /dev/null @@ -1,93 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseCourt: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "courts" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - var index: Int = 0 - var club: String = "" - var name: String? = nil - var exitAllowed: Bool = false - var indoor: Bool = false - - init( - id: String = Store.randomId(), - index: Int = 0, - club: String = "", - name: String? = nil, - exitAllowed: Bool = false, - indoor: Bool = false - ) { - super.init() - self.id = id - self.index = index - self.club = club - self.name = name - self.exitAllowed = exitAllowed - self.indoor = indoor - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - case _index = "index" - case _club = "club" - case _name = "name" - case _exitAllowed = "exitAllowed" - case _indoor = "indoor" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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) - } - - 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.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? { - return Store.main.findById(club) - } - - func copy(from other: any Storable) { - guard let court = other as? BaseCourt else { return } - self.id = court.id - 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), - ] - } - -} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseCustomUser.swift b/PadelClub/Data/Gen/BaseCustomUser.swift deleted file mode 100644 index f229bd4..0000000 --- a/PadelClub/Data/Gen/BaseCustomUser.swift +++ /dev/null @@ -1,262 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseCustomUser: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "users" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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 disableRankingFederalRuling: Bool = false - var deviceId: String? = nil - var agents: [String] = [] - var userRole: Int? = nil - var registrationPaymentMode: RegistrationPaymentMode = RegistrationPaymentMode.disabled - var umpireCustomMail: String? = nil - var umpireCustomContact: String? = nil - var umpireCustomPhone: String? = nil - var hideUmpireMail: Bool = false - var hideUmpirePhone: Bool = true - - init( - id: String = Store.randomId(), - 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, - disableRankingFederalRuling: Bool = false, - deviceId: String? = nil, - agents: [String] = [], - userRole: Int? = nil, - registrationPaymentMode: RegistrationPaymentMode = RegistrationPaymentMode.disabled, - umpireCustomMail: String? = nil, - umpireCustomContact: String? = nil, - umpireCustomPhone: String? = nil, - hideUmpireMail: Bool = false, - hideUmpirePhone: Bool = true - ) { - super.init() - self.id = id - 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.disableRankingFederalRuling = disableRankingFederalRuling - self.deviceId = deviceId - self.agents = agents - self.userRole = userRole - self.registrationPaymentMode = registrationPaymentMode - self.umpireCustomMail = umpireCustomMail - self.umpireCustomContact = umpireCustomContact - self.umpireCustomPhone = umpireCustomPhone - self.hideUmpireMail = hideUmpireMail - self.hideUmpirePhone = hideUmpirePhone - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - 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 _disableRankingFederalRuling = "disableRankingFederalRuling" - case _deviceId = "deviceId" - case _agents = "agents" - case _userRole = "userRole" - case _registrationPaymentMode = "registrationPaymentMode" - case _umpireCustomMail = "umpireCustomMail" - case _umpireCustomContact = "umpireCustomContact" - case _umpireCustomPhone = "umpireCustomPhone" - case _hideUmpireMail = "hideUmpireMail" - case _hideUmpirePhone = "hideUmpirePhone" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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.disableRankingFederalRuling = try container.decodeIfPresent(Bool.self, forKey: ._disableRankingFederalRuling) ?? false - self.deviceId = try container.decodeIfPresent(String.self, forKey: ._deviceId) ?? nil - self.agents = try container.decodeIfPresent([String].self, forKey: ._agents) ?? [] - self.userRole = try container.decodeIfPresent(Int.self, forKey: ._userRole) ?? nil - self.registrationPaymentMode = try container.decodeIfPresent(RegistrationPaymentMode.self, forKey: ._registrationPaymentMode) ?? RegistrationPaymentMode.disabled - self.umpireCustomMail = try container.decodeIfPresent(String.self, forKey: ._umpireCustomMail) ?? nil - self.umpireCustomContact = try container.decodeIfPresent(String.self, forKey: ._umpireCustomContact) ?? nil - self.umpireCustomPhone = try container.decodeIfPresent(String.self, forKey: ._umpireCustomPhone) ?? nil - self.hideUmpireMail = try container.decodeIfPresent(Bool.self, forKey: ._hideUmpireMail) ?? false - self.hideUmpirePhone = try container.decodeIfPresent(Bool.self, forKey: ._hideUmpirePhone) ?? true - try super.init(from: decoder) - } - - 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.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.disableRankingFederalRuling, forKey: ._disableRankingFederalRuling) - try container.encode(self.deviceId, forKey: ._deviceId) - try container.encode(self.agents, forKey: ._agents) - try container.encode(self.userRole, forKey: ._userRole) - try container.encode(self.registrationPaymentMode, forKey: ._registrationPaymentMode) - try container.encode(self.umpireCustomMail, forKey: ._umpireCustomMail) - try container.encode(self.umpireCustomContact, forKey: ._umpireCustomContact) - try container.encode(self.umpireCustomPhone, forKey: ._umpireCustomPhone) - try container.encode(self.hideUmpireMail, forKey: ._hideUmpireMail) - try container.encode(self.hideUmpirePhone, forKey: ._hideUmpirePhone) - try super.encode(to: encoder) - } - - func copy(from other: any Storable) { - guard let customuser = other as? BaseCustomUser else { return } - self.id = customuser.id - 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.disableRankingFederalRuling = customuser.disableRankingFederalRuling - self.deviceId = customuser.deviceId - self.agents = customuser.agents - self.userRole = customuser.userRole - self.registrationPaymentMode = customuser.registrationPaymentMode - self.umpireCustomMail = customuser.umpireCustomMail - self.umpireCustomContact = customuser.umpireCustomContact - self.umpireCustomPhone = customuser.umpireCustomPhone - self.hideUmpireMail = customuser.hideUmpireMail - self.hideUmpirePhone = customuser.hideUmpirePhone - } - - 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 deleted file mode 100644 index 4f3f09c..0000000 --- a/PadelClub/Data/Gen/BaseDateInterval.swift +++ /dev/null @@ -1,80 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseDateInterval: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "date-intervals" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - var event: String = "" - var courtIndex: Int = 0 - var startDate: Date = Date() - var endDate: Date = Date() - - init( - id: String = Store.randomId(), - event: String = "", - courtIndex: Int = 0, - startDate: Date = Date(), - endDate: Date = Date() - ) { - super.init() - self.id = id - self.event = event - self.courtIndex = courtIndex - self.startDate = startDate - self.endDate = endDate - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - case _event = "event" - case _courtIndex = "courtIndex" - case _startDate = "startDate" - case _endDate = "endDate" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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) - } - - 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.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.event = dateinterval.event - self.courtIndex = dateinterval.courtIndex - 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/BaseDrawLog.swift b/PadelClub/Data/Gen/BaseDrawLog.swift deleted file mode 100644 index b60159e..0000000 --- a/PadelClub/Data/Gen/BaseDrawLog.swift +++ /dev/null @@ -1,100 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseDrawLog: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "draw-logs" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - var tournament: String = "" - var drawDate: Date = Date() - var drawSeed: Int = 0 - var drawMatchIndex: Int = 0 - var drawTeamPosition: TeamPosition = TeamPosition.one - var drawType: DrawType = DrawType.seed - - init( - id: String = Store.randomId(), - tournament: String = "", - drawDate: Date = Date(), - drawSeed: Int = 0, - drawMatchIndex: Int = 0, - drawTeamPosition: TeamPosition = TeamPosition.one, - drawType: DrawType = DrawType.seed - ) { - super.init() - self.id = id - self.tournament = tournament - self.drawDate = drawDate - self.drawSeed = drawSeed - self.drawMatchIndex = drawMatchIndex - self.drawTeamPosition = drawTeamPosition - self.drawType = drawType - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - case _tournament = "tournament" - case _drawDate = "drawDate" - case _drawSeed = "drawSeed" - case _drawMatchIndex = "drawMatchIndex" - case _drawTeamPosition = "drawTeamPosition" - case _drawType = "drawType" - } - - required init(from decoder: Decoder) throws { - 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.drawDate = try container.decodeIfPresent(Date.self, forKey: ._drawDate) ?? Date() - self.drawSeed = try container.decodeIfPresent(Int.self, forKey: ._drawSeed) ?? 0 - self.drawMatchIndex = try container.decodeIfPresent(Int.self, forKey: ._drawMatchIndex) ?? 0 - self.drawTeamPosition = try container.decodeIfPresent(TeamPosition.self, forKey: ._drawTeamPosition) ?? TeamPosition.one - self.drawType = try container.decodeIfPresent(DrawType.self, forKey: ._drawType) ?? DrawType.seed - try super.init(from: decoder) - } - - 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) - try container.encode(self.drawDate, forKey: ._drawDate) - try container.encode(self.drawSeed, forKey: ._drawSeed) - try container.encode(self.drawMatchIndex, forKey: ._drawMatchIndex) - try container.encode(self.drawTeamPosition, forKey: ._drawTeamPosition) - try container.encode(self.drawType, forKey: ._drawType) - try super.encode(to: encoder) - } - - func tournamentValue() -> Tournament? { - return Store.main.findById(tournament) - } - - func copy(from other: any Storable) { - guard let drawlog = other as? BaseDrawLog else { return } - self.id = drawlog.id - self.tournament = drawlog.tournament - self.drawDate = drawlog.drawDate - self.drawSeed = drawlog.drawSeed - self.drawMatchIndex = drawlog.drawMatchIndex - self.drawTeamPosition = drawlog.drawTeamPosition - self.drawType = drawlog.drawType - } - - static func relationships() -> [Relationship] { - return [ - Relationship(type: Tournament.self, keyPath: \BaseDrawLog.tournament), - ] - } - -} \ No newline at end of file diff --git a/PadelClub/Data/Gen/BaseEvent.swift b/PadelClub/Data/Gen/BaseEvent.swift deleted file mode 100644 index 910f8ca..0000000 --- a/PadelClub/Data/Gen/BaseEvent.swift +++ /dev/null @@ -1,100 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseEvent: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "events" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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(), - creator: String? = nil, - club: String? = nil, - creationDate: Date = Date(), - name: String? = nil, - tenupId: String? = nil - ) { - super.init() - self.id = id - self.creator = creator - self.club = club - self.creationDate = creationDate - self.name = name - self.tenupId = tenupId - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - case _creator = "creator" - case _club = "club" - case _creationDate = "creationDate" - case _name = "name" - case _tenupId = "tenupId" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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) - } - - 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.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? { - 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.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), - 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 deleted file mode 100644 index 97c9131..0000000 --- a/PadelClub/Data/Gen/BaseGroupStage.swift +++ /dev/null @@ -1,107 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseGroupStage: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "group-stages" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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 - - init( - id: String = Store.randomId(), - tournament: String = "", - index: Int = 0, - size: Int = 0, - format: MatchFormat? = nil, - startDate: Date? = nil, - name: String? = nil, - step: Int = 0 - ) { - super.init() - self.id = id - self.tournament = tournament - self.index = index - self.size = size - self.format = format - self.startDate = startDate - self.name = name - self.step = step - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - case _tournament = "tournament" - case _index = "index" - case _size = "size" - case _format = "format" - case _startDate = "startDate" - case _name = "name" - case _step = "step" - } - - required init(from decoder: Decoder) throws { - 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.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 - try super.init(from: decoder) - } - - 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) - 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 super.encode(to: encoder) - } - - 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.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 - } - - 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 deleted file mode 100644 index 7333921..0000000 --- a/PadelClub/Data/Gen/BaseMatch.swift +++ /dev/null @@ -1,156 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseMatch: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "matches" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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 - - init( - id: String = Store.randomId(), - 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 - ) { - super.init() - self.id = id - 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 - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - 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" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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 - try super.init(from: decoder) - } - - 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.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 super.encode(to: encoder) - } - - 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.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 - } - - 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 deleted file mode 100644 index 75bd7f5..0000000 --- a/PadelClub/Data/Gen/BaseMatchScheduler.swift +++ /dev/null @@ -1,163 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseMatchScheduler: BaseModelObject, Storable { - - static func resourceName() -> String { return "match-scheduler" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - var tournament: String = "" - var timeDifferenceLimit: Int = 5 - var loserBracketRotationDifference: Int = 0 - var upperBracketRotationDifference: Int = 1 - var accountUpperBracketBreakTime: Bool = true - var accountLoserBracketBreakTime: Bool = false - var randomizeCourts: Bool = true - var rotationDifferenceIsImportant: Bool = false - var shouldHandleUpperRoundSlice: Bool = false - var shouldEndRoundBeforeStartingNext: Bool = true - var groupStageChunkCount: Int? = nil - var overrideCourtsUnavailability: Bool = false - var shouldTryToFillUpCourtsAvailable: Bool = false - var courtsAvailable: Set = Set() - var simultaneousStart: Bool = true - - init( - id: String = Store.randomId(), - tournament: String = "", - timeDifferenceLimit: Int = 5, - loserBracketRotationDifference: Int = 0, - upperBracketRotationDifference: Int = 1, - accountUpperBracketBreakTime: Bool = true, - accountLoserBracketBreakTime: Bool = false, - randomizeCourts: Bool = true, - rotationDifferenceIsImportant: Bool = false, - shouldHandleUpperRoundSlice: Bool = false, - shouldEndRoundBeforeStartingNext: Bool = true, - groupStageChunkCount: Int? = nil, - overrideCourtsUnavailability: Bool = false, - shouldTryToFillUpCourtsAvailable: Bool = false, - courtsAvailable: Set = Set(), - simultaneousStart: Bool = true - ) { - 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 - self.courtsAvailable = courtsAvailable - self.simultaneousStart = simultaneousStart - } - required public override init() { - super.init() - } - - 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" - case _courtsAvailable = "courtsAvailable" - case _simultaneousStart = "simultaneousStart" - } - - required init(from decoder: Decoder) throws { - 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) ?? 5 - self.loserBracketRotationDifference = try container.decodeIfPresent(Int.self, forKey: ._loserBracketRotationDifference) ?? 0 - self.upperBracketRotationDifference = try container.decodeIfPresent(Int.self, forKey: ._upperBracketRotationDifference) ?? 1 - self.accountUpperBracketBreakTime = try container.decodeIfPresent(Bool.self, forKey: ._accountUpperBracketBreakTime) ?? true - self.accountLoserBracketBreakTime = try container.decodeIfPresent(Bool.self, forKey: ._accountLoserBracketBreakTime) ?? false - self.randomizeCourts = try container.decodeIfPresent(Bool.self, forKey: ._randomizeCourts) ?? true - 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) ?? true - 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.courtsAvailable = try container.decodeIfPresent(Set.self, forKey: ._courtsAvailable) ?? Set() - self.simultaneousStart = try container.decodeIfPresent(Bool.self, forKey: ._simultaneousStart) ?? true - try super.init(from: decoder) - } - - 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) - 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) - try container.encode(self.courtsAvailable, forKey: ._courtsAvailable) - try container.encode(self.simultaneousStart, forKey: ._simultaneousStart) - try super.encode(to: encoder) - } - - 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 - self.courtsAvailable = matchscheduler.courtsAvailable - self.simultaneousStart = matchscheduler.simultaneousStart - } - - 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 deleted file mode 100644 index 3c7c176..0000000 --- a/PadelClub/Data/Gen/BaseMonthData.swift +++ /dev/null @@ -1,122 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseMonthData: BaseModelObject, Storable { - - static func resourceName() -> String { return "month-data" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = 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 - } - required public override init() { - super.init() - } - - 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 { - 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 - try super.init(from: decoder) - } - - 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) - 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) - try super.encode(to: encoder) - } - - 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 - } - - 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 deleted file mode 100644 index 47a501c..0000000 --- a/PadelClub/Data/Gen/BasePlayerRegistration.swift +++ /dev/null @@ -1,227 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BasePlayerRegistration: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "player-registrations" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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: PlayerRegistration.PlayerDataSource? = nil - var hasArrived: Bool = false - var coach: Bool = false - var captain: Bool = false - var registeredOnline: Bool = false - var timeToConfirm: Date? = nil - var registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting - var paymentId: String? = nil - - init( - id: String = Store.randomId(), - 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: PlayerRegistration.PlayerDataSource? = nil, - hasArrived: Bool = false, - coach: Bool = false, - captain: Bool = false, - registeredOnline: Bool = false, - timeToConfirm: Date? = nil, - registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting, - paymentId: String? = nil - ) { - super.init() - self.id = id - 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.coach = coach - self.captain = captain - self.registeredOnline = registeredOnline - self.timeToConfirm = timeToConfirm - self.registrationStatus = registrationStatus - self.paymentId = paymentId - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - 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 _coach = "coach" - case _captain = "captain" - case _registeredOnline = "registeredOnline" - case _timeToConfirm = "timeToConfirm" - case _registrationStatus = "registrationStatus" - case _paymentId = "paymentId" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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(PlayerRegistration.PlayerDataSource.self, forKey: ._source) ?? nil - self.hasArrived = try container.decodeIfPresent(Bool.self, forKey: ._hasArrived) ?? false - self.coach = try container.decodeIfPresent(Bool.self, forKey: ._coach) ?? false - self.captain = try container.decodeIfPresent(Bool.self, forKey: ._captain) ?? false - self.registeredOnline = try container.decodeIfPresent(Bool.self, forKey: ._registeredOnline) ?? false - self.timeToConfirm = try container.decodeIfPresent(Date.self, forKey: ._timeToConfirm) ?? nil - self.registrationStatus = try container.decodeIfPresent(PlayerRegistration.RegistrationStatus.self, forKey: ._registrationStatus) ?? PlayerRegistration.RegistrationStatus.waiting - self.paymentId = try container.decodeIfPresent(String.self, forKey: ._paymentId) ?? nil - try super.init(from: decoder) - } - - 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.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.coach, forKey: ._coach) - try container.encode(self.captain, forKey: ._captain) - try container.encode(self.registeredOnline, forKey: ._registeredOnline) - try container.encode(self.timeToConfirm, forKey: ._timeToConfirm) - try container.encode(self.registrationStatus, forKey: ._registrationStatus) - try container.encode(self.paymentId, forKey: ._paymentId) - try super.encode(to: encoder) - } - - 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.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.coach = playerregistration.coach - self.captain = playerregistration.captain - self.registeredOnline = playerregistration.registeredOnline - self.timeToConfirm = playerregistration.timeToConfirm - self.registrationStatus = playerregistration.registrationStatus - self.paymentId = playerregistration.paymentId - } - - 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 deleted file mode 100644 index bd16fda..0000000 --- a/PadelClub/Data/Gen/BasePurchase.swift +++ /dev/null @@ -1,99 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage - -class BasePurchase: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "purchases" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: UInt64 = 0 - 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, - user: String = "", - purchaseDate: Date = Date(), - productId: String = "", - quantity: Int? = nil, - revocationDate: Date? = nil, - expirationDate: Date? = nil - ) { - super.init() - self.id = id - self.user = user - self.purchaseDate = purchaseDate - self.productId = productId - self.quantity = quantity - self.revocationDate = revocationDate - self.expirationDate = expirationDate - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case id = "id" - case user = "user" - case purchaseDate = "purchaseDate" - case productId = "productId" - case quantity = "quantity" - case revocationDate = "revocationDate" - case expirationDate = "expirationDate" - } - - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(UInt64.self, forKey: .id) ?? 0 - 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) - } - - override func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(self.id, forKey: .id) - 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? { - return Store.main.findById(user) - } - - func copy(from other: any Storable) { - guard let purchase = other as? BasePurchase else { return } - self.id = purchase.id - self.user = purchase.user - self.purchaseDate = purchase.purchaseDate - self.productId = purchase.productId - self.quantity = purchase.quantity - 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 deleted file mode 100644 index b009607..0000000 --- a/PadelClub/Data/Gen/BaseRound.swift +++ /dev/null @@ -1,107 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseRound: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "rounds" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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 - - init( - id: String = Store.randomId(), - tournament: String = "", - index: Int = 0, - parent: String? = nil, - format: MatchFormat? = nil, - startDate: Date? = nil, - groupStageLoserBracket: Bool = false, - loserBracketMode: LoserBracketMode = .automatic - ) { - super.init() - self.id = id - self.tournament = tournament - self.index = index - self.parent = parent - self.format = format - self.startDate = startDate - self.groupStageLoserBracket = groupStageLoserBracket - self.loserBracketMode = loserBracketMode - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - case _tournament = "tournament" - case _index = "index" - case _parent = "parent" - case _format = "format" - case _startDate = "startDate" - case _groupStageLoserBracket = "groupStageLoserBracket" - case _loserBracketMode = "loserBracketMode" - } - - required init(from decoder: Decoder) throws { - 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.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 - try super.init(from: decoder) - } - - 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) - 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 super.encode(to: encoder) - } - - 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.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 - } - - 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 deleted file mode 100644 index 8710c5e..0000000 --- a/PadelClub/Data/Gen/BaseTeamRegistration.swift +++ /dev/null @@ -1,199 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseTeamRegistration: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "team-registrations" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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 - - init( - id: String = Store.randomId(), - 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 - ) { - super.init() - self.id = id - 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 - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - 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" - } - - required init(from decoder: Decoder) throws { - 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.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 - try super.init(from: decoder) - } - - 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) - 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 super.encode(to: encoder) - } - - 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.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 - } - - 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 deleted file mode 100644 index 4f3ab82..0000000 --- a/PadelClub/Data/Gen/BaseTeamScore.swift +++ /dev/null @@ -1,99 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseTeamScore: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "team-scores" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - var match: String = "" - var teamRegistration: String? = nil - var score: String? = nil - var walkOut: Int? = nil - var luckyLoser: Int? = nil - - init( - id: String = Store.randomId(), - match: String = "", - teamRegistration: String? = nil, - score: String? = nil, - walkOut: Int? = nil, - luckyLoser: Int? = nil - ) { - super.init() - self.id = id - self.match = match - self.teamRegistration = teamRegistration - self.score = score - self.walkOut = walkOut - self.luckyLoser = luckyLoser - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case _id = "id" - case _match = "match" - case _teamRegistration = "teamRegistration" - case _score = "score" - case _walkOut = "walkOut" - case _luckyLoser = "luckyLoser" - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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 - try super.init(from: decoder) - } - - 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.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 super.encode(to: encoder) - } - - 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.match = teamscore.match - self.teamRegistration = teamscore.teamRegistration - self.score = teamscore.score - self.walkOut = teamscore.walkOut - self.luckyLoser = teamscore.luckyLoser - } - - 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 deleted file mode 100644 index cb2d489..0000000 --- a/PadelClub/Data/Gen/BaseTournament.swift +++ /dev/null @@ -1,590 +0,0 @@ -// Generated by SwiftModelGenerator -// Do not modify this file manually - -import Foundation -import LeStorage -import SwiftUI - -@Observable -class BaseTournament: SyncedModelObject, SyncedStorable { - - static func resourceName() -> String { return "tournaments" } - static func tokenExemptedMethods() -> [HTTPMethod] { return [] } - static var copyServerResponse: Bool = false - - var id: String = Store.randomId() - 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 = 4 - var rankSourceDate: Date? = nil - var dayDuration: Int = 1 - var teamCount: Int = 24 - var teamSorting: TeamSortingType = TeamSortingType.inscriptionDate - var federalCategory: TournamentCategory = TournamentCategory.men - var federalLevelCategory: TournamentLevel = TournamentLevel.p100 - var federalAgeCategory: FederalTournamentAge = FederalTournamentAge.senior - var closedRegistrationDate: Date? = nil - var groupStageAdditionalQualified: Int = 0 - var courtCount: Int = 2 - var prioritizeClubMembers: Bool = false - var qualifiedPerGroupStage: Int = 1 - var teamsPerGroupStage: Int = 4 - 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 - var initialSeedRound: Int = 0 - var initialSeedCount: Int = 0 - var enableOnlineRegistration: Bool = false - var registrationDateLimit: Date? = nil - var openingRegistrationDate: Date? = nil - var waitingListLimit: Int? = nil - var accountIsRequired: Bool = true - var licenseIsRequired: Bool = true - var minimumPlayerPerTeam: Int = 2 - var maximumPlayerPerTeam: Int = 2 - var information: String? = nil - var umpireCustomMail: String? = nil - var umpireCustomContact: String? = nil - var umpireCustomPhone: String? = nil - var hideUmpireMail: Bool = false - var hideUmpirePhone: Bool = true - var disableRankingFederalRuling: Bool = false - var teamCountLimit: Bool = true - var enableOnlinePayment: Bool = false - var onlinePaymentIsMandatory: Bool = false - var enableOnlinePaymentRefund: Bool = false - var refundDateLimit: Date? = nil - var stripeAccountId: String? = nil - var enableTimeToConfirm: Bool = false - var isCorporateTournament: Bool = false - var isTemplate: Bool = false - - init( - id: String = Store.randomId(), - 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 = 4, - rankSourceDate: Date? = nil, - dayDuration: Int = 1, - teamCount: Int = 24, - teamSorting: TeamSortingType = TeamSortingType.inscriptionDate, - federalCategory: TournamentCategory = TournamentCategory.men, - federalLevelCategory: TournamentLevel = TournamentLevel.p100, - federalAgeCategory: FederalTournamentAge = FederalTournamentAge.senior, - closedRegistrationDate: Date? = nil, - groupStageAdditionalQualified: Int = 0, - courtCount: Int = 2, - prioritizeClubMembers: Bool = false, - qualifiedPerGroupStage: Int = 1, - teamsPerGroupStage: Int = 4, - 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, - initialSeedRound: Int = 0, - initialSeedCount: Int = 0, - enableOnlineRegistration: Bool = false, - registrationDateLimit: Date? = nil, - openingRegistrationDate: Date? = nil, - waitingListLimit: Int? = nil, - accountIsRequired: Bool = true, - licenseIsRequired: Bool = true, - minimumPlayerPerTeam: Int = 2, - maximumPlayerPerTeam: Int = 2, - information: String? = nil, - umpireCustomMail: String? = nil, - umpireCustomContact: String? = nil, - umpireCustomPhone: String? = nil, - hideUmpireMail: Bool = false, - hideUmpirePhone: Bool = true, - disableRankingFederalRuling: Bool = false, - teamCountLimit: Bool = true, - enableOnlinePayment: Bool = false, - onlinePaymentIsMandatory: Bool = false, - enableOnlinePaymentRefund: Bool = false, - refundDateLimit: Date? = nil, - stripeAccountId: String? = nil, - enableTimeToConfirm: Bool = false, - isCorporateTournament: Bool = false, - isTemplate: Bool = false - ) { - super.init() - self.id = id - 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 - self.initialSeedRound = initialSeedRound - self.initialSeedCount = initialSeedCount - self.enableOnlineRegistration = enableOnlineRegistration - self.registrationDateLimit = registrationDateLimit - self.openingRegistrationDate = openingRegistrationDate - self.waitingListLimit = waitingListLimit - self.accountIsRequired = accountIsRequired - self.licenseIsRequired = licenseIsRequired - self.minimumPlayerPerTeam = minimumPlayerPerTeam - self.maximumPlayerPerTeam = maximumPlayerPerTeam - self.information = information - self.umpireCustomMail = umpireCustomMail - self.umpireCustomContact = umpireCustomContact - self.umpireCustomPhone = umpireCustomPhone - self.hideUmpireMail = hideUmpireMail - self.hideUmpirePhone = hideUmpirePhone - self.disableRankingFederalRuling = disableRankingFederalRuling - self.teamCountLimit = teamCountLimit - self.enableOnlinePayment = enableOnlinePayment - self.onlinePaymentIsMandatory = onlinePaymentIsMandatory - self.enableOnlinePaymentRefund = enableOnlinePaymentRefund - self.refundDateLimit = refundDateLimit - self.stripeAccountId = stripeAccountId - self.enableTimeToConfirm = enableTimeToConfirm - self.isCorporateTournament = isCorporateTournament - self.isTemplate = isTemplate - } - required public override init() { - super.init() - } - - enum CodingKeys: String, CodingKey { - case isCanceled = "isCanceled" - case payment = "payment" - case _id = "id" - 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 = "globalId" - case _additionalEstimationDuration = "additionalEstimationDuration" - case _isDeleted = "isDeleted" - case _isCanceled = "localId" - 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" - case _initialSeedRound = "initialSeedRound" - case _initialSeedCount = "initialSeedCount" - case _enableOnlineRegistration = "enableOnlineRegistration" - case _registrationDateLimit = "registrationDateLimit" - case _openingRegistrationDate = "openingRegistrationDate" - case _waitingListLimit = "waitingListLimit" - case _accountIsRequired = "accountIsRequired" - case _licenseIsRequired = "licenseIsRequired" - case _minimumPlayerPerTeam = "minimumPlayerPerTeam" - case _maximumPlayerPerTeam = "maximumPlayerPerTeam" - case _information = "information" - case _umpireCustomMail = "umpireCustomMail" - case _umpireCustomContact = "umpireCustomContact" - case _umpireCustomPhone = "umpireCustomPhone" - case _hideUmpireMail = "hideUmpireMail" - case _hideUmpirePhone = "hideUmpirePhone" - case _disableRankingFederalRuling = "disableRankingFederalRuling" - case _teamCountLimit = "teamCountLimit" - case _enableOnlinePayment = "enableOnlinePayment" - case _onlinePaymentIsMandatory = "onlinePaymentIsMandatory" - case _enableOnlinePaymentRefund = "enableOnlinePaymentRefund" - case _refundDateLimit = "refundDateLimit" - case _stripeAccountId = "stripeAccountId" - case _enableTimeToConfirm = "enableTimeToConfirm" - case _isCorporateTournament = "isCorporateTournament" - case _isTemplate = "isTemplate" - } - - private static func _decodePayment(container: KeyedDecodingContainer) throws -> TournamentPayment? { - var data = try container.decodeIfPresent(Data.self, forKey: ._payment) - if data == nil { - 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 { - var data = try container.decodeIfPresent(Data.self, forKey: ._isCanceled) - if data == nil { - 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 { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() - 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) ?? 4 - self.rankSourceDate = try container.decodeIfPresent(Date.self, forKey: ._rankSourceDate) ?? nil - self.dayDuration = try container.decodeIfPresent(Int.self, forKey: ._dayDuration) ?? 1 - self.teamCount = try container.decodeIfPresent(Int.self, forKey: ._teamCount) ?? 24 - 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.p100 - self.federalAgeCategory = try container.decodeIfPresent(FederalTournamentAge.self, forKey: ._federalAgeCategory) ?? FederalTournamentAge.senior - 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) ?? 1 - self.teamsPerGroupStage = try container.decodeIfPresent(Int.self, forKey: ._teamsPerGroupStage) ?? 4 - 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 - self.initialSeedRound = try container.decodeIfPresent(Int.self, forKey: ._initialSeedRound) ?? 0 - self.initialSeedCount = try container.decodeIfPresent(Int.self, forKey: ._initialSeedCount) ?? 0 - self.enableOnlineRegistration = try container.decodeIfPresent(Bool.self, forKey: ._enableOnlineRegistration) ?? false - self.registrationDateLimit = try container.decodeIfPresent(Date.self, forKey: ._registrationDateLimit) ?? nil - self.openingRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._openingRegistrationDate) ?? nil - self.waitingListLimit = try container.decodeIfPresent(Int.self, forKey: ._waitingListLimit) ?? nil - self.accountIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._accountIsRequired) ?? true - self.licenseIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._licenseIsRequired) ?? true - self.minimumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._minimumPlayerPerTeam) ?? 2 - self.maximumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._maximumPlayerPerTeam) ?? 2 - self.information = try container.decodeIfPresent(String.self, forKey: ._information) ?? nil - self.umpireCustomMail = try container.decodeIfPresent(String.self, forKey: ._umpireCustomMail) ?? nil - self.umpireCustomContact = try container.decodeIfPresent(String.self, forKey: ._umpireCustomContact) ?? nil - self.umpireCustomPhone = try container.decodeIfPresent(String.self, forKey: ._umpireCustomPhone) ?? nil - self.hideUmpireMail = try container.decodeIfPresent(Bool.self, forKey: ._hideUmpireMail) ?? false - self.hideUmpirePhone = try container.decodeIfPresent(Bool.self, forKey: ._hideUmpirePhone) ?? true - self.disableRankingFederalRuling = try container.decodeIfPresent(Bool.self, forKey: ._disableRankingFederalRuling) ?? false - self.teamCountLimit = try container.decodeIfPresent(Bool.self, forKey: ._teamCountLimit) ?? true - self.enableOnlinePayment = try container.decodeIfPresent(Bool.self, forKey: ._enableOnlinePayment) ?? false - self.onlinePaymentIsMandatory = try container.decodeIfPresent(Bool.self, forKey: ._onlinePaymentIsMandatory) ?? false - self.enableOnlinePaymentRefund = try container.decodeIfPresent(Bool.self, forKey: ._enableOnlinePaymentRefund) ?? false - self.refundDateLimit = try container.decodeIfPresent(Date.self, forKey: ._refundDateLimit) ?? nil - self.stripeAccountId = try container.decodeIfPresent(String.self, forKey: ._stripeAccountId) ?? nil - self.enableTimeToConfirm = try container.decodeIfPresent(Bool.self, forKey: ._enableTimeToConfirm) ?? false - self.isCorporateTournament = try container.decodeIfPresent(Bool.self, forKey: ._isCorporateTournament) ?? false - self.isTemplate = try container.decodeIfPresent(Bool.self, forKey: ._isTemplate) ?? false - try super.init(from: decoder) - } - - 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.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) - try container.encode(self.initialSeedRound, forKey: ._initialSeedRound) - try container.encode(self.initialSeedCount, forKey: ._initialSeedCount) - try container.encode(self.enableOnlineRegistration, forKey: ._enableOnlineRegistration) - try container.encode(self.registrationDateLimit, forKey: ._registrationDateLimit) - try container.encode(self.openingRegistrationDate, forKey: ._openingRegistrationDate) - try container.encode(self.waitingListLimit, forKey: ._waitingListLimit) - try container.encode(self.accountIsRequired, forKey: ._accountIsRequired) - try container.encode(self.licenseIsRequired, forKey: ._licenseIsRequired) - try container.encode(self.minimumPlayerPerTeam, forKey: ._minimumPlayerPerTeam) - try container.encode(self.maximumPlayerPerTeam, forKey: ._maximumPlayerPerTeam) - try container.encode(self.information, forKey: ._information) - try container.encode(self.umpireCustomMail, forKey: ._umpireCustomMail) - try container.encode(self.umpireCustomContact, forKey: ._umpireCustomContact) - try container.encode(self.umpireCustomPhone, forKey: ._umpireCustomPhone) - try container.encode(self.hideUmpireMail, forKey: ._hideUmpireMail) - try container.encode(self.hideUmpirePhone, forKey: ._hideUmpirePhone) - try container.encode(self.disableRankingFederalRuling, forKey: ._disableRankingFederalRuling) - try container.encode(self.teamCountLimit, forKey: ._teamCountLimit) - try container.encode(self.enableOnlinePayment, forKey: ._enableOnlinePayment) - try container.encode(self.onlinePaymentIsMandatory, forKey: ._onlinePaymentIsMandatory) - try container.encode(self.enableOnlinePaymentRefund, forKey: ._enableOnlinePaymentRefund) - try container.encode(self.refundDateLimit, forKey: ._refundDateLimit) - try container.encode(self.stripeAccountId, forKey: ._stripeAccountId) - try container.encode(self.enableTimeToConfirm, forKey: ._enableTimeToConfirm) - try container.encode(self.isCorporateTournament, forKey: ._isCorporateTournament) - try container.encode(self.isTemplate, forKey: ._isTemplate) - try super.encode(to: encoder) - } - - 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.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 - self.initialSeedRound = tournament.initialSeedRound - self.initialSeedCount = tournament.initialSeedCount - self.enableOnlineRegistration = tournament.enableOnlineRegistration - self.registrationDateLimit = tournament.registrationDateLimit - self.openingRegistrationDate = tournament.openingRegistrationDate - self.waitingListLimit = tournament.waitingListLimit - self.accountIsRequired = tournament.accountIsRequired - self.licenseIsRequired = tournament.licenseIsRequired - self.minimumPlayerPerTeam = tournament.minimumPlayerPerTeam - self.maximumPlayerPerTeam = tournament.maximumPlayerPerTeam - self.information = tournament.information - self.umpireCustomMail = tournament.umpireCustomMail - self.umpireCustomContact = tournament.umpireCustomContact - self.umpireCustomPhone = tournament.umpireCustomPhone - self.hideUmpireMail = tournament.hideUmpireMail - self.hideUmpirePhone = tournament.hideUmpirePhone - self.disableRankingFederalRuling = tournament.disableRankingFederalRuling - self.teamCountLimit = tournament.teamCountLimit - self.enableOnlinePayment = tournament.enableOnlinePayment - self.onlinePaymentIsMandatory = tournament.onlinePaymentIsMandatory - self.enableOnlinePaymentRefund = tournament.enableOnlinePaymentRefund - self.refundDateLimit = tournament.refundDateLimit - self.stripeAccountId = tournament.stripeAccountId - self.enableTimeToConfirm = tournament.enableTimeToConfirm - self.isCorporateTournament = tournament.isCorporateTournament - self.isTemplate = tournament.isTemplate - } - - 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 deleted file mode 100644 index cdaf5e2..0000000 --- a/PadelClub/Data/Gen/Club.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "models": [ - { - "name": "Club", - "synchronizable": true, - "observable": true, - "copy_server_response": "true", - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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" - }, - { - "name": "timezone", - "type": "String", - "optional": true, - "defaultValue": "TimeZone.current.identifier" - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/Court.json b/PadelClub/Data/Gen/Court.json deleted file mode 100644 index ec2bbe4..0000000 --- a/PadelClub/Data/Gen/Court.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "models": [ - { - "name": "Court", - "synchronizable": true, - "observable": true, - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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": [] - } - ] -} diff --git a/PadelClub/Data/Gen/CustomUser.json b/PadelClub/Data/Gen/CustomUser.json deleted file mode 100644 index 763d975..0000000 --- a/PadelClub/Data/Gen/CustomUser.json +++ /dev/null @@ -1,177 +0,0 @@ -{ - "models": [ - { - "name": "CustomUser", - "resource_name": "users", - "synchronizable": true, - "observable": true, - "tokenExemptedMethods": ["post"], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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": "disableRankingFederalRuling", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "deviceId", - "type": "String", - "optional": true, - "defaultValue": "nil" - }, - { - "name": "agents", - "type": "[String]", - "defaultValue": "[]" - }, - { - "name": "userRole", - "type": "Int", - "optional": true, - "defaultValue": "nil" - }, - { - "name": "registrationPaymentMode", - "type": "RegistrationPaymentMode", - "defaultValue": "RegistrationPaymentMode.disabled" - }, - { - "name": "umpireCustomMail", - "type": "String", - "optional": true - }, - { - "name": "umpireCustomContact", - "type": "String", - "optional": true - }, - { - "name": "umpireCustomPhone", - "type": "String", - "optional": true - }, - { - "name": "hideUmpireMail", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "hideUmpirePhone", - "type": "Bool", - "defaultValue": "true" - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/DateInterval.json b/PadelClub/Data/Gen/DateInterval.json deleted file mode 100644 index b7c1532..0000000 --- a/PadelClub/Data/Gen/DateInterval.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "models": [ - { - "name": "DateInterval", - "synchronizable": true, - "observable": true, - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "name": "event", - "type": "String" - }, - { - "name": "courtIndex", - "type": "Int" - }, - { - "name": "startDate", - "type": "Date" - }, - { - "name": "endDate", - "type": "Date" - } - ], - "tokenExemptedMethods": [] - } - ] -} diff --git a/PadelClub/Data/Gen/Drawlog.json b/PadelClub/Data/Gen/Drawlog.json deleted file mode 100644 index 8f872d9..0000000 --- a/PadelClub/Data/Gen/Drawlog.json +++ /dev/null @@ -1,47 +0,0 @@ - -{ - "models": [ - { - "name": "DrawLog", - "synchronizable": true, - "sideStorable": true, - "observable": true, - "relationshipNames": [], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "name": "tournament", - "type": "String", - "foreignKey": "Tournament" - }, - { - "name": "drawDate", - "type": "Date", - "defaultValue": "Date()" - }, - { - "name": "drawSeed", - "type": "Int" - }, - { - "name": "drawMatchIndex", - "type": "Int" - }, - { - "name": "drawTeamPosition", - "type": "TeamPosition", - "defaultValue": "TeamPosition.one" - }, - { - "name": "drawType", - "type": "DrawType", - "defaultValue": "DrawType.seed" - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/Event.json b/PadelClub/Data/Gen/Event.json deleted file mode 100644 index 0fa8d00..0000000 --- a/PadelClub/Data/Gen/Event.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "models": [ - { - "name": "Event", - "synchronizable": true, - "observable": true, - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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": [] - } - ] -} diff --git a/PadelClub/Data/Gen/GroupStage.json b/PadelClub/Data/Gen/GroupStage.json deleted file mode 100644 index ecfdad5..0000000 --- a/PadelClub/Data/Gen/GroupStage.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "models": [ - { - "name": "GroupStage", - "synchronizable": true, - "observable": true, - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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" - } - ], - "tokenExemptedMethods": [] - } - ] -} diff --git a/PadelClub/Data/Gen/Match.json b/PadelClub/Data/Gen/Match.json deleted file mode 100644 index 913014c..0000000 --- a/PadelClub/Data/Gen/Match.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "models": [ - { - "name": "Match", - "synchronizable": true, - "observable": true, - "tokenExemptedMethods": [], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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" - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/MatchScheduler.json b/PadelClub/Data/Gen/MatchScheduler.json deleted file mode 100644 index 132bb36..0000000 --- a/PadelClub/Data/Gen/MatchScheduler.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "models": [ - { - "name": "MatchScheduler", - "resource_name": "match-scheduler", - "synchronizable": false, - "observable": true, - "tokenExemptedMethods": [], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "name": "tournament", - "type": "String", - "foreignKey": "Tournament" - }, - { - "name": "timeDifferenceLimit", - "type": "Int", - "defaultValue": "5" - }, - { - "name": "loserBracketRotationDifference", - "type": "Int", - "defaultValue": "0" - }, - { - "name": "upperBracketRotationDifference", - "type": "Int", - "defaultValue": "1" - }, - { - "name": "accountUpperBracketBreakTime", - "type": "Bool", - "defaultValue": "true" - }, - { - "name": "accountLoserBracketBreakTime", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "randomizeCourts", - "type": "Bool", - "defaultValue": "true" - }, - { - "name": "rotationDifferenceIsImportant", - "type": "Bool" - }, - { - "name": "shouldHandleUpperRoundSlice", - "type": "Bool" - }, - { - "name": "shouldEndRoundBeforeStartingNext", - "type": "Bool", - "defaultValue": "true" - }, - { - "name": "groupStageChunkCount", - "type": "Int", - "optional": true - }, - { - "name": "overrideCourtsUnavailability", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "shouldTryToFillUpCourtsAvailable", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "courtsAvailable", - "type": "Set", - "defaultValue": "Set()" - }, - { - "name": "simultaneousStart", - "type": "Bool", - "defaultValue": "true" - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/MonthData.json b/PadelClub/Data/Gen/MonthData.json deleted file mode 100644 index 896038f..0000000 --- a/PadelClub/Data/Gen/MonthData.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "models": [ - { - "name": "MonthData", - "resource_name": "month-data", - "synchronizable": false, - "observable": true, - "tokenExemptedMethods": [], - "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 deleted file mode 100644 index 72dc484..0000000 --- a/PadelClub/Data/Gen/PlayerRegistration.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "models": [ - { - "name": "PlayerRegistration", - "synchronizable": true, - "sideStorable": true, - "observable": true, - "relationshipNames": ["teamRegistration"], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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": "PlayerRegistration.PlayerDataSource", - "optional": true - }, - { - "name": "hasArrived", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "coach", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "captain", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "registeredOnline", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "timeToConfirm", - "type": "Date", - "optional": true - }, - { - "name": "registrationStatus", - "type": "PlayerRegistration.RegistrationStatus", - "choices": "PlayerRegistration.RegistrationStatus", - "defaultValue": "PlayerRegistration.RegistrationStatus.waiting" - }, - { - "name": "paymentId", - "type": "String", - "optional": true - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/Purchase.json b/PadelClub/Data/Gen/Purchase.json deleted file mode 100644 index a8dd49b..0000000 --- a/PadelClub/Data/Gen/Purchase.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "models": [ - { - "name": "Purchase", - "synchronizable": true, - "properties": [ - { - "name": "id", - "type": "UInt64", - "defaultValue": "0" - }, - { - "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": [], - "relationshipNames": [] - } - ] -} diff --git a/PadelClub/Data/Gen/Round.json b/PadelClub/Data/Gen/Round.json deleted file mode 100644 index 48a6cf7..0000000 --- a/PadelClub/Data/Gen/Round.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "models": [ - { - "name": "Round", - "synchronizable": true, - "sideStorable": true, - "observable": true, - "relationshipNames": [], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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" - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/TeamRegistration.json b/PadelClub/Data/Gen/TeamRegistration.json deleted file mode 100644 index 995d08e..0000000 --- a/PadelClub/Data/Gen/TeamRegistration.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "models": [ - { - "name": "TeamRegistration", - "synchronizable": true, - "sideStorable": true, - "observable": true, - "relationshipNames": [], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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 - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/TeamScore.json b/PadelClub/Data/Gen/TeamScore.json deleted file mode 100644 index 9165c44..0000000 --- a/PadelClub/Data/Gen/TeamScore.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "models": [ - { - "name": "TeamScore", - "synchronizable": true, - "sideStorable": true, - "observable": true, - "relationshipNames": ["match"], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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 - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/Tournament.json b/PadelClub/Data/Gen/Tournament.json deleted file mode 100644 index ce4f01c..0000000 --- a/PadelClub/Data/Gen/Tournament.json +++ /dev/null @@ -1,355 +0,0 @@ -{ - "models": [ - { - "name": "Tournament", - "synchronizable": true, - "copyable": true, - "observable": true, - "relationshipNames": [], - "properties": [ - { - "name": "id", - "type": "String", - "defaultValue": "Store.randomId()" - }, - { - "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", - "defaultValue": "4" - }, - { - "name": "rankSourceDate", - "type": "Date", - "optional": true - }, - { - "name": "dayDuration", - "type": "Int", - "defaultValue": "1" - }, - { - "name": "teamCount", - "type": "Int", - "defaultValue": "24" - }, - { - "name": "teamSorting", - "type": "TeamSortingType", - "defaultValue": "TeamSortingType.inscriptionDate" - }, - { - "name": "federalCategory", - "type": "TournamentCategory", - "defaultValue": "TournamentCategory.men" - }, - { - "name": "federalLevelCategory", - "type": "TournamentLevel", - "defaultValue": "TournamentLevel.p100" - }, - { - "name": "federalAgeCategory", - "type": "FederalTournamentAge", - "defaultValue": "FederalTournamentAge.senior" - }, - { - "name": "closedRegistrationDate", - "type": "Date", - "optional": true - }, - { - "name": "groupStageAdditionalQualified", - "type": "Int", - "defaultValue": "0" - }, - { - "name": "courtCount", - "type": "Int", - "defaultValue": "2" - }, - { - "name": "prioritizeClubMembers", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "qualifiedPerGroupStage", - "type": "Int", - "defaultValue": "1" - }, - { - "name": "teamsPerGroupStage", - "type": "Int", - "defaultValue": "4" - }, - { - "name": "entryFee", - "type": "Double", - "optional": true - }, - { - "name": "payment", - "type": "TournamentPayment", - "optional": true, - "defaultValue": "nil", - "encryption": "tournament_payment", - "codingKey": "globalId" - }, - { - "name": "additionalEstimationDuration", - "type": "Int", - "defaultValue": "0" - }, - { - "name": "isDeleted", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "isCanceled", - "type": "Bool", - "defaultValue": "false", - "encryption": "tournament_iscanceled", - "codingKey": "localId" - }, - { - "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" - }, - { - "name": "initialSeedRound", - "type": "Int", - "defaultValue": "0" - }, - { - "name": "initialSeedCount", - "type": "Int", - "defaultValue": "0" - }, - { - "name": "enableOnlineRegistration", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "registrationDateLimit", - "type": "Date", - "optional": true - }, - { - "name": "openingRegistrationDate", - "type": "Date", - "optional": true - }, - { - "name": "waitingListLimit", - "type": "Int", - "optional": true - }, - { - "name": "accountIsRequired", - "type": "Bool", - "defaultValue": "true" - }, - { - "name": "licenseIsRequired", - "type": "Bool", - "defaultValue": "true" - }, - { - "name": "minimumPlayerPerTeam", - "type": "Int", - "defaultValue": "2" - }, - { - "name": "maximumPlayerPerTeam", - "type": "Int", - "defaultValue": "2" - }, - { - "name": "information", - "type": "String", - "optional": true - }, - { - "name": "umpireCustomMail", - "type": "String", - "optional": true - }, - { - "name": "umpireCustomContact", - "type": "String", - "optional": true - }, - { - "name": "umpireCustomPhone", - "type": "String", - "optional": true - }, - { - "name": "hideUmpireMail", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "hideUmpirePhone", - "type": "Bool", - "defaultValue": "true" - }, - { - "name": "disableRankingFederalRuling", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "teamCountLimit", - "type": "Bool", - "defaultValue": "true" - }, - { - "name": "enableOnlinePayment", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "onlinePaymentIsMandatory", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "enableOnlinePaymentRefund", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "refundDateLimit", - "type": "Date", - "optional": true - }, - { - "name": "stripeAccountId", - "type": "String", - "optional": true - }, - { - "name": "enableTimeToConfirm", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "isCorporateTournament", - "type": "Bool", - "defaultValue": "false" - }, - { - "name": "isTemplate", - "type": "Bool", - "defaultValue": "false" - } - ] - } - ] -} diff --git a/PadelClub/Data/Gen/generator.py b/PadelClub/Data/Gen/generator.py deleted file mode 100644 index 7faa6e9..0000000 --- a/PadelClub/Data/Gen/generator.py +++ /dev/null @@ -1,593 +0,0 @@ -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", []) - copy_server_response = model_data.get("copy_server_response", "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" - base_class = "SyncedModelObject" if is_sync else "BaseModelObject" - lines.append(f"class Base{model_name}: {base_class}, {protocol} {{") - lines.append("") - - # Add SyncedStorable protocol requirements - lines.extend(self._generate_protocol_requirements(resource_name, token_exempted, copy_server_response)) - 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(model_name, 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, is_sync)) - lines.append("") - lines.extend(self._generate_encoder(properties, is_observable, is_sync)) - 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("") - - # Add relationships function - lines.extend(self._generate_relationships(model_name, properties)) - lines.append("") - - 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(" }") - - lines.extend([ - " required public override init() {", - " super.init()", - " }", - - ]) - - - 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, model_name: str, properties: List[Dict[str, Any]], is_observable: bool) -> List[str]: - lines = [" enum CodingKeys: String, CodingKey {"] - - if model_name == 'Tournament': - lines.append(" case isCanceled = \"isCanceled\"") - lines.append(" case payment = \"payment\"") - - for prop in properties: - name = prop['name'] - # Add underscore prefix to case name if observable - case_name = f"_{name}" if is_observable else name - - # Use custom codingKey if provided, otherwise use the property name - coding_key_value = prop.get("codingKey", name) - - lines.append(f" case {case_name} = \"{coding_key_value}\"") - - 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" var data = try container.decodeIfPresent(Data.self, forKey: ._{name})", - " if data == nil {", - " 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", - " }", - "", - 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" var data = try container.decodeIfPresent(Data.self, forKey: ._{name})", - " if data == nil {", - " 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", - " }", - "", - 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, is_sync: bool = False) -> List[str]: - lines = [" required init(from decoder: Decoder) throws {", - " 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)) - option = prop.get("option") - - # 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: - # 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(" try super.init(from: decoder)") - - lines.append(" }") - return lines - - 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: - 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 - - 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: - 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(" try super.encode(to: encoder)") - 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], copy_server_response: 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] - 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 var copyServerResponse: Bool = {copy_server_response}", - ] - - 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""" - 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 deleted file mode 100644 index c515b98..0000000 --- a/PadelClub/Data/GroupStage.swift +++ /dev/null @@ -1,684 +0,0 @@ -// -// GroupStage.swift -// Padel Tournament -// -// Created by razmig on 10/03/2024. -// - -import Foundation -import LeStorage -import Algorithms -import SwiftUI - -@Observable -final class GroupStage: BaseGroupStage, SideStorable { - - var matchFormat: MatchFormat { - get { - format ?? .defaultFormatForMatchType(.groupStage) - } - set { - format = newValue - } - } - - var tournamentStore: TournamentStore? { - return TournamentLibrary.shared.store(tournamentId: self.tournament) - } - - // MARK: - Computed dependencies - - func _matches() -> [Match] { - 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 } - } - - func tournamentObject() -> Tournament? { - Store.main.findById(self.tournament) - } - - // MARK: - - - func teamAt(groupStagePosition: Int) -> TeamRegistration? { - if step > 0 { - return teams().first(where: { $0.groupStagePositionAtStep(step) == groupStagePosition }) - } - return teams().first(where: { $0.groupStagePosition == groupStagePosition }) - } - - func groupStageTitle(_ displayStyle: DisplayStyle = .wide) -> String { - if let name { return name } - - var stepLabel = "" - if step > 0 { - stepLabel = " (" + (step + 1).ordinalFormatted(feminine: true) + " phase)" - } - - switch displayStyle { - case .title: - return "Poule \(index + 1)" + stepLabel - case .wide: - return "Poule \(index + 1)" - case .short: - return "#\(index + 1)" - } - } - - var computedOrder: Int { - index + step * 100 - } - - func isRunning() -> Bool { // at least a match has started - _matches().anySatisfy({ $0.isRunning() }) - } - - func hasStarted() -> Bool { // meaning at least one match is over - _matches().filter { $0.hasEnded() }.isEmpty == false - } - - func hasEnded() -> Bool { - let _matches = _matches() - if _matches.isEmpty { return false } - //guard teams().count == size else { return false } - return _matches.anySatisfy { $0.hasEnded() == false } == false - } - - fileprivate func _createMatch(index: Int) -> Match { - let match: Match = Match(groupStage: self.id, - index: index, - format: self.matchFormat, - name: self.localizedMatchUpLabel(for: index)) - match.store = self.store -// print("_createMatch(index)", index) - return match - } - - func removeReturnMatches(onlyLast: Bool = false) { - - var returnMatches = _matches().filter({ $0.index >= matchCount }) - if onlyLast { - let matchPhaseCount = matchPhaseCount - 1 - returnMatches = returnMatches.filter({ $0.index >= matchCount * matchPhaseCount }) - } - do { - try self.tournamentStore?.matches.delete(contentOfs: returnMatches) - } catch { - Logger.error(error) - } - } - - var matchPhaseCount: Int { - let count = _matches().count - if matchCount > 0 { - return count / matchCount - } else { - return 0 - } - } - - func addReturnMatches() { - var teamScores = [TeamScore]() - var matches = [Match]() - let matchPhaseCount = matchPhaseCount - for i in 0..<_numberOfMatchesToBuild() { - let newMatch = self._createMatch(index: i + matchCount * matchPhaseCount) -// let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i)) - teamScores.append(contentsOf: newMatch.createTeamScores()) - matches.append(newMatch) - } - - self.tournamentStore?.matches.addOrUpdate(contentOfs: matches) - self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores) - } - - func buildMatches(keepExistingMatches: Bool = false) { - var teamScores = [TeamScore]() - var matches = [Match]() - clearScoreCache() - - if keepExistingMatches == false { - _removeMatches() - - for i in 0..<_numberOfMatchesToBuild() { - let newMatch = self._createMatch(index: i) - // let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i)) - teamScores.append(contentsOf: newMatch.createTeamScores()) - matches.append(newMatch) - } - } else { - for match in _matches() { - match.resetTeamScores(outsideOf: []) - teamScores.append(contentsOf: match.createTeamScores()) - } - } - - do { - try self.tournamentStore?.matches.addOrUpdate(contentOfs: matches) - try self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores) - } catch { - Logger.error(error) - } - } - - func playedMatches() -> [Match] { - let ordered = _matches() - let order = _matchOrder() - let matchCount = max(1, matchCount) - let count = ordered.count / matchCount - if ordered.isEmpty == false && ordered.count % order.count == 0 { - let repeatedArray = (0.. Int { - _matchOrder()[safe: match.index] ?? match.index - } - - func updateGroupStageState() { - clearScoreCache() - - if hasEnded(), let tournament = tournamentObject() { - - let teams = teams(true) - for (index, team) in teams.enumerated() { - team.qualified = index < tournament.qualifiedPerGroupStage - if team.bracketPosition != nil && team.qualified == false { - tournamentObject()?.shouldVerifyBracket = true - } - } - try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - if let tournamentObject = tournamentObject() { - try DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject) - } - self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - - let groupStagesAreOverAtFirstStep = tournament.groupStagesAreOver(atStep: 0) - let nextStepGroupStages = tournament.groupStages(atStep: 1) - let groupStagesAreOverAtSecondStep = tournament.groupStagesAreOver(atStep: 1) - - if groupStagesAreOverAtFirstStep, nextStepGroupStages.isEmpty || groupStagesAreOverAtSecondStep == true, tournament.groupStageLoserBracketAreOver(), tournament.rounds().isEmpty { - tournament.endDate = Date() - DataStore.shared.tournaments.addOrUpdate(instance: tournament) - } - - tournament.updateTournamentState() - } - } - - func scoreLabel(forGroupStagePosition groupStagePosition: Int, score: TeamGroupStageScore? = nil) -> (wins: String, losses: String, setsDifference: String?, gamesDifference: String?)? { - if let scoreData = (score ?? _score(forGroupStagePosition: groupStagePosition, nilIfEmpty: true)) { - let hideSetDifference = matchFormat.setsToWin == 1 - let setDifference = scoreData.setDifference.formatted(.number.sign(strategy: .always(includingZero: true))) + " set" + scoreData.setDifference.pluralSuffix - let gameDifference = scoreData.gameDifference.formatted(.number.sign(strategy: .always(includingZero: true))) + " jeu" + scoreData.gameDifference.localizedPluralSuffix("x") - return (wins: scoreData.wins.formatted(), losses: scoreData.loses.formatted(), setsDifference: hideSetDifference ? nil : setDifference, gamesDifference: gameDifference) -// return "\(scoreData.wins)/\(scoreData.loses) " + differenceAsString - } else { - return nil - } - } - -// func _score(forGroupStagePosition groupStagePosition: Int, nilIfEmpty: Bool = false) -> TeamGroupStageScore? { -// guard let team = teamAt(groupStagePosition: groupStagePosition) else { return nil } -// let matches = matches(forGroupStagePosition: groupStagePosition).filter({ $0.hasEnded() }) -// if matches.isEmpty && nilIfEmpty { return nil } -// let wins = matches.filter { $0.winningTeamId == team.id }.count -// let loses = matches.filter { $0.losingTeamId == team.id }.count -// let differences = matches.compactMap { $0.scoreDifference(groupStagePosition, atStep: step) } -// let setDifference = differences.map { $0.set }.reduce(0,+) -// let gameDifference = differences.map { $0.game }.reduce(0,+) -// return (team, wins, loses, setDifference, gameDifference) -// /* -// • 2 points par rencontre gagnée -// • 1 point par rencontre perdue -// • -1 point en cas de rencontre perdue par disqualification (scores de 6/0 6/0 attribués aux trois matchs) -// • -2 points en cas de rencontre perdu par WO (scores de 6/0 6/0 attribués aux trois matchs) -// */ -// } -// - func matches(forGroupStagePosition groupStagePosition: Int) -> [Match] { - let combos = Array((0.. Date? { - guard let groupStagePosition = team.groupStagePositionAtStep(step) else { return nil } - return matches(forGroupStagePosition: groupStagePosition).compactMap({ $0.startDate }).sorted().first ?? startDate - } - - func matchPlayed(by groupStagePosition: Int, againstPosition: Int) -> Match? { - if groupStagePosition == againstPosition { return nil } - let combos = Array((0.. [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func group stage availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return playedMatches.filter({ $0.isRunning() == false && $0.canBeStarted(inMatches: runningMatches, checkCanPlay: checkCanPlay) }).sorted(by: \.computedStartDateForSorting) - } - - func runningMatches(playedMatches: [Match]) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func group stage runningMatches", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return playedMatches.filter({ $0.isRunning() }).sorted(by: \.computedStartDateForSorting) - } - - func readyMatches(playedMatches: [Match]) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func group stage readyMatches", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return playedMatches.filter({ $0.isReady() && $0.isRunning() == false && $0.hasEnded() == false }) - } - - func finishedMatches(playedMatches: [Match]) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func group stage finishedMatches", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return playedMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed() - } - - func isReturnMatchEnabled() -> Bool { - _matches().count > matchCount - } - - private func _matchOrder() -> [Int] { - var order: [Int] - - switch size { - case 3: - order = [1, 2, 0] - case 4: - order = [2, 3, 1, 4, 5, 0] - case 5: - order = [3, 5, 8, 2, 6, 1, 9, 4, 7, 0] - case 6: - order = [4, 7, 9, 3, 6, 11, 2, 8, 10, 1, 13, 5, 12, 14, 0] - default: - order = [] - } - - return order - } - - - func indexOf(_ matchIndex: Int) -> Int { - _matchOrder().firstIndex(of: matchIndex) ?? matchIndex - } - - func _matchUp(for matchIndex: Int) -> [Int] { - let combinations = Array((0.. String { - if matchCount > 0 { - let count = _matches().count - if count > matchCount * 2 { - return " - vague \((matchIndex / matchCount) + 1)" - } - - if matchIndex >= matchCount { - return " - retour" - } - } - - return "" - } - - func localizedMatchUpLabel(for matchIndex: Int) -> String { - let matchUp = _matchUp(for: matchIndex) - if let index = matchUp.first, let index2 = matchUp.last { - return "#\(index + 1) vs #\(index2 + 1)" + returnMatchesSuffix(for: matchIndex) - } else { - return "--" - } - } - - var matchCount: Int { - (size * (size - 1)) / 2 - } - - func team(teamPosition team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? { - let _teams = _teams(for: matchIndex) - switch team { - case .one: - return _teams.first ?? nil - case .two: - return _teams.last ?? nil - } - } - - private func _teams(for matchIndex: Int) -> [TeamRegistration?] { - let combinations = Array(0.. Int { - (size * (size - 1)) / 2 - } - - func unsortedPlayers() -> [PlayerRegistration] { - unsortedTeams().flatMap({ $0.unsortedPlayers() }) - } - - typealias TeamScoreAreInIncreasingOrder = (TeamGroupStageScore, TeamGroupStageScore) -> Bool - - typealias TeamGroupStageScore = (team: TeamRegistration, wins: Int, loses: Int, setDifference: Int, gameDifference: Int) - - fileprivate func _headToHead(_ teamPosition: TeamRegistration, _ otherTeam: TeamRegistration) -> Bool { - let indexes = [teamPosition, otherTeam].compactMap({ $0.groupStagePosition }).sorted() - let combos = Array((0.. 1 { - let scoreA = calculateScore(for: teamPosition, matches: matches, groupStagePosition: teamPosition.groupStagePosition!) - let scoreB = calculateScore(for: otherTeam, matches: matches, groupStagePosition: otherTeam.groupStagePosition!) - - let teamsSorted = [scoreA, scoreB].sorted { (lhs, rhs) in - let predicates: [TeamScoreAreInIncreasingOrder] = [ - { $0.wins < $1.wins }, - { $0.setDifference < $1.setDifference }, - { $0.gameDifference < $1.gameDifference}, - { [self] in $0.team.groupStagePositionAtStep(self.step)! > $1.team.groupStagePositionAtStep(self.step)! } - ] - - for predicate in predicates { - if !predicate(lhs, rhs) && !predicate(rhs, lhs) { - continue - } - - return predicate(lhs, rhs) - } - - return false - }.map({ $0.team }) - - return teamsSorted.first == teamPosition - } else { - - if let matchIndex = combos.firstIndex(of: indexes), let match = _matches().first(where: { $0.index == matchIndex }) { - return teamPosition.id == match.losingTeamId - } else { - return false - } - - } - } - - func unsortedTeams() -> [TeamRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - if step > 0 { - return tournamentStore.groupStages.filter({ $0.step == step - 1 }).compactMap({ $0.teams(true)[safe: index] }) - } - return tournamentStore.teamRegistrations.filter { $0.groupStage == self.id && $0.groupStagePosition != nil } - } - - - var scoreCache: [Int: TeamGroupStageScore] = [:] - - func computedScore(forTeam team: TeamRegistration, step: Int = 0) -> TeamGroupStageScore? { - guard let groupStagePositionAtStep = team.groupStagePositionAtStep(step) else { - return nil - } - if let cachedScore = scoreCache[groupStagePositionAtStep] { - return cachedScore - } else { - let score = _score(forGroupStagePosition: groupStagePositionAtStep) - if let score = score { - scoreCache[groupStagePositionAtStep] = score - } - return score - } - } - - func teams(_ sortedByScore: Bool = false, scores: [TeamGroupStageScore]? = nil) -> [TeamRegistration] { - if sortedByScore { - return unsortedTeams().compactMap({ team in - // Check cache or use provided scores, otherwise calculate and store in cache - scores?.first(where: { $0.team.id == team.id }) ?? { - return computedScore(forTeam: team, step: step) - }() - }).sorted { (lhs, rhs) in - let predicates: [TeamScoreAreInIncreasingOrder] = [ - { $0.wins < $1.wins }, - { $0.setDifference < $1.setDifference }, - { $0.gameDifference < $1.gameDifference}, - { self._headToHead($0.team, $1.team) }, - { [self] in $0.team.groupStagePositionAtStep(self.step)! > $1.team.groupStagePositionAtStep(self.step)! } - ] - - for predicate in predicates { - if !predicate(lhs, rhs) && !predicate(rhs, lhs) { - continue - } - - return predicate(lhs, rhs) - } - - return false - }.map({ $0.team }).reversed() - } else { - return unsortedTeams().sorted(by: \TeamRegistration.groupStagePosition!) - } - } - - func _score(forGroupStagePosition groupStagePosition: Int, nilIfEmpty: Bool = false) -> TeamGroupStageScore? { - // Check if the score for this position is already cached - if let cachedScore = scoreCache[groupStagePosition] { - return cachedScore - } - - guard let team = teamAt(groupStagePosition: groupStagePosition) else { return nil } - let matches = matches(forGroupStagePosition: groupStagePosition).filter({ $0.hasEnded() }) - if matches.isEmpty && nilIfEmpty { return nil } - let score = calculateScore(for: team, matches: matches, groupStagePosition: groupStagePosition) - scoreCache[groupStagePosition] = score - return score - } - - private func calculateScore(for team: TeamRegistration, matches: [Match], groupStagePosition: Int) -> TeamGroupStageScore { - let wins = matches.filter { $0.winningTeamId == team.id }.count - let loses = matches.filter { $0.losingTeamId == team.id }.count - let differences = matches.compactMap { $0.scoreDifference(groupStagePosition, atStep: step) } - let setDifference = differences.map { $0.set }.reduce(0,+) - let gameDifference = differences.map { $0.game }.reduce(0,+) - - return (team, wins, loses, setDifference, gameDifference) - } - - // Clear the cache if necessary, for example when starting a new step or when matches update - func clearScoreCache() { - scoreCache.removeAll() - } - -// func teams(_ sortedByScore: Bool = false, scores: [TeamGroupStageScore]? = nil) -> [TeamRegistration] { -// if sortedByScore { -// return unsortedTeams().compactMap({ team in -// scores?.first(where: { $0.team.id == team.id }) ?? _score(forGroupStagePosition: team.groupStagePositionAtStep(step)!) -// }).sorted { (lhs, rhs) in -// // Calculate intermediate values once and reuse them -// let lhsWins = lhs.wins -// let rhsWins = rhs.wins -// let lhsSetDifference = lhs.setDifference -// let rhsSetDifference = rhs.setDifference -// let lhsGameDifference = lhs.gameDifference -// let rhsGameDifference = rhs.gameDifference -// let lhsHeadToHead = self._headToHead(lhs.team, rhs.team) -// let rhsHeadToHead = self._headToHead(rhs.team, lhs.team) -// let lhsGroupStagePosition = lhs.team.groupStagePositionAtStep(self.step)! -// let rhsGroupStagePosition = rhs.team.groupStagePositionAtStep(self.step)! -// -// // Define comparison predicates in the same order -// let predicates: [(Bool, Bool)] = [ -// (lhsWins < rhsWins, lhsWins > rhsWins), -// (lhsSetDifference < rhsSetDifference, lhsSetDifference > rhsSetDifference), -// (lhsGameDifference < rhsGameDifference, lhsGameDifference > rhsGameDifference), -// (lhsHeadToHead, rhsHeadToHead), -// (lhsGroupStagePosition > rhsGroupStagePosition, lhsGroupStagePosition < rhsGroupStagePosition) -// ] -// -// // Iterate over predicates and return as soon as a valid comparison is found -// for (lhsPredicate, rhsPredicate) in predicates { -// if lhsPredicate { return true } -// if rhsPredicate { return false } -// } -// -// return false -// }.map({ $0.team }).reversed() -// } else { -// return unsortedTeams().sorted(by: \TeamRegistration.groupStagePosition!) -// } -// } - - func updateMatchFormat(_ updatedMatchFormat: MatchFormat) { - self.matchFormat = updatedMatchFormat - self.updateAllMatchesFormat() - } - - func updateAllMatchesFormat() { - let playedMatches = playedMatches() - playedMatches.forEach { match in - match.matchFormat = matchFormat - } - self.tournamentStore?.matches.addOrUpdate(contentOfs: playedMatches) - } - - func pasteData() -> String { - var data: [String] = [] - data.append(self.groupStageTitle()) - teams().forEach { team in - data.append(team.teamLabelRanked(displayRank: true, displayTeamName: true)) - } - - return data.joined(separator: "\n") - } - - func finalPosition(ofTeam team: TeamRegistration) -> Int? { - guard hasEnded() else { return nil } - return teams(true).firstIndex(of: team) - } - - override func deleteDependencies() { - let matches = self._matches() - for match in matches { - match.deleteDependencies() - } - 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) -// 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) - for match in self._matches() { - match.insertOnServer() - } - } - -} - -extension GroupStage { - enum CodingKeys: String, CodingKey { - case _id = "id" - case _storeId = "storeId" - case _lastUpdate = "lastUpdate" - case _tournament = "tournament" - case _index = "index" - case _size = "size" - case _format = "format" - case _startDate = "startDate" - case _name = "name" - case _step = "step" - } -} - -extension GroupStage: Selectable { - func selectionLabel(index: Int) -> String { - groupStageTitle() - } - - func badgeValue() -> Int? { - return runningMatches(playedMatches: _matches()).count - } - - func badgeValueColor() -> Color? { - return nil - } - - func badgeImage() -> Badge? { - if teams().count < size { - return .xmark - } else { - return hasEnded() ? .checkmark : nil - } - } -} diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift deleted file mode 100644 index f4132a8..0000000 --- a/PadelClub/Data/Match.swift +++ /dev/null @@ -1,1088 +0,0 @@ -// -// Match.swift -// Padel Tournament -// -// Created by razmig on 10/03/2024. -// - -import Foundation -import LeStorage - -@Observable -final class Match: BaseMatch, SideStorable { - - static func == (lhs: Match, rhs: Match) -> Bool { - lhs.id == rhs.id && lhs.startDate == rhs.startDate - } - - static func setServerTitle(upperRound: Round, matchIndex: Int) -> String { - if upperRound.index == 0 { return upperRound.roundTitle() } - return upperRound.roundTitle() + " #" + (matchIndex + 1).formatted() - } - - var byeState: Bool = false - - 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) - - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - - func setMatchName(_ serverName: String?) { - self.name = serverName - } - - func isFromLastRound() -> Bool { - guard let roundObject, roundObject.parent == nil else { return false } - guard let currentTournament = currentTournament() else { return false } - if currentTournament.rounds().count - 1 == roundObject.index { - return true - } else { - return false - } - } - - var tournamentStore: TournamentStore? { - if let id = self.store?.identifier { - return TournamentLibrary.shared.store(tournamentId: id) - } - fatalError("missing store for \(String(describing: type(of: self)))") - } - - var courtIndexForSorting: Int { - courtIndex ?? Int.max - } - - // MARK: - Computed dependencies - - var teamScores: [TeamScore] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.teamScores.filter { $0.match == self.id } - } - - // MARK: - - - override func deleteDependencies() { - let teamScores = self.teamScores - for teamScore in teamScores { - teamScore.deleteDependencies() - } - self.tournamentStore?.teamScores.deleteDependencies(teamScores) - } - - func indexInRound(in matches: [Match]? = nil) -> Int { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func indexInRound(in", matches?.count, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - if groupStage != nil { - return index - } else if let matches, let index = matches.firstIndex(where: { $0.id == id }) { - return index - } else if let roundObject, roundObject.isUpperBracket(), let index = roundObject.playedMatches().firstIndex(where: { $0.id == id }) { - return index - } - return RoundRule.matchIndexWithinRound(fromMatchIndex: index) - } - - func matchWarningSubject() -> String { - [roundTitle(), matchTitle(.short)].compacted().joined(separator: " ") - } - - func matchWarningMessage() -> String { - [roundAndMatchTitle(), startDate?.localizedDate(), courtName(), matchFormat.computedLongLabel].compacted().joined(separator: "\n") - } - - func matchTitle(_ displayStyle: DisplayStyle = .wide, inMatches matches: [Match]? = nil) -> String { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func matchTitle", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - - if roundObject?.groupStageLoserBracket == true { - return "\(index)\(index.ordinalFormattedSuffix()) place" - } - if let groupStageObject { - return groupStageObject.localizedMatchUpLabel(for: index) - } - - switch displayStyle { - case .wide, .title: - return "Match \(indexInRound(in: matches) + 1)" - case .short: - return "n˚\(indexInRound(in: matches) + 1)" - } - } - - func isSeedLocked(atTeamPosition teamPosition: TeamPosition) -> Bool { - return previousMatch(teamPosition)?.disabled == true - } - - func unlockSeedPosition(atTeamPosition teamPosition: TeamPosition) { - previousMatch(teamPosition)?.enableMatch() - } - - @discardableResult - func lockAndGetSeedPosition(atTeamPosition teamPosition: TeamPosition) -> Int { - let matchIndex = index - previousMatch(teamPosition)?.disableMatch() - return matchIndex * 2 + teamPosition.rawValue - } - - func isSeededBy(team: TeamRegistration, inTeamPosition teamPosition: TeamPosition) -> Bool { - guard let roundObject, roundObject.isUpperBracket() else { return false } - guard let bracketPosition = team.bracketPosition else { return false } - return index * 2 + teamPosition.rawValue == bracketPosition - } - - func estimatedEndDate(_ additionalEstimationDuration: Int) -> Date? { - let minutesToAdd = Double(matchFormat.getEstimatedDuration(additionalEstimationDuration)) - return startDate?.addingTimeInterval(minutesToAdd * 60.0) - } - - func winner() -> TeamRegistration? { - guard let winningTeamId else { return nil } - return self.tournamentStore?.teamRegistrations.findById(winningTeamId) - } - - func loser() -> TeamRegistration? { - guard let losingTeamId else { return nil } - return self.tournamentStore?.teamRegistrations.findById(losingTeamId) - } - - - func localizedStartDate() -> String { - if let startDate { - return startDate.formatted(date: .abbreviated, time: .shortened) - } else { - return "" - } - } - - func scoreLabel() -> String { - if hasWalkoutTeam() == true { - return "WO" - } - let scoreOne = teamScore(.one)?.score?.components(separatedBy: ",") ?? [] - let scoreTwo = teamScore(.two)?.score?.components(separatedBy: ",") ?? [] - let tuples = zip(scoreOne, scoreTwo).map { ($0, $1) } - let scores = tuples.map { $0 + "/" + $1 }.joined(separator: " ") - return scores - } - - func cleanScheduleAndSave(_ targetStartDate: Date? = nil) { - startDate = targetStartDate ?? startDate - confirmed = false - endDate = nil - followingMatch()?.cleanScheduleAndSave(nil) - _loserMatch()?.cleanScheduleAndSave(nil) - self.tournamentStore?.matches.addOrUpdate(instance: self) - } - - func resetMatch() { - losingTeamId = nil - winningTeamId = nil - endDate = nil - removeCourt() - servingTeamId = nil - groupStageObject?.updateGroupStageState() - roundObject?.updateTournamentState() - teams().forEach({ $0.resetRestingTime() }) - } - - func resetScores() { - teamScores.forEach({ $0.score = nil }) - 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) - } - - func luckyLosers() -> [TeamRegistration] { - return roundObject?.previousRound()?.losers() ?? [] - } - - func isWalkOutSpot(_ teamPosition: TeamPosition) -> Bool { - return teamScore(teamPosition)?.walkOut == 1 - } - - func setLuckyLoser(team: TeamRegistration, teamPosition: TeamPosition) { - resetMatch() - - let matchIndex = index - let position = matchIndex * 2 + teamPosition.rawValue - - let previousScores = teamScores.filter({ $0.luckyLoser == position }) - 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) - } - - func disableMatch() { - _toggleMatchDisableState(true) - } - - func enableMatch() { - _toggleMatchDisableState(false) - } - - private func _loserMatch() -> Match? { - let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index) - return roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) - } - - func _toggleLoserMatchDisableState(_ state: Bool) { - guard let loserMatch = _loserMatch() else { return } - guard let next = _otherMatch() else { return } - loserMatch.byeState = true - if next.disabled { - loserMatch.byeState = false - } - loserMatch._toggleMatchDisableState(state, forward: true) - } - - fileprivate func _otherMatch() -> Match? { - guard let round else { return nil } - guard index > 0 else { return nil } - let nextIndex = (index - 1) / 2 - let topMatchIndex = (nextIndex * 2) + 1 - let bottomMatchIndex = (nextIndex + 1) * 2 - let isTopMatch = topMatchIndex + 1 == index - let lookingForIndex = isTopMatch ? topMatchIndex : bottomMatchIndex - - 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 }) - } - - func _toggleForwardMatchDisableState(_ state: Bool) { - guard let roundObject else { return } - guard roundObject.parent != nil else { return } - guard let forwardMatch = _forwardMatch(inRound: roundObject) else { return } - guard let next = _otherMatch() else { return } - if next.disabled && byeState == false && next.byeState == false { - if forwardMatch.disabled != state || forwardMatch.byeState { - forwardMatch.byeState = false - forwardMatch._toggleMatchDisableState(state, forward: true) - } - } else if byeState && next.byeState { - print("don't disable forward match") - if forwardMatch.byeState || forwardMatch.disabled { - forwardMatch.byeState = false - forwardMatch._toggleMatchDisableState(false, forward: true) - } - } else if forwardMatch.byeState == false { - forwardMatch.byeState = true - forwardMatch._toggleMatchDisableState(false, forward: true) - } else if forwardMatch.disabled != state { - forwardMatch._toggleMatchDisableState(state, forward: true) - } - -// if next.disabled == false { -// forwardMatch.byeState = state -// } - - - -// -// if next.disabled == state { -// if next.byeState != byeState { -// //forwardMatch.byeState = state -// forwardMatch._toggleMatchDisableState(state) -// } else { -// forwardMatch._toggleByeState(state) -// } -// } else { -// } -// forwardMatch._toggleByeState(state) - } - - func isSeededBy(team: TeamRegistration) -> Bool { - isSeededBy(team: team, inTeamPosition: .one) || isSeededBy(team: team, inTeamPosition: .two) - } - - func isSeeded() -> Bool { - return isSeededAt(.one) || isSeededAt(.two) - } - - func isSeededAt(_ teamPosition: TeamPosition) -> Bool { - if let team = team(teamPosition) { - return isSeededBy(team: team) - } - return false - } - - func _toggleMatchDisableState(_ state: Bool, forward: Bool = false, single: Bool = false) { - //if disabled == state { return } - let tournamentStore = self.tournamentStore - let currentState = disabled - disabled = state - - - if disabled != currentState { - tournamentStore?.teamScores.delete(contentOfs: teamScores) - } - if state == true, state != currentState { - let teams = teams() - for team in teams { - if isSeededBy(team: team) { - team.bracketPosition = nil - tournamentStore?.teamRegistrations.addOrUpdate(instance: team) - } - } - } - //byeState = false - if state != currentState { - roundObject?.invalidateCache() - name = nil - tournamentStore?.matches.addOrUpdate(instance: self) - } - - if single == false { - _toggleLoserMatchDisableState(state) - if forward { - _toggleForwardMatchDisableState(state) - } else { - topPreviousRoundMatch()?._toggleMatchDisableState(state) - bottomPreviousRoundMatch()?._toggleMatchDisableState(state) - } - } - } - - func next() -> Match? { - 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 - } - - func followingMatch() -> Match? { - guard let nextRoundId = roundObject?.nextRound()?.id else { return nil } - return getFollowingMatch(fromNextRoundId: nextRoundId) - } - - func getFollowingMatch(fromNextRoundId nextRoundId: String) -> Match? { - return self.tournamentStore?.matches.first(where: { $0.round == nextRoundId && $0.index == (index - 1) / 2 }) - } - - func getDuration() -> Int { - if let tournament = currentTournament() { - matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) - } else { - matchFormat.getEstimatedDuration() - } - } - - func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String? { - if groupStage != nil { return groupStageObject?.groupStageTitle() } - else if let roundObject { return roundObject.roundTitle() } - else { return nil } - } - - func roundAndMatchTitle(_ displayStyle: DisplayStyle = .wide) -> String { - [roundTitle(displayStyle), matchTitle(displayStyle)].compactMap({ $0 }).joined(separator: " ") - } - - func topPreviousRoundMatchIndex() -> Int { - return index * 2 + 1 - } - - func bottomPreviousRoundMatchIndex() -> Int { - return (index + 1) * 2 - } - - func topPreviousRoundMatch() -> Match? { - guard let roundObject else { return nil } - let topPreviousRoundMatchIndex = topPreviousRoundMatchIndex() - let roundObjectPreviousRoundId = roundObject.previousRound()?.id - return self.tournamentStore?.matches.first(where: { match in - match.round != nil && match.round == roundObjectPreviousRoundId && match.index == topPreviousRoundMatchIndex - }) - } - - func bottomPreviousRoundMatch() -> Match? { - guard let roundObject else { return nil } - let bottomPreviousRoundMatchIndex = bottomPreviousRoundMatchIndex() - let roundObjectPreviousRoundId = roundObject.previousRound()?.id - return self.tournamentStore?.matches.first(where: { match in - match.round != nil && match.round == roundObjectPreviousRoundId && match.index == bottomPreviousRoundMatchIndex - }) - } - - func previousMatch(_ teamPosition: TeamPosition) -> Match? { - if teamPosition == .one { - return topPreviousRoundMatch() - } else { - return bottomPreviousRoundMatch() - } - } - - func loserMatches() -> [Match] { - guard let roundObject else { return [] } - return [roundObject.upperBracketTopMatch(ofMatchIndex: index, previousRound: nil), roundObject.upperBracketBottomMatch(ofMatchIndex: index, previousRound: nil)].compactMap({ $0 }) - } - - func loserMatch(_ teamPosition: TeamPosition) -> Match? { - if teamPosition == .one { - return roundObject?.upperBracketTopMatch(ofMatchIndex: index, previousRound: nil) - } else { - return roundObject?.upperBracketBottomMatch(ofMatchIndex: index, previousRound: nil) - } - - } - - var computedOrder: Int { - if let groupStageObject { - return (groupStageObject.index + 1) * 100 + groupStageObject.indexOf(index) - } - guard let roundObject else { return index } - return (300 - (roundObject.theoryCumulativeMatchCount * 10 + roundObject.index * 22)) * 10 + RoundRule.matchIndexWithinRound(fromMatchIndex: index) - } - - func previousMatches() -> [Match] { - guard let roundObject else { return [] } - guard let tournamentStore = self.tournamentStore else { return [] } - - let roundObjectPreviousRoundId = roundObject.previousRound()?.id - return tournamentStore.matches.filter { match in - match.round == roundObjectPreviousRoundId && (match.index == topPreviousRoundMatchIndex() || match.index == bottomPreviousRoundMatchIndex()) - }.sorted(by: \.index) - } - - var matchFormat: MatchFormat { - get { - format ?? .defaultFormatForMatchType(.groupStage) - } - set { - format = newValue - } - } - - func removeWalkOut() { - teamScores.forEach { teamScore in - teamScore.walkOut = nil - teamScore.score = nil - } - tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores) - endDate = nil - winningTeamId = nil - losingTeamId = nil - groupStageObject?.updateGroupStageState() - roundObject?.updateTournamentState() - updateFollowingMatchTeamScore() - } - - func setWalkOut(_ teamPosition: TeamPosition) { - let teamScoreWalkout = teamScore(teamPosition) ?? TeamScore(match: id, team: team(teamPosition)) - teamScoreWalkout.walkOut = 0 - teamScoreWalkout.score = matchFormat.defaultWalkOutScore(true).compactMap({ String($0) }).joined(separator: ",") - let teamScoreWinning = teamScore(teamPosition.otherTeam) ?? TeamScore(match: id, team: team(teamPosition.otherTeam)) - teamScoreWinning.walkOut = nil - teamScoreWinning.score = matchFormat.defaultWalkOutScore(false).compactMap({ String($0) }).joined(separator: ",") - do { - try self.tournamentStore?.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning]) - } catch { - Logger.error(error) - } - - if endDate == nil { - endDate = Date() - } - teams().forEach({ $0.resetRestingTime() }) - winningTeamId = teamScoreWinning.teamRegistration - losingTeamId = teamScoreWalkout.teamRegistration - groupStageObject?.updateGroupStageState() - roundObject?.updateTournamentState() - updateFollowingMatchTeamScore() - } - - func setUnfinishedScore(fromMatchDescriptor matchDescriptor: MatchDescriptor) { - updateScore(fromMatchDescriptor: matchDescriptor) - if endDate == nil { - endDate = Date() - } - if startDate == nil { - startDate = endDate?.addingTimeInterval(Double(-getDuration()*60)) - } else if let startDate, let endDate, startDate >= endDate { - self.startDate = endDate.addingTimeInterval(Double(-getDuration()*60)) - } - confirmed = true - } - - func setScore(fromMatchDescriptor matchDescriptor: MatchDescriptor) { - updateScore(fromMatchDescriptor: matchDescriptor) - if endDate == nil { - endDate = Date() - } - if startDate == nil { - startDate = endDate?.addingTimeInterval(Double(-getDuration()*60)) - } else if let startDate, let endDate, startDate >= endDate { - self.startDate = endDate.addingTimeInterval(Double(-getDuration()*60)) - } - - let teamOne = team(matchDescriptor.winner) - let teamTwo = team(matchDescriptor.winner.otherTeam) - - teamOne?.hasArrived() - teamTwo?.hasArrived() - teamOne?.resetRestingTime() - teamTwo?.resetRestingTime() - - winningTeamId = teamOne?.id - losingTeamId = teamTwo?.id - - confirmed = true - - groupStageObject?.updateGroupStageState() - roundObject?.updateTournamentState() - if let tournament = currentTournament(), let endDate, let startDate { - if endDate.isEarlierThan(tournament.startDate) { - tournament.startDate = startDate - } - do { - try DataStore.shared.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) - } - } - updateFollowingMatchTeamScore() - } - - func updateScore(fromMatchDescriptor matchDescriptor: MatchDescriptor) { - let teamScoreOne = teamScore(.one) ?? TeamScore(match: id, team: team(.one)) - teamScoreOne.score = matchDescriptor.teamOneScores.joined(separator: ",") - let teamScoreTwo = teamScore(.two) ?? TeamScore(match: id, team: team(.two)) - teamScoreTwo.score = matchDescriptor.teamTwoScores.joined(separator: ",") - - if matchDescriptor.teamOneScores.last?.contains("-") == true && matchDescriptor.teamTwoScores.last?.contains("-") == false { - teamScoreTwo.score = (matchDescriptor.teamTwoScores.dropLast() + [matchDescriptor.teamTwoScores.last! + "-0"]).joined(separator: ",") - } else if matchDescriptor.teamTwoScores.last?.contains("-") == true && matchDescriptor.teamOneScores.last?.contains("-") == false { - teamScoreOne.score = (matchDescriptor.teamOneScores.dropLast() + [matchDescriptor.teamOneScores.last! + "-0"]).joined(separator: ",") - } - - do { - try self.tournamentStore?.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo]) - } catch { - Logger.error(error) - } - matchFormat = matchDescriptor.matchFormat - } - - func updateFollowingMatchTeamScore() { - followingMatch()?.updateTeamScores() - _loserMatch()?.updateTeamScores() - } - - func resetTeamScores(outsideOf newTeamScores: [TeamScore]) { - 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) - followingMatch()?.resetTeamScores(outsideOf: []) - _loserMatch()?.resetTeamScores(outsideOf: []) - } - } - - func createTeamScores() -> [TeamScore] { - let teamOne = team(.one) - let teamTwo = team(.two) - let teams = [teamOne, teamTwo].compactMap({ $0 }).map { TeamScore(match: id, team: $0) } - return teams - } - - func getOrCreateTeamScores() -> [TeamScore] { - let teamOne = team(.one) - let teamTwo = team(.two) - let teams = [teamOne, teamTwo].compactMap({ $0 }).map { teamScore(ofTeam: $0) ?? TeamScore(match: id, team: $0) } - return teams - } - - func updateTeamScores() { - let teams = getOrCreateTeamScores() - - self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teams) - resetTeamScores(outsideOf: teams) - if teams.isEmpty == false { - updateFollowingMatchTeamScore() - } - } - - func validateMatch(fromStartDate: Date, toEndDate: Date, fieldSetup: MatchFieldSetup, forced: Bool = false) { - if hasEnded() == false { - startDate = fromStartDate - - switch fieldSetup { - case .fullRandom: - if let _courtIndex = allCourts().randomElement() { - setCourt(_courtIndex) - } - case .random: - let runningMatches: [Match] = DataStore.shared.runningMatches() - if let _courtIndex = availableCourts(runningMatches: runningMatches).randomElement() { - setCourt(_courtIndex) - } - case .field(let _courtIndex): - setCourt(_courtIndex) - } - - } else { - startDate = fromStartDate - endDate = toEndDate - } - - if let startDate, startDate.timeIntervalSinceNow <= 300 { - confirmed = true - } else { - confirmed = false - } - } - - func courtName() -> String? { - guard let courtIndex else { return nil } - if let courtName = currentTournament()?.courtName(atIndex: courtIndex) { - return courtName - } else { - return Court.courtIndexedTitle(atIndex: courtIndex) - } - } - - func courtName(for selectedIndex: Int) -> String { - if let courtName = currentTournament()?.courtName(atIndex: selectedIndex) { - return courtName - } else { - return Court.courtIndexedTitle(atIndex: selectedIndex) - } - } - - func courtCount() -> Int { - return currentTournament()?.courtCount ?? 1 - } - - func courtIsAvailable(_ courtIndex: Int, in runningMatches: [Match]) -> Bool { - let courtUsed = currentTournament()?.courtUsed(runningMatches: runningMatches) ?? [] - return courtUsed.contains(courtIndex) == false - } - - func courtIsPreferred(_ courtIndex: Int) -> Bool { - return false - } - - func allCourts() -> [Int] { - let availableCourts = Array(0.. [Int] { - let courtUsed = currentTournament()?.courtUsed(runningMatches: runningMatches) ?? [] - return Set(allCourts().map { $0 }).subtracting(Set(courtUsed)).sorted() - } - - func removeCourt() { - courtIndex = nil - } - - func setCourt(_ courtIndex: Int) { - self.courtIndex = courtIndex - } - - func canBeStarted(inMatches matches: [Match], checkCanPlay: Bool) -> Bool { - let teams = teamScores - guard teams.count == 2 else { - //print("teams.count != 2") - return false - } - guard hasEnded() == false else { return false } - guard hasStarted() == false else { return false } - return teams.compactMap({ $0.team }).allSatisfy({ - ((checkCanPlay && $0.canPlay()) || checkCanPlay == false) && isTeamPlaying($0, inMatches: matches) == false - }) - } - - func isTeamPlaying(_ team: TeamRegistration, inMatches matches: [Match]) -> Bool { - return matches.filter({ $0.teamScores.compactMap { $0.teamRegistration }.contains(team.id) }).isEmpty == false - } - - var computedStartDateForSorting: Date { - return startDate ?? .distantFuture - } - - var computedEndDateForSorting: Date { - return endDate ?? .distantFuture - } - - func hasSpaceLeft() -> Bool { - return teamScores.count < 2 - } - - func isReady() -> Bool { - return teamScores.count >= 2 -// teams().count == 2 - } - - func isEmpty() -> Bool { - return teamScores.isEmpty -// teams().isEmpty - } - - func hasEnded() -> Bool { - return endDate != nil - } - - func isGroupStage() -> Bool { - return groupStage != nil - } - - func isBracket() -> Bool { - return round != nil - } - - func walkoutTeam() -> [TeamRegistration] { - //walkout 0 means real walkout, walkout 1 means lucky loser situation - return scores().filter({ $0.walkOut == 0 }).compactMap { $0.team } - } - - func hasWalkoutTeam() -> Bool { - return walkoutTeam().isEmpty == false - } - - func currentTournament() -> Tournament? { - return groupStageObject?.tournamentObject() ?? roundObject?.tournamentObject() - } - - func tournamentId() -> String? { - return groupStageObject?.tournament ?? roundObject?.tournament - } - - func scores() -> [TeamScore] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.teamScores.filter { $0.match == id } - } - - func teams() -> [TeamRegistration] { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func teams()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - - if groupStage != nil { - return [groupStageProjectedTeam(.one), groupStageProjectedTeam(.two)].compactMap { $0 } - } - guard let roundObject else { return [] } - let previousRound = roundObject.previousRound() - return [roundObject.roundProjectedTeam(.one, inMatch: self, previousRound: previousRound), roundObject.roundProjectedTeam(.two, inMatch: self, previousRound: previousRound)].compactMap { $0 } - -// return [roundProjectedTeam(.one), roundProjectedTeam(.two)].compactMap { $0 } - } - - func scoreDifference(_ teamPosition: Int, atStep step: Int) -> (set: Int, game: Int)? { - guard let teamScoreTeam = teamScore(.one), let teamScoreOtherTeam = teamScore(.two) else { return nil } - var reverseValue = 1 - if teamPosition == team(.two)?.groupStagePositionAtStep(step) { - reverseValue = -1 - } - let endedSetsOne = teamScoreTeam.score?.components(separatedBy: ",").compactMap({ $0.components(separatedBy: "-").first }).compactMap({ Int($0) }) ?? matchFormat.defaultWalkOutScore(teamScoreTeam.isWalkOut()) - let endedSetsTwo = teamScoreOtherTeam.score?.components(separatedBy: ",").compactMap({ $0.components(separatedBy: "-").first }).compactMap({ Int($0) }) ?? matchFormat.defaultWalkOutScore(teamScoreOtherTeam.isWalkOut()) - var setDifference : Int = 0 - let zip = zip(endedSetsOne, endedSetsTwo) - if matchFormat.setsToWin == 1 { - setDifference = endedSetsOne[0] - endedSetsTwo[0] - } else { - setDifference = zip.filter { $0 > $1 }.count - zip.filter { $1 > $0 }.count - } - - // si 3 sets et 3eme set super tie break, different des 2 premiers sets, alors super tie points ne sont pas des jeux et doivent etre compté comme un jeu - - if matchFormat.canSuperTie, endedSetsOne.count == 3 { - let games = zip.map { ($0, $1) } - let gameDifference = games.enumerated().map({ index, pair in - if index < 2 { - return pair.0 - pair.1 - } else { - return pair.0 < pair.1 ? -1 : 1 - } - }) - .reduce(0,+) - return (setDifference * reverseValue, gameDifference * reverseValue) - } else { - let gameDifference = zip.map { ($0, $1) }.map { $0.0 - $0.1 }.reduce(0,+) - return (setDifference * reverseValue, gameDifference * reverseValue) - } - } - - func groupStageProjectedTeam(_ team: TeamPosition) -> TeamRegistration? { - guard let groupStageObject else { return nil } - return groupStageObject.team(teamPosition: team, inMatchIndex: index) - } - - func roundProjectedTeam(_ team: TeamPosition) -> TeamRegistration? { - guard let roundObject else { return nil } - let previousRound = roundObject.previousRound() - return roundObject.roundProjectedTeam(team, inMatch: self, previousRound: previousRound) - } - - func teamWon(_ team: TeamRegistration?) -> Bool { - guard let winningTeamId else { return false } - return winningTeamId == team?.id - } - - func teamWon(atPosition teamPosition: TeamPosition) -> Bool { - guard let winningTeamId else { return false } - return winningTeamId == team(teamPosition)?.id - } - - func team(_ team: TeamPosition) -> TeamRegistration? { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func match get team", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - if groupStage != nil { - return groupStageProjectedTeam(team) - } else { - return roundProjectedTeam(team) - } - } - - func teamNames(_ team: TeamRegistration?) -> [String]? { - return team?.players().map { $0.playerLabel() } - } - - func teamWalkOut(_ team: TeamRegistration?) -> Bool { - return teamScore(ofTeam: team)?.isWalkOut() == true - } - - func teamScore(_ team: TeamPosition) -> TeamScore? { - return teamScore(ofTeam: self.team(team)) - } - - func teamScore(ofTeam team: TeamRegistration?) -> TeamScore? { - return scores().first(where: { $0.teamRegistration == team?.id }) - } - - func isRunning() -> Bool { // at least a match has started - return confirmed && hasStarted() && hasEnded() == false - } - - func hasStarted() -> Bool { // meaning at least one match is over - if let startDate { - return startDate.timeIntervalSinceNow < 0 && confirmed - } - if hasEnded() { - return true - } - return false - - //todo scores -// if let score { -// return score.hasEnded == false && score.sets.isEmpty == false -// } else { -// return false -// } - } - - var roundObject: Round? { - guard let round else { return nil } - return self.tournamentStore?.rounds.findById(round) - } - - var groupStageObject: GroupStage? { - guard let groupStage else { return nil } - return self.tournamentStore?.groupStages.findById(groupStage) - } - - var isLoserBracket: Bool { - if let roundObject { - if roundObject.parent != nil || roundObject.groupStageLoserBracket { - return true - } - } - return false - } - - var matchType: MatchType { - if isLoserBracket { - return .loserBracket - } else if isGroupStage() { - return .groupStage - } else { - return .bracket - } - } - - var restingTimeForSorting: TimeInterval { - (teams().compactMap({ $0.restingTime() }).max() ?? .distantFuture).timeIntervalSinceNow - } - - func isValidSpot() -> Bool { - previousMatches().allSatisfy({ $0.isSeeded() == false }) - } - - func expectedToBeRunning() -> Bool { - guard let startDate else { return false } - return confirmed == false && startDate.timeIntervalSinceNow < 0 - } - - func expectedFormattedStartDate(canBePlayedInSpecifiedCourt: Bool, availableCourts: [Int], estimatedStartDate: CourtIndexAndDate?, updatedField: Int?) -> String { - guard let startDate else { return "" } - guard hasEnded() == false, isRunning() == false else { return "" } - let depthReadiness = depthReadiness() - if depthReadiness == 0 { - if canBePlayedInSpecifiedCourt { - return "possible tout de suite" - } else if let updatedField, availableCourts.contains(updatedField) { - return "possible tout de suite \(courtName(for: updatedField))" - } else if let first = availableCourts.first { - return "possible tout de suite \(courtName(for: first))" - } else if let estimatedStartDate { - return "dans ~" + estimatedStartDate.1.timeElapsedString() + " " + courtName(for: estimatedStartDate.0) - } - return "était prévu à " + startDate.formattedAsHourMinute() - } else if depthReadiness == 1 { - return "possible prochaine rotation" - } else { - return "dans \(depthReadiness) rotation\(depthReadiness.pluralSuffix), ~\((getDuration() * depthReadiness).durationInHourMinutes())" - } - } - - func runningDuration() -> String { - guard let startDate else { return "" } - return " depuis " + startDate.timeElapsedString() - } - - func canBePlayedInSpecifiedCourt(runningMatches: [Match]) -> Bool { - guard let courtIndex else { return false } - if expectedToBeRunning() { - return courtIsAvailable(courtIndex, in: runningMatches) - } else { - return true - } - } - - typealias CourtIndexAndDate = (courtIndex: Int, startDate: Date) - - func nextCourtsAvailable(availableCourts: [Int], runningMatches: [Match]) -> [CourtIndexAndDate] { - guard let tournament = currentTournament() else { return [] } - let startDate = Date().withoutSeconds() - if runningMatches.isEmpty { - return availableCourts.map { - ($0, startDate) - } - } - - let optionalDates : [CourtIndexAndDate?] = runningMatches.map({ match in - guard let endDate = match.estimatedEndDate(tournament.additionalEstimationDuration) else { return nil } - guard let courtIndex = match.courtIndex else { return nil } - if endDate <= startDate { - return (courtIndex, startDate.addingTimeInterval(600)) - } else { - return (courtIndex, endDate) - } - }) - - let dates : [CourtIndexAndDate] = optionalDates.compacted().sorted { a, b in - a.1 < b.1 - } - return dates - } - - func estimatedStartDate(availableCourts: [Int], runningMatches: [Match]) -> CourtIndexAndDate? { - guard isReady() else { return nil } - guard let tournament = currentTournament() else { return nil } - let availableCourts = nextCourtsAvailable(availableCourts: availableCourts, runningMatches: runningMatches) - return availableCourts.first(where: { (courtIndex, startDate) in - let endDate = startDate.addingTimeInterval(TimeInterval(matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60) - if tournament.courtUnavailable(courtIndex: courtIndex, from: startDate, to: endDate) == false { - return true - } - - return false - }) - } - - func depthReadiness() -> Int { - // Base case: If this match is ready, the depth is 0 - if isReady() { - return 0 - } - - // Recursive case: If not ready, check the maximum depth of readiness among previous matches - // If previousMatches() is empty, return a default depth of -1 - let previousDepth = ancestors().map { $0.depthReadiness() }.max() ?? -1 - return previousDepth + 1 - } - - func ancestors() -> [Match] { - previousMatches() + loserMatches() - } - - func matchSpots() -> [MatchSpot] { - [MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)] - } - - func insertOnServer() { - self.tournamentStore?.matches.writeChangeAndInsertOnServer(instance: self) - for teamScore in self.teamScores { - teamScore.insertOnServer() - } - } - -} - -enum MatchDateSetup: Hashable, Identifiable { - case inMinutes(Int) - case now - case customDate - case previousRotation - case nextRotation - - var id: Int { hashValue } -} - -enum MatchFieldSetup: Hashable, Identifiable { - case random - case fullRandom -// case firstAvailable - case field(Int) - - var courtIndex: Int? { - switch self { - case .random: - return nil - case .fullRandom: - return nil - case .field(let int): - return int - } - } - - var id: Int { hashValue } -} diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift deleted file mode 100644 index 3e92642..0000000 --- a/PadelClub/Data/MatchScheduler.swift +++ /dev/null @@ -1,888 +0,0 @@ -// -// MatchScheduler.swift -// PadelClub -// -// Created by Razmig Sarkissian on 08/04/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -@Observable -final class MatchScheduler: BaseMatchScheduler, SideStorable { -// -// init(tournament: String, -// timeDifferenceLimit: Int = 5, -// loserBracketRotationDifference: Int = 0, -// upperBracketRotationDifference: Int = 1, -// accountUpperBracketBreakTime: Bool = true, -// accountLoserBracketBreakTime: Bool = false, -// randomizeCourts: Bool = true, -// rotationDifferenceIsImportant: Bool = false, -// shouldHandleUpperRoundSlice: Bool = false, -// shouldEndRoundBeforeStartingNext: Bool = true, -//<<<<<<< HEAD -// groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false, shouldTryToFillUpCourtsAvailable: Bool = false) { -// super.init() -//======= -// groupStageChunkCount: Int? = nil, -// overrideCourtsUnavailability: Bool = false, -// shouldTryToFillUpCourtsAvailable: Bool = true, -// courtsAvailable: Set = Set(), -// simultaneousStart: Bool = true) { -//>>>>>>> main -// 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 -// self.courtsAvailable = courtsAvailable -// self.simultaneousStart = simultaneousStart -// } - - var courtsUnavailability: [DateInterval]? { - guard let event = tournamentObject()?.eventObject() else { return nil } - return event.courtsUnavailability + (overrideCourtsUnavailability ? [] : event.tournamentsCourtsUsed(exluding: tournament)) - } - - var additionalEstimationDuration : Int { - return tournamentObject()?.additionalEstimationDuration ?? 0 - } - - var tournamentStore: TournamentStore? { - return TournamentLibrary.shared.store(tournamentId: self.tournament) -// TournamentStore.instance(tournamentId: self.tournament) - } - - func tournamentObject() -> Tournament? { - return Store.main.findById(tournament) - } - - @discardableResult - func updateGroupStageSchedule(tournament: Tournament, specificGroupStage: GroupStage? = nil, atStep step: Int = 0, startDate: Date? = nil) -> Date { - let computedGroupStageChunkCount = groupStageChunkCount ?? tournament.getGroupStageChunkValue() - var groupStages: [GroupStage] = tournament.groupStages(atStep: step) - if let specificGroupStage { - groupStages = [specificGroupStage] - } - - let matches = groupStages.flatMap { $0._matches() } - matches.forEach({ - $0.removeCourt() - $0.startDate = nil - $0.confirmed = false - }) - - var lastDate : Date = startDate ?? tournament.startDate - let times = Set(groupStages.compactMap { $0.startDate }).sorted() - - if let first = times.first { - if first.isEarlierThan(tournament.startDate) { - tournament.startDate = first - do { - try DataStore.shared.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) - } - } - } - - times.forEach({ time in - if lastDate.isEarlierThan(time) { - lastDate = time - } - let groups = groupStages.filter({ $0.startDate == time }) - let dispatch = groupStageDispatcher(groupStages: groups, startingDate: lastDate) - - dispatch.timedMatches.forEach { matchSchedule in - if let match = matches.first(where: { $0.id == matchSchedule.matchID }) { - let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) - let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 - if let startDate = match.groupStageObject?.startDate { - let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) - match.startDate = matchStartDate - lastDate = matchStartDate.addingTimeInterval(Double(estimatedDuration) * 60) - } - match.setCourt(matchSchedule.courtIndex) - } - } - }) - - 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) - } catch { - Logger.error(error) - } - - let dispatch = groupStageDispatcher(groupStages: groups, startingDate: lastDate) - - dispatch.timedMatches.forEach { matchSchedule in - if let match = matches.first(where: { $0.id == matchSchedule.matchID }) { - let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) - let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 - if let startDate = match.groupStageObject?.startDate { - let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) - match.startDate = matchStartDate - lastDate = matchStartDate.addingTimeInterval(Double(estimatedDuration) * 60) - } - match.setCourt(matchSchedule.courtIndex) - } - } - } - do { - try self.tournamentStore?.matches.addOrUpdate(contentOfs: matches) - } catch { - Logger.error(error) - } - return lastDate - } - - func groupStageDispatcher(groupStages: [GroupStage], startingDate: Date) -> GroupStageMatchDispatcher { - - let _groupStages = groupStages - - // Get the maximum count of matches in any group - let maxMatchesCount = _groupStages.map { $0._matches().count }.max() ?? 0 - var flattenedMatches = [Match]() - if simultaneousStart { - // Flatten matches in a round-robin order by cycling through each group - flattenedMatches = (0.. 0, simultaneousStart == false { - rotationMatches = rotationMatches.sorted(by: { - if counts[$0.groupStageObject!.index] ?? 0 == counts[$1.groupStageObject!.index] ?? 0 { - return $0.groupStageObject!.index < $1.groupStageObject!.index - } else { - return counts[$0.groupStageObject!.index] ?? 0 < counts[$1.groupStageObject!.index] ?? 0 - } - }) - } - - courtsAvailable.forEach { courtIndex in - print("Checking availability for court \(courtIndex) in rotation \(rotationIndex)") - if let first = rotationMatches.first(where: { match in - let estimatedDuration = match.matchFormat.getEstimatedDuration(additionalEstimationDuration) - let timeIntervalToAdd = Double(rotationIndex) * Double(estimatedDuration) * 60 - let rotationStartDate: Date = startingDate.addingTimeInterval(timeIntervalToAdd) - - let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), courtsUnavailability: courtsUnavailability) - - if courtsUnavailable.contains(courtIndex) { - print("Court \(courtIndex) is unavailable at \(rotationStartDate)") - return false - } - - let teamsAvailable = teamsPerRotation[rotationIndex]!.allSatisfy({ !match.containsTeamIndex($0) }) - if !teamsAvailable { - print("Teams from match \(match.roundAndMatchTitle()) are already scheduled in this rotation") - return false - } - - print("Match \(match.roundAndMatchTitle()) is available for court \(courtIndex) at \(rotationStartDate)") - return true - }) { - let timeMatch = GroupStageTimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, groupIndex: first.groupStageObject!.index) - - print("Scheduled match: \(first.roundAndMatchTitle()) on court \(courtIndex) at rotation \(rotationIndex)") - - slots.append(timeMatch) - teamsPerRotation[rotationIndex]!.append(contentsOf: first.matchUp()) - rotationMatches.removeAll(where: { $0.id == first.id }) - availableMatches.removeAll(where: { $0.id == first.id }) - - if let index = first.groupStageObject?.index { - groupLastRotation[index] = rotationIndex - } - } else { - print("No available matches for court \(courtIndex) in rotation \(rotationIndex), adding to free court list") - freeCourtPerRotation[rotationIndex]!.append(courtIndex) - } - } - - rotationIndex += 1 - } - - print("All matches scheduled. Total rotations: \(rotationIndex)") - - // Organize slots and ensure courts are randomized or sorted - var organizedSlots = [GroupStageTimeMatch]() - for i in 0.. Int { - if loserBracket { - return loserBracketRotationDifference - } else { - return upperBracketRotationDifference - } - } - - func roundMatchCanBePlayed(_ match: Match, roundObject: Round, slots: [TimeMatch], rotationIndex: Int, targetedStartDate: Date, minimumTargetedEndDate: inout Date) -> Bool { - print("Evaluating match: \(match.roundAndMatchTitle()) in round: \(roundObject.roundTitle()) with index: \(match.index)") - - if let roundStartDate = roundObject.startDate, targetedStartDate < roundStartDate { - print("Cannot start at \(targetedStartDate), earlier than round start date \(roundStartDate)") - if targetedStartDate == minimumTargetedEndDate { - print("Updating minimumTargetedEndDate to roundStartDate: \(roundStartDate)") - minimumTargetedEndDate = roundStartDate - } else { - print("Setting minimumTargetedEndDate to the earlier of \(roundStartDate) and \(minimumTargetedEndDate)") - minimumTargetedEndDate = min(roundStartDate, minimumTargetedEndDate) - } - print("Returning false: Match cannot start earlier than the round start date.") - return false - } - - let previousMatches = roundObject.precedentMatches(ofMatch: match) - if previousMatches.isEmpty { - print("No ancestors matches for this match, returning true. (eg beginning of tournament 1st bracket") - return true - } - - let previousMatchSlots = slots.filter { previousMatches.map { $0.id }.contains($0.matchID) } - - if previousMatchSlots.isEmpty { - if previousMatches.filter({ !$0.disabled }).allSatisfy({ $0.startDate != nil }) { - print("All previous matches have start dates, returning true.") - return true - } - print("Some previous matches are pending, returning false.") - return false - } - - if previousMatches.filter({ !$0.disabled }).count > previousMatchSlots.count { - if previousMatches.filter({ !$0.disabled }).anySatisfy({ $0.startDate != nil }) { - print("Some previous matches started, returning true.") - return true - } - print("Not enough previous matches have started, returning false.") - return false - } - - var includeBreakTime = false - if accountLoserBracketBreakTime && roundObject.isLoserBracket() { - includeBreakTime = true - print("Including break time for loser bracket.") - } - - if accountUpperBracketBreakTime && !roundObject.isLoserBracket() { - includeBreakTime = true - print("Including break time for upper bracket.") - } - - let previousMatchIsInPreviousRotation = previousMatchSlots.allSatisfy { - $0.rotationIndex + rotationDifference(loserBracket: roundObject.isLoserBracket()) < rotationIndex - } - - if previousMatchIsInPreviousRotation { - print("All previous matches are from earlier rotations, returning true.") - } else { - print("Some previous matches are from the current rotation.") - } - - guard let minimumPossibleEndDate = previousMatchSlots.map({ - $0.estimatedEndDate(includeBreakTime: includeBreakTime) - }).max() else { - print("No valid previous match end date, returning \(previousMatchIsInPreviousRotation).") - return previousMatchIsInPreviousRotation - } - - if targetedStartDate >= minimumPossibleEndDate { - if rotationDifferenceIsImportant { - print("Targeted start date is after the minimum possible end date and rotation difference is important, returning \(previousMatchIsInPreviousRotation).") - return previousMatchIsInPreviousRotation - } else { - print("Targeted start date is after the minimum possible end date, returning true.") - return true - } - } else { - if targetedStartDate == minimumTargetedEndDate { - print("Updating minimumTargetedEndDate to minimumPossibleEndDate: \(minimumPossibleEndDate)") - minimumTargetedEndDate = minimumPossibleEndDate - } else { - print("Setting minimumTargetedEndDate to the earlier of \(minimumPossibleEndDate) and \(minimumTargetedEndDate)") - minimumTargetedEndDate = min(minimumPossibleEndDate, minimumTargetedEndDate) - } - print("Targeted start date \(targetedStartDate) is before the minimum possible end date, returning false. \(minimumTargetedEndDate)") - return false - } - } - - - func getNextStartDate(fromPreviousRotationSlots slots: [TimeMatch], includeBreakTime: Bool) -> Date? { - slots.map { $0.estimatedEndDate(includeBreakTime: includeBreakTime) }.min() - } - - func getNextEarliestAvailableDate(from slots: [TimeMatch]) -> [(Int, Date)] { - let byCourt = Dictionary(grouping: slots, by: { $0.courtIndex }) - return (byCourt.keys.flatMap { courtIndex in - let matchesByCourt = byCourt[courtIndex]?.sorted(by: \.startDate) - let lastMatch = matchesByCourt?.last - var results = [(Int, Date)]() - if let courtFreeDate = lastMatch?.estimatedEndDate(includeBreakTime: false) { - results.append((courtIndex, courtFreeDate)) - } - return results - } - ) - } - - func getAvailableCourts(from matches: [Match]) -> [(Int, Date)] { - let validMatches = matches.filter({ $0.courtIndex != nil && $0.startDate != nil }) - let byCourt = Dictionary(grouping: validMatches, by: { $0.courtIndex! }) - return (byCourt.keys.flatMap { court in - let matchesByCourt = byCourt[court]?.sorted(by: \.startDate!) - let lastMatch = matchesByCourt?.last - var results = [(Int, Date)]() - if let courtFreeDate = lastMatch?.estimatedEndDate(additionalEstimationDuration) { - results.append((court, courtFreeDate)) - } - return results - } - ) - } - - func roundDispatcher(flattenedMatches: [Match], dispatcherStartDate: Date, initialCourts: [Int]?) -> MatchDispatcher { - var slots = [TimeMatch]() - var _startDate: Date? - var rotationIndex = 0 - var availableMatchs = flattenedMatches.filter({ $0.startDate == nil }) - let courtsUnavailability = courtsUnavailability - var issueFound: Bool = false - - // Log start of the function - print("Starting roundDispatcher with \(availableMatchs.count) matches and \(courtsAvailable) courts available") - - flattenedMatches.filter { $0.startDate != nil }.sorted(by: \.startDate!).forEach { match in - if _startDate == nil { - _startDate = match.startDate - } else if match.startDate! > _startDate! { - _startDate = match.startDate - rotationIndex += 1 - } - - let timeMatch = TimeMatch(matchID: match.id, rotationIndex: rotationIndex, courtIndex: match.courtIndex ?? 0, startDate: match.startDate!, durationLeft: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), minimumBreakTime: match.matchFormat.breakTime.breakTime) - slots.append(timeMatch) - } - - if !slots.isEmpty { - rotationIndex += 1 - } - - var freeCourtPerRotation = [Int: [Int]]() - var courts = initialCourts ?? Array(courtsAvailable) - var shouldStartAtDispatcherDate = rotationIndex > 0 - var suitableDate: Date? - - while !availableMatchs.isEmpty && !issueFound && rotationIndex < 50 { - freeCourtPerRotation[rotationIndex] = [] - let previousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 1 }) - - var rotationStartDate: Date - if previousRotationSlots.isEmpty && rotationIndex > 0 { - let computedSuitableDate = slots.sorted(by: \.computedEndDateForSorting).last?.computedEndDateForSorting - print("Previous rotation was empty, find a suitable rotationStartDate \(suitableDate)") - rotationStartDate = suitableDate ?? computedSuitableDate ?? dispatcherStartDate - } else { - rotationStartDate = getNextStartDate(fromPreviousRotationSlots: previousRotationSlots, includeBreakTime: false) ?? dispatcherStartDate - } - - if shouldStartAtDispatcherDate { - rotationStartDate = dispatcherStartDate - shouldStartAtDispatcherDate = false - } else { - courts = rotationIndex == 0 ? courts : Array(courtsAvailable) - } - courts.sort() - - // Log courts availability and start date - print("Courts available at rotation \(rotationIndex): \(courts)") - print("Rotation start date: \(rotationStartDate)") - - // Check for court availability and break time conflicts - if rotationIndex > 0, let freeCourtPreviousRotation = freeCourtPerRotation[rotationIndex - 1], !freeCourtPreviousRotation.isEmpty { - print("Handling break time conflicts or waiting for free courts") - let previousPreviousRotationSlots = slots.filter { $0.rotationIndex == rotationIndex - 2 && freeCourtPreviousRotation.contains($0.courtIndex) } - var previousEndDate = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: accountUpperBracketBreakTime) - var previousEndDateNoBreak = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: false) - - if let courtsUnavailability, previousEndDate != nil { - previousEndDate = getFirstFreeCourt(startDate: previousEndDate!, duration: 0, courts: courts, courtsUnavailability: courtsUnavailability).earliestFreeDate - } - - if let courtsUnavailability, previousEndDateNoBreak != nil { - previousEndDateNoBreak = getFirstFreeCourt(startDate: previousEndDateNoBreak!, duration: 0, courts: courts, courtsUnavailability: courtsUnavailability).earliestFreeDate - } - - let noBreakAlreadyTested = previousRotationSlots.anySatisfy { $0.startDate == previousEndDateNoBreak } - - if let previousEndDate, let previousEndDateNoBreak { - let differenceWithBreak = rotationStartDate.timeIntervalSince(previousEndDate) - let differenceWithoutBreak = rotationStartDate.timeIntervalSince(previousEndDateNoBreak) - print("Difference with break: \(differenceWithBreak), without break: \(differenceWithoutBreak)") - - let timeDifferenceLimitInSeconds = Double(timeDifferenceLimit * 60) - var difference = differenceWithBreak - - if differenceWithBreak <= 0, accountUpperBracketBreakTime == false { - difference = differenceWithoutBreak - } else if differenceWithBreak > timeDifferenceLimitInSeconds && differenceWithoutBreak > timeDifferenceLimitInSeconds { - difference = noBreakAlreadyTested ? differenceWithBreak : max(differenceWithBreak, differenceWithoutBreak) - } - - print("Final difference to evaluate: \(difference)") - - if (difference > timeDifferenceLimitInSeconds && rotationStartDate.addingTimeInterval(-difference) != previousEndDate) || difference < 0 { - print(""" - Adjusting rotation start: - - Initial rotationStartDate: \(rotationStartDate) - - Adjusted by difference: \(difference) - - Adjusted rotationStartDate: \(rotationStartDate.addingTimeInterval(-difference)) - - PreviousEndDate: \(previousEndDate) - """) - - courts.removeAll(where: { freeCourtPreviousRotation.contains($0) }) - freeCourtPerRotation[rotationIndex] = courts - courts = freeCourtPreviousRotation - rotationStartDate = rotationStartDate.addingTimeInterval(-difference) - } - } - } else if let firstMatch = availableMatchs.first { - let duration = firstMatch.matchFormat.getEstimatedDuration(additionalEstimationDuration) - let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: duration, courtsUnavailability: courtsUnavailability) - - if Array(Set(courtsAvailable).subtracting(Set(courtsUnavailable))).isEmpty { - print("Issue: All courts unavailable in this rotation") - if let courtsUnavailability { - let computedStartDateAndCourts = getFirstFreeCourt(startDate: rotationStartDate, duration: duration, courts: courts, courtsUnavailability: courtsUnavailability) - rotationStartDate = computedStartDateAndCourts.earliestFreeDate - courts = computedStartDateAndCourts.availableCourts - } else { - issueFound = true - } - } else { - courts = Array(Set(courtsAvailable).subtracting(Set(courtsUnavailable))) - } - } - - // Dispatch courts and schedule matches - suitableDate = dispatchCourts(courts: courts, availableMatchs: &availableMatchs, slots: &slots, rotationIndex: rotationIndex, rotationStartDate: rotationStartDate, freeCourtPerRotation: &freeCourtPerRotation, courtsUnavailability: courtsUnavailability) - rotationIndex += 1 - } - - // Organize matches in slots - var organizedSlots = [TimeMatch]() - for i in 0.. Date { - var matchPerRound = [String: Int]() - var minimumTargetedEndDate = rotationStartDate - - // Log dispatch attempt - print("Dispatching courts for rotation \(rotationIndex) with start date \(rotationStartDate) and available courts \(courts.sorted())") - - for (courtPosition, courtIndex) in courts.sorted().enumerated() { - if let firstMatch = availableMatchs.first(where: { match in - print("Trying to find a match for court \(courtIndex) in rotation \(rotationIndex)") - - let roundObject = match.roundObject! - let duration = match.matchFormat.getEstimatedDuration(additionalEstimationDuration) - - let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: duration, courtsUnavailability: courtsUnavailability) - - if courtsUnavailable.contains(courtIndex) { - print("Returning false: Court \(courtIndex) unavailable due to schedule conflicts during \(rotationStartDate).") - return false - } - - let canBePlayed = roundMatchCanBePlayed(match, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &minimumTargetedEndDate) - - if !canBePlayed { - print("Returning false: Match \(match.roundAndMatchTitle()) can't be played due to constraints.") - return false - } - - let currentRotationSameRoundMatches = matchPerRound[roundObject.id] ?? 0 - let roundMatchesCount = roundObject.playedMatches().count - - if shouldHandleUpperRoundSlice { - if roundObject.parent == nil, roundObject.index > 1, currentRotationSameRoundMatches == 0, courts.count - matchPerRound.count < 2 { - return false - } - else if roundObject.parent == nil, roundObject.index == 0, matchPerRound.isEmpty == false { - return false - } - else if roundObject.parent == nil, roundObject.index == 1, currentRotationSameRoundMatches >= 0, courtsAvailable.count > 0 { - return true - } - else if roundObject.parent == nil && roundMatchesCount > courts.count && currentRotationSameRoundMatches >= min(roundMatchesCount / 2, courts.count) { - print("Returning false: Too many matches already played in the current rotation for round \(roundObject.roundTitle()).") - return false - } - } - - let indexInRound = match.indexInRound() - - - if shouldTryToFillUpCourtsAvailable == false { - if roundObject.parent == nil && roundObject.index > 1 && indexInRound == 0, let nextMatch = match.next() { - - var nextMinimumTargetedEndDate = minimumTargetedEndDate - if courtPosition < courts.count - 1 && canBePlayed && roundMatchCanBePlayed(nextMatch, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &nextMinimumTargetedEndDate) { - print("Returning true: Both current \(match.index) and next match \(nextMatch.index) can be played in rotation \(rotationIndex).") - return true - } else { - print("Returning false: Either current match or next match cannot be played in rotation \(rotationIndex).") - return false - } - } - } - - print("Returning true: Match \(match.roundAndMatchTitle()) can be played on court \(courtIndex).") - return canBePlayed - }) { - print("Found match: \(firstMatch.roundAndMatchTitle()) for court \(courtIndex) at \(rotationStartDate)") - - matchPerRound[firstMatch.roundObject!.id, default: 0] += 1 - - let timeMatch = TimeMatch( - matchID: firstMatch.id, - rotationIndex: rotationIndex, - courtIndex: courtIndex, - startDate: rotationStartDate, - durationLeft: firstMatch.matchFormat.getEstimatedDuration(additionalEstimationDuration), - minimumBreakTime: firstMatch.matchFormat.breakTime.breakTime - ) - - slots.append(timeMatch) - availableMatchs.removeAll(where: { $0.id == firstMatch.id }) - } else { - print("No suitable match found for court \(courtIndex) in rotation \(rotationIndex). Adding court to freeCourtPerRotation.") - freeCourtPerRotation[rotationIndex]?.append(courtIndex) - } - - } - - if freeCourtPerRotation[rotationIndex]?.count == courtsAvailable.count { - print("All courts in rotation \(rotationIndex) are free, minimumTargetedEndDate : \(minimumTargetedEndDate)") - } - - if let courtsUnavailability { - let computedStartDateAndCourts = getFirstFreeCourt(startDate: minimumTargetedEndDate, duration: 0, courts: courts, courtsUnavailability: courtsUnavailability) - return computedStartDateAndCourts.earliestFreeDate - } - - return minimumTargetedEndDate - } - - @discardableResult func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) -> Bool { - - let upperRounds: [Round] = tournament.rounds() - let allMatches: [Match] = tournament.allMatches().filter({ $0.hasEnded() == false && $0.hasStarted() == false }) - - var rounds = [Round]() - - if let groupStageLoserBracketRound = tournament.groupStageLoserBracket() { - rounds.append(groupStageLoserBracketRound) - } - - if shouldEndRoundBeforeStartingNext { - rounds.append(contentsOf: upperRounds.flatMap { - [$0] + $0.loserRoundsAndChildren() - }) - } else { - rounds.append(contentsOf: upperRounds.map { - $0 - } + upperRounds.flatMap { - $0.loserRoundsAndChildren() - }) - } - - let flattenedMatches = rounds.flatMap { round in - round._matches().filter({ $0.disabled == false && $0.hasEnded() == false && $0.hasStarted() == false }).sorted(by: \.index) - } - - flattenedMatches.forEach({ - if (roundId == nil && matchId == nil) || $0.startDate?.isEarlierThan(startDate) == false { - $0.startDate = nil - $0.removeCourt() - $0.confirmed = false - } - }) - -// if let roundId { -// if let round : Round = Store.main.findById(roundId) { -// let matches = round._matches().filter({ $0.disabled == false }).sorted(by: \.index) -// round.resetFromRoundAllMatchesStartDate() -// flattenedMatches = matches + flattenedMatches -// } -// -// } else if let matchId { -// if let match : Match = Store.main.findById(matchId) { -// if let round = match.roundObject { -// round.resetFromRoundAllMatchesStartDate(from: match) -// } -// flattenedMatches = [match] + flattenedMatches -// } -// } - - if let roundId, let matchId { - //todo - if let index = flattenedMatches.firstIndex(where: { $0.round == roundId && $0.id == matchId }) { - flattenedMatches[index...].forEach { - $0.startDate = nil - $0.removeCourt() - $0.confirmed = false - } - } - } else if let roundId { - //todo - if let index = flattenedMatches.firstIndex(where: { $0.round == roundId }) { - flattenedMatches[index...].forEach { - $0.startDate = nil - $0.removeCourt() - $0.confirmed = false - } - } - } - - - let matches: [Match] = allMatches.filter { $0.startDate?.isEarlierThan(startDate) == true && $0.startDate?.dayInt == startDate.dayInt } - let usedCourts = getAvailableCourts(from: matches) - let initialCourts: [Int] = usedCourts.filter { (court, availableDate) in - availableDate <= startDate - }.sorted(by: \.1).compactMap { $0.0 } - - let courts : [Int]? = initialCourts.isEmpty ? nil : initialCourts - - print("initial available courts at beginning: \(courts ?? [])") - - let roundDispatch = self.roundDispatcher(flattenedMatches: flattenedMatches, dispatcherStartDate: startDate, initialCourts: courts) - - roundDispatch.timedMatches.forEach { matchSchedule in - if let match = flattenedMatches.first(where: { $0.id == matchSchedule.matchID }) { - match.startDate = matchSchedule.startDate - match.setCourt(matchSchedule.courtIndex) - } - } - - do { - try self.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches) - } catch { - Logger.error(error) - } - - return roundDispatch.issueFound - } - - - func courtsUnavailable(startDate: Date, duration: Int, courtsUnavailability: [DateInterval]?) -> [Int] { - let endDate = startDate.addingTimeInterval(Double(duration) * 60) - guard let courtsUnavailability else { return [] } - let groupedBy = Dictionary(grouping: courtsUnavailability, by: { $0.courtIndex }) - let courts = groupedBy.keys - return courts.filter { - courtUnavailable(courtIndex: $0, from: startDate, to: endDate, source: courtsUnavailability) - } - } - - func courtUnavailable(courtIndex: Int, from startDate: Date, to endDate: Date, source: [DateInterval]) -> Bool { - let courtLockedSchedule = source.filter({ $0.courtIndex == courtIndex }) - return courtLockedSchedule.anySatisfy({ dateInterval in - let range = startDate.. (earliestFreeDate: Date, availableCourts: [Int]) { - var earliestEndDate: Date? - var availableCourtsAtEarliest: [Int] = [] - - // Iterate through each court and find the earliest time it becomes free - for courtIndex in courts { - let unavailabilityForCourt = courtsUnavailability.filter { $0.courtIndex == courtIndex } - var isAvailable = true - - for interval in unavailabilityForCourt { - if interval.startDate <= startDate && interval.endDate > startDate { - isAvailable = false - if let currentEarliest = earliestEndDate { - earliestEndDate = min(currentEarliest, interval.endDate) - } else { - earliestEndDate = interval.endDate - } - } - } - - // If the court is available at the start date, add it to the list of available courts - if isAvailable { - availableCourtsAtEarliest.append(courtIndex) - } - } - - // If there are no unavailable courts, return the original start date and all courts - if let earliestEndDate = earliestEndDate { - // Find which courts will be available at the earliest free date - let courtsAvailableAtEarliest = courts.filter { courtIndex in - let unavailabilityForCourt = courtsUnavailability.filter { $0.courtIndex == courtIndex } - return unavailabilityForCourt.allSatisfy { $0.endDate <= earliestEndDate } - } - return (earliestFreeDate: earliestEndDate, availableCourts: courtsAvailableAtEarliest) - } else { - // If no courts were unavailable, all courts are available at the start date - return (earliestFreeDate: startDate.addingTimeInterval(Double(duration) * 60), availableCourts: courts) - } - } - - func updateSchedule(tournament: Tournament) -> Bool { - if tournament.courtCount < courtsAvailable.count { - courtsAvailable = Set(tournament.courtsAvailable()) - } - var lastDate = tournament.startDate - if tournament.groupStageCount > 0 { - lastDate = updateGroupStageSchedule(tournament: tournament) - } - if tournament.groupStages(atStep: 1).isEmpty == false { - lastDate = updateGroupStageSchedule(tournament: tournament, atStep: 1, startDate: lastDate) - } - return updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate) - } -} - -struct GroupStageTimeMatch { - let matchID: String - let rotationIndex: Int - var courtIndex: Int - let groupIndex: Int -} - -struct TimeMatch { - let matchID: String - let rotationIndex: Int - var courtIndex: Int - var startDate: Date - var durationLeft: Int //in minutes - var minimumBreakTime: Int //in minutes - - func estimatedEndDate(includeBreakTime: Bool) -> Date { - let minutesToAdd = Double(durationLeft + (includeBreakTime ? minimumBreakTime : 0)) - return startDate.addingTimeInterval(minutesToAdd * 60.0) - } - - var computedEndDateForSorting: Date { - estimatedEndDate(includeBreakTime: false) - } -} - -struct GroupStageMatchDispatcher { - let timedMatches: [GroupStageTimeMatch] - let freeCourtPerRotation: [Int: [Int]] - let rotationCount: Int - let groupLastRotation: [Int: Int] -} - -struct MatchDispatcher { - let timedMatches: [TimeMatch] - let freeCourtPerRotation: [Int: [Int]] - let rotationCount: Int - let issueFound: Bool -} - -extension Match { - func teamIds() -> [String] { - return teams().map { $0.id } - } - - func containsTeamId(_ id: String) -> Bool { - return teamIds().contains(id) - } - - func containsTeamIndex(_ id: String) -> Bool { - matchUp().contains(id) - } - - func matchUp() -> [String] { - guard let groupStageObject else { - return [] - } - - return groupStageObject._matchUp(for: index).map { groupStageObject.id + "_\($0)" } - } -} diff --git a/PadelClub/Data/MockData.swift b/PadelClub/Data/MockData.swift deleted file mode 100644 index 58d098a..0000000 --- a/PadelClub/Data/MockData.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// MockData.swift -// PadelClub -// -// Created by Razmig Sarkissian on 20/03/2024. -// - -import Foundation - -extension Court { - static func mock() -> Court { - Court(index: 0, club: "", name: "Test") - } -} - -extension Event { - static func mock() -> Event { - Event() - } -} - -extension Club { - static func mock() -> Club { - Club(name: "AUC", acronym: "AUC") - } - - static func newEmptyInstance() -> Club { - Club(name: "", acronym: "") - } -} - -extension GroupStage { - static func mock() -> GroupStage { - GroupStage(tournament: "", index: 0, size: 4) - } -} - -extension Round { - static func mock() -> Round { - Round(tournament: "", index: 0) - } -} - -extension Tournament { - static func mock() -> Tournament { - return Tournament(groupStageSortMode: .snake, teamSorting: .inscriptionDate, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .senior) - } -} - -extension Match { - static func mock() -> Match { - return Match(index: 0) - } -} - -extension TeamRegistration { - static func mock() -> TeamRegistration { - return TeamRegistration(tournament: "") - } -} - -extension PlayerRegistration { - static func mock() -> PlayerRegistration { - return PlayerRegistration(firstName: "Raz", lastName: "Shark", sex: .male) - } -} diff --git a/PadelClub/Data/PlayerPaymentType.swift b/PadelClub/Data/PlayerPaymentType.swift deleted file mode 100644 index 4ce8325..0000000 --- a/PadelClub/Data/PlayerPaymentType.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// PlayerPaymentType.swift -// PadelClub -// -// Created by Laurent Morvillier on 11/02/2025. -// - -import Foundation - -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/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift deleted file mode 100644 index 99a1196..0000000 --- a/PadelClub/Data/PlayerRegistration.swift +++ /dev/null @@ -1,510 +0,0 @@ -// -// PlayerRegistration.swift -// Padel Tournament -// -// Created by razmig on 10/03/2024. -// - -import Foundation -import LeStorage - -@Observable -final class PlayerRegistration: BasePlayerRegistration, SideStorable { - - func localizedSourceLabel() -> String { - switch source { - case .frenchFederation: - return "base fédérale" - case .beachPadel: - return "beach-padel" - case nil: - if registeredOnline { - return "à vérifier vous-même" - } else { - return "créé par vous-même" - } - } - } - - 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: PlayerRegistration.PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false, registeredOnline: Bool = false, timeToConfirm: Date? = nil, registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting, paymentId: String? = nil) { - super.init() - 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.coach = coach - self.captain = captain - self.registeredOnline = registeredOnline - self.timeToConfirm = timeToConfirm - self.registrationStatus = registrationStatus - self.paymentId = paymentId - } - - internal init(importedPlayer: ImportedPlayer) { - super.init() - self.teamRegistration = "" - self.firstName = (importedPlayer.firstName ?? "").prefixTrimmed(50).capitalized - self.lastName = (importedPlayer.lastName ?? "").prefixTrimmed(50).uppercased() - self.licenceId = importedPlayer.license?.prefixTrimmed(50) ?? nil - self.rank = Int(importedPlayer.rank) - self.sex = importedPlayer.male ? .male : .female - self.tournamentPlayed = importedPlayer.tournamentPlayed - self.points = importedPlayer.getPoints() - self.clubName = importedPlayer.clubName?.prefixTrimmed(200) - self.ligueName = importedPlayer.ligueName?.prefixTrimmed(200) - self.assimilation = importedPlayer.assimilation?.prefixTrimmed(50) - self.source = .frenchFederation - self.birthdate = importedPlayer.birthYear?.prefixTrimmed(50) - } - - internal init?(federalData: [String], sex: Int, sexUnknown: Bool) { - super.init() - let _lastName = federalData[0].trimmed.uppercased() - let _firstName = federalData[1].trimmed.capitalized - if _lastName.isEmpty && _firstName.isEmpty { return nil } - lastName = _lastName.prefixTrimmed(50) - firstName = _firstName.prefixTrimmed(50) - birthdate = federalData[2].formattedAsBirthdate().prefixTrimmed(50) - licenceId = federalData[3].prefixTrimmed(50) - clubName = federalData[4].prefixTrimmed(200) - let stringRank = federalData[5] - if stringRank.isEmpty { - rank = nil - } else { - rank = Int(stringRank) - } - let _email = federalData[6] - if _email.isEmpty == false { - self.email = _email.prefixTrimmed(50) - } - let _phoneNumber = federalData[7] - if _phoneNumber.isEmpty == false { - self.phoneNumber = _phoneNumber.prefixTrimmed(50) - } - - source = .beachPadel - if sexUnknown { - if sex == 1 && FileImportManager.shared.foundInWomenData(license: federalData[3]) { - self.sex = .female - } else if FileImportManager.shared.foundInMenData(license: federalData[3]) { - self.sex = .male - } else { - self.sex = nil - } - } else { - self.sex = PlayerSexType(rawValue: sex) - } - } - - required init(from decoder: any Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - - var tournamentStore: TournamentStore? { - guard let storeId else { - fatalError("missing store id for \(String(describing: type(of: self)))") - } - return TournamentLibrary.shared.store(tournamentId: storeId) -// if let store = self.store as? TournamentStore { -// return store -// } - } - - var computedAge: Int? { - if let birthdate { - let components = birthdate.components(separatedBy: "/") - if let age = components.last, let ageInt = Int(age) { - let year = Calendar.current.getSportAge() - - if age.count == 2 { //si l'année est sur 2 chiffres dans le fichier - if ageInt < 23 { - return year - 2000 - ageInt - } else { - return year - 2000 + 100 - ageInt - } - } else { //si l'année est représenté sur 4 chiffres - return year - ageInt - } - } - } - return nil - } - - func pasteData(_ exportFormat: ExportFormat = .rawText) -> String { - switch exportFormat { - case .rawText: - return [firstName.capitalized, lastName.capitalized, licenceId?.computedLicense].compactMap({ $0 }).joined(separator: exportFormat.separator()) - case .csv: - return [lastName.uppercased() + " " + firstName.capitalized].joined(separator: exportFormat.separator()) - } - } - - func isPlaying() -> Bool { - team()?.isPlaying() == true - } - - func contains(_ searchField: String) -> Bool { - let nameComponents = searchField.canonicalVersion.split(separator: " ") - - if nameComponents.count > 1 { - let pairs = nameComponents.pairs() - return pairs.contains(where: { - (firstName.canonicalVersion.localizedCaseInsensitiveContains(String($0)) && - lastName.canonicalVersion.localizedCaseInsensitiveContains(String($1))) || - (firstName.canonicalVersion.localizedCaseInsensitiveContains(String($1)) && - lastName.canonicalVersion.localizedCaseInsensitiveContains(String($0))) - }) - } else { - return nameComponents.contains { component in - firstName.canonicalVersion.localizedCaseInsensitiveContains(component) || - lastName.canonicalVersion.localizedCaseInsensitiveContains(component) - } - } - } - - func isSameAs(_ player: PlayerRegistration) -> Bool { - firstName.trimmedMultiline.canonicalVersion.localizedCaseInsensitiveCompare(player.firstName.trimmedMultiline.canonicalVersion) == .orderedSame && - lastName.trimmedMultiline.canonicalVersion.localizedCaseInsensitiveCompare(player.lastName.trimmedMultiline.canonicalVersion) == .orderedSame - } - - func tournament() -> Tournament? { - guard let tournament = team()?.tournament else { return nil } - return Store.main.findById(tournament) - } - - func team() -> TeamRegistration? { - guard let teamRegistration else { return nil } - return self.tournamentStore?.teamRegistrations.findById(teamRegistration) - } - - func isHere() -> Bool { - hasArrived - } - - func hasPaid() -> Bool { - paymentType != nil - } - - func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch displayStyle { - case .wide, .title: - return lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized - case .short: - let names = lastName.components(separatedBy: .whitespaces) - if lastName.components(separatedBy: .whitespaces).count > 1 { - if let firstLongWord = names.first(where: { $0.count > 3 }) { - return firstLongWord.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "." - } - } - return lastName.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "." - } - } - - func isImported() -> Bool { - source == .beachPadel - } - - func unrankedOrUnknown() -> Bool { - source == nil - } - - func isValidLicenseNumber(year: Int) -> Bool { - guard let licenceId else { return false } - guard licenceId.isLicenseNumber else { return false } - guard licenceId.suffix(6) == "(\(year))" else { return false } - return true - } - - @objc - var canonicalName: String { - playerLabel().folding(options: .diacriticInsensitive, locale: .current).lowercased() - } - - func rankLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if let rank, rank > 0 { - if rank != computedRank { - return computedRank.formatted() + " (" + rank.formatted() + ")" - } else { - return rank.formatted() - } - } else { - return "non classé" + (isMalePlayer() ? "" : "e") - } - } - - func updateRank(from sources: [CSVParser], lastRank: Int?) async throws { - #if DEBUG_TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - - if let dataFound = try await history(from: sources) { - await MainActor.run { - rank = dataFound.rankValue?.toInt() - points = dataFound.points - tournamentPlayed = dataFound.tournamentCountValue?.toInt() - } - } else if let dataFound = try await historyFromName(from: sources) { - await MainActor.run { - rank = dataFound.rankValue?.toInt() - points = dataFound.points - tournamentPlayed = dataFound.tournamentCountValue?.toInt() - } - } else { - await MainActor.run { - rank = lastRank - } - } - } - - func history(from sources: [CSVParser]) async throws -> Line? { - #if DEBUG_TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func history()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - - guard let license = licenceId?.strippedLicense else { - return nil // Do NOT call historyFromName here, let updateRank handle it - } - - let filteredSources = sources.filter { $0.maleData == isMalePlayer() } - - return await withTaskGroup(of: Line?.self) { group in - for source in filteredSources { - group.addTask { - guard !Task.isCancelled else { return nil } - return try? await source.first { $0.rawValue.contains(";\(license);") } - } - } - - for await result in group { - if let result { - group.cancelAll() // Stop other tasks as soon as we find a match - return result - } - } - return nil - } - } - - func historyFromName(from sources: [CSVParser]) async throws -> Line? { - #if DEBUG - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func historyFromName()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - - let filteredSources = sources.filter { $0.maleData == isMalePlayer() } - let normalizedLastName = lastName.canonicalVersionWithPunctuation - let normalizedFirstName = firstName.canonicalVersionWithPunctuation - - return await withTaskGroup(of: Line?.self) { group in - for source in filteredSources { - group.addTask { - guard !Task.isCancelled else { print("Cancelled"); return nil } - return try? await source.first { - let lineValue = $0.rawValue.canonicalVersionWithPunctuation - return lineValue.contains(";\(normalizedLastName);\(normalizedFirstName);") - } - } - } - - for await result in group { - if let result { - group.cancelAll() // Stop other tasks as soon as we find a match - return result - } - } - return nil - } - } - - func setComputedRank(in tournament: Tournament) { - let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 90_000 - switch tournament.tournamentCategory { - case .men: - computedRank = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0) - default: - computedRank = currentRank - } - } - - func isMalePlayer() -> Bool { - sex == .male - } - - func validateLicenceId(_ year: Int) { - if let currentLicenceId = licenceId { - if currentLicenceId.trimmed.hasSuffix("(\(year-1))") { - self.licenceId = currentLicenceId.replacingOccurrences(of: "\(year-1)", with: "\(year)") - } else if let computedLicense = currentLicenceId.strippedLicense?.computedLicense { - self.licenceId = computedLicense + " (\(year))" - } - } - } - - func hasHomonym() -> Bool { - let federalContext = PersistenceController.shared.localContainer.viewContext - let fetchRequest = ImportedPlayer.fetchRequest() - let predicate = NSPredicate(format: "firstName == %@ && lastName == %@", firstName, lastName) - fetchRequest.predicate = predicate - - do { - let count = try federalContext.count(for: fetchRequest) - return count > 1 - } catch { - - } - return false - } - - func hasPaidOnline() -> Bool { - registrationStatus == .confirmed && paymentId != nil && paymentType == .creditCard - } - - func hasConfirmed() -> Bool { - registrationStatus == .confirmed - } - - func confirmRegistration() { - registrationStatus = .confirmed - } - - enum PlayerDataSource: Int, Codable { - case frenchFederation = 0 - case beachPadel = 1 - } - - enum RegistrationStatus: Int, Codable, CaseIterable, Identifiable { - case waiting = 0 - case pending = 1 - case confirmed = 2 - case canceled = 3 - - var id: Int { self.rawValue } - - func localizedRegistrationStatus() -> String { - switch self { - case .waiting: - return "En attente" - case .pending: - return "En cours" - case .confirmed: - return "Confirmé" - case .canceled: - return "Annulé" - } - } - } - - static func addon(for playerRank: Int, manMax: Int, womanMax: Int) -> Int { - switch playerRank { - case 0: return 0 - case womanMax: return manMax - womanMax - case manMax: return 0 - default: - return TournamentCategory.femaleInMaleAssimilationAddition(playerRank) - } - } - - func insertOnServer() { - self.tournamentStore?.playerRegistrations.writeChangeAndInsertOnServer(instance: self) - } - -} - -extension PlayerRegistration: PlayerHolder { - func getAssimilatedAsMaleRank() -> Int? { - nil - } - - func getFirstName() -> String { - firstName - } - - func getLastName() -> String { - lastName - } - - func getPoints() -> Double? { - self.points - } - - func getRank() -> Int? { - rank - } - - func isUnranked() -> Bool { - rank == nil - } - - func formattedRank() -> String { - self.rankLabel() - } - - func formattedLicense() -> String { - if let licenceId { return licenceId.computedLicense } - return "aucune licence" - } - - var male: Bool { - isMalePlayer() - } - - func getBirthYear() -> Int? { - nil - } - - func getProgression() -> Int { - 0 - } - - func getComputedRank() -> Int? { - 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 -} diff --git a/PadelClub/Data/README.md b/PadelClub/Data/README.md deleted file mode 100644 index d72ad3a..0000000 --- a/PadelClub/Data/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# 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 . -- il faut avoir inflect: pip install inflect - -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 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 - -Dans Swift: -- 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 un champ dans **CustomUser**: - - Ajouter le champ à la méthode fields_for_update - - Ajouter le champ dans UserSerializer > create > create_user dans serializers.py - - L'ajouter aussi dans admin.py si nécéssaire -- Faire le *makemigrations* + *migrate* - -Note: Les nouvelles classes de model doivent étendre BaseModel ou SideStoreModel - -Enfin, revenir dans Xcode, ouvrir ServerDataTests et lancer le test mis à jour diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift deleted file mode 100644 index f64f991..0000000 --- a/PadelClub/Data/Round.swift +++ /dev/null @@ -1,1049 +0,0 @@ -// -// Round.swift -// Padel Tournament -// -// Created by razmig on 10/03/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -@Observable -final class Round: BaseRound, SideStorable { - - private var _cachedSeedInterval: SeedInterval? - private var _cachedLoserRounds: [Round]? - private var _cachedLoserRoundsAndChildren: [Round]? - - internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { - - 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) - } - - required public init() { - super.init() - } - - // MARK: - Computed dependencies - - var tournamentStore: TournamentStore? { - return TournamentLibrary.shared.store(tournamentId: self.tournament) - } - - func tournamentObject() -> Tournament? { - return Store.main.findById(tournament) - } - - func _unsortedMatches(includeDisabled: Bool) -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - if includeDisabled { - return tournamentStore.matches.filter { $0.round == self.id } - } else { - return tournamentStore.matches.filter { $0.round == self.id && $0.disabled == false } - } - } - - func _matches() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter { $0.round == self.id }.sorted(by: \.index) - } - - func getDisabledMatches() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter { $0.round == self.id && $0.disabled == true } - } - - // MARK: - - - var matchFormat: MatchFormat { - get { - format ?? .defaultFormatForMatchType(.bracket) - } - set { - format = newValue - } - } - - func hasStarted() -> Bool { - return playedMatches().anySatisfy({ $0.hasStarted() }) - } - - func hasEnded() -> Bool { - if isUpperBracket() { - return playedMatches().anySatisfy({ $0.hasEnded() == false }) == false - } else { - return enabledMatches().anySatisfy({ $0.hasEnded() == false }) == false - } - } - - func upperMatches(upperRound: Round, match: Match) -> [Match] { - let matchIndex = match.index - let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) - return [upperRound.getMatch(atMatchIndexInRound: indexInRound * 2), upperRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1)].compactMap({ $0 }) - } - - func previousMatches(previousRound: Round, match: Match) -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter { - $0.round == previousRound.id && ($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex()) - } - } - - func upperMatches(ofMatch match: Match) -> [Match] { - if parent != nil, previousRound() == nil, let parentRound { - let matchIndex = match.index - let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) - return [parentRound.getMatch(atMatchIndexInRound: indexInRound * 2), parentRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1)].compactMap({ $0 }) - } - return [] - } - - func previousMatches(ofMatch match: Match) -> [Match] { - guard let previousRound = previousRound() else { return [] } - guard let tournamentStore = self.tournamentStore else { return [] } - - return tournamentStore.matches.filter { - $0.round == previousRound.id && ($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex()) - } - -// return Store.main.filter { -// ($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex()) && $0.round == previousRound.id -// } - } - - func precedentMatches(ofMatch match: Match) -> [Match] { - let upper = upperMatches(ofMatch: match) - if upper.isEmpty == false { - return upper - } - let previous : [Match] = previousMatches(ofMatch: match) - if previous.isEmpty == false && previous.allSatisfy({ $0.disabled }), let previousRound = previousRound() { - return previous.flatMap({ previousRound.precedentMatches(ofMatch: $0) }) - } else { - return previous - } - } - - func team(_ team: TeamPosition, inMatch match: Match, previousRound: Round?) -> TeamRegistration? { - return roundProjectedTeam(team, inMatch: match, previousRound: previousRound) - } - - func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? { - return self.tournamentStore?.teamRegistrations.first(where: { - $0.bracketPosition != nil - && ($0.bracketPosition! / 2) == matchIndex - && ($0.bracketPosition! % 2) == team.rawValue - }) - } - - func seeds(inMatchIndex matchIndex: Int) -> [TeamRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.teamRegistrations.filter { - - $0.tournament == tournament - && $0.bracketPosition != nil - && ($0.bracketPosition! / 2) == matchIndex - } - -// return Store.main.filter(isIncluded: { -// $0.tournament == tournament -// && $0.bracketPosition != nil -// && ($0.bracketPosition! / 2) == matchIndex -// }) - } - - func seeds() -> [TeamRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - let initialMatchIndex = RoundRule.matchIndex(fromRoundIndex: index) - let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index) - return tournamentStore.teamRegistrations.filter { - $0.bracketPosition != nil - && ($0.bracketPosition! / 2) >= initialMatchIndex - && ($0.bracketPosition! / 2) < initialMatchIndex + numberOfMatches - } - } - - func teamsOrSeeds() -> [TeamRegistration] { - let seeds = seeds() - if seeds.isEmpty { - return playedMatches().flatMap({ $0.teams() }) - } else { - return seeds - } - } - - - func losers() -> [TeamRegistration] { - let teamIds: [String] = self._unsortedMatches(includeDisabled: false).compactMap { $0.losingTeamId } - return teamIds.compactMap { self.tournamentStore?.teamRegistrations.findById($0) } - } - - func winners() -> [TeamRegistration] { - let teamIds: [String] = self._unsortedMatches(includeDisabled: false).compactMap { $0.winningTeamId } - return teamIds.compactMap { self.tournamentStore?.teamRegistrations.findById($0) } - } - - func teams() -> [TeamRegistration] { - return playedMatches().flatMap({ $0.teams() }) - } - - func roundProjectedTeam(_ team: TeamPosition, inMatch match: Match, previousRound: Round?) -> TeamRegistration? { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func roundProjectedTeam", team.rawValue, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - if isLoserBracket() == false, let seed = seed(team, inMatchIndex: match.index) { - return seed - } - - switch team { - case .one: - if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 }) { - return luckyLoser.team - } else if let previousMatch = topPreviousRoundMatch(ofMatch: match, previousRound: previousRound) { - if let teamId = previousMatch.winningTeamId { - return self.tournamentStore?.teamRegistrations.findById(teamId) - } else if previousMatch.disabled { - return previousMatch.teams().first - } - } else if let parent = upperBracketTopMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId { - return self.store?.findById(parent) - } - case .two: - if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 + 1 }) { - return luckyLoser.team - } else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match, previousRound: previousRound) { - if let teamId = previousMatch.winningTeamId { - return self.tournamentStore?.teamRegistrations.findById(teamId) - } else if previousMatch.disabled { - return previousMatch.teams().first - } - } else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId { - return self.store?.findById(parent) - } - } - - return nil - } - - func upperBracketTopMatch(ofMatchIndex matchIndex: Int, previousRound: Round?) -> Match? { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func upperBracketTopMatch", matchIndex, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - let parentRound = parentRound - if let parentRound, parentRound.parent == nil, groupStageLoserBracket == false, parentRound.loserBracketMode != .automatic { - return nil - } - - let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) - if isLoserBracket(), previousRound == nil, let upperBracketTopMatch = parentRound?.getMatch(atMatchIndexInRound: indexInRound * 2) { - return upperBracketTopMatch - } - return nil - } - - func upperBracketBottomMatch(ofMatchIndex matchIndex: Int, previousRound: Round?) -> Match? { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func upperBracketBottomMatch", matchIndex, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - - let parentRound = parentRound - if let parentRound, parentRound.parent == nil, groupStageLoserBracket == false, parentRound.loserBracketMode != .automatic { - return nil - } - - let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) - if isLoserBracket(), previousRound == nil, let upperBracketBottomMatch = parentRound?.getMatch(atMatchIndexInRound: indexInRound * 2 + 1) { - return upperBracketBottomMatch - } - return nil - } - - - func topPreviousRoundMatch(ofMatch match: Match, previousRound: Round?) -> Match? { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func topPreviousRoundMatch", match.id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - - guard let previousRound else { return nil } - let topPreviousRoundMatchIndex = match.topPreviousRoundMatchIndex() - return self.tournamentStore?.matches.first(where: { - $0.round == previousRound.id && $0.index == topPreviousRoundMatchIndex - }) - } - - func bottomPreviousRoundMatch(ofMatch match: Match, previousRound: Round?) -> Match? { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func bottomPreviousRoundMatch", match.id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - - guard let previousRound else { return nil } - let bottomPreviousRoundMatchIndex = match.bottomPreviousRoundMatchIndex() - return self.tournamentStore?.matches.first(where: { - $0.round == previousRound.id && $0.index == bottomPreviousRoundMatchIndex - }) - } - - func getMatch(atMatchIndexInRound matchIndexInRound: Int) -> Match? { - self.tournamentStore?.matches.first(where: { - let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index) - return $0.round == id && index == matchIndexInRound - }) - } - - func enabledMatches() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter { $0.disabled == false && $0.round == self.id }.sorted(by: \.index) - } - -// func displayableMatches() -> [Match] { -//#if _DEBUG_TIME //DEBUGING TIME -// let start = Date() -// defer { -// let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) -// print("func displayableMatches of round: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -// } -//#endif -// -// if index == 0 && isUpperBracket() { -// var matches : [Match?] = [playedMatches().first] -// matches.append(loserRounds().first?.playedMatches().first) -// return matches.compactMap({ $0 }) -// } else { -// return playedMatches() -// } -// } - - func playedMatches() -> [Match] { - if isUpperBracket() { - return enabledMatches() - } else { - return _matches() - } - } - - func previousRound() -> Round? { -#if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - 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 }) - } - - func nextRound() -> Round? { - return self.tournamentStore?.rounds.first(where: { $0.parent == parent && $0.index == index - 1 }) - } - - func loserRounds(forRoundIndex roundIndex: Int) -> [Round] { - return loserRoundsAndChildren().filter({ $0.index == roundIndex }).sorted(by: \.theoryCumulativeMatchCount) - } - - func loserRounds(forRoundIndex roundIndex: Int, loserRoundsAndChildren: [Round]) -> [Round] { - return loserRoundsAndChildren.filter({ $0.index == roundIndex }).sorted(by: \.theoryCumulativeMatchCount) - } - - func isEnabled() -> Bool { - return _unsortedMatches(includeDisabled: false).isEmpty == false - } - - func isDisabled() -> Bool { - return _unsortedMatches(includeDisabled: true).allSatisfy({ $0.disabled }) - } - - func isRankDisabled() -> Bool { - return _unsortedMatches(includeDisabled: true).allSatisfy({ $0.disabled && $0.teamScores.isEmpty }) - } - - func resetFromRoundAllMatchesStartDate() { - _unsortedMatches(includeDisabled: false).forEach({ - $0.startDate = nil - }) - loserRoundsAndChildren().forEach { round in - round.resetFromRoundAllMatchesStartDate() - } - nextRound()?.resetFromRoundAllMatchesStartDate() - } - - func resetFromRoundAllMatchesStartDate(from match: Match) { - let matches = _unsortedMatches(includeDisabled: false) - if let index = matches.firstIndex(where: { $0.id == match.id }) { - matches[index...].forEach { match in - match.startDate = nil - } - } - loserRoundsAndChildren().forEach { round in - round.resetFromRoundAllMatchesStartDate() - } - nextRound()?.resetFromRoundAllMatchesStartDate() - } - - func getActiveLoserRound() -> Round? { - // Get all loser rounds once - let allLoserRounds = loserRounds() - var lowestIndexRound: Round? = nil - let currentRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: index) - let roundCount = RoundRule.numberOfRounds(forTeams: currentRoundMatchCount) - - for currentIndex in 0..100 rounds - // Find non-disabled round with current index - let roundAtIndex = allLoserRounds.first(where: { $0.index == currentIndex && $0.isEnabled() }) - - // No round at this index, we've checked all available rounds - if roundAtIndex == nil { - break - } - - // Save the first non-disabled round we find (should be index 0) - if lowestIndexRound == nil { - lowestIndexRound = roundAtIndex - } - - // If this round is active, return it immediately - if roundAtIndex!.hasStarted() && !roundAtIndex!.hasEnded() { - return roundAtIndex - } - } - - // If no active round found, return the one with lowest index - return lowestIndexRound - } - - func enableRound() { - _toggleRound(disable: false) - } - - func disableRound() { - _toggleRound(disable: true) - } - - private func _toggleRound(disable: Bool) { - let _matches = _unsortedMatches(includeDisabled: true) - _matches.forEach { match in - match.disabled = disable - match.resetMatch() - //we need to keep teamscores to handle disable ranking match round stuff -// do { -// try DataStore.shared.teamScores.delete(contentOfs: match.teamScores) -// } catch { -// Logger.error(error) -// } - } - self.tournamentStore?.matches.addOrUpdate(contentOfs: _matches) - } - - var cumulativeMatchCount: Int { - var totalMatches = _unsortedMatches(includeDisabled: false).count - if let parentRound { - totalMatches += parentRound.cumulativeMatchCount - } - return totalMatches - } - - func initialRound() -> Round? { - if let parentRound { - return parentRound.initialRound() - } else { - return self - } - } - - func estimatedEndDate(_ additionalEstimationDuration: Int) -> Date? { - return enabledMatches().last?.estimatedEndDate(additionalEstimationDuration) - } - - func getLoserRoundStartDate() -> Date? { - return loserRoundsAndChildren().first(where: { $0.isDisabled() == false })?.enabledMatches().first?.startDate - } - - func estimatedLoserRoundEndDate(_ additionalEstimationDuration: Int) -> Date? { - let lastMatch = loserRoundsAndChildren().last(where: { $0.isDisabled() == false })?.enabledMatches().last - return lastMatch?.estimatedEndDate(additionalEstimationDuration) - } - - func disabledMatches() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter { $0.round == self.id && $0.disabled == true } - } - - func allLoserRoundMatches() -> [Match] { - loserRoundsAndChildren().flatMap({ $0._unsortedMatches(includeDisabled: false) }) - } - - var theoryCumulativeMatchCount: Int { - var totalMatches = RoundRule.numberOfMatches(forRoundIndex: index) - if let parentRound { - totalMatches += parentRound.theoryCumulativeMatchCount - } - return totalMatches - } - - - func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String { - if let _cachedSeedInterval { return _cachedSeedInterval.localizedLabel(displayStyle) } - -#if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func correspondingLoserRoundTitle()", duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index) - 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: { -// $0.tournament == tournament -// && $0.bracketPosition != nil -// && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex -// }) - - var seedsCount = seedsAfterThisRound.count - if seedsAfterThisRound.isEmpty { - let nextRoundsDisableMatches = nextRoundsDisableMatches() - seedsCount = disabledMatches().count - nextRoundsDisableMatches - } - let playedMatches = playedMatches() - let seedInterval = SeedInterval(first: playedMatches.count + seedsCount + 1, last: playedMatches.count * 2 + seedsCount) - - _cachedSeedInterval = seedInterval - return seedInterval.localizedLabel(displayStyle) - } - - func hasNextRound() -> Bool { - return nextRound()?.isRankDisabled() == false - } - - func pasteData() -> String { - var data: [String] = [] - data.append(self.roundTitle()) - - playedMatches().forEach { match in - data.append(match.matchTitle()) - data.append(match.team(.one)?.teamLabelRanked(displayRank: true, displayTeamName: true) ?? "-----") - data.append(match.team(.two)?.teamLabelRanked(displayRank: true, displayTeamName: true) ?? "-----") - } - - return data.joined(separator: "\n") - } - - func seedInterval(initialMode: Bool = false) -> SeedInterval? { - if initialMode == false, let _cachedSeedInterval { return _cachedSeedInterval } - -#if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func seedInterval(initialMode)", id, index, initialMode, _cachedSeedInterval, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - - if isUpperBracket() { - if index == 0 { return SeedInterval(first: 1, last: 2) } - let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index) - - if initialMode { - let playedMatches = RoundRule.numberOfMatches(forRoundIndex: index) - let seedInterval = SeedInterval(first: playedMatches + 1, last: playedMatches * 2) - //print(seedInterval.localizedLabel()) - return seedInterval - } else { - var seedsAfterThisRound : [TeamRegistration] = [] - if let tournamentStore = self.tournamentStore { - seedsAfterThisRound = tournamentStore.teamRegistrations.filter { - $0.bracketPosition != nil - && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex - } - } - - var seedsCount = seedsAfterThisRound.count - if seedsAfterThisRound.isEmpty { - let nextRoundsDisableMatches = nextRoundsDisableMatches() - seedsCount = disabledMatches().count - nextRoundsDisableMatches - } - - let playedMatches = playedMatches() - //print("playedMatches \(playedMatches)", initialMode, parent, parentRound?.roundTitle(), seedsAfterThisRound.count) - let seedInterval = SeedInterval(first: playedMatches.count + seedsCount + 1, last: playedMatches.count * 2 + seedsCount) - //print(seedInterval.localizedLabel()) - _cachedSeedInterval = seedInterval - return seedInterval - - } - } - - if let previousRound = previousRound() { - if (previousRound.enabledMatches().isEmpty == false || initialMode) { - _cachedSeedInterval = previousRound.seedInterval(initialMode: initialMode)?.chunks()?.first - return _cachedSeedInterval - } else { - _cachedSeedInterval = previousRound.seedInterval(initialMode: initialMode) - return _cachedSeedInterval - } - } else if let parentRound { - if parentRound.isUpperBracket() { - _cachedSeedInterval = parentRound.seedInterval(initialMode: initialMode) - return _cachedSeedInterval - } - _cachedSeedInterval = parentRound.seedInterval(initialMode: initialMode)?.chunks()?.last - return _cachedSeedInterval - } - - return nil - } - - func roundTitle(_ displayStyle: DisplayStyle = .wide, initialMode: Bool = false) -> String { - if groupStageLoserBracket { - return "Classement Poules" - } - - if parent != nil { - if let seedInterval = seedInterval(initialMode: initialMode) { - return seedInterval.localizedLabel(displayStyle) - } -// print("Round pas trouvé", id, parent, index) - return "Match de classement" - } - return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle) - } - - func updateTournamentState() { - let tournamentObject = tournamentObject() - if let tournamentObject, index == 0, isUpperBracket(), hasEnded() { - tournamentObject.endDate = Date() - DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject) - } - - tournamentObject?.updateTournamentState() - } - - func roundStatus(playedMatches: [Match]) -> String { - let hasEnded = playedMatches.anySatisfy({ $0.hasEnded() == false }) == false - let hasStarted = playedMatches.anySatisfy({ $0.hasStarted() }) - if hasStarted && hasEnded == false { - return "en cours" - } else if hasEnded { - return "terminée" - } else if let tournamentObject = tournamentObject(), tournamentObject.groupStagesAreOver() == false { - return "en attente" - } else { - return "à démarrer" - } - } - - func loserRounds() -> [Round] { - if let _cachedLoserRounds { - return _cachedLoserRounds - } - guard let tournamentStore = self.tournamentStore else { return [] } -#if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func loserRounds: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - - // Filter first to reduce sorting work - let filteredRounds = tournamentStore.rounds.filter { $0.parent == id } - - // Return empty array early if no rounds match - if filteredRounds.isEmpty { - return [] - } - - // Sort directly in descending order to avoid the separate reversed() call - _cachedLoserRounds = filteredRounds.sorted { $0.index > $1.index } - return _cachedLoserRounds! - } - - func loserRoundsAndChildren() -> [Round] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func loserRoundsAndChildren: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - - // Return cached result if available - if let cached = _cachedLoserRoundsAndChildren { - return cached - } - - // Calculate result if cache is invalid or unavailable - let direct = loserRounds() - - // Return quickly if there are no direct loser rounds - if direct.isEmpty { - // Update cache with empty result - _cachedLoserRoundsAndChildren = [] - return [] - } - - // Pre-allocate capacity to avoid reallocations (estimate based on typical tournament structure) - var allRounds = direct - let estimatedChildrenCount = direct.count * 2 // Rough estimate - allRounds.reserveCapacity(estimatedChildrenCount) - - // Collect all children rounds in one pass - for round in direct { - allRounds.append(contentsOf: round.loserRoundsAndChildren()) - } - - // Store result in cache - _cachedLoserRoundsAndChildren = allRounds - - return allRounds - } - - func isUpperBracket() -> Bool { - return parent == nil && groupStageLoserBracket == false - } - - func isLoserBracket() -> Bool { - return parent != nil || groupStageLoserBracket - } - - func deleteLoserBracket() { -#if DEBUG //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func deleteLoserBracket: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - self.tournamentStore?.rounds.delete(contentOfs: self.loserRounds()) - self.invalidateCache() - } - - func buildLoserBracket() { -#if DEBUG //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func buildLoserBracket: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - guard loserRounds().isEmpty else { return } - self.invalidateCache() - let currentRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: index) - guard currentRoundMatchCount > 1 else { return } - guard let tournamentStore else { return } - let roundCount = RoundRule.numberOfRounds(forTeams: currentRoundMatchCount) - let loserBracketMatchFormat = tournamentObject()?.loserBracketMatchFormat -// if let parentRound { -// loserBracketMatchFormat = tournamentObject()?.loserBracketSmartMatchFormat(parentRound.index) -// } - - var titles = [String: String]() - - let rounds = (0.. 0 { - match.disabled = true - if upperRound.isUpperBracket(), prmc == 1 { - match.byeState = true - } - } else { - match.disabled = false - } - } - } - tournamentStore?.matches.addOrUpdate(contentOfs: m) - - loserRounds().forEach { loserRound in - loserRound.disableUnplayedLoserBracketMatches() - } - } - - var parentRound: Round? { - guard let parent = parent else { return nil } - return self.tournamentStore?.rounds.findById(parent) - } - - func nextRoundsDisableMatches() -> Int { - if parent == nil, index > 0 { - return tournamentObject()?.rounds().suffix(index).flatMap { $0.disabledMatches() }.count ?? 0 - } else { - return 0 - } - } - - func updateMatchFormat(_ updatedMatchFormat: MatchFormat, checkIfPossible: Bool, andLoserBracket: Bool) { - if updatedMatchFormat.weight < self.matchFormat.weight { - updateMatchFormatAndAllMatches(updatedMatchFormat) - if andLoserBracket { - loserRoundsAndChildren().forEach { round in - round.updateMatchFormat(updatedMatchFormat, checkIfPossible: checkIfPossible, andLoserBracket: true) - } - } - } - } - - func updateMatchFormatAndAllMatches(_ updatedMatchFormat: MatchFormat) { - self.matchFormat = updatedMatchFormat - self.updateMatchFormatOfAllMatches(updatedMatchFormat) - } - - func updateMatchFormatOfAllMatches(_ updatedMatchFormat: MatchFormat) { - let playedMatches = _unsortedMatches(includeDisabled: true) - playedMatches.forEach { match in - match.matchFormat = updatedMatchFormat - } - self.tournamentStore?.matches.addOrUpdate(contentOfs: playedMatches) - } - - func loserBracketTurns() -> [LoserRound] { -#if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func loserBracketTurns()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - var rounds = [LoserRound]() - let currentRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: index) - let roundCount = RoundRule.numberOfRounds(forTeams: currentRoundMatchCount) - - for index in 0.. String { - if let parentRound { - return "Tour #\(parentRound.loserRounds().count - index)" - } else { - return roundTitle(.short) - } - } - - func badgeValue() -> Int? { -#if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func badgeValue round of: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - - - if let parentRound { - return parentRound.loserRounds(forRoundIndex: index).flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count - } else { - return playedMatches().filter({ $0.isRunning() }).count - } - } - - func badgeValueColor() -> Color? { - return nil - } - - func badgeImage() -> Badge? { -#if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func badgeImage of round: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } -#endif - return hasEnded() ? .checkmark : nil - } -} - - -enum LoserBracketMode: Int, CaseIterable, Identifiable, Codable { - var id: Int { self.rawValue } - - case automatic - case manual - - func localizedLoserBracketMode() -> String { - switch self { - case .automatic: - "Automatique" - case .manual: - "Manuelle" - } - } - - func localizedLoserBracketModeDescription() -> String { - switch self { - case .automatic: - "Les perdants du tableau principal sont placés à leur place prévue." - case .manual: - "Aucun placement automatique n'est fait. Vous devez choisir les perdants qui se jouent." - } - } -} diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift deleted file mode 100644 index 75f28d3..0000000 --- a/PadelClub/Data/TeamRegistration.swift +++ /dev/null @@ -1,779 +0,0 @@ -// -// TeamRegistration.swift -// Padel Tournament -// -// Created by razmig on 10/03/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -@Observable -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 - ) { - - super.init() - - // self.storeId = tournament - self.tournament = tournament - self.groupStage = groupStage - self.registrationDate = registrationDate ?? Date() - 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 - } - - func hasRegisteredOnline() -> Bool { - players().anySatisfy({ $0.registeredOnline }) - } - - func hasPaidOnline() -> Bool { - players().anySatisfy({ $0.hasPaidOnline() }) - } - - func hasConfirmed() -> Bool { - players().allSatisfy({ $0.hasConfirmed() }) - } - - func confirmRegistration() { - let players = players() - players.forEach({ $0.confirmRegistration() }) - tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) - } - - func unrankedOrUnknown() -> Bool { - players().anySatisfy({ $0.source == nil }) - } - - func isOutOfTournament() -> Bool { - walkOut - } - - required init(from decoder: any Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - - var tournamentStore: TournamentStore? { - return TournamentLibrary.shared.store(tournamentId: self.tournament) - } - - // MARK: - Computed dependencies - - func unsortedPlayers() -> [PlayerRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.playerRegistrations.filter { - $0.teamRegistration == self.id && $0.coach == false - } - } - - // MARK: - - - func deleteTeamScores() { - guard let tournamentStore = self.tournamentStore else { return } - let ts = tournamentStore.teamScores.filter({ $0.teamRegistration == id }) - tournamentStore.teamScores.delete(contentOfs: ts) - } - - override func deleteDependencies() { - let unsortedPlayers = unsortedPlayers() - for player in unsortedPlayers { - player.deleteDependencies() - } - self.tournamentStore?.playerRegistrations.deleteDependencies(unsortedPlayers) - - let teamScores = teamScores() - for teamScore in teamScores { - teamScore.deleteDependencies() - } - self.tournamentStore?.teamScores.deleteDependencies(teamScores) - } - - func hasArrived(isHere: Bool = false) { - let unsortedPlayers = unsortedPlayers() - unsortedPlayers.forEach({ $0.hasArrived = !isHere }) - self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers) - } - - func isHere() -> Bool { - let unsortedPlayers = unsortedPlayers() - if unsortedPlayers.isEmpty { return false } - return unsortedPlayers.allSatisfy({ $0.hasArrived }) - } - - func isSeedable() -> Bool { - bracketPosition == nil && groupStage == nil - } - - func setSeedPosition(inSpot match: Match, slot: TeamPosition?, opposingSeeding: Bool) { - var teamPosition: TeamPosition { - if let slot { - return slot - } else { - let matchIndex = match.index - let seedRound = RoundRule.roundIndex(fromMatchIndex: matchIndex) - let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: seedRound) - let isUpper = - RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) - < (numberOfMatches / 2) - var teamPosition = slot ?? (isUpper ? .one : .two) - if opposingSeeding { - teamPosition = slot ?? (isUpper ? .two : .one) - } - return teamPosition - } - } - - let seedPosition: Int = match.lockAndGetSeedPosition(atTeamPosition: teamPosition) - tournamentObject()?.resetTeamScores(in: bracketPosition) - self.bracketPosition = seedPosition - if groupStagePosition != nil && qualified == false { - qualified = true - } - if let tournament = tournamentObject() { - 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) - } catch { - Logger.error(error) - } - } - tournament.updateTeamScores(in: bracketPosition) - } - } - - func expectedSummonDate() -> Date? { - if let groupStageStartDate = groupStageObject()?.startDate { - return groupStageStartDate - } else if let roundMatchStartDate = initialMatch()?.startDate { - return roundMatchStartDate - } - return nil - } - - var initialWeight: Int { - return lockedWeight ?? weight - } - - func called() -> Bool { - return callDate != nil - } - - func confirmed() -> Bool { - return confirmationDate != nil - } - - func getPhoneNumbers() -> [String] { - return players().compactMap { $0.phoneNumber }.filter({ $0.isEmpty == false }) - } - - func getMail() -> [String] { - let mails = players().compactMap({ $0.email }) - return mails - } - - func isImported() -> Bool { - let unsortedPlayers = unsortedPlayers() - if unsortedPlayers.isEmpty { return false } - - return unsortedPlayers.allSatisfy({ $0.isImported() }) - } - - func isWildCard() -> Bool { - return wildCardBracket || wildCardGroupStage - } - - func isPlaying() -> Bool { - return currentMatch() != nil - } - - func currentMatch() -> Match? { - return teamScores().compactMap { $0.matchObject() }.first(where: { $0.isRunning() }) - } - - func teamScores() -> [TeamScore] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.teamScores.filter({ $0.teamRegistration == id }) - } - - func wins() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter({ $0.winningTeamId == id }) - } - - func loses() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter({ $0.losingTeamId == id }) - } - - func matches() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter({ $0.losingTeamId == id || $0.winningTeamId == id }) - } - - var tournamentCategory: TournamentCategory { - tournamentObject()?.tournamentCategory ?? .men - } - - @objc - var canonicalName: String { - players().map { $0.canonicalName }.joined(separator: " ") - } - - func hasMemberOfClub(_ codeClubOrClubName: String?) -> Bool { - guard let codeClubOrClubName else { return true } - return unsortedPlayers().anySatisfy({ - $0.clubName?.contains(codeClubOrClubName) == true - || $0.clubName?.contains(codeClubOrClubName) == true - }) - } - - func updateWeight(inTournamentCategory tournamentCategory: TournamentCategory) { - self.setWeight(from: self.players(), inTournamentCategory: tournamentCategory) - } - - func teamLabel( - _ displayStyle: DisplayStyle = .wide, twoLines: Bool = false, separator: String = "&" - ) -> String { - if let name { return name } - return players().map { $0.playerLabel(displayStyle) }.joined( - separator: twoLines ? "\n" : " \(separator) ") - } - - func teamLabelRanked(displayRank: Bool, displayTeamName: Bool) -> String { - [ - displayTeamName ? name : nil, displayRank ? seedIndex() : nil, - displayTeamName ? (name == nil ? teamLabel() : name) : teamLabel(), - ].compactMap({ $0 }).joined(separator: " ") - } - - func seedIndex() -> String? { - guard let tournament = tournamentObject() else { return nil } - guard let index = index(in: tournament.selectedSortedTeams()) else { return nil } - return "(\(index + 1))" - } - - func index(in teams: [TeamRegistration]) -> Int? { - return teams.firstIndex(where: { $0.id == id }) - } - - func formattedSeed(in teams: [TeamRegistration]? = nil) -> String { - let selectedSortedTeams = teams ?? tournamentObject()?.selectedSortedTeams() ?? [] - if let index = index(in: selectedSortedTeams) { - return "#\(index + 1)" - } else { - return "###" - } - } - - func contains(_ searchField: String) -> Bool { - return unsortedPlayers().anySatisfy({ $0.contains(searchField) }) - || self.name?.localizedCaseInsensitiveContains(searchField) == true - } - - func containsExactlyPlayerLicenses(_ playerLicenses: [String?]) -> Bool { - let arrayOfIds: [String] = unsortedPlayers().compactMap({ - $0.licenceId?.strippedLicense?.canonicalVersion - }) - let ids: Set = Set(arrayOfIds.sorted()) - let searchedIds = Set( - playerLicenses.compactMap({ $0?.strippedLicense?.canonicalVersion }).sorted()) - if ids.isEmpty || searchedIds.isEmpty { return false } - return ids.hashValue == searchedIds.hashValue - } - - func includes(players: [PlayerRegistration]) -> Bool { - let unsortedPlayers = unsortedPlayers() - guard players.count == unsortedPlayers.count else { return false } - return players.allSatisfy { player in - unsortedPlayers.anySatisfy { _player in - _player.isSameAs(player) - } - } - } - - func includes(player: PlayerRegistration) -> Bool { - return unsortedPlayers().anySatisfy { _player in - _player.isSameAs(player) - } - } - - func canPlay() -> Bool { - let unsortedPlayers = unsortedPlayers() - if unsortedPlayers.isEmpty { return false } - - return matches().isEmpty == false - || unsortedPlayers.allSatisfy({ $0.hasPaid() || $0.hasArrived }) - } - - func availableForSeedPick() -> Bool { - return groupStage == nil && bracketPosition == nil - } - - func inGroupStage() -> Bool { - return groupStagePosition != nil - } - - func inRound() -> Bool { - return bracketPosition != nil - } - - func positionLabel() -> String? { - if groupStagePosition != nil { return "Poule" } - if let initialRound = initialRound() { - return initialRound.roundTitle() - } else { - return nil - } - } - - func initialRoundColor() -> Color? { - if walkOut { return Color.logoRed } - if groupStagePosition != nil || wildCardGroupStage { return Color.blue } - if let initialRound = initialRound(), let colorHex = RoundRule.colors[safe: initialRound.index] { - return Color(uiColor: .init(fromHex: colorHex)) - } else if wildCardBracket { - return Color.mint - } else { - return nil - } - } - - func resetGroupeStagePosition() { - guard let tournamentStore = self.tournamentStore else { return } - if let groupStage { - 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 - groupStagePosition = nil - } - - func resetBracketPosition() { - 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 - } - - func resetPositions() { - resetGroupeStagePosition() - resetBracketPosition() - } - - func pasteData(_ exportFormat: ExportFormat = .rawText, _ index: Int = 0) -> String { - switch exportFormat { - case .rawText: - return [playersPasteData(exportFormat), formattedInscriptionDate(exportFormat), name] - .compactMap({ $0 }).joined(separator: exportFormat.newLineSeparator()) - case .csv: - return [ - index.formatted(), playersPasteData(exportFormat), - isWildCard() ? "WC" : weight.formatted(), - ].joined(separator: exportFormat.separator()) - } - } - - var computedRegistrationDate: Date { - return registrationDate ?? .distantFuture - } - - func formattedInscriptionDate(_ exportFormat: ExportFormat = .rawText) -> String? { - guard let registrationDate else { return nil } - - let formattedDate = registrationDate.formatted( - .dateTime.weekday().day().month().hour().minute()) - let onlineSuffix = hasRegisteredOnline() ? " en ligne" : "" - - switch exportFormat { - case .rawText: - return "Inscrit\(onlineSuffix) le \(formattedDate)" - case .csv: - return formattedDate - } - } - - func formattedSummonDate(_ exportFormat: ExportFormat = .rawText) -> String? { - - switch exportFormat { - case .rawText: - if let callDate { - return "Convoqué le " - + callDate.formatted(.dateTime.weekday().day().month().hour().minute()) - } else { - return nil - } - case .csv: - if let callDate { - return callDate.formatted(.dateTime.weekday().day().month().hour().minute()) - } else { - return nil - } - } - } - - func playersPasteData(_ exportFormat: ExportFormat = .rawText) -> String { - switch exportFormat { - case .rawText: - return players().map { $0.pasteData(exportFormat) }.joined( - separator: exportFormat.newLineSeparator()) - case .csv: - return players().map { - [$0.pasteData(exportFormat), isWildCard() ? "WC" : $0.computedRank.formatted()] - .joined(separator: exportFormat.separator()) - }.joined(separator: exportFormat.separator()) - } - } - - func updatePlayers( - _ players: Set, - inTournamentCategory tournamentCategory: TournamentCategory - ) { - let previousPlayers = Set(unsortedPlayers()) - - players.forEach { player in - previousPlayers.forEach { oldPlayer in - if player.licenceId?.strippedLicense == oldPlayer.licenceId?.strippedLicense, - player.licenceId?.strippedLicense != nil - { - player.registeredOnline = oldPlayer.registeredOnline - player.paymentType = oldPlayer.paymentType - player.paymentId = oldPlayer.paymentId - player.registrationStatus = oldPlayer.registrationStatus - player.timeToConfirm = oldPlayer.timeToConfirm - player.coach = oldPlayer.coach - player.tournamentPlayed = oldPlayer.tournamentPlayed - player.points = oldPlayer.points - player.captain = oldPlayer.captain - player.assimilation = oldPlayer.assimilation - player.ligueName = oldPlayer.ligueName - } - } - } - - let playersToRemove = previousPlayers.subtracting(players) - self.tournamentStore?.playerRegistrations.delete(contentOfs: Array(playersToRemove)) - setWeight(from: Array(players), inTournamentCategory: tournamentCategory) - - players.forEach { player in - player.teamRegistration = id - } - - // do { - // try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - // } catch { - // Logger.error(error) - // } - } - - typealias TeamRange = (left: TeamRegistration?, right: TeamRegistration?) - - func replacementRange() -> TeamRange? { - guard let tournamentObject = tournamentObject() else { return nil } - guard let index = tournamentObject.indexOf(team: self) else { return nil } - let selectedSortedTeams = tournamentObject.selectedSortedTeams() - let left = selectedSortedTeams[safe: index - 1] - let right = selectedSortedTeams[safe: index + 1] - return (left: left, right: right) - } - - func replacementRangeExtended() -> TeamRange? { - guard let tournamentObject = tournamentObject() else { return nil } - guard let groupStagePosition else { return nil } - let selectedSortedTeams = tournamentObject.selectedSortedTeams() - var left: TeamRegistration? = nil - if groupStagePosition == 0 { - left = tournamentObject.seeds().last - } else { - let previousHat = selectedSortedTeams.filter({ - $0.groupStagePosition == groupStagePosition - 1 - }).sorted(by: \.weight) - left = previousHat.last - } - var right: TeamRegistration? = nil - if groupStagePosition == tournamentObject.teamsPerGroupStage - 1 { - right = nil - } else { - let previousHat = selectedSortedTeams.filter({ - $0.groupStagePosition == groupStagePosition + 1 - }).sorted(by: \.weight) - right = previousHat.first - } - return (left: left, right: right) - } - - typealias AreInIncreasingOrder = (PlayerRegistration, PlayerRegistration) -> Bool - - func players() -> [PlayerRegistration] { - - self.unsortedPlayers().sorted { (lhs, rhs) in - let predicates: [AreInIncreasingOrder] = [ - { $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 }, - { $0.rank ?? Int.max < $1.rank ?? Int.max }, - { $0.lastName < $1.lastName }, - { $0.firstName < $1.firstName }, - ] - - for predicate in predicates { - if !predicate(lhs, rhs) && !predicate(rhs, lhs) { - continue - } - - return predicate(lhs, rhs) - } - - return false - } - } - - func coaches() -> [PlayerRegistration] { - guard let store = self.tournamentStore else { return [] } - return store.playerRegistrations.filter { $0.coach } - } - - func setWeight( - from players: [PlayerRegistration], - inTournamentCategory tournamentCategory: TournamentCategory - ) { - let significantPlayerCount = significantPlayerCount() - let sortedPlayers = players.sorted(by: \.computedRank, order: .ascending) - weight = (sortedPlayers.prefix(significantPlayerCount).map { $0.computedRank } + missingPlayerType(inTournamentCategory: tournamentCategory).map { unrankValue(for: $0 == 1 ? true : false ) }).prefix(significantPlayerCount).reduce(0,+) - } - - func significantPlayerCount() -> Int { - return tournamentObject()?.significantPlayerCount() ?? 2 - } - - func missingPlayerType(inTournamentCategory tournamentCategory: TournamentCategory) -> [Int] { - let players = unsortedPlayers() - if players.count >= 2 { return [] } - let s = players.compactMap { $0.sex?.rawValue } - var missing = tournamentCategory.mandatoryPlayerType() - s.forEach { i in - if let index = missing.firstIndex(of: i) { - missing.remove(at: index) - } - } - return missing - } - - func unrankValue(for malePlayer: Bool) -> Int { - return tournamentObject()?.unrankValue(for: malePlayer) ?? 90_000 - } - - func groupStageObject() -> GroupStage? { - guard let groupStage else { return nil } - 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 }) - } - - 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 - }) - } - - func toggleSummonConfirmation() { - if confirmationDate == nil { confirmationDate = Date() } else { confirmationDate = nil } - } - - func didConfirmSummon() -> Bool { - confirmationDate != nil - } - - func tournamentObject() -> Tournament? { - return Store.main.findById(tournament) - } - - func groupStagePositionAtStep(_ step: Int) -> Int? { - guard let groupStagePosition else { return nil } - if step == 0 { - return groupStagePosition - } else if let groupStageObject = groupStageObject(), groupStageObject.hasEnded() { - return groupStageObject.index - } - return nil - } - - func wildcardLabel() -> String? { - if isWildCard() { - let wildcardLabel: String = ["Wildcard", (wildCardBracket ? "Tableau" : "Poule")].joined(separator: " ") - return wildcardLabel - } else { - return nil - } - } - - var _cachedRestingTime: (Bool, Date?)? - - func restingTime() -> Date? { - if let _cachedRestingTime { return _cachedRestingTime.1 } - let restingTime = matches().filter({ $0.hasEnded() }).sorted( - by: \.computedEndDateForSorting - ).last?.endDate - _cachedRestingTime = (true, restingTime) - return restingTime - } - - func resetRestingTime() { - _cachedRestingTime = nil - } - - var restingTimeForSorting: Date { - restingTime()! - } - - func teamNameLabel() -> String { - if let name, name.isEmpty == false { - return name - } else { - return "Toute l'équipe" - } - } - - func isDifferentPosition(_ drawMatchIndex: Int?) -> Bool { - if let bracketPosition, let drawMatchIndex { - return drawMatchIndex != bracketPosition - } else if bracketPosition != nil { - return true - } else if drawMatchIndex != nil { - return true - } - return false - } - - func shouldDisplayRankAndWeight() -> Bool { - unsortedPlayers().count > 0 - } - - func teamInitialPositionBracket() -> String? { - let round = initialMatch()?.roundAndMatchTitle() - if let round { - return round - } - return nil - } - - func teamInitialPositionGroupStage() -> String? { - let groupStage = self.groupStageObject() - let group = groupStage?.groupStageTitle(.title) - var groupPositionLabel: String? = nil - if let finalPosition = groupStage?.finalPosition(ofTeam: self) { - groupPositionLabel = (finalPosition + 1).ordinalFormatted() - } else if let groupStagePosition { - groupPositionLabel = "\(groupStagePosition + 1)" - } - - if let group { - if let groupPositionLabel { - return [group, "#\(groupPositionLabel)"].joined(separator: " ") - } else { - return group - } - } - return nil - } - - func qualifiedStatus(hideBracketStatus: Bool = false) -> String? { - let teamInitialPositionBracket = teamInitialPositionBracket() - let groupStageTitle = teamInitialPositionGroupStage() - - let base: String? = qualified ? "Qualifié" : nil - if let groupStageTitle, let teamInitialPositionBracket, hideBracketStatus == false { - return [base, groupStageTitle, ">", teamInitialPositionBracket].compactMap({ $0 }).joined(separator: " ") - } else if let groupStageTitle { - return [base, groupStageTitle].compactMap({ $0 }).joined(separator: " ") - } else if hideBracketStatus == false { - return teamInitialPositionBracket - } - - return nil - } - - func insertOnServer() { - self.tournamentStore?.teamRegistrations.writeChangeAndInsertOnServer(instance: self) - for playerRegistration in self.unsortedPlayers() { - playerRegistration.insertOnServer() - } - } - -} - -enum TeamDataSource: Int, Codable { - case beachPadel -} diff --git a/PadelClub/Data/TeamScore.swift b/PadelClub/Data/TeamScore.swift deleted file mode 100644 index 18930eb..0000000 --- a/PadelClub/Data/TeamScore.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// TeamScore.swift -// Padel Tournament -// -// Created by razmig on 10/03/2024. -// - -import Foundation -import LeStorage - -@Observable -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 - - init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) { - 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?) { - super.init(match: match) - if let team { - self.teamRegistration = team.id - } - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - - var tournamentStore: TournamentStore? { - guard let storeId else { - fatalError("missing store id 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 - - func matchObject() -> Match? { - return self.tournamentStore?.matches.findById(self.match) - } - - var team: TeamRegistration? { - guard let teamRegistration else { - return nil - } - return self.tournamentStore?.teamRegistrations.findById(teamRegistration) - } - - // MARK: - - - func isWalkOut() -> Bool { - 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) -// } - - func insertOnServer() { - self.tournamentStore?.teamScores.writeChangeAndInsertOnServer(instance: self) - } - -} diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift deleted file mode 100644 index 9c32551..0000000 --- a/PadelClub/Data/Tournament.swift +++ /dev/null @@ -1,2688 +0,0 @@ -// -// swift -// PadelClub -// -// Created by Laurent Morvillier on 02/02/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -@Observable -final class Tournament: BaseTournament { - - //local variable - var refreshInProgress: Bool = false - var lastTeamRefresh: Date? - var refreshRanking: Bool = false - - func shouldRefreshTeams(forced: Bool) -> Bool { - if forced { - return true - } - guard let lastTeamRefresh else { return true } - return lastTeamRefresh.timeIntervalSinceNow < -600 - } - - @ObservationIgnored - var navigationPath: [Screen] = [] - - var tournamentStore: TournamentStore? { - return TournamentLibrary.shared.store(tournamentId: self.id) - } - - override func deleteDependencies() { - guard let store = self.tournamentStore else { return } - - let drawLogs = Array(store.drawLogs) - for drawLog in drawLogs { - drawLog.deleteDependencies() - } - store.drawLogs.deleteDependencies(drawLogs) - - let teams = Array(store.teamRegistrations) - for team in teams { - team.deleteDependencies() - } - store.teamRegistrations.deleteDependencies(teams) - - let groups = Array(store.groupStages) - for group in groups { - group.deleteDependencies() - } - store.groupStages.deleteDependencies(groups) - - let rounds = Array(store.rounds) - for round in rounds { - round.deleteDependencies() - } - store.rounds.deleteDependencies(rounds) - - store.matchSchedulers.deleteDependencies(self._matchSchedulers()) - - } - - // MARK: - Computed Dependencies - - func unsortedTeams() -> [TeamRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - return Array(tournamentStore.teamRegistrations) - } - - func unsortedTeamsCount() -> Int { - return self.tournamentStore?.teamRegistrations.count ?? 0 - } - - func groupStages(atStep step: Int = 0) -> [GroupStage] { - 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] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.groupStages.sorted(by: \GroupStage.computedOrder) - } - - func allRounds() -> [Round] { - guard let tournamentStore = self.tournamentStore else { return [] } - return Array(tournamentStore.rounds) - } - - // MARK: - - - enum State { - case initial - case build - case running - case canceled - case finished - } - - func eventLabel() -> String { - if let event = eventObject(), let name = event.name { - return name - } else { - return "" - } - } - - func publishedTournamentDate() -> Date { - return min(creationDate.tomorrowAtNine, startDate) - } - - func publishedTeamsDate() -> Date { - return self.startDate - } - - func canBePublished() -> Bool { - switch state() { - case .build, .finished, .running: - return unsortedTeams().count > 3 - default: - return false - } - } - - func isTournamentPublished() -> Bool { - return (Date() >= publishedTournamentDate()) || publishTournament - } - - func areTeamsPublished() -> Bool { - return Date() >= startDate || publishTeams - } - - func areSummonsPublished() -> Bool { - return Date() >= startDate || publishSummons - } - - fileprivate func _publishedDateFromMatches(_ matches: [Match]) -> Date? { - let startDates: [Date] = matches.compactMap { $0.startDate } - let sortedDates: [Date] = startDates.sorted() - - if let first: Date = sortedDates.first?.atEightAM() { - if first.isEarlierThan(startDate) { - return startDate - } else { - return first - } - } else { - return startDate - } - } - - func publishedGroupStagesDate() -> Date? { - let matches: [Match] = self.groupStages().flatMap { $0.playedMatches() } - return self._publishedDateFromMatches(matches) - } - - func areGroupStagesPublished() -> Bool { - if publishGroupStages { return true } - if let publishedGroupStagesDate = publishedGroupStagesDate() { - return Date() >= publishedGroupStagesDate - } else { - return false - } - } - - func publishedBracketsDate() -> Date? { - let matches: [Match] = self.rounds().flatMap { $0.playedMatches() } - return self._publishedDateFromMatches(matches) - } - - func areBracketsPublished() -> Bool { - if publishBrackets { return true } - if let publishedBracketsDate = publishedBracketsDate() { - return Date() >= publishedBracketsDate - } else { - return false - } - } - - func shareURL(_ pageLink: PageLink = .matches) -> URL? { - if pageLink == .clubBroadcast { - let club = club() -// 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 { - return nil - } - } - return URLs.main.url.appending(path: "tournament/\(id)").appending(path: pageLink.path) - } - - func courtUsed(runningMatches: [Match]) -> [Int] { -#if _DEBUGING_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func courtUsed()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - return Set(runningMatches.compactMap { $0.courtIndex }).sorted() - } - - func hasStarted() -> Bool { - return startDate <= Date() - } - - func eventObject() -> Event? { - guard let event else { return nil } - return Store.main.findById(event) - } - - func pasteDataForImporting(_ exportFormat: ExportFormat = .rawText) -> String { - let _selectedSortedTeams = selectedSortedTeams() - let selectedSortedTeams = _selectedSortedTeams + waitingListSortedTeams(selectedSortedTeams: _selectedSortedTeams) - switch exportFormat { - case .rawText: - return (selectedSortedTeams.compactMap { $0.pasteData(exportFormat) } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData(exportFormat) }).joined(separator: exportFormat.newLineSeparator(2)) - case .csv: - let headers = ["N°", "Nom Prénom", "rang", "Nom Prénom", "rang", "poids"].joined(separator: exportFormat.separator()) - var teamPaste = [headers] - for (index, team) in selectedSortedTeams.enumerated() { - teamPaste.append(team.pasteData(exportFormat, index + 1)) - } - return teamPaste.joined(separator: exportFormat.newLineSeparator()) - } - } - - func club() -> Club? { - return eventObject()?.clubObject() - } - - func locationLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if let club = club() { - switch displayStyle { - case .wide, .title: - return club.name - case .short: - return club.acronym - } - } else { - return "" - } - } - - func hasEnded() -> Bool { - return endDate != nil - } - - func state() -> State { - if self.isCanceled == true { - return .canceled - } - - if self.hasEnded() { return .finished } - - let isBuild = (groupStageCount > 0 && groupStages().isEmpty == false) - || rounds().isEmpty == false - - if isBuild && startDate <= Date() { return .running } - - if isBuild { - return .build - } - return .initial - } - - func seededTeams() -> [TeamRegistration] { - return selectedSortedTeams().filter({ $0.bracketPosition != nil && $0.groupStagePosition == nil }) - } - - func groupStageTeams() -> [TeamRegistration] { - return selectedSortedTeams().filter({ $0.groupStagePosition != nil }) - } - - func groupStageSpots() -> Int { - return groupStages().map { $0.size }.reduce(0,+) - } - - func seeds() -> [TeamRegistration] { - let selectedSortedTeams = selectedSortedTeams() - let seeds = max(selectedSortedTeams.count - groupStageSpots() , 0) - return Array(selectedSortedTeams.prefix(seeds)) - } - - func availableSeeds() -> [TeamRegistration] { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func availableSeeds()", duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - - - return seeds().filter { $0.isSeedable() } - } - - func lastSeedRound() -> Int { - if let last = seeds().filter({ $0.bracketPosition != nil }).last { - return RoundRule.roundIndex(fromMatchIndex: last.bracketPosition! / 2) - } else { - return 0 - } - } - - func getRound(atRoundIndex roundIndex: Int) -> Round? { - return self.tournamentStore?.rounds.first(where: { $0.index == roundIndex }) -// return Store.main.filter(isIncluded: { $0.tournament == id && $0.index == roundIndex }).first - } - - func availableSeedSpot(inRoundIndex roundIndex: Int) -> [Match] { - return getRound(atRoundIndex: roundIndex)?.playedMatches().filter { $0.isEmpty() } ?? [] - } - - func availableSeedOpponentSpot(inRoundIndex roundIndex: Int) -> [Match] { - return getRound(atRoundIndex: roundIndex)?.playedMatches().filter { $0.hasSpaceLeft() } ?? [] - } - func availableSeedGroups(includeAll: Bool = false) -> [SeedInterval] { - let seeds = seeds() - var availableSeedGroup = Set() - for (index, seed) in seeds.enumerated() { - if seed.isSeedable(), let seedGroup = seedGroup(for: index) { - if includeAll { - if let chunks = seedGroup.chunks() { - chunksBy(in: chunks, availableSeedGroup: &availableSeedGroup) - } - } else { - availableSeedGroup.insert(seedGroup) - } - } - } - return availableSeedGroup.sorted(by: <) - } - - func chunksBy(in chunks: [SeedInterval], availableSeedGroup: inout Set) { - chunks.forEach { chunk in - availableSeedGroup.insert(chunk) - if let moreChunk = chunk.chunks() { - self.chunksBy(in: moreChunk, availableSeedGroup: &availableSeedGroup) - } - } - } - - func seedGroup(for alreadySetupSeeds: Int) -> SeedInterval? { - switch alreadySetupSeeds { - case 0...1: - return SeedInterval(first: 1, last: 2) - case 2...3: - return SeedInterval(first: 3, last: 4) - case 4...7: - return SeedInterval(first: 5, last: 8) - case 8...15: - return SeedInterval(first: 9, last: 16) - case 16...23: - return SeedInterval(first: 17, last: 24) - case 24...31: - return SeedInterval(first: 25, last: 32) - default: - return nil - } - } - - func availableSeedGroup() -> SeedInterval? { - let seeds = seeds() - if let firstIndex = seeds.firstIndex(where: { $0.isSeedable() }) { - guard let seedGroup = seedGroup(for: firstIndex) else { return nil } - return seedGroup - } - return nil - } - - func randomSeed(fromSeedGroup seedGroup: SeedInterval) -> TeamRegistration? { - let availableSeeds = seeds(inSeedGroup: seedGroup) - return availableSeeds.randomElement() - } - - func seeds(inSeedGroup seedGroup: SeedInterval) -> [TeamRegistration] { - let availableSeedInSeedGroup = (seedGroup.last - seedGroup.first) + 1 - let availableSeeds = seeds().dropFirst(seedGroup.first - 1).prefix(availableSeedInSeedGroup).filter({ $0.isSeedable() }) - return availableSeeds - } - - func seedGroupAvailable(atRoundIndex roundIndex: Int) -> SeedInterval? { - if let availableSeedGroup = availableSeedGroup() { - return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: availableSeedGroup) - } else { - return nil - } - } - - func seedGroupAvailable(atRoundIndex roundIndex: Int, availableSeedGroup: SeedInterval) -> SeedInterval? { - let fullLeftSeeds = availableSeeds() - if fullLeftSeeds.isEmpty == false && roundIndex >= lastSeedRound() { - - if availableSeedGroup == SeedInterval(first: 1, last: 2) { return availableSeedGroup } - let availableSeeds = seeds(inSeedGroup: availableSeedGroup) - let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex) - let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex) - let targetSpots = availableSeedSpot.isEmpty ? availableSeedOpponentSpot.count : availableSeedSpot.count - if availableSeedGroup == SeedInterval(first: 3, last: 4), availableSeedSpot.count == 6 { - return availableSeedGroup - } - if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.count == availableSeeds.count { - return availableSeedGroup - } else if availableSeeds.count == availableSeedOpponentSpot.count && availableSeedGroup.count == availableSeedOpponentSpot.count { - return availableSeedGroup - } else if let chunks = availableSeedGroup.chunks() { - let seededTeamsCount = self.seededTeams().count - if let chunk = chunks.first(where: { seedInterval in - return seedInterval.first == seededTeamsCount - }) { - return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: chunk) - } else if fullLeftSeeds.count > 1, targetSpots > 1, fullLeftSeeds.count >= targetSpots { - let currentSeeds = seeds() - if let firstIndex = currentSeeds.firstIndex(where: { $0.isSeedable() }) { - - if firstIndex < seededTeamsCount { - return nil - } else { - let sg = SeedInterval(first: seededTeamsCount + 1, last: seededTeamsCount + targetSpots) - let futureAvailableSeeds = self.seeds(inSeedGroup: sg) - if futureAvailableSeeds.count == targetSpots { - return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: sg) - } else { - return nil - } - } - } - } - } - } - - return nil - } - - func setSeeds(inRoundIndex roundIndex: Int, inSeedGroup seedGroup: SeedInterval) { - if seedGroup == SeedInterval(first: 1, last: 2) { - let seeds = seeds() - if let matches = getRound(atRoundIndex: roundIndex)?.playedMatches() { - if let lastMatch = matches.last { - seeds.prefix(1).first?.setSeedPosition(inSpot: lastMatch, slot: .two, opposingSeeding: false) - } - if let firstMatch = matches.first { - seeds.prefix(2).dropFirst().first?.setSeedPosition(inSpot: firstMatch, slot: .one, opposingSeeding: false) - } - } - } else { - - let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex) - let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex) - let availableSeeds = seeds(inSeedGroup: seedGroup) - - if seedGroup == SeedInterval(first: 3, last: 4), availableSeedSpot.count == 6 { - var spots = [Match]() - spots.append(availableSeedSpot[1]) - spots.append(availableSeedSpot[4]) - spots = spots.shuffled() - for (index, seed) in availableSeeds.enumerated() { - seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) - } -// } else if seedGroup == SeedInterval(first: 5, last: 6), availableSeedSpot.count == 4 { -// var spots = [Match]() -// spots.append(availableSeedSpot[1]) -// spots.append(availableSeedSpot[2]) -// spots = spots.shuffled() -// for (index, seed) in availableSeeds.enumerated() { -// seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) -// } - } else { - if availableSeeds.count <= availableSeedSpot.count { - let spots = availableSeedSpot.shuffled() - for (index, seed) in availableSeeds.enumerated() { - seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) - } - } else if (availableSeeds.count <= availableSeedOpponentSpot.count && availableSeeds.count <= self.availableSeeds().count) { - - let spots = availableSeedOpponentSpot.shuffled() - for (index, seed) in availableSeeds.enumerated() { - seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: true) - } - } else if let chunks = seedGroup.chunks() { - if let chunk = chunks.first(where: { seedInterval in - seedInterval.first >= self.seededTeams().count - }) { - setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk) - } - } - } - } - } - - - func inscriptionClosed() -> Bool { - closedRegistrationDate != nil - } - - func getActiveGroupStage(atStep step: Int = 0) -> GroupStage? { - let groupStages = groupStages(atStep: step) - return groupStages.filter({ $0.hasStarted() && $0.hasEnded() == false }).sorted(by: \.index).first ?? groupStages.first - } - - func matchesWithSpace() -> [Match] { - getActiveRound()?.playedMatches().filter({ $0.hasSpaceLeft() }) ?? [] - } - - func getActiveRound(withSeeds: Bool = false) -> Round? { - let rounds: [Round] = self.rounds() - - for round in rounds { - let playedMatches = round.playedMatches() - - // Optimization: If no matches have started in this round, return nil immediately - if !playedMatches.contains(where: { $0.hasStarted() }) { - return round - } - - if playedMatches.contains(where: { $0.hasStarted() && !$0.hasEnded() }) { - if withSeeds { - if !round.seeds().isEmpty { - return round - } else { - return nil - } - } else { - return round - } - } - } - - return nil - } - - func getActiveRoundAndStatus() -> (Round, String)? { - let rounds: [Round] = self.rounds() - - for round in rounds { - let playedMatches = round.playedMatches() - - // Optimization: If no matches have started in this round, return nil immediately - if !playedMatches.contains(where: { $0.hasStarted() }) { - return (round, round.roundStatus(playedMatches: playedMatches)) - } - - if playedMatches.contains(where: { $0.hasStarted() && !$0.hasEnded() }) { - return (round, round.roundStatus(playedMatches: playedMatches)) - } - } - return nil - } - - func getPlayedMatchDateIntervals(in event: Event) -> [DateInterval] { - let allMatches: [Match] = self.allMatches().filter { $0.courtIndex != nil && $0.startDate != nil } - return allMatches.map { match in - DateInterval(event: event.id, courtIndex: match.courtIndex!, startDate: match.startDate!, endDate: match.estimatedEndDate(additionalEstimationDuration)!) - } - } - - func allRoundMatches() -> [Match] { - return allRounds().flatMap { $0._matches() } - } - - func allMatches() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matches.filter { $0.disabled == false } - } - - func _allMatchesIncludingDisabled() -> [Match] { - guard let tournamentStore = self.tournamentStore else { return [] } - return Array(tournamentStore.matches) - } - - func rounds() -> [Round] { - guard let tournamentStore = self.tournamentStore else { return [] } - let rounds: [Round] = tournamentStore.rounds.filter { $0.isUpperBracket() } - return rounds.sorted { $0.index > $1.index } - } - - func sortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] { - let teams = selectedSortedTeams - return teams + waitingListTeams(in: teams, includingWalkOuts: true) - } - - func waitingListSortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] { - let teams = selectedSortedTeams - return waitingListTeams(in: teams, includingWalkOuts: false) - } - - - func selectedSortedTeams() -> [TeamRegistration] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func selectedSortedTeams", id, tournamentTitle(), duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - var _sortedTeams : [TeamRegistration] = [] - var _teams = unsortedTeams().filter({ $0.isOutOfTournament() == false }) - - if let closedRegistrationDate { - _teams = _teams.filter({ team in - if let registrationDate = team.registrationDate { - return registrationDate <= closedRegistrationDate - } else { - return true - } - }) - } - - let defaultSorting : [MySortDescriptor] = _defaultSorting() - - let _completeTeams = _teams.sorted(using: defaultSorting, order: .ascending).filter { $0.isWildCard() == false }.prefix(teamCount).sorted(using: [.keyPath(\.initialWeight), .keyPath(\.id)], order: .ascending) - - let wcGroupStage = _teams.filter { $0.wildCardGroupStage }.sorted(using: _currentSelectionSorting, order: .ascending) - - let wcBracket = _teams.filter { $0.wildCardBracket }.sorted(using: _currentSelectionSorting, order: .ascending) - - let groupStageSpots: Int = self.groupStageSpots() - var bracketSeeds: Int = teamCount - groupStageSpots - wcBracket.count - var groupStageTeamCount: Int = groupStageSpots - wcGroupStage.count - if groupStageTeamCount < 0 { groupStageTeamCount = 0 } - if bracketSeeds < 0 { bracketSeeds = 0 } - - if prioritizeClubMembers { - - var bracketTeams: [TeamRegistration] = [] - bracketTeams.append(contentsOf: _completeTeams.filter { $0.hasMemberOfClub(clubName) }) - - let others: [TeamRegistration] = _completeTeams.filter { $0.hasMemberOfClub(clubName) == false } - let sortedOthers: [TeamRegistration] = others.sorted(using: defaultSorting, order: .ascending) - bracketTeams.append(contentsOf: sortedOthers) - - bracketTeams = bracketTeams - .prefix(bracketSeeds) - .sorted(using: _currentSelectionSorting, order: .ascending) - bracketTeams.append(contentsOf: wcBracket) - -// let bracketTeams: [TeamRegistration] = (_completeTeams.filter { $0.hasMemberOfClub(clubName) } + _completeTeams.filter { $0.hasMemberOfClub(clubName) == false }.sorted(using: defaultSorting, order: .ascending)).prefix(bracketSeeds).sorted(using: _currentSelectionSorting, order: .ascending) + wcBracket - - let groupStageTeamsNoFiltering = Set(_completeTeams).subtracting(bracketTeams) - let groupStageTeams = (groupStageTeamsNoFiltering.filter { $0.hasMemberOfClub(clubName) } + groupStageTeamsNoFiltering.filter { $0.hasMemberOfClub(clubName) == false }.sorted(using: defaultSorting, order: .ascending)).prefix(groupStageTeamCount).sorted(using: _currentSelectionSorting, order: .ascending) + wcGroupStage - - _sortedTeams = bracketTeams.sorted(using: _currentSelectionSorting, order: .ascending) + groupStageTeams.sorted(using: _currentSelectionSorting, order: .ascending) - } else { - let bracketTeams = _completeTeams.prefix(bracketSeeds).sorted(using: _currentSelectionSorting, order: .ascending) + wcBracket - let groupStageTeams = Set(_completeTeams).subtracting(bracketTeams).sorted(using: defaultSorting, order: .ascending).prefix(groupStageTeamCount).sorted(using: _currentSelectionSorting, order: .ascending) + wcGroupStage - _sortedTeams = bracketTeams.sorted(using: _currentSelectionSorting, order: .ascending) + groupStageTeams.sorted(using: _currentSelectionSorting, order: .ascending) - } - return _sortedTeams - } - - func waitingListTeams(in teams: [TeamRegistration], includingWalkOuts: Bool) -> [TeamRegistration] { - let waitingList = Set(unsortedTeams()).subtracting(teams) - let waitings = waitingList.filter { $0.isOutOfTournament() == false }.sorted(using: _defaultSorting(), order: .ascending) - let walkOuts = waitingList.filter { $0.walkOut == true }.sorted(using: _defaultSorting(), order: .ascending) - if includingWalkOuts { - return waitings + walkOuts - } else { - return waitings - } - } - - func bracketCut(teamCount: Int, groupStageCut: Int) -> Int { - return self.teamCount - groupStageCut - } - - func groupStageCut() -> Int { - return groupStageSpots() - } - - func cutLabel(index: Int, teamCount: Int?) -> String { - let _teamCount = teamCount ?? selectedSortedTeams().count - let groupStageCut = groupStageCut() - let bracketCut = bracketCut(teamCount: _teamCount, groupStageCut: groupStageCut) - - if index < bracketCut { - return "Tableau" - } else if index - bracketCut < groupStageCut && _teamCount > 0 { - return "Poule" - } else { - return "Attente" - } - } - - func cutLabelColor(index: Int?, teamCount: Int?) -> Color { - guard let index else { return Color.grayNotUniversal } - let _teamCount = teamCount ?? selectedSortedTeams().count - let groupStageCut = groupStageCut() - let bracketCut = bracketCut(teamCount: _teamCount, groupStageCut: groupStageCut) - if index < bracketCut { - return Color.mint - } else if index - bracketCut < groupStageCut && _teamCount > 0 { - return Color.indigo - } else { - return Color.grayNotUniversal - } - } - - func unsortedTeamsWithoutWO() -> [TeamRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.teamRegistrations.filter { $0.isOutOfTournament() == false } - } - - func walkoutTeams() -> [TeamRegistration] { - 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 } - } - - func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] { - var duplicates = [PlayerRegistration]() - Set(players.compactMap({ $0.licenceId })).forEach { licenceId in - let found = players.filter({ $0.licenceId?.strippedLicense == licenceId.strippedLicense }) - if found.count > 1 { - duplicates.append(found.first!) - } - } - return duplicates - } - - func homonyms(in players: [PlayerRegistration]) -> [PlayerRegistration] { - players.filter({ $0.hasHomonym() }) - } - - func unsortedPlayers() -> [PlayerRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - return Array(tournamentStore.playerRegistrations) - } - - func selectedPlayers() -> [PlayerRegistration] { - return self.selectedSortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.computedRank) - } - - func paidSelectedPlayers(type: PlayerPaymentType) -> Double? { - if let entryFee { - let flat = self.selectedSortedTeams().flatMap { $0.unsortedPlayers() } - let count = flat.filter { $0.paymentType == type }.count - return Double(count) * entryFee - } else { - return nil - } - } - - func players() -> [PlayerRegistration] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.playerRegistrations.sorted(by: \.computedRank) - } - - func unrankValue(for malePlayer: Bool) -> Int? { - switch tournamentCategory { - case .unlisted: - return nil - case .men: - return maleUnrankedValue - case .women: - return femaleUnrankedValue - case .mix: - return malePlayer ? maleUnrankedValue : femaleUnrankedValue - } - } - - //todo - var clubName: String? { - return self.eventObject()?.clubObject()?.name - } - - //todo - func significantPlayerCount() -> Int { - return minimumPlayerPerTeam - } - - func inadequatePlayers(in players: [PlayerRegistration]) -> [PlayerRegistration] { - if startDate.isInCurrentYear() == false { - return [] - } - return players.filter { player in - return isPlayerRankInadequate(player: player) - } - } - - func isPlayerRankInadequate(player: PlayerHolder) -> Bool { - guard let rank = player.getRank() else { return false } - let _rank = player.male ? rank : rank + PlayerRegistration.addon(for: rank, manMax: maleUnrankedValue ?? 0, womanMax: femaleUnrankedValue ?? 0) - if _rank <= tournamentLevel.minimumPlayerRank(category: tournamentCategory, ageCategory: federalTournamentAge) { - return true - } else { - return false - } - } - - func ageInadequatePlayers(in players: [PlayerRegistration]) -> [PlayerRegistration] { - if startDate.isInCurrentYear() == false { - return [] - } - return players.filter { player in - return isPlayerAgeInadequate(player: player) - } - } - - func isPlayerAgeInadequate(player: PlayerHolder) -> Bool { - guard let computedAge = player.computedAge else { return false } - if federalTournamentAge.isAgeValid(age: computedAge) == false { - return true - } else { - return false - } - } - - func licenseYearValidity() -> Int { - if startDate.get(.month) > 8 { - return startDate.get(.year) + 1 - } else { - return startDate.get(.year) - } - } - - func playersWithoutValidLicense(in players: [PlayerRegistration], isImported: Bool) -> [PlayerRegistration] { - let licenseYearValidity = self.licenseYearValidity() - return players.filter({ player in - if player.isImported() { - // Player is marked as imported: check if the license is valid - return !player.isValidLicenseNumber(year: licenseYearValidity) - } else { - // Player is not imported: validate license and handle `isImported` flag for non-imported players - let noLicenseId = player.licenceId == nil || player.licenceId?.isEmpty == true - let invalidFormattedLicense = player.formattedLicense().isLicenseNumber == false - - // If global `isImported` is true, check license number as well - let invalidLicenseForImportedFlag = isImported && !player.isValidLicenseNumber(year: licenseYearValidity) - - return noLicenseId || invalidFormattedLicense || invalidLicenseForImportedFlag - } - }) - } - - func getStartDate(ofSeedIndex seedIndex: Int?) -> Date? { - guard let seedIndex else { return nil } - return selectedSortedTeams()[safe: seedIndex]?.callDate - } - - func importTeams(_ teams: [FileImportManager.TeamHolder]) { - var teamsToImport = [TeamRegistration]() - let players = players().filter { $0.licenceId != nil } - teams.forEach { team in - if let previousTeam = team.previousTeam { - previousTeam.updatePlayers(team.players, inTournamentCategory: team.tournamentCategory) - teamsToImport.append(previousTeam) - } else { - var registrationDate = team.registrationDate - if let previousPlayer = players.first(where: { player in - let ids = team.players.compactMap({ $0.licenceId }) - return ids.contains(player.licenceId!) - }), let previousTeamRegistrationDate = previousPlayer.team()?.registrationDate { - registrationDate = previousTeamRegistrationDate - } - let newTeam = addTeam(team.players, registrationDate: registrationDate, name: team.name) - if isAnimation() { - if newTeam.weight == 0 { - newTeam.weight = team.index(in: teams) ?? 0 - } - } - teamsToImport.append(newTeam) - } - } - - 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) - } - } - - func maximumCourtsPerGroupSage() -> Int { - if teamsPerGroupStage > 1 { - return min(teamsPerGroupStage / 2, courtCount) - } else { - return max(1, courtCount) - } - } - - func registrationIssues(selectedTeams: [TeamRegistration]) async -> Int { - let players : [PlayerRegistration] = unsortedPlayers() - let callDateIssue : [TeamRegistration] = selectedTeams.filter { $0.callDate != nil && isStartDateIsDifferentThanCallDate($0) } - let duplicates : [PlayerRegistration] = duplicates(in: players) - let problematicPlayers : [PlayerRegistration] = players.filter({ $0.sex == nil }) - let inadequatePlayers : [PlayerRegistration] = inadequatePlayers(in: players) - let homonyms = homonyms(in: players) - let ageInadequatePlayers = ageInadequatePlayers(in: players) - let isImported = players.anySatisfy({ $0.isImported() }) - let playersWithoutValidLicense : [PlayerRegistration] = playersWithoutValidLicense(in: players, isImported: isImported) - let playersMissing : [TeamRegistration] = selectedTeams.filter({ $0.unsortedPlayers().count < 2 }) - let waitingList : [TeamRegistration] = waitingListTeams(in: selectedTeams, includingWalkOuts: true) - let waitingListInBracket = waitingList.filter({ $0.bracketPosition != nil }) - let waitingListInGroupStage = waitingList.filter({ $0.groupStage != nil }) - - return callDateIssue.count + duplicates.count + problematicPlayers.count + inadequatePlayers.count + playersWithoutValidLicense.count + playersMissing.count + waitingListInBracket.count + waitingListInGroupStage.count + ageInadequatePlayers.count + homonyms.count - } - - func isStartDateIsDifferentThanCallDate(_ team: TeamRegistration, expectedSummonDate: Date? = nil) -> Bool { - guard let summonDate = team.callDate else { return true } - let expectedSummonDate : Date? = team.expectedSummonDate() ?? expectedSummonDate - guard let expectedSummonDate else { return true } - return Calendar.current.compare(summonDate, to: expectedSummonDate, toGranularity: .minute) != ComparisonResult.orderedSame - } - - func groupStagesMatches(atStep step: Int = 0) -> [Match] { - return groupStages(atStep: step).flatMap({ $0._matches() }) -// return Store.main.filter(isIncluded: { $0.groupStage != nil && groupStageIds.contains($0.groupStage!) }) - } - - static let defaultSorting : [MySortDescriptor] = [.keyPath(\Match.computedStartDateForSorting), .keyPath(\Match.computedOrder)] - - static func availableToStart(_ allMatches: [Match], in runningMatches: [Match], checkCanPlay: Bool = true) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func tournament availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return allMatches.filter({ $0.isRunning() == false && $0.canBeStarted(inMatches: runningMatches, checkCanPlay: checkCanPlay) }).sorted(using: defaultSorting, order: .ascending) - } - - static func runningMatches(_ allMatches: [Match]) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func tournament runningMatches", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return allMatches.filter({ $0.isRunning() && $0.isReady() }).sorted(using: defaultSorting, order: .ascending) - } - - static func readyMatches(_ allMatches: [Match]) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func tournament readyMatches", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return allMatches.filter({ $0.isReady() && $0.isRunning() == false && $0.hasEnded() == false }).sorted(using: defaultSorting, order: .ascending) - } - - static func matchesLeft(_ allMatches: [Match]) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func tournament readyMatches", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - return allMatches.filter({ $0.isRunning() == false && $0.hasEnded() == false }).sorted(using: defaultSorting, order: .ascending) - } - - - static func finishedMatches(_ allMatches: [Match], limit: Int?) -> [Match] { - #if _DEBUG_TIME //DEBUGING TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func tournament finishedMatches", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - if let limit { - return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed().prefix(limit)) - } else { - return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed()) - } - } - - func teamsRanked() -> [TeamRegistration] { - let selected = selectedSortedTeams().filter({ $0.finalRanking != nil }) - return selected.sorted(by: \.finalRanking!, order: .ascending) - } - - private func _removeStrings(from dictionary: inout [Int: [String]], stringsToRemove: [String]) { - for key in dictionary.keys { - if var stringArray = dictionary[key] { - // Remove all instances of each string in stringsToRemove - stringArray.removeAll { stringsToRemove.contains($0) } - dictionary[key] = stringArray - } - } - } - - - func finalRanking() async -> [Int: [String]] { - var teams: [Int: [String]] = [:] - var ids: Set = Set() - let rounds = rounds() - let lastStep = lastStep() - if rounds.isEmpty, lastStep > 0 { - let groupStages = groupStages(atStep: lastStep) - - for groupStage in groupStages { - let groupStageTeams = groupStage.teams(true) - for teamIndex in 0.. 0 { - baseRank += qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified - 1 - } - let alreadyPlaceTeams = Array(teams.values.flatMap({ $0 })) - groupStages.forEach { groupStage in - let groupStageTeams = groupStage.teams(true) - for (index, team) in groupStageTeams.enumerated() { - if groupStage.hasEnded() { - if team.qualified == false && alreadyPlaceTeams.contains(team.id) == false { - let groupStageWidth = max(((index == qualifiedPerGroupStage) ? groupStageCount - groupStageAdditionalQualified : groupStageCount) * (index - qualifiedPerGroupStage), 0) - - let _index = baseRank + groupStageWidth + 1 - (index > qualifiedPerGroupStage ? groupStageAdditionalQualified : 0) - print("finalRanking", team.teamLabel() , _index, baseRank, groupStageWidth) - if let existingTeams = teams[_index] { - teams[_index] = existingTeams + [team.id] - } else { - teams[_index] = [team.id] - } - } - } - } - } - } - - return teams - } - - func setRankings(assimilationLevel: TournamentLevel? = nil, finalRanks: [Int: [String]]) async -> [Int: [TeamRegistration]] { - guard let tournamentStore = self.tournamentStore else { return [:] } - let tournamentLevel = assimilationLevel ?? tournamentLevel - var rankings: [Int: [TeamRegistration]] = [:] - - finalRanks.keys.sorted().forEach { rank in - if let rankedTeamIds = finalRanks[rank] { - let teams: [TeamRegistration] = rankedTeamIds.compactMap { tournamentStore.teamRegistrations.findById($0) } - rankings[rank] = teams - } - } - - rankings.keys.sorted().forEach { rank in - if let rankedTeams = rankings[rank] { - rankedTeams.forEach { team in - team.finalRanking = rank - team.pointsEarned = isAnimation() ? nil : tournamentLevel.points(for: rank - 1, count: teamCount) - } - } - } - - do { - try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) - } catch { - Logger.error(error) - } - - if self.publishRankings == false { - self.publishRankings = true - do { - try DataStore.shared.tournaments.addOrUpdate(instance: self) - } catch { - Logger.error(error) - } - } - - return rankings - } - - func refreshPointsEarned(assimilationLevel: TournamentLevel? = nil) { - guard let tournamentStore = self.tournamentStore else { return } - let tournamentLevel = assimilationLevel ?? tournamentLevel - let unsortedTeams = unsortedTeams() - unsortedTeams.forEach { team in - if let finalRanking = team.finalRanking { - team.pointsEarned = isAnimation() ? nil : tournamentLevel.points(for: finalRanking - 1, count: teamCount) - } - } - tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams) - } - - - func lockRegistration() { - closedRegistrationDate = Date() - let count = selectedSortedTeams().count - if teamCount != count { - teamCount = count - } - let teams = unsortedTeams() - teams.forEach { team in - team.lockedWeight = team.weight - } - self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - } - - func unlockRegistration() { - closedRegistrationDate = nil - let teams = unsortedTeams() - teams.forEach { team in - team.lockedWeight = nil - } - self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - } - - func updateWeights() { - let teams = self.unsortedTeams() - teams.forEach { team in - let players = team.unsortedPlayers() - players.forEach { $0.setComputedRank(in: self) } - team.setWeight(from: players, inTournamentCategory: tournamentCategory) - self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) - } - self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - } - - func updateRank(to newDate: Date?, forceRefreshLockWeight: Bool, providedSources: [CSVParser]?) async throws { - refreshRanking = true - #if DEBUG_TIME - let start = Date() - defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) - } - #endif - - guard let newDate else { return } - rankSourceDate = newDate - - // Fetch current month data only once - var monthData = currentMonthData() - - if monthData == nil { - async let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate) - async let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate) - - let formatted = URL.importDateFormatter.string(from: newDate) - let newMonthData = MonthData(monthKey: formatted) - - newMonthData.maleUnrankedValue = await lastRankMan - newMonthData.femaleUnrankedValue = await lastRankWoman - - do { - try DataStore.shared.monthData.addOrUpdate(instance: newMonthData) - } catch { - Logger.error(error) - } - - monthData = newMonthData - } - - let lastRankMan = monthData?.maleUnrankedValue - let lastRankWoman = monthData?.femaleUnrankedValue - - var chunkedParsers: [CSVParser] = [] - if let providedSources { - chunkedParsers = providedSources - } else { - // Fetch only the required files - let dataURLs = SourceFileManager.shared.allFiles.filter { $0.dateFromPath == newDate } - guard !dataURLs.isEmpty else { return } // Early return if no files found - - let sources = dataURLs.map { CSVParser(url: $0) } - chunkedParsers = try await chunkAllSources(sources: sources, size: 10000) - } - - let players = unsortedPlayers() - try await players.concurrentForEach { player in - let lastRank = (player.sex == .female) ? lastRankWoman : lastRankMan - try await player.updateRank(from: chunkedParsers, lastRank: lastRank) - player.setComputedRank(in: self) - } - - if providedSources == nil { - try chunkedParsers.forEach { chunk in - try FileManager.default.removeItem(at: chunk.url) - } - } - - try tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) - - let unsortedTeams = unsortedTeams() - unsortedTeams.forEach { team in - team.setWeight(from: team.players(), inTournamentCategory: tournamentCategory) - if forceRefreshLockWeight { - team.lockedWeight = team.weight - } - } - try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams) - refreshRanking = false - } - - - func missingUnrankedValue() -> Bool { - return maleUnrankedValue == nil || femaleUnrankedValue == nil - } - - func findTeam(_ players: [PlayerRegistration]) -> TeamRegistration? { - return unsortedTeams().first(where: { $0.includes(players: players) }) - } - - func tournamentTitle(_ displayStyle: DisplayStyle = .wide, hideSenior: Bool = false) -> String { - if tournamentLevel == .unlisted, displayStyle == .title { - if let name { - return name - } else { - return tournamentLevel.localizedLevelLabel(.title) - } - } - let displayStyleCategory = hideSenior ? .short : displayStyle - var levelCategory = [tournamentLevel.localizedLevelLabel(displayStyle), tournamentCategory.localizedCategoryLabel(displayStyle, ageCategory: federalAgeCategory)] - if displayStyle == .short { - levelCategory = [tournamentLevel.localizedLevelLabel(displayStyle) + tournamentCategory.localizedCategoryLabel(displayStyle, ageCategory: federalAgeCategory)] - } - let array = levelCategory + [federalTournamentAge.localizedFederalAgeLabel(displayStyleCategory)] - let title: String = array.filter({ $0.isEmpty == false }).joined(separator: " ") - if displayStyle == .wide, let name { - return [title, name].joined(separator: " - ") - } else { - return title - } - } - - func localizedTournamentType() -> String { - switch tournamentLevel { - case .unlisted, .championship: - return tournamentLevel.localizedLevelLabel(.short) - default: - return tournamentLevel.localizedLevelLabel(.short) + tournamentCategory.localizedCategoryLabel(.short, ageCategory: federalAgeCategory) - } - } - - func hideWeight() -> Bool { - return hideTeamsWeight - } - - func isAnimation() -> Bool { - federalLevelCategory.isAnimation() - } - - func subtitle(_ displayStyle: DisplayStyle = .wide) -> String { - return name ?? "" - } - - func formattedDate(_ displayStyle: DisplayStyle = .wide) -> String { - switch displayStyle { - case .title: - startDate.formatted(.dateTime.weekday(.abbreviated).day().month(.abbreviated).year()) - case .wide: - startDate.formatted(date: Date.FormatStyle.DateStyle.complete, time: Date.FormatStyle.TimeStyle.omitted) - case .short: - startDate.formatted(date: .numeric, time: .omitted) - } - } - - func qualifiedFromGroupStage() -> Int { - return groupStageCount * qualifiedPerGroupStage - } - - - func availableQualifiedTeams() -> [TeamRegistration] { -#if _DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func availableQualifiedTeams()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - - return unsortedTeams().filter({ $0.qualified && $0.bracketPosition == nil }) - } - - func qualifiedTeams() -> [TeamRegistration] { - return unsortedTeams().filter({ $0.qualified }) - } - - func moreQualifiedToDraw() -> Int { - return max((qualifiedFromGroupStage() + groupStageAdditionalQualified) - qualifiedTeams().count, 0) - } - - func missingQualifiedFromGroupStages() -> [TeamRegistration] { - if groupStageAdditionalQualified > 0 && groupStagesAreOver() { - return groupStages().filter { $0.hasEnded() }.compactMap { groupStage in - groupStage.teams(true)[safe: qualifiedPerGroupStage] - } - .filter({ $0.qualified == false }) - } else { - return [] - } - } - - func groupStagesAreOver(atStep: Int = 0) -> Bool { - let groupStages = groupStages(atStep: atStep) - guard groupStages.isEmpty == false else { - return true - } - return groupStages.allSatisfy({ $0.hasEnded() }) - //return qualifiedTeams().count == qualifiedFromGroupStage() + groupStageAdditionalQualified - } - - func groupStageLoserBracketAreOver() -> Bool { - guard let groupStageLoserBracket = groupStageLoserBracket() else { - return true - } - return groupStageLoserBracket.hasEnded() - } - - fileprivate func _paymentMethodMessage() -> String? { - return DataStore.shared.user.summonsAvailablePaymentMethods ?? ContactType.defaultAvailablePaymentMethods - } - - var entryFeeMessage: String { - if let entryFee { - let message: String = "Inscription : \(entryFee.formatted(.currency(code: Locale.defaultCurrency()))) par joueur." - return [message, self._paymentMethodMessage()].compactMap { $0 }.joined(separator: "\n") - } else { - return "Inscription : gratuite." - } - } - - func umpireMail() -> [String]? { - return [umpireCustomMail ?? DataStore.shared.user.email] - } - - func earnings() -> Double { - if let entryFee { - return Double(selectedPlayers().filter { $0.hasPaid() }.count) * entryFee - } else { - return 0.0 - } - } - - func paidCompletion() -> Double { - let selectedPlayers = selectedPlayers() - if selectedPlayers.isEmpty { return 0 } - return Double(selectedPlayers.filter { $0.hasPaid() }.count) / Double(selectedPlayers.count) - } - - func presenceStatus() -> Double { - let selectedPlayers = selectedPlayers() - if selectedPlayers.isEmpty { return 0 } - return Double(selectedPlayers.filter { $0.hasArrived }.count) / Double(selectedPlayers.count) - } - - typealias TournamentStatus = (label:String, completion: String) - func cashierStatus() async -> TournamentStatus { - let selectedPlayers = selectedPlayers() - var filteredPlayers = [PlayerRegistration]() - var wording = "" - if isFree() { - wording = "présent" - filteredPlayers = selectedPlayers.filter({ $0.hasArrived }) - } else { - wording = "encaissé" - filteredPlayers = selectedPlayers.filter({ $0.hasPaid() }) - } -// let label = paid.count.formatted() + " / " + selectedPlayers.count.formatted() + " joueurs encaissés" - let label = "\(filteredPlayers.count.formatted()) / \(selectedPlayers.count.formatted()) joueurs \(wording)\(filteredPlayers.count.pluralSuffix)" - let completion = (Double(filteredPlayers.count) / Double(selectedPlayers.count)) - let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0))) - return TournamentStatus(label: label, completion: completionLabel) - } - - func scheduleStatus() async -> TournamentStatus { - let allMatches = allMatches() - let ready = allMatches.filter({ $0.startDate != nil }) -// let label = ready.count.formatted() + " / " + allMatches.count.formatted() + " matchs programmés" - let label = "\(ready.count.formatted()) / \(allMatches.count.formatted()) matchs programmés" - let completion = (Double(ready.count) / Double(allMatches.count)) - let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0))) - return TournamentStatus(label: label, completion: completionLabel) - } - - func callStatus() async -> TournamentStatus { - let selectedSortedTeams = selectedSortedTeams() - let called = selectedSortedTeams.filter { isStartDateIsDifferentThanCallDate($0) == false } - let justCalled = selectedSortedTeams.filter { $0.called() } - - let label = "\(justCalled.count.formatted()) / \(selectedSortedTeams.count.formatted()) (\(called.count.formatted()) au bon horaire)" - let completion = (Double(called.count) / Double(selectedSortedTeams.count)) - let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0))) - return TournamentStatus(label: label, completion: completionLabel) - } - - func confirmedSummonStatus() async -> TournamentStatus { - let selectedSortedTeams = selectedSortedTeams() - let called = selectedSortedTeams.filter { $0.confirmationDate != nil } - let label = "\(called.count.formatted()) / \(selectedSortedTeams.count.formatted()) confirmées" - let completion = (Double(called.count) / Double(selectedSortedTeams.count)) - let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0))) - return TournamentStatus(label: label, completion: completionLabel) - } - - func bracketStatus() async -> (status: String, description: String?, cut: TeamRegistration.TeamRange?) { - let availableSeeds = availableSeeds() - var description: String? = nil - if availableSeeds.isEmpty == false { - description = "placer \(availableSeeds.count) équipe\(availableSeeds.count.pluralSuffix)" - } - if description == nil { - let availableQualifiedTeams = availableQualifiedTeams() - if availableQualifiedTeams.isEmpty == false { - description = "placer \(availableQualifiedTeams.count) qualifié" + availableQualifiedTeams.count.pluralSuffix - } - } - - var cut: TeamRegistration.TeamRange? = nil - if description == nil && isAnimation() == false { - cut = TeamRegistration.TeamRange(availableSeeds.first, availableSeeds.last) - } - - if let roundAndStatus = getActiveRoundAndStatus() { - return ([roundAndStatus.0.roundTitle(.short), roundAndStatus.1].joined(separator: " ").lowercased(), description, cut) - } else { - return ("", description, nil) - } - } - - func groupStageStatus() async -> (status: String, cut: TeamRegistration.TeamRange?) { - let groupStageTeams = groupStageTeams() - let groupStageTeamsCount = groupStageTeams.count - if groupStageTeamsCount == 0 || groupStageTeamsCount != groupStageSpots() { - return ("à compléter", nil) - } - - let cut : TeamRegistration.TeamRange? = isAnimation() ? nil : TeamRegistration.TeamRange(groupStageTeams.first, groupStageTeams.last) - - if groupStagesAreOver() { return ("terminées", cut) } - let groupStages = groupStages() - let runningGroupStages = groupStages.filter({ $0.isRunning() }) - if runningGroupStages.isEmpty { - - let ongoingGroupStages = runningGroupStages.filter({ $0.hasStarted() && $0.hasEnded() == false }) - if ongoingGroupStages.isEmpty == false { - return ("Poule" + ongoingGroupStages.count.pluralSuffix + " " + ongoingGroupStages.map { ($0.index + 1).formatted() }.joined(separator: ", ") + " en cours", cut) - } - return (groupStages.count.formatted() + " poule" + groupStages.count.pluralSuffix, cut) - } else { - return ("Poule" + runningGroupStages.count.pluralSuffix + " " + runningGroupStages.map { ($0.index + 1).formatted() }.joined(separator: ", ") + " en cours", cut) - } - } - - func settingsDescriptionLocalizedLabel() -> String { - [courtCount.formatted() + " terrain\(courtCount.pluralSuffix)", entryFeeMessage].joined(separator: ", ") - } - - func structureDescriptionLocalizedLabel() -> String { - let groupStageLabel: String? = groupStageCount > 0 ? groupStageCount.formatted() + " poule\(groupStageCount.pluralSuffix)" : nil - return [teamCount.formatted() + " équipes", groupStageLabel].compactMap({ $0 }).joined(separator: ", ") - } - - func deleteAndBuildEverything(preset: PadelTournamentStructurePreset = .manual) { - resetBracketPosition() - deleteStructure() - deleteGroupStages() - - switch preset { - case .doubleGroupStage: - buildGroupStages() - addNewGroupStageStep() - qualifiedPerGroupStage = 0 - groupStageAdditionalQualified = 0 - default: - buildGroupStages() - buildBracket() - } - } - - func addWildCardIfNeeded(_ count: Int, _ type: MatchType) { - let currentCount = selectedSortedTeams().filter({ - if type == .bracket { - return $0.wildCardBracket - } else { - return $0.wildCardGroupStage - } - }).count - - if currentCount < count { - let _diff = count - currentCount - addWildCard(_diff, type) - } - } - - func addWildCard(_ count: Int, _ type: MatchType) { - let wcs = (0.. Int { - let bracketTeamCount = teamCount - (teamsPerGroupStage - qualifiedPerGroupStage) * groupStageCount + (groupStageAdditionalQualified * (groupStageCount > 0 ? 1 : 0)) - return bracketTeamCount - } - - func buildBracket(minimalBracketTeamCount: Int? = nil) { - guard rounds().isEmpty else { return } - let roundCount = RoundRule.numberOfRounds(forTeams: minimalBracketTeamCount ?? bracketTeamCount()) - let matchCount = RoundRule.numberOfMatches(forTeams: minimalBracketTeamCount ?? bracketTeamCount()) - - let rounds = (0.. Match? { - guard let bracketPosition else { return nil } - let matchIndex = bracketPosition / 2 - let roundIndex = RoundRule.roundIndex(fromMatchIndex: matchIndex) - if let round: Round = self.getRound(atRoundIndex: roundIndex) { - return self.tournamentStore?.matches.first(where: { $0.round == round.id && $0.index == matchIndex }) -// return Store.main.filter(isIncluded: { $0.round == round.id && $0.index == matchIndex }).first - - } - return nil - } - - func resetTeamScores(in matchOfBracketPosition: Int?, outsideOf: [TeamScore] = []) { - guard let match = match(for: matchOfBracketPosition) else { return } - match.resetTeamScores(outsideOf: outsideOf) - } - - func updateTeamScores(in matchOfBracketPosition: Int?) { - guard let match = match(for: matchOfBracketPosition) else { return } - match.updateTeamScores() - } - - func deleteStructure() { - self.tournamentStore?.rounds.delete(contentOfs: rounds()) - } - - func resetBracketPosition() { - unsortedTeams().forEach({ $0.bracketPosition = nil }) - } - - func deleteGroupStages() { - self.tournamentStore?.groupStages.delete(contentOfs: allGroupStages()) - } - - func refreshGroupStages(keepExistingMatches: Bool = false) { - unsortedTeams().forEach { team in - team.groupStage = nil - team.groupStagePosition = nil - } - - if groupStageCount > 0 { - switch groupStageOrderingMode { - case .random: - setGroupStage(randomize: true, keepExistingMatches: keepExistingMatches) - case .snake: - setGroupStage(randomize: false, keepExistingMatches: keepExistingMatches) - case .swiss: - setGroupStage(randomize: true, keepExistingMatches: keepExistingMatches) - } - } - } - - func setGroupStage(randomize: Bool, keepExistingMatches: Bool = false) { - let groupStages = groupStages() - let numberOfBracketsAsInt = groupStages.count -// let teamsPerBracket = teamsPerBracket - if groupStageCount != numberOfBracketsAsInt { - deleteGroupStages() - buildGroupStages() - } else { - setGroupStageTeams(randomize: randomize) - groupStages.forEach { $0.buildMatches(keepExistingMatches: keepExistingMatches) } - } - } - - func removeWildCards() { - let wcs = unsortedTeams().filter({ $0.isWildCard() && $0.unsortedPlayers().isEmpty }) - do { - try tournamentStore?.teamRegistrations.delete(contentOfs: wcs) - } catch { - Logger.error(error) - } - } - - func setGroupStageTeams(randomize: Bool) { - let groupStages = groupStages() - let max = groupStages.map { $0.size }.reduce(0,+) - var chunks = selectedSortedTeams().filter({ $0.wildCardBracket == false }).suffix(max).chunked(into: groupStageCount) - for (index, _) in chunks.enumerated() { - if randomize { - chunks[index].shuffle() - } else if index % 2 != 0 { - chunks[index].reverse() - } - - print("Equipes \(chunks[index].map { $0.weight })") - for (jIndex, _) in chunks[index].enumerated() { - print("Position \(index + 1) Poule \(groupStages[jIndex].index)") - chunks[index][jIndex].groupStage = groupStages[jIndex].id - chunks[index][jIndex].groupStagePosition = index - } - } - - self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) - } - - func isFree() -> Bool { - return entryFee == nil || entryFee == 0 - } - - func indexOf(team: TeamRegistration) -> Int? { - return selectedSortedTeams().firstIndex(where: { $0.id == team.id }) - } - - func labelIndexOf(team: TeamRegistration) -> String? { - if let teamIndex = indexOf(team: team) { - return "Tête de série #" + (teamIndex + 1).formatted() - } else { - return nil - } - } - - func addTeam(_ players: Set, registrationDate: Date? = nil, name: String? = nil) -> TeamRegistration { - let team = TeamRegistration(tournament: id, registrationDate: registrationDate, name: name) - team.setWeight(from: Array(players), inTournamentCategory: tournamentCategory) - players.forEach { player in - player.teamRegistration = team.id - } - if isAnimation() { - if team.weight == 0 { - team.weight = unsortedTeams().count - } - } - return team - } - - var matchFormat: MatchFormat { - get { - roundFormat ?? .defaultFormatForMatchType(.bracket) - } - set { - roundFormat = newValue - } - } - - var groupStageMatchFormat: MatchFormat { - get { - groupStageFormat ?? .defaultFormatForMatchType(.groupStage) - } - set { - groupStageFormat = newValue - } - } - - var loserBracketMatchFormat: MatchFormat { - get { - loserRoundFormat ?? .defaultFormatForMatchType(.loserBracket) - } - set { - loserRoundFormat = newValue - } - } - - var groupStageOrderingMode: GroupStageOrderingMode { - get { - groupStageSortMode - } - set { - groupStageSortMode = newValue - } - } - - var tournamentCategory: TournamentCategory { - get { - federalCategory - } - set { - if federalCategory != newValue { - federalCategory = newValue - updateWeights() - } else { - federalCategory = newValue - } - } - } - - var tournamentLevel: TournamentLevel { - get { - federalLevelCategory - } - set { - federalLevelCategory = newValue - teamSorting = newValue.defaultTeamSortingType - groupStageMatchFormat = groupStageSmartMatchFormat() - loserBracketMatchFormat = loserBracketSmartMatchFormat(1) - matchFormat = roundSmartMatchFormat(5) - } - } - - var federalTournamentAge: FederalTournamentAge { - get { - federalAgeCategory - } - set { - federalAgeCategory = newValue - } - } - - func loserBracketSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { - let format = tournamentLevel.federalFormatForLoserBracketRound(roundIndex) - if tournamentLevel == .p25 { return .superTie } - if format.rank < loserBracketMatchFormat.rank { - return format - } else { - return loserBracketMatchFormat - } - } - - func groupStageSmartMatchFormat() -> MatchFormat { - let format = tournamentLevel.federalFormatForGroupStage() - if tournamentLevel == .p25 { return .superTie } - if format.rank < groupStageMatchFormat.rank { - return format - } else { - return groupStageMatchFormat - } - } - - func initSettings(templateTournament: Tournament?) { - setupDefaultPrivateSettings(templateTournament: templateTournament) - setupUmpireSettings(defaultTournament: nil) //default is not template, default is for event sharing settings - if let templateTournament { - setupRegistrationSettings(templateTournament: templateTournament) - } - setupFederalSettings() - } - - func setupDefaultPrivateSettings(templateTournament: Tournament?) { -#if DEBUG - self.isPrivate = false - self.publishTeams = true - self.publishSummons = true - self.publishBrackets = true - self.publishGroupStages = true - self.publishRankings = true - self.publishTournament = true -#else - var shouldBePrivate = templateTournament?.isPrivate ?? true - - if Guard.main.currentPlan == .monthlyUnlimited { - shouldBePrivate = false - } else if Guard.main.purchasedTransactions.isEmpty == false { - shouldBePrivate = false - } - - self.isPrivate = shouldBePrivate -#endif - } - - func setupUmpireSettings(defaultTournament: Tournament? = nil) { - if let defaultTournament { - self.umpireCustomMail = defaultTournament.umpireCustomMail - self.umpireCustomPhone = defaultTournament.umpireCustomPhone - self.umpireCustomContact = defaultTournament.umpireCustomContact - self.hideUmpireMail = defaultTournament.hideUmpireMail - self.hideUmpirePhone = defaultTournament.hideUmpirePhone - self.disableRankingFederalRuling = defaultTournament.disableRankingFederalRuling - self.loserBracketMode = defaultTournament.loserBracketMode - } else { - let user = DataStore.shared.user - self.umpireCustomMail = user.umpireCustomMail - self.umpireCustomPhone = user.umpireCustomPhone - self.umpireCustomContact = user.umpireCustomContact - self.hideUmpireMail = user.hideUmpireMail - self.hideUmpirePhone = user.hideUmpirePhone - self.disableRankingFederalRuling = user.disableRankingFederalRuling - self.loserBracketMode = user.loserBracketMode - } - } - - func setupRegistrationSettings(templateTournament: Tournament) { - self.enableOnlineRegistration = templateTournament.enableOnlineRegistration - self.accountIsRequired = templateTournament.accountIsRequired - self.licenseIsRequired = templateTournament.licenseIsRequired - self.minimumPlayerPerTeam = templateTournament.minimumPlayerPerTeam - self.maximumPlayerPerTeam = templateTournament.maximumPlayerPerTeam - self.waitingListLimit = templateTournament.waitingListLimit - self.teamCountLimit = templateTournament.teamCountLimit - self.enableOnlinePayment = templateTournament.enableOnlinePayment - self.onlinePaymentIsMandatory = templateTournament.onlinePaymentIsMandatory - self.enableOnlinePaymentRefund = templateTournament.enableOnlinePaymentRefund - self.stripeAccountId = templateTournament.stripeAccountId - self.enableTimeToConfirm = templateTournament.enableTimeToConfirm - self.isCorporateTournament = templateTournament.isCorporateTournament - - if self.registrationDateLimit == nil, templateTournament.registrationDateLimit != nil { - self.registrationDateLimit = startDate.truncateMinutesAndSeconds() - } - self.openingRegistrationDate = templateTournament.openingRegistrationDate != nil ? creationDate.truncateMinutesAndSeconds() : nil - self.refundDateLimit = templateTournament.enableOnlinePaymentRefund ? startDate.truncateMinutesAndSeconds() : nil - } - - func setupFederalSettings() { - teamSorting = tournamentLevel.defaultTeamSortingType - groupStageMatchFormat = groupStageSmartMatchFormat() - loserBracketMatchFormat = loserBracketSmartMatchFormat(5) - matchFormat = roundSmartMatchFormat(5) - entryFee = tournamentLevel.entryFee - registrationDateLimit = deadline(for: .inscription) - if enableOnlineRegistration, isAnimation() == false { - accountIsRequired = true - licenseIsRequired = true - } - } - - func customizeUsingPreferences() { - guard let lastTournamentWithSameBuild = DataStore.shared.tournaments.filter({ tournament in - tournament.tournamentLevel == self.tournamentLevel - && tournament.tournamentCategory == self.tournamentCategory - && tournament.federalTournamentAge == self.federalTournamentAge - && tournament.hasEnded() == true - && tournament.isCanceled == false - && tournament.isDeleted == false - }).sorted(by: \.endDate!, order: .descending).first else { - return - } - - self.dayDuration = lastTournamentWithSameBuild.dayDuration - self.teamCount = (lastTournamentWithSameBuild.teamCount / 2) * 2 - self.enableOnlineRegistration = lastTournamentWithSameBuild.enableOnlineRegistration - } - - func onlineRegistrationCanBeEnabled() -> Bool { - true -// isAnimation() == false - } - - func roundSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { - let format = tournamentLevel.federalFormatForBracketRound(roundIndex) - if tournamentLevel == .p25 { return .superTie } - if format.rank < matchFormat.rank { - return format - } else { - return matchFormat - } - } - - private func _defaultSorting() -> [MySortDescriptor] { - switch teamSorting { - case .rank: - [.keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.id)] - case .inscriptionDate: - [.keyPath(\TeamRegistration.registrationDate!), .keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.id)] - } - } - - func isSameBuild(_ build: any TournamentBuildHolder) -> Bool { - tournamentLevel == build.level - && tournamentCategory == build.category - && federalTournamentAge == build.age - } - - private let _currentSelectionSorting : [MySortDescriptor] = [.keyPath(\.weight), .keyPath(\.id)] - - private func _matchSchedulers() -> [MatchScheduler] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.matchSchedulers.filter { $0.tournament == self.id } -// DataStore.shared.matchSchedulers.filter(isIncluded: { $0.tournament == self.id }) - } - - func matchScheduler() -> MatchScheduler? { - return self._matchSchedulers().first - } - - func courtsAvailable() -> [Int] { - (0.. MonthData? { - guard let rankSourceDate else { return nil } - let dateString = URL.importDateFormatter.string(from: rankSourceDate) - return DataStore.shared.monthData.first(where: { $0.monthKey == dateString }) - } - - var maleUnrankedValue: Int? { - return currentMonthData()?.maleUnrankedValue - } - - var femaleUnrankedValue: Int? { - return currentMonthData()?.femaleUnrankedValue - } - - func courtNameIfAvailable(atIndex courtIndex: Int) -> String? { - return club()?.customizedCourts.first(where: { $0.index == courtIndex })?.name - } - - func courtName(atIndex courtIndex: Int) -> String { - return courtNameIfAvailable(atIndex: courtIndex) ?? Court.courtIndexedTitle(atIndex: courtIndex) - } - - func tournamentWinner() -> TeamRegistration? { - let finals: Round? = self.tournamentStore?.rounds.first(where: { $0.index == 0 && $0.isUpperBracket() }) - return finals?.playedMatches().first?.winner() - } - - func getGroupStageChunkValue() -> Int { - if groupStageCount > 0 && teamsPerGroupStage >= 2 { - let result = courtCount / (teamsPerGroupStage / 2) - let remainder = courtCount % (teamsPerGroupStage / 2) - let value = remainder == 0 ? result : result + 1 - return min(groupStageCount, value) - } else { - return 1 - } - } - - func replacementRangeExtended(groupStagePosition: Int) -> TeamRegistration.TeamRange? { - let selectedSortedTeams = selectedSortedTeams() - var left: TeamRegistration? = nil - if groupStagePosition == 0 { - left = seeds().last - } else { - let previousHat = selectedSortedTeams.filter({ $0.groupStagePosition == groupStagePosition - 1 }).sorted(by: \.weight) - left = previousHat.last - } - var right: TeamRegistration? = nil - if groupStagePosition == teamsPerGroupStage - 1 { - right = nil - } else { - let previousHat = selectedSortedTeams.filter({ $0.groupStagePosition == groupStagePosition + 1 }).sorted(by: \.weight) - right = previousHat.first - } - return (left: left, right: right) - } - - - typealias TeamPlacementIssue = (shouldBeInIt: [String], shouldNotBeInIt: [String]) - func groupStageTeamPlacementIssue() -> TeamPlacementIssue { - let selected = selectedSortedTeams() - let allTeams = unsortedTeams() - let newGroup = selected.suffix(groupStageSpots()) - let currentGroup = allTeams.filter({ $0.groupStagePosition != nil }) - let selectedIds = newGroup.map { $0.id } - let groupIds = currentGroup.map { $0.id } - let shouldBeInIt = Set(selectedIds).subtracting(groupIds) - let shouldNotBeInIt = Set(groupIds).subtracting(selectedIds) - return (Array(shouldBeInIt), Array(shouldNotBeInIt)) - } - - func bracketTeamPlacementIssue() -> TeamPlacementIssue { - let selected = selectedSortedTeams() - let allTeams = unsortedTeams() - let seedCount = max(selected.count - groupStageSpots(), 0) - let newGroup = selected.prefix(seedCount) + selected.filter({ $0.qualified }) - let currentGroup = allTeams.filter({ $0.bracketPosition != nil }) - let selectedIds = newGroup.map { $0.id } - let groupStageTeamsInBracket = selected.filter({ $0.qualified == false && $0.inGroupStage() && $0.inRound() }) - let groupIds = currentGroup.map { $0.id } + groupStageTeamsInBracket.map { $0.id } - let shouldBeInIt = Set(selectedIds).subtracting(groupIds) - let shouldNotBeInIt = Set(groupIds).subtracting(selectedIds) - return (Array(shouldBeInIt), Array(shouldNotBeInIt)) - } - - func groupStageLoserBracket() -> Round? { - self.tournamentStore?.rounds.first(where: { $0.groupStageLoserBracket }) - } - - func groupStageLoserBracketsInitialPlace() -> Int { - return teamCount - teamsPerGroupStage * groupStageCount + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified + 1 - } - - func addNewGroupStageStep() { - let lastStep = lastStep() + 1 - for i in 0.. Int { - self.tournamentStore?.groupStages.sorted(by: \.step).last?.step ?? 0 - } - - func generateSmartLoserGroupStageBracket() { - guard let groupStageLoserBracket = groupStageLoserBracket() else { return } - for i in qualifiedPerGroupStage.. [Match] { - rounds().flatMap { $0.allLoserRoundMatches() } - } - - func seedsCount() -> Int { - selectedSortedTeams().count - groupStageSpots() - } - - func lastDrawnDate() -> Date? { - drawLogs().last?.drawDate - } - - func drawLogs() -> [DrawLog] { - guard let tournamentStore = self.tournamentStore else { return [] } - return tournamentStore.drawLogs.sorted(by: \.drawDate) - } - - func seedSpotsLeft() -> Bool { - let alreadySeededRounds = rounds().filter({ $0.seeds().isEmpty == false }) - if alreadySeededRounds.isEmpty { return true } - let spotsLeft = alreadySeededRounds.flatMap({ $0.playedMatches() }).filter { $0.isEmpty() || $0.isValidSpot() } - - return spotsLeft.isEmpty == false - } - - func isRoundValidForSeeding(roundIndex: Int) -> Bool { - if let lastRoundWithSeeds = rounds().last(where: { $0.seeds().isEmpty == false }) { - return roundIndex >= lastRoundWithSeeds.index - } else { - return true - } - } - - - func updateSeedsBracketPosition() async { - await removeAllSeeds(saveTeamsAtTheEnd: false) - let drawLogs = drawLogs().reversed() - let seeds = seeds() - - await MainActor.run { - for (index, seed) in seeds.enumerated() { - if let drawLog = drawLogs.first(where: { $0.drawSeed == index }) { - drawLog.updateTeamBracketPosition(seed) - } - } - } - - do { - try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: seeds) - } catch { - Logger.error(error) - } - } - - func removeAllSeeds(saveTeamsAtTheEnd: Bool) async { - let teams = unsortedTeams() - teams.forEach({ team in - team.bracketPosition = nil - team._cachedRestingTime = nil - team.finalRanking = nil - team.pointsEarned = nil - }) - let allMatches = allRoundMatches() - let ts = allMatches.flatMap { match in - match.teamScores - } - allMatches.forEach { match in - match.disabled = false - match.losingTeamId = nil - match.winningTeamId = nil - match.endDate = nil - match.removeCourt() - match.servingTeamId = nil - } - - do { - try tournamentStore?.teamScores.delete(contentOfs: ts) - } catch { - Logger.error(error) - } - - if saveTeamsAtTheEnd { - do { - try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) - } - } - } - - func addNewRound(_ roundIndex: Int) async { - await MainActor.run { - let round = Round(tournament: id, index: roundIndex, matchFormat: matchFormat) - let matchCount = RoundRule.numberOfMatches(forRoundIndex: roundIndex) - let matchStartIndex = RoundRule.matchIndex(fromRoundIndex: roundIndex) - let nextRound = round.nextRound() - let tournamentStore = self.tournamentStore - var currentIndex = 0 - let matches = (0.. String { - var logs : [String] = ["Journal des tirages\n\n"] - logs.append(drawLogs().map { $0.exportedDrawLog() }.joined(separator: "\n\n")) - return logs.joined() - } - - - func courtUnavailable(courtIndex: Int, from startDate: Date, to endDate: Date) -> Bool { - guard let source = eventObject()?.courtsUnavailability else { return false } - let courtLockedSchedule = source.filter({ $0.courtIndex == courtIndex }) - return courtLockedSchedule.anySatisfy({ dateInterval in - let range = startDate.. OnlineRegistrationStatus { - if hasStarted() { - return .inProgress - } - if closedRegistrationDate != nil { - return .ended - } - if endDate != nil { - return .endedWithResults - } - - let now = Date() - - if let openingRegistrationDate = openingRegistrationDate { - let timezonedDateTime = openingRegistrationDate // Assuming dates are already in local timezone - if now < timezonedDateTime { - return .notStarted - } - } - - if let registrationDateLimit = registrationDateLimit { - let timezonedDateTime = registrationDateLimit // Assuming dates are already in local timezone - if now > timezonedDateTime { - return .ended - } - } - - let currentTeamCount = unsortedTeamsWithoutWO().count - - if currentTeamCount >= teamCount { - if let waitingListLimit = waitingListLimit { - let waitingListCount = currentTeamCount - teamCount - if waitingListCount >= waitingListLimit { - return .waitingListFull - } - } - return .waitingListPossible - } - - return .open - } - - // MARK: - Status - func shouldTournamentBeOver() async -> Bool { - if hasEnded() { - return true - } - if hasStarted() == false { - return false - } - if hasStarted(), self.startDate.timeIntervalSinceNow > -3600*24 { - return false - } - if tournamentStore?.store.fileCollectionsAllLoaded() == false { - return false - } -#if DEBUG //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func shouldTournamentBeOver()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif - if isDeleted == false && hasEnded() == false && hasStarted() { - let allMatches = allMatches() - let remainingMatches = allMatches.filter({ $0.hasEnded() == false && $0.startDate != nil }) - - let calendar = Calendar.current - let anyTomorrow = remainingMatches.anySatisfy({ calendar.isDateInTomorrow($0.startDate!) }) - - - if anyTomorrow == false, let endDate = allMatches.filter({ $0.hasEnded() }).sorted(by: \.endDate!, order: .ascending).last?.endDate, endDate.timeIntervalSinceNow <= -2 * 3600 { - return true - } - } - - return false - } - - func rankSourceShouldBeRefreshed() -> Date? { - if let mostRecentDate = SourceFileManager.shared.lastDataSourceDate(), let currentRankSourceDate = rankSourceDate, currentRankSourceDate < mostRecentDate, hasEnded() == false { - return mostRecentDate - } else { - return nil - } - } - - func onlineTeams() -> [TeamRegistration] { - unsortedTeams().filter({ $0.hasRegisteredOnline() }) - } - - func paidOnlineTeams() -> [TeamRegistration] { - unsortedTeams().filter({ $0.hasPaidOnline() }) - } - - func shouldWarnOnlineRegistrationUpdates() -> Bool { - enableOnlineRegistration && onlineTeams().isEmpty == false && hasEnded() == false && hasStarted() == false - } - - func refreshTeamList(forced: Bool) async { - guard StoreCenter.main.isAuthenticated else { return } - guard tournamentStore?.store.fileCollectionsAllLoaded() == true else { return } - guard shouldRefreshTeams(forced: forced), refreshInProgress == false else { return } - if forced == false { - guard enableOnlineRegistration, hasEnded() == false else { - return - } - } - refreshInProgress = true - do { - try await self.tournamentStore?.playerRegistrations.loadDataFromServerIfAllowed(clear: true) - //try await self.tournamentStore?.teamScores.loadDataFromServerIfAllowed(clear: true) - try await self.tournamentStore?.teamRegistrations.loadDataFromServerIfAllowed(clear: true) - refreshInProgress = false - lastTeamRefresh = Date() - } catch { - Logger.error(error) - refreshInProgress = false - lastTeamRefresh = Date() - } - } - - func mailSubject() -> String { - let subject = [tournamentTitle(hideSenior: true), formattedDate(.short), clubName].compactMap({ $0 }).joined(separator: " | ") - return subject - } - - // MARK: - - - func insertOnServer() throws { - - DataStore.shared.tournaments.writeChangeAndInsertOnServer(instance: self) - - if let teamRegistrations = self.tournamentStore?.teamRegistrations { - for teamRegistration in teamRegistrations { - teamRegistration.insertOnServer() - } - } - - if let groupStages = self.tournamentStore?.groupStages { - for groupStage in groupStages { - groupStage.insertOnServer() - } - } - if let rounds = self.tournamentStore?.rounds { - for round in rounds { - round.insertOnServer() - } - } - - } - - // MARK: - Payments & Crypto - - enum PaymentError: Error { - case cantPayTournament - } - - func payIfNecessary() throws { - if self.payment != nil { return } - if let payment = Guard.main.paymentForNewTournament() { - self.payment = payment - DataStore.shared.tournaments.addOrUpdate(instance: self) - return - } - throw PaymentError.cantPayTournament - } - -} - -extension Bool { - var encodedValue: Int { - switch self { - case true: - return Int.random(in: (0...4)) - case false: - return Int.random(in: (5...9)) - } - } - static func decodeInt(_ int: Int) -> Bool { - switch int { - case (0...4): - return true - default: - return false - } - } -} - -//extension Tournament { -// enum CodingKeys: String, CodingKey { -// case _id = "id" -// 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 _groupStageCourtCount = "groupStageCourtCount" -// 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" -// } -//} - -extension Tournament: FederalTournamentHolder { - func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String { - if isAnimation() { - if let name { - return name.trunc(length: DeviceHelper.charLength()) - } else if build.age == .unlisted, build.category == .unlisted { - return build.level.localizedLevelLabel(.title) - } else { - return build.level.localizedLevelLabel(displayStyle) - } - } - return build.level.localizedLevelLabel(displayStyle) - } - - var codeClub: String? { - club()?.code - } - - var holderId: String { id } - - func clubLabel() -> String { - locationLabel() - } - - func subtitleLabel(forBuild build: any TournamentBuildHolder) -> String { - if isAnimation() { - if displayAgeAndCategory(forBuild: build) == false { - return [build.category.localizedCategoryLabel(ageCategory: build.age), build.age.localizedFederalAgeLabel()].filter({ $0.isEmpty == false }).joined(separator: " ") - } else if name != nil { - return build.level.localizedLevelLabel(.title) - } else { - return "" - } - } else { - return subtitle() - } - } - - var tournaments: [any TournamentBuildHolder] { - [ - self - ] - } - - var dayPeriod: DayPeriod { - let day = startDate.get(.weekday) - switch day { - case 2...6: - return .week - default: - return .weekend - } - } - - func displayAgeAndCategory(forBuild build: any TournamentBuildHolder) -> Bool { - if isAnimation() { - if let name, name.count < DeviceHelper.maxCharacter() { - return true - } else if build.age == .unlisted, build.category == .unlisted { - return true - } else { - return DeviceHelper.isBigScreen() - } - } - return true - } -} - -extension Tournament: TournamentBuildHolder { - func buildHolderTitle(_ displayStyle: DisplayStyle) -> String { - tournamentTitle(.short) - } - - var category: TournamentCategory { - tournamentCategory - } - - var level: TournamentLevel { - tournamentLevel - } - - var age: FederalTournamentAge { - federalTournamentAge - } -} - -extension Tournament { - static func getTemplateTournament() -> Tournament? { - return DataStore.shared.tournaments.filter { $0.isTemplate && $0.isDeleted == false }.sorted(by: \.startDate, order: .descending).first - } - - static func newEmptyInstance() -> Tournament { - let lastDataSource: String? = DataStore.shared.appSettings.lastDataSource - var _mostRecentDateAvailable: Date? { - guard let lastDataSource else { return nil } - return URL.importDateFormatter.date(from: lastDataSource) - } - - let rankSourceDate = _mostRecentDateAvailable - return Tournament(rankSourceDate: rankSourceDate) - } - - static func fake() -> Tournament { - return Tournament(event: "Roland Garros", name: "Magic P100", startDate: Date(), endDate: Date(), creationDate: Date(), isPrivate: false, groupStageFormat: .nineGames, roundFormat: nil, loserRoundFormat: nil, groupStageSortMode: .snake, groupStageCount: 4, rankSourceDate: nil, dayDuration: 2, teamCount: 24, teamSorting: .rank, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .a45, closedRegistrationDate: nil, groupStageAdditionalQualified: 0, courtCount: 4, prioritizeClubMembers: false, qualifiedPerGroupStage: 2, teamsPerGroupStage: 4, entryFee: nil) - } - -} - -extension Tournament { - func deadline(for type: TournamentDeadlineType) -> Date? { - guard [.p500, .p1000, .p1500, .p2000].contains(tournamentLevel) else { return nil } - - let daysOffset = type.daysOffset(level: tournamentLevel) - if let date = Calendar.current.date(byAdding: .day, value: daysOffset, to: startDate) { - let startOfDay = Calendar.current.startOfDay(for: date) - return Calendar.current.date(byAdding: type.timeOffset, to: startOfDay) - } - return nil - } -} - -/// 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 - } - } - -} diff --git a/PadelClub/Data/TournamentLibrary.swift b/PadelClub/Data/TournamentLibrary.swift deleted file mode 100644 index 062083f..0000000 --- a/PadelClub/Data/TournamentLibrary.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// 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? { - guard let tournament = DataStore.shared.tournaments.first(where: { $0.id == tournamentId }) else { return nil } - - 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 - } - - func reset() { - self._stores.removeAll() - } - -} diff --git a/PadelClub/Data/TournamentStore.swift b/PadelClub/Data/TournamentStore.swift deleted file mode 100644 index 44125f7..0000000 --- a/PadelClub/Data/TournamentStore.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// TournamentStore.swift -// PadelClub -// -// Created by Laurent Morvillier on 26/06/2024. -// - -import Foundation -import LeStorage -import SwiftUI - -class TournamentStore: ObservableObject { - - var store: Store - - fileprivate(set) var groupStages: SyncedCollection = SyncedCollection.placeholder() - fileprivate(set) var matches: SyncedCollection = SyncedCollection.placeholder() - fileprivate(set) var teamRegistrations: SyncedCollection = SyncedCollection.placeholder() - fileprivate(set) var playerRegistrations: SyncedCollection = SyncedCollection.placeholder() - fileprivate(set) var rounds: SyncedCollection = SyncedCollection.placeholder() - fileprivate(set) var teamScores: SyncedCollection = SyncedCollection.placeholder() - - fileprivate(set) var matchSchedulers: StoredCollection = StoredCollection.placeholder() - fileprivate(set) var drawLogs: SyncedCollection = SyncedCollection.placeholder() - -// 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() - } - - fileprivate func _initialize() { - - let indexed: Bool = true - - 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.drawLogs = self.store.registerSynchronizedCollection(indexed: indexed) - - self.store.loadCollectionsFromServerIfNoFile() - - NotificationCenter.default.addObserver( - self, - selector: #selector(_leStorageDidSynchronize), - name: NSNotification.Name.LeStorageDidSynchronize, - object: nil) - - } - - @objc func _leStorageDidSynchronize(notification: Notification) { -// Logger.log("SYNCED > teamRegistrations count = \(self.teamRegistrations.count)") - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - -} diff --git a/PadelClub/Extensions/Array+Extensions.swift b/PadelClub/Extensions/Array+Extensions.swift deleted file mode 100644 index baadf6c..0000000 --- a/PadelClub/Extensions/Array+Extensions.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// Array+Extensions.swift -// PadelClub -// -// Created by Razmig Sarkissian on 03/03/2024. -// - -import Foundation - -extension Array { - func chunked(into size: Int) -> [[Element]] { - return stride(from: 0, to: count, by: size).map { - Array(self[$0 ..< Swift.min($0 + size, count)]) - } - } - - func anySatisfy(_ p: (Element) -> Bool) -> Bool { - return first(where: { p($0) }) != nil - //return !self.allSatisfy { !p($0) } - } - - // Check if the number of elements in the sequence is even - var isEven: Bool { - return self.count % 2 == 0 - } - - // Check if the number of elements in the sequence is odd - var isOdd: Bool { - return self.count % 2 != 0 - } -} - -extension Array where Element: Equatable { - - /// Remove first collection element that is equal to the given `object` or `element`: - mutating func remove(elements: [Element]) { - elements.forEach { - if let index = firstIndex(of: $0) { - remove(at: index) - } - } - } -} - -extension Array where Element: CustomStringConvertible { - func customJoined(separator: String, lastSeparator: String) -> String { - switch count { - case 0: - return "" - case 1: - return "\(self[0])" - case 2: - return "\(self[0]) \(lastSeparator) \(self[1])" - default: - let firstPart = dropLast().map { "\($0)" }.joined(separator: ", ") - let lastPart = "\(lastSeparator) \(last!)" - return "\(firstPart) \(lastPart)" - } - } -} - - -extension Dictionary where Key == Int, Value == [String] { - mutating func setOrAppend(_ element: String?, at key: Int) { - // Check if the element is nil; do nothing if it is - guard let element = element else { - return - } - - // Check if the key exists in the dictionary - if var array = self[key] { - // If it exists, append the element to the array - array.append(element) - self[key] = array - } else { - // If it doesn't exist, create a new array with the element - self[key] = [element] - } - } -} - - -extension Array where Element == String { - func formatList(maxDisplay: Int = 2) -> [String] { - // Check if the array has fewer or equal elements than the maximum display limit - if self.count <= maxDisplay { - // Join all elements with commas - return self - } else { - // Join only the first `maxDisplay` elements and add "et plus" - let displayedItems = self.prefix(maxDisplay) - let remainingCount = self.count - maxDisplay - return displayedItems.dropLast() + [displayedItems.last! + " et \(remainingCount) de plus"] - } - } -} diff --git a/PadelClub/Extensions/Badge+Extensions.swift b/PadelClub/Extensions/Badge+Extensions.swift new file mode 100644 index 0000000..46a0fe8 --- /dev/null +++ b/PadelClub/Extensions/Badge+Extensions.swift @@ -0,0 +1,25 @@ +// +// Badge+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 15/04/2025. +// + +import Foundation +import SwiftUI +import PadelClubData + +extension Badge { + + func color() -> Color { + switch self { + case .checkmark: + .green + case .xmark: + .logoRed + case .custom(_, let color): + color + } + } + +} diff --git a/PadelClub/Extensions/Calendar+Extensions.swift b/PadelClub/Extensions/Calendar+Extensions.swift deleted file mode 100644 index bc7861a..0000000 --- a/PadelClub/Extensions/Calendar+Extensions.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// Calendar+Extensions.swift -// PadelClub -// -// Created by Razmig Sarkissian on 28/03/2024. -// - -import Foundation - -extension Calendar { - func numberOfDaysBetween(_ from: Date?, and to: Date?) -> Int { - guard let from, let to else { return 0 } - let fromDate = startOfDay(for: from) - let toDate = startOfDay(for: to) - let numberOfDays = dateComponents([.day], from: fromDate, to: toDate) - - return numberOfDays.day! // <1> - } - - func isSameDay(date1: Date?, date2: Date?) -> Bool { - guard let date1, let date2 else { return false } - - return numberOfDaysBetween(date1, and: date2) == 0 - } - - func getSportAge() -> Int { - let currentDate = Date() - - // Get the current year - let currentYear = component(.year, from: currentDate) - - // Define the date components for 1st September and 31st December of the current year - let septemberFirstComponents = DateComponents(year: currentYear, month: 9, day: 1) - let decemberThirtyFirstComponents = DateComponents(year: currentYear, month: 12, day: 31) - - // Get the actual dates for 1st September and 31st December - let septemberFirst = date(from: septemberFirstComponents)! - let decemberThirtyFirst = date(from: decemberThirtyFirstComponents)! - - // Determine the sport year - let sportYear: Int - if currentDate >= septemberFirst && currentDate <= decemberThirtyFirst { - // If after 1st September and before 31st December, use current year + 1 - sportYear = currentYear + 1 - } else { - // Otherwise, use the current year - sportYear = currentYear - } - return sportYear - } -} - -extension Calendar { - // Add or subtract months from a date - func addMonths(_ months: Int, to date: Date) -> Date { - return self.date(byAdding: .month, value: months, to: date)! - } - - // Generate a list of month start dates between two dates - func generateMonthRange(startDate: Date, endDate: Date) -> [Date] { - var dates: [Date] = [] - var currentDate = startDate - - while currentDate <= endDate { - dates.append(currentDate) - currentDate = self.addMonths(1, to: currentDate) - } - - return dates - } -} diff --git a/PadelClub/Extensions/CodingContainer+Extensions.swift b/PadelClub/Extensions/CodingContainer+Extensions.swift deleted file mode 100644 index f0b63e6..0000000 --- a/PadelClub/Extensions/CodingContainer+Extensions.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// KeyedEncodingContainer+Extensions.swift -// PadelClub -// -// Created by Laurent Morvillier on 18/09/2024. -// - -import Foundation -import LeStorage - -extension KeyedDecodingContainer { - - func decodeEncrypted(key: Key) throws -> String { - let data = try self.decode(Data.self, forKey: key) - return try data.decryptData(pass: CryptoKey.pass.rawValue) - } - - func decodeEncryptedIfPresent(key: Key) throws -> String? { - let data = try self.decodeIfPresent(Data.self, forKey: key) - if let data { - return try data.decryptData(pass: CryptoKey.pass.rawValue) - } - return nil - } - -} - -extension KeyedEncodingContainer { - - mutating func encodeAndEncrypt(_ value: Data, forKey key: Key) throws { - let encryped: Data = try value.encrypt(pass: CryptoKey.pass.rawValue) - try self.encode(encryped, forKey: key) - } - - mutating func encodeAndEncryptIfPresent(_ value: Data?, forKey key: Key) throws { - guard let value else { - try encodeNil(forKey: key) - return - } - try self.encodeAndEncrypt(value, forKey: key) - } - -} - - diff --git a/PadelClub/Extensions/CustomUser+Extensions.swift b/PadelClub/Extensions/CustomUser+Extensions.swift new file mode 100644 index 0000000..89f9d85 --- /dev/null +++ b/PadelClub/Extensions/CustomUser+Extensions.swift @@ -0,0 +1,22 @@ +// +// CustomUser+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 15/04/2025. +// + +import Foundation +import PadelClubData + +extension CustomUser { + + func currentPlayerData() -> ImportedPlayer? { + guard let licenceId = self.licenceId?.strippedLicense else { return nil } + let federalContext = PersistenceController.shared.localContainer.viewContext + let fetchRequest = ImportedPlayer.fetchRequest() + let predicate = NSPredicate(format: "license == %@", licenceId) + fetchRequest.predicate = predicate + return try? federalContext.fetch(fetchRequest).first + } + +} diff --git a/PadelClub/Extensions/Date+Extensions.swift b/PadelClub/Extensions/Date+Extensions.swift deleted file mode 100644 index 73fb079..0000000 --- a/PadelClub/Extensions/Date+Extensions.swift +++ /dev/null @@ -1,266 +0,0 @@ -// -// Date+Extensions.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/03/2024. -// - -import Foundation -enum TimeOfDay { - case morning - case noon - case afternoon - case evening - case night - - var hello: String { - switch self { - case .morning, .noon, .afternoon: - return "Bonjour" - case .evening, .night: - return "Bonsoir" - } - } - - var goodbye: String { - switch self { - case .morning, .noon, .afternoon: - return "Bonne journée" - case .evening, .night: - return "Bonne soirée" - } - } - -} - - - -extension Date { - func withoutSeconds() -> Date { - let calendar = Calendar.current - return calendar.date(bySettingHour: calendar.component(.hour, from: self), - minute: calendar.component(.minute, from: self), - second: 0, - of: self)! - } - - func localizedDate() -> String { - self.formatted(.dateTime.weekday().day().month()) + " à " + self.formattedAsHourMinute() - } - - func formattedAsHourMinute() -> String { - formatted(.dateTime.hour().minute()) - } - - func formattedAsDate() -> String { - formatted(.dateTime.weekday().day(.twoDigits).month().year()) - } - - var dateFormatted: String { - formatted(.dateTime.day(.twoDigits).month(.twoDigits).year(.twoDigits)) - } - - var monthYearFormatted: String { - formatted(.dateTime.month(.wide).year(.defaultDigits)) - } - - var twoDigitsYearFormatted: String { - formatted(Date.FormatStyle(date: .numeric, time: .omitted).locale(Locale(identifier: "fr_FR")).year(.twoDigits)) - } - - var timeOfDay: TimeOfDay { - let hour = Calendar.current.component(.hour, from: self) - switch hour { - case 6..<12 : return .morning - case 12 : return .noon - case 13..<17 : return .afternoon - case 17..<22 : return .evening - default: return .night - } - } - -} - -extension Date { - func isInCurrentYear() -> Bool { - let calendar = Calendar.current - let currentYear = calendar.component(.year, from: Date()) - let yearOfDate = calendar.component(.year, from: self) - - return currentYear == yearOfDate - } - - func get(_ components: Calendar.Component..., calendar: Calendar = Calendar.current) -> DateComponents { - return calendar.dateComponents(Set(components), from: self) - } - - func get(_ component: Calendar.Component, calendar: Calendar = Calendar.current) -> Int { - return calendar.component(component, from: self) - } - - var tomorrowAtNine: Date { - let currentHour = Calendar.current.component(.hour, from: self) - let startOfDay = Calendar.current.startOfDay(for: self) - if currentHour < 8 { - return Calendar.current.date(byAdding: .hour, value: 9, to: startOfDay)! - } else { - let date = Calendar.current.date(byAdding: .day, value: 1, to: startOfDay) - return Calendar.current.date(byAdding: .hour, value: 9, to: date!)! - } - } - - func atBeginningOfDay(hourInt: Int = 9) -> Date { - Calendar.current.date(byAdding: .hour, value: hourInt, to: self.startOfDay)! - } - - static var firstDayOfWeek = Calendar.current.firstWeekday - static var capitalizedFirstLettersOfWeekdays: [String] = { - let calendar = Calendar.current - // let weekdays = calendar.shortWeekdaySymbols - - // return weekdays.map { weekday in - // guard let firstLetter = weekday.first else { return "" } - // return String(firstLetter).capitalized - // } - // Adjusted for the different weekday starts - var weekdays = calendar.veryShortStandaloneWeekdaySymbols - if firstDayOfWeek > 1 { - for _ in 1..= firstWeekDayBeforeStart && $0 <= endOfMonth }.sorted(by: <) - } - - var monthInt: Int { - Calendar.current.component(.month, from: self) - } - - var yearInt: Int { - Calendar.current.component(.year, from: self) - } - - var dayInt: Int { - Calendar.current.component(.day, from: self) - } - - var startOfDay: Date { - Calendar.current.startOfDay(for: self) - } - - func endOfDay() -> Date { - let calendar = Calendar.current - return calendar.date(bySettingHour: 23, minute: 59, second: 59, of: self)! - } - - func atNine() -> Date { - let calendar = Calendar.current - return calendar.date(bySettingHour: 9, minute: 0, second: 0, of: self)! - } - - func atEightAM() -> Date { - let calendar = Calendar.current - return calendar.date(bySettingHour: 8, minute: 0, second: 0, of: self)! - } -} - -extension Date { - func isEarlierThan(_ date: Date) -> Bool { - Calendar.current.compare(self, to: date, toGranularity: .minute) == .orderedAscending - } -} - -extension Date { - func localizedTime() -> String { - self.formattedAsHourMinute() - } - - func localizedDay() -> String { - self.formatted(.dateTime.weekday(.wide).day()) - } - - func localizedWeekDay() -> String { - self.formatted(.dateTime.weekday(.wide)) - } - - func timeElapsedString() -> String { - let timeInterval = abs(Date().timeIntervalSince(self)) - let duration = Duration.seconds(timeInterval) - - let formatStyle = Duration.UnitsFormatStyle(allowedUnits: [.hours, .minutes], width: .narrow) - return formatStyle.format(duration) - } - - static var hourMinuteFormatter: DateComponentsFormatter = { - let formatter = DateComponentsFormatter() - formatter.allowedUnits = [.hour, .minute] // Customize units - formatter.unitsStyle = .abbreviated // You can choose .abbreviated or .short - return formatter - }() - - func truncateMinutesAndSeconds() -> Date { - let calendar = Calendar.current - return calendar.date(bySetting: .minute, value: 0, of: self)!.withoutSeconds() - } -} diff --git a/PadelClub/Extensions/FixedWidthInteger+Extensions.swift b/PadelClub/Extensions/FixedWidthInteger+Extensions.swift deleted file mode 100644 index 37815d3..0000000 --- a/PadelClub/Extensions/FixedWidthInteger+Extensions.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// FixedWidthInteger+Extensions.swift -// PadelClub -// -// Created by Razmig Sarkissian on 03/03/2024. -// - -import Foundation - -public extension FixedWidthInteger { - func ordinalFormattedSuffix(feminine: Bool = false) -> String { - switch self { - case 1: return feminine ? "ère" : "er" - default: return "ème" - } - } - - func ordinalFormatted(feminine: Bool = false) -> String { - return self.formatted() + self.ordinalFormattedSuffix(feminine: feminine) - } - - private var isMany: Bool { - self > 1 || self < -1 - } - - var pluralSuffix: String { - return isMany ? "s" : "" - } - - func localizedPluralSuffix(_ plural: String = "s") -> String { - return isMany ? plural : "" - } - - func formattedAsRawString() -> String { - String(self) - } - - func durationInHourMinutes() -> String { - let duration = Duration.seconds(self*60) - let formatStyle = Duration.UnitsFormatStyle(allowedUnits: [.hours, .minutes], width: .narrow) - return formatStyle.format(duration) - } -} diff --git a/PadelClub/Extensions/Locale+Extensions.swift b/PadelClub/Extensions/Locale+Extensions.swift deleted file mode 100644 index 646be4c..0000000 --- a/PadelClub/Extensions/Locale+Extensions.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Locale+Extensions.swift -// PadelClub -// -// Created by Laurent Morvillier on 03/04/2024. -// - -import Foundation - -extension Locale { - - static func countries() -> [String] { - var countries: [String] = [] - - for countryCode in Locale.Region.isoRegions { - if let countryName = Locale.current.localizedString(forRegionCode: countryCode.identifier) { - countries.append(countryName) - } - } - - return countries.sorted() - } - - static func defaultCurrency() -> String { -// return "EUR" - Locale.current.currency?.identifier ?? "EUR" - } -} diff --git a/PadelClub/Data/MonthData.swift b/PadelClub/Extensions/MonthData+Extensions.swift similarity index 78% rename from PadelClub/Data/MonthData.swift rename to PadelClub/Extensions/MonthData+Extensions.swift index 6b486b6..0f6bbf6 100644 --- a/PadelClub/Data/MonthData.swift +++ b/PadelClub/Extensions/MonthData+Extensions.swift @@ -1,34 +1,14 @@ // -// MonthData.swift +// MonthData+Extensions.swift // PadelClub // -// Created by Razmig Sarkissian on 18/04/2024. +// Created by Laurent Morvillier on 15/04/2025. // import Foundation -import SwiftUI -import LeStorage +import PadelClubData -@Observable -final class MonthData: BaseMonthData { - - init(monthKey: String) { - super.init() - self.monthKey = monthKey - self.creationDate = Date() - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - - func total() -> Int { - return (maleCount ?? 0) + (femaleCount ?? 0) - } +extension MonthData { static func calculateCurrentUnrankedValues(fromDate: Date) async { diff --git a/PadelClub/Extensions/MySortDescriptor.swift b/PadelClub/Extensions/MySortDescriptor.swift deleted file mode 100644 index 45e2efb..0000000 --- a/PadelClub/Extensions/MySortDescriptor.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// MySortDescriptor.swift -// PadelClub -// -// Created by Razmig Sarkissian on 26/03/2024. -// - -import Foundation - -struct MySortDescriptor { - var comparator: (Value, Value) -> ComparisonResult -} - -extension MySortDescriptor { - static func keyPath(_ keyPath: KeyPath) -> Self { - Self { rootA, rootB in - let valueA = rootA[keyPath: keyPath] - let valueB = rootB[keyPath: keyPath] - - guard valueA != valueB else { - return .orderedSame - } - - return valueA < valueB ? .orderedAscending : .orderedDescending - } - } -} diff --git a/PadelClub/Extensions/NumberFormatter+Extensions.swift b/PadelClub/Extensions/NumberFormatter+Extensions.swift deleted file mode 100644 index 7225048..0000000 --- a/PadelClub/Extensions/NumberFormatter+Extensions.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// NumberFormatter+Extensions.swift -// PadelClub -// -// Created by Razmig Sarkissian on 27/03/2024. -// - -import Foundation - -extension NumberFormatter { - static var ordinal: NumberFormatter = { - let formatter = NumberFormatter() - formatter.numberStyle = .ordinal - return formatter - }() - - static var standard: NumberFormatter = { - return NumberFormatter() - }() -} diff --git a/PadelClub/Extensions/PlayerRegistration+Extensions.swift b/PadelClub/Extensions/PlayerRegistration+Extensions.swift new file mode 100644 index 0000000..e702d05 --- /dev/null +++ b/PadelClub/Extensions/PlayerRegistration+Extensions.swift @@ -0,0 +1,229 @@ +// +// PlayerRegistration+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 15/04/2025. +// + +import Foundation +import PadelClubData + +extension PlayerRegistration { + + convenience init(importedPlayer: ImportedPlayer) { + self.init() + self.teamRegistration = "" + self.firstName = (importedPlayer.firstName ?? "").prefixTrimmed(50).capitalized + self.lastName = (importedPlayer.lastName ?? "").prefixTrimmed(50).uppercased() + self.licenceId = importedPlayer.license?.prefixTrimmed(50) ?? nil + self.rank = Int(importedPlayer.rank) + self.sex = importedPlayer.male ? .male : .female + self.tournamentPlayed = importedPlayer.tournamentPlayed + self.points = importedPlayer.getPoints() + self.clubName = importedPlayer.clubName?.prefixTrimmed(200) + self.ligueName = importedPlayer.ligueName?.prefixTrimmed(200) + self.assimilation = importedPlayer.assimilation?.prefixTrimmed(50) + self.source = .frenchFederation + self.birthdate = importedPlayer.birthYear?.prefixTrimmed(50) + } + + convenience init?(federalData: [String], sex: Int, sexUnknown: Bool) { + self.init() + let _lastName = federalData[0].trimmed.uppercased() + let _firstName = federalData[1].trimmed.capitalized + if _lastName.isEmpty && _firstName.isEmpty { return nil } + lastName = _lastName.prefixTrimmed(50) + firstName = _firstName.prefixTrimmed(50) + birthdate = federalData[2].formattedAsBirthdate().prefixTrimmed(50) + licenceId = federalData[3].prefixTrimmed(50) + clubName = federalData[4].prefixTrimmed(200) + let stringRank = federalData[5] + if stringRank.isEmpty { + rank = nil + } else { + rank = Int(stringRank) + } + let _email = federalData[6] + if _email.isEmpty == false { + self.email = _email.prefixTrimmed(50) + } + let _phoneNumber = federalData[7] + if _phoneNumber.isEmpty == false { + self.phoneNumber = _phoneNumber.prefixTrimmed(50) + } + + source = .beachPadel + if sexUnknown { + if sex == 1 && FileImportManager.shared.foundInWomenData(license: federalData[3]) { + self.sex = .female + } else if FileImportManager.shared.foundInMenData(license: federalData[3]) { + self.sex = .male + } else { + self.sex = nil + } + } else { + self.sex = PlayerSexType(rawValue: sex) + } + } + +} + +extension PlayerRegistration { + + func hasHomonym() -> Bool { + let federalContext = PersistenceController.shared.localContainer.viewContext + let fetchRequest = ImportedPlayer.fetchRequest() + let predicate = NSPredicate(format: "firstName == %@ && lastName == %@", firstName, lastName) + fetchRequest.predicate = predicate + + do { + let count = try federalContext.count(for: fetchRequest) + return count > 1 + } catch { + + } + return false + } + + func updateRank(from sources: [CSVParser], lastRank: Int?) async throws { + #if DEBUG_TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif + + if let dataFound = try await history(from: sources) { + rank = dataFound.rankValue?.toInt() + points = dataFound.points + tournamentPlayed = dataFound.tournamentCountValue?.toInt() + } else if let dataFound = try await historyFromName(from: sources) { + rank = dataFound.rankValue?.toInt() + points = dataFound.points + tournamentPlayed = dataFound.tournamentCountValue?.toInt() + } else { + rank = lastRank + } + } + + func history(from sources: [CSVParser]) async throws -> Line? { + #if DEBUG_TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func history()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif + + guard let license = licenceId?.strippedLicense else { + return nil // Do NOT call historyFromName here, let updateRank handle it + } + + let filteredSources = sources.filter { $0.maleData == isMalePlayer() } + + return await withTaskGroup(of: Line?.self) { group in + for source in filteredSources { + group.addTask { + guard !Task.isCancelled else { return nil } + return try? await source.first { $0.rawValue.contains(";\(license);") } + } + } + + for await result in group { + if let result { + group.cancelAll() // Stop other tasks as soon as we find a match + return result + } + } + return nil + } + } + + func historyFromName(from sources: [CSVParser]) async throws -> Line? { + #if DEBUG + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func historyFromName()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif + + let filteredSources = sources.filter { $0.maleData == isMalePlayer() } + let normalizedLastName = lastName.canonicalVersionWithPunctuation + let normalizedFirstName = firstName.canonicalVersionWithPunctuation + + return await withTaskGroup(of: Line?.self) { group in + for source in filteredSources { + group.addTask { + guard !Task.isCancelled else { print("Cancelled"); return nil } + return try? await source.first { + let lineValue = $0.rawValue.canonicalVersionWithPunctuation + return lineValue.contains(";\(normalizedLastName);\(normalizedFirstName);") + } + } + } + + for await result in group { + if let result { + group.cancelAll() // Stop other tasks as soon as we find a match + return result + } + } + return nil + } + } + +} + +extension PlayerRegistration: PlayerHolder { + + func getAssimilatedAsMaleRank() -> Int? { + nil + } + + func getFirstName() -> String { + firstName + } + + func getLastName() -> String { + lastName + } + + func getPoints() -> Double? { + self.points + } + + func getRank() -> Int? { + rank + } + + func isUnranked() -> Bool { + rank == nil + } + + func formattedRank() -> String { + self.rankLabel() + } + + func formattedLicense() -> String { + if let licenceId { return licenceId.computedLicense } + return "aucune licence" + } + + var male: Bool { + isMalePlayer() + } + + func getBirthYear() -> Int? { + nil + } + + func getProgression() -> Int { + 0 + } + + func getComputedRank() -> Int? { + computedRank + } +} diff --git a/PadelClub/Extensions/Round+Extensions.swift b/PadelClub/Extensions/Round+Extensions.swift new file mode 100644 index 0000000..06ba751 --- /dev/null +++ b/PadelClub/Extensions/Round+Extensions.swift @@ -0,0 +1,33 @@ +// +// Round+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 30/04/2025. +// + +import Foundation +import PadelClubData + +extension Round { + + func loserBracketTurns() -> [LoserRound] { + #if _DEBUG_TIME //DEBUGING TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func loserBracketTurns()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif + var rounds = [LoserRound]() + let currentRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: index) + let roundCount = RoundRule.numberOfRounds(forTeams: currentRoundMatchCount) + + for index in 0.. Element? { - return indices.contains(index) ? self[index] : nil - } -} - -extension Sequence { - func sorted(by keyPath: KeyPath) -> [Element] { - return sorted { a, b in - return a[keyPath: keyPath] < b[keyPath: keyPath] - } - } -} - -extension Sequence { - func pairs() -> AnySequence<(Element, Element)> { - AnySequence(zip(self, self.dropFirst())) - } -} - -extension Sequence { - func concurrentForEach( - _ operation: @escaping (Element) async throws -> Void - ) async throws { - try await withThrowingTaskGroup(of: Void.self) { group in - // First, create all tasks - for element in self { - group.addTask { - try await operation(element) - } - } - - // Then wait for all tasks to complete - for try await _ in group {} - } - } - - func concurrentForEach( - _ operation: @escaping (Element) async -> Void - ) async { - await withTaskGroup(of: Void.self) { group in - // First, add all tasks - for element in self { - group.addTask { - await operation(element) - } - } - - // Then wait for all tasks to complete - for await _ in group {} - } - } - -} - -enum SortOrder { - case ascending - case descending -} - -extension Sequence { - func sorted(using descriptors: [MySortDescriptor], - order: SortOrder) -> [Element] { - sorted { valueA, valueB in - for descriptor in descriptors { - let result = descriptor.comparator(valueA, valueB) - - switch result { - case .orderedSame: - // Keep iterating if the two elements are equal, - // since that'll let the next descriptor determine - // the sort order: - break - case .orderedAscending: - return order == .ascending - case .orderedDescending: - return order == .descending - } - } - - // If no descriptor was able to determine the sort - // order, we'll default to false (similar to when - // using the '<' operator with the built-in API): - return false - } - } -} -extension Sequence { - func sorted(using descriptors: MySortDescriptor...) -> [Element] { - sorted(using: descriptors, order: .ascending) - } -} - diff --git a/PadelClub/Extensions/SourceFileManager+Extensions.swift b/PadelClub/Extensions/SourceFileManager+Extensions.swift new file mode 100644 index 0000000..4505837 --- /dev/null +++ b/PadelClub/Extensions/SourceFileManager+Extensions.swift @@ -0,0 +1,34 @@ +// +// SourceFileManager+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 15/04/2025. +// + +import Foundation +import LeStorage +import PadelClubData + +extension SourceFileManager { + + func exportToCSV(_ prefix: String = "", players: [FederalPlayer], sourceFileType: SourceFile, date: Date) { + let lastDateString = URL.importDateFormatter.string(from: date) + let dateString = [prefix, "CLASSEMENT-PADEL", sourceFileType.rawValue, lastDateString].filter({ $0.isEmpty == false }).joined(separator: "-") + "." + "csv" + + let documentsUrl:URL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)! + let destinationFileUrl = documentsUrl.appendingPathComponent("\(dateString)") + var csvText : String = "" + for player in players { + csvText.append(player.exportToCSV() + "\n") + } + + do { + try csvText.write(to: destinationFileUrl, atomically: true, encoding: .utf8) + print("CSV file exported successfully.") + } catch { + print("Error writing CSV file:", error) + Logger.error(error) + } + } + +} diff --git a/PadelClub/Extensions/SpinDrawable+Extensions.swift b/PadelClub/Extensions/SpinDrawable+Extensions.swift new file mode 100644 index 0000000..b005a0c --- /dev/null +++ b/PadelClub/Extensions/SpinDrawable+Extensions.swift @@ -0,0 +1,42 @@ +// +// SpinDrawable+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 15/04/2025. +// + +import Foundation +import PadelClubData + +extension String: SpinDrawable { + public func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { + [self] + } +} + +extension Match: SpinDrawable { + public func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { + let teams = teams() + if teams.count == 1, hideNames == false { + return teams.first!.segmentLabel(displayStyle, hideNames: hideNames) + } else { + return [roundTitle(), matchTitle(displayStyle)].compactMap { $0 } + } + } +} + +extension TeamRegistration: SpinDrawable { + public func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { + var strings: [String] = [] + let indexLabel = tournamentObject()?.labelIndexOf(team: self) + if let indexLabel { + strings.append(indexLabel) + if hideNames { + return strings + } + } + + strings.append(contentsOf: self.players().map { $0.playerLabel(displayStyle) }) + return strings + } +} diff --git a/PadelClub/Extensions/String+Crypto.swift b/PadelClub/Extensions/String+Crypto.swift deleted file mode 100644 index 83b97de..0000000 --- a/PadelClub/Extensions/String+Crypto.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// String+Crypto.swift -// PadelClub -// -// Created by Laurent Morvillier on 30/04/2024. -// - -import Foundation -import CryptoKit - -enum CryptoError: Error { - case invalidUTF8 - case cantConvertUTF8 - case invalidBase64String - case nilSeal -} - -extension Data { - - func encrypt(pass: String) throws -> Data { - let key = try self._createSymmetricKey(fromString: pass) - let sealedBox = try AES.GCM.seal(self, using: key) - if let combined = sealedBox.combined { - return combined - } - throw CryptoError.nilSeal - } - - func decryptData(pass: String) throws -> String { - let key = try self._createSymmetricKey(fromString: pass) - let sealedBox = try AES.GCM.SealedBox(combined: self) - let decryptedData = try AES.GCM.open(sealedBox, using: key) - guard let decryptedMessage = String(data: decryptedData, encoding: .utf8) else { - throw CryptoError.invalidUTF8 - } - return decryptedMessage - } - - fileprivate func _createSymmetricKey(fromString keyString: String) throws -> SymmetricKey { - guard let keyData = Data(base64Encoded: keyString) else { - throw CryptoError.invalidBase64String - } - return SymmetricKey(data: keyData) - } - -} - diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift deleted file mode 100644 index eef13a6..0000000 --- a/PadelClub/Extensions/String+Extensions.swift +++ /dev/null @@ -1,313 +0,0 @@ -// -// String+Extensions.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/03/2024. -// - -import Foundation - -// MARK: - Trimming and stuff -extension String { - func trunc(length: Int, trailing: String = "…") -> String { - if length <= 0 { return self } - return (self.count > length) ? self.prefix(length) + trailing : self - } - - func prefixTrimmed(_ length: Int) -> String { - String(trimmed.prefix(length)) - } - - func prefixMultilineTrimmed(_ length: Int) -> String { - String(trimmedMultiline.prefix(length)) - } - - var trimmed: String { - replaceCharactersFromSet(characterSet: .newlines, replacementString: " ").trimmingCharacters(in: .whitespacesAndNewlines) - } - - var trimmedMultiline: String { - self.trimmingCharacters(in: .whitespacesAndNewlines) - } - - func replaceCharactersFromSet(characterSet: CharacterSet, replacementString: String = "") -> String { - components(separatedBy: characterSet).joined(separator:replacementString) - } - - var canonicalVersion: String { - trimmed.replaceCharactersFromSet(characterSet: .punctuationCharacters, replacementString: " ").folding(options: .diacriticInsensitive, locale: .current).lowercased() - } - - var canonicalVersionWithPunctuation: String { - trimmed.folding(options: .diacriticInsensitive, locale: .current).lowercased() - } - - var removingFirstCharacter: String { - String(dropFirst()) - } - - func isValidEmail() -> Bool { - let emailRegEx = "^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}$" - let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailRegEx) - return emailPredicate.evaluate(with: self) - } - -} - -// MARK: - Club Name -extension String { - func acronym() -> String { - let acronym = canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines) - if acronym.count > 10 { - return concatenateFirstLetters().uppercased() - } else { - return acronym.uppercased() - } - } - - func concatenateFirstLetters() -> String { - // Split the input into sentences - let sentences = self.components(separatedBy: .whitespacesAndNewlines) - if sentences.count == 1 { - return String(self.prefix(10)) - } - // Extract the first character of each sentence - let firstLetters = sentences.compactMap { sentence -> Character? in - let trimmedSentence = sentence.trimmingCharacters(in: .whitespacesAndNewlines) - if trimmedSentence.count > 2 { - if let firstCharacter = trimmedSentence.first { - return firstCharacter - } - } - return nil - } - - // Join the first letters together into a string - let result = String(firstLetters) - return String(result.prefix(10)) - } -} - -// MARK: - FFT License -extension String { - var computedLicense: String { - if let licenseKey { - return self + licenseKey - } else { - return self - } - } - - var strippedLicense: String? { - var dropFirst = 0 - if hasPrefix("0") { - dropFirst = 1 - } - if let match = self.dropFirst(dropFirst).firstMatch(of: /[0-9]{6,8}/) { - let lic = String(self.dropFirst(dropFirst)[match.range.lowerBound..= "I" { - value += 1 - if let newS = UnicodeScalar(i + value) { - c = Character(newS) - } - } - - if c >= "O" { - value += 1 - if let newS = UnicodeScalar(i + value) { - c = Character(newS) - } - } - - - if c >= "Q" { - value += 1 - if let newS = UnicodeScalar(i + value) { - c = Character(newS) - } - } - - return String(c) - } - } - return nil - } - - func licencesFound() -> [String] { - // First try to find licenses with format: 5-8 digits followed by optional letter - let precisePattern = /[1-9][0-9]{5,7}[ ]?[A-Za-z]?/ - let preciseMatches = self.matches(of: precisePattern) - let preciseResults = preciseMatches.map { String(self[$0.range]).trimmingCharacters(in: .whitespaces) } - - // If we find potential licenses with the precise pattern - if !preciseResults.isEmpty { - // Filter to only include those with trailing letters - let licensesWithLetters = preciseResults.filter { - let lastChar = $0.last - return lastChar != nil && lastChar!.isLetter - } - - print("🎫 Found \(preciseResults.count) potential licenses, filtering to \(licensesWithLetters.count) with trailing letters") - - // If we have licenses with letters, validate them - if !licensesWithLetters.isEmpty { - let validLicenses = licensesWithLetters.filter { $0.isLicenseNumber } - - // If we have valid licenses, return the numeric part of each - if !validLicenses.isEmpty { - let numericLicenses = validLicenses.map { license -> String in - // Extract just the numeric part (all characters except the last letter) - if let lastChar = license.last, lastChar.isLetter { - return String(license.dropLast()) - } - return license - } - - if numericLicenses.isEmpty == false { - print("🎫 Found valid licenses: \(validLicenses), returning numeric parts: \(numericLicenses)") - return numericLicenses - } - } - } - } - - // Fallback to just number pattern if we didn't find good matches - let numberPattern = /[1-9][0-9]{5,7}/ - let numberMatches = self.matches(of: numberPattern) - let numberResults = numberMatches.map { String(self[$0.range]) } - - print("🎫 Falling back to number-only pattern, found: \(numberResults)") - return numberResults - } -} - -// MARK: - FFT Source Importing -extension String { - enum RegexStatic { - static let mobileNumber = /^(?:\+33|0033|0)[6-7](?:[ .-]?[0-9]{2}){4}$/ - static let phoneNumber = /^(?:\+33|0033|0)[1-9](?:[ .-]?[0-9]{2}){4}$/ - } - - func isMobileNumber() -> Bool { - firstMatch(of: RegexStatic.mobileNumber) != nil - } - - func isPhoneNumber() -> Bool { - firstMatch(of: RegexStatic.phoneNumber) != nil - } - - func cleanSearchText() -> String { - // Create a character set of all punctuation except slashes and hyphens - var punctuationToRemove = CharacterSet.punctuationCharacters - punctuationToRemove.remove(charactersIn: "/-") - - // Remove the unwanted punctuation - return self.components(separatedBy: punctuationToRemove) - .joined(separator: " ") - .trimmingCharacters(in: .whitespacesAndNewlines) - } - - //april 04-2024 bug with accent characters / adobe / fft - mutating func replace(characters: [(Character, Character)]) { - for (targetChar, replacementChar) in characters { - self = String(self.map { $0 == targetChar ? replacementChar : $0 }) - } - } -} - -// MARK: - Player Names -extension StringProtocol { - var firstUppercased: String { prefix(1).uppercased() + dropFirst() } - var firstCapitalized: String { prefix(1).capitalized + dropFirst() } -} - -// MARK: - todo clean up ?? -extension LosslessStringConvertible { - var string: String { .init(self) } -} - -extension String { - func createFile(_ withName: String = "temp", _ exportedFormat: ExportFormat = .rawText) -> URL { - let url = FileManager.default.temporaryDirectory - .appendingPathComponent(withName) - .appendingPathExtension(exportedFormat.suffix) - let string = self - try? FileManager.default.removeItem(at: url) - try? string.write(to: url, atomically: true, encoding: .utf8) - return url - } -} - -extension String { - func toInt() -> Int? { - Int(self) - } -} - -extension String : @retroactive Identifiable { - public var id: String { self } -} - -extension String { - /// Parses the birthdate string into a `Date` based on multiple formats. - /// - Returns: A `Date` object if parsing is successful, or `nil` if the format is unrecognized. - func parseAsBirthdate() -> Date? { - let dateFormats = [ - "yyyy-MM-dd", // Format for "1993-01-31" - "dd/MM/yyyy", // Format for "27/07/1992" - "dd/MM/yy" // Format for "27/07/92" - ] - - let dateFormatter = DateFormatter() - dateFormatter.locale = Locale(identifier: "en_US_POSIX") // Ensure consistent parsing - - for format in dateFormats { - dateFormatter.dateFormat = format - - if let date = dateFormatter.date(from: self) { - return date // Return the parsed date if successful - } - } - - return nil // Return nil if no format matches - } - - /// Formats the birthdate string into "DD/MM/YYYY". - /// - Returns: A formatted birthdate string, or the original string if parsing fails. - func formattedAsBirthdate() -> String { - if let parsedDate = self.parseAsBirthdate() { - let outputFormatter = DateFormatter() - outputFormatter.dateFormat = "dd/MM/yyyy" // Desired output format - return outputFormatter.string(from: parsedDate) - } - return self // Return the original string if parsing fails - } -} diff --git a/PadelClub/Extensions/TeamRegistration+Extensions.swift b/PadelClub/Extensions/TeamRegistration+Extensions.swift new file mode 100644 index 0000000..dd1e8a1 --- /dev/null +++ b/PadelClub/Extensions/TeamRegistration+Extensions.swift @@ -0,0 +1,67 @@ +// +// TeamRegistration+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 15/04/2025. +// + +import Foundation +import SwiftUI +import PadelClubData + +extension TeamRegistration { + + func initialRoundColor() -> Color? { + if walkOut { return Color.logoRed } + if groupStagePosition != nil || wildCardGroupStage { return Color.blue } + if let initialRound = initialRound(), let colorHex = RoundRule.colors[safe: initialRound.index] { + return Color(uiColor: .init(fromHex: colorHex)) + } else if wildCardBracket { + return Color.mint + } else { + return nil + } + } + + func updateWeight(inTournamentCategory tournamentCategory: TournamentCategory) { + self.setWeight(from: self.players(), inTournamentCategory: tournamentCategory) + } + + func updatePlayers( + _ players: Set, + inTournamentCategory tournamentCategory: TournamentCategory + ) { + let previousPlayers = Set(unsortedPlayers()) + + players.forEach { player in + previousPlayers.forEach { oldPlayer in + if player.licenceId?.strippedLicense == oldPlayer.licenceId?.strippedLicense, + player.licenceId?.strippedLicense != nil + { + player.registeredOnline = oldPlayer.registeredOnline + player.coach = oldPlayer.coach + player.tournamentPlayed = oldPlayer.tournamentPlayed + player.points = oldPlayer.points + player.captain = oldPlayer.captain + player.assimilation = oldPlayer.assimilation + player.ligueName = oldPlayer.ligueName + } + } + } + + let playersToRemove = previousPlayers.subtracting(players) + self.tournamentStore?.playerRegistrations.delete(contentOfs: Array(playersToRemove)) + setWeight(from: Array(players), inTournamentCategory: tournamentCategory) + + players.forEach { player in + player.teamRegistration = id + } + + // do { + // try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + // } catch { + // Logger.error(error) + // } + } + +} diff --git a/PadelClub/Extensions/Tournament+Extensions.swift b/PadelClub/Extensions/Tournament+Extensions.swift new file mode 100644 index 0000000..7b09900 --- /dev/null +++ b/PadelClub/Extensions/Tournament+Extensions.swift @@ -0,0 +1,445 @@ +// +// Tournament+Extensions.swift +// PadelClub +// +// Created by Laurent Morvillier on 15/04/2025. +// + +import Foundation +import SwiftUI +import PadelClubData +import LeStorage + +extension Tournament { + + func setupFederalSettings() { + teamSorting = tournamentLevel.defaultTeamSortingType + groupStageMatchFormat = groupStageSmartMatchFormat() + loserBracketMatchFormat = loserBracketSmartMatchFormat(5) + matchFormat = roundSmartMatchFormat(5) + entryFee = tournamentLevel.entryFee + registrationDateLimit = deadline(for: .inscription) + if enableOnlineRegistration, isAnimation() == false { + accountIsRequired = true + licenseIsRequired = true + } + } + + func customizeUsingPreferences() { + guard let lastTournamentWithSameBuild = DataStore.shared.tournaments.filter({ tournament in + tournament.tournamentLevel == self.tournamentLevel + && tournament.tournamentCategory == self.tournamentCategory + && tournament.federalTournamentAge == self.federalTournamentAge + && tournament.hasEnded() == true + && tournament.isCanceled == false + && tournament.isDeleted == false + }).sorted(by: \.endDate!, order: .descending).first else { + return + } + + self.dayDuration = lastTournamentWithSameBuild.dayDuration + self.teamCount = (lastTournamentWithSameBuild.teamCount / 2) * 2 + self.enableOnlineRegistration = lastTournamentWithSameBuild.enableOnlineRegistration + } + + func addTeam(_ players: Set, registrationDate: Date? = nil, name: String? = nil) -> TeamRegistration { + let team = TeamRegistration(tournament: id, registrationDate: registrationDate, name: name) + team.setWeight(from: Array(players), inTournamentCategory: tournamentCategory) + players.forEach { player in + player.teamRegistration = team.id + } + if isAnimation() { + if team.weight == 0 { + team.weight = unsortedTeams().count + } + } + return team + } + + func addWildCardIfNeeded(_ count: Int, _ type: MatchType) { + let currentCount = selectedSortedTeams().filter({ + if type == .bracket { + return $0.wildCardBracket + } else { + return $0.wildCardGroupStage + } + }).count + + if currentCount < count { + let _diff = count - currentCount + addWildCard(_diff, type) + } + } + + func addEmptyTeamRegistration(_ count: Int) { + + guard let tournamentStore = self.tournamentStore else { return } + + let teams = (0.. [TeamRegistration] { + let selected = selectedSortedTeams().filter({ $0.finalRanking != nil }) + return selected.sorted(by: \.finalRanking!, order: .ascending) + } + + func playersWithoutValidLicense(in players: [PlayerRegistration], isImported: Bool) -> [PlayerRegistration] { + let licenseYearValidity = self.licenseYearValidity() + return players.filter({ player in + if player.isImported() { + // Player is marked as imported: check if the license is valid + return !player.isValidLicenseNumber(year: licenseYearValidity) + } else { + // Player is not imported: validate license and handle `isImported` flag for non-imported players + let noLicenseId = player.licenceId == nil || player.licenceId?.isEmpty == true + let invalidFormattedLicense = player.formattedLicense().isLicenseNumber == false + + // If global `isImported` is true, check license number as well + let invalidLicenseForImportedFlag = isImported && !player.isValidLicenseNumber(year: licenseYearValidity) + + return noLicenseId || invalidFormattedLicense || invalidLicenseForImportedFlag + } + }) + } + + func homonyms(in players: [PlayerRegistration]) -> [PlayerRegistration] { + players.filter({ $0.hasHomonym() }) + } + + func payIfNecessary() throws { + if self.payment != nil { return } + if let payment = Guard.main.paymentForNewTournament() { + self.payment = payment + DataStore.shared.tournaments.addOrUpdate(instance: self) + return + } + throw PaymentError.cantPayTournament + } + + func cutLabelColor(index: Int?, teamCount: Int?) -> Color { + guard let index else { return Color.grayNotUniversal } + let _teamCount = teamCount ?? selectedSortedTeams().count + let groupStageCut = groupStageCut() + let bracketCut = bracketCut(teamCount: _teamCount, groupStageCut: groupStageCut) + if index < bracketCut { + return Color.mint + } else if index - bracketCut < groupStageCut && _teamCount > 0 { + return Color.indigo + } else { + return Color.grayNotUniversal + } + } + + func isPlayerAgeInadequate(player: PlayerHolder) -> Bool { + guard let computedAge = player.computedAge else { return false } + if federalTournamentAge.isAgeValid(age: computedAge) == false { + return true + } else { + return false + } + } + + func isPlayerRankInadequate(player: PlayerHolder) -> Bool { + guard let rank = player.getRank() else { return false } + let _rank = player.male ? rank : rank + PlayerRegistration.addon(for: rank, manMax: maleUnrankedValue ?? 0, womanMax: femaleUnrankedValue ?? 0) + if _rank <= tournamentLevel.minimumPlayerRank(category: tournamentCategory, ageCategory: federalTournamentAge) { + return true + } else { + return false + } + } + + func inadequatePlayers(in players: [PlayerRegistration]) -> [PlayerRegistration] { + if startDate.isInCurrentYear() == false { + return [] + } + return players.filter { player in + return isPlayerRankInadequate(player: player) + } + } + + func ageInadequatePlayers(in players: [PlayerRegistration]) -> [PlayerRegistration] { + if startDate.isInCurrentYear() == false { + return [] + } + return players.filter { player in + return isPlayerAgeInadequate(player: player) + } + } + + func importTeams(_ teams: [FileImportManager.TeamHolder]) { + var teamsToImport = [TeamRegistration]() + let players = players().filter { $0.licenceId != nil } + teams.forEach { team in + if let previousTeam = team.previousTeam { + previousTeam.updatePlayers(team.players, inTournamentCategory: team.tournamentCategory) + teamsToImport.append(previousTeam) + } else { + var registrationDate = team.registrationDate + if let previousPlayer = players.first(where: { player in + let ids = team.players.compactMap({ $0.licenceId }) + return ids.contains(player.licenceId!) + }), let previousTeamRegistrationDate = previousPlayer.team()?.registrationDate { + registrationDate = previousTeamRegistrationDate + } + let newTeam = addTeam(team.players, registrationDate: registrationDate, name: team.name) + if isAnimation() { + if newTeam.weight == 0 { + newTeam.weight = team.index(in: teams) ?? 0 + } + } + teamsToImport.append(newTeam) + } + } + + 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) + } + } + + func registrationIssues(selectedTeams: [TeamRegistration]) async -> Int { + let players : [PlayerRegistration] = unsortedPlayers() + let callDateIssue : [TeamRegistration] = selectedTeams.filter { $0.callDate != nil && isStartDateIsDifferentThanCallDate($0) } + let duplicates : [PlayerRegistration] = duplicates(in: players) + let problematicPlayers : [PlayerRegistration] = players.filter({ $0.sex == nil }) + let inadequatePlayers : [PlayerRegistration] = inadequatePlayers(in: players) + let homonyms = homonyms(in: players) + let ageInadequatePlayers = ageInadequatePlayers(in: players) + let isImported = players.anySatisfy({ $0.isImported() }) + let playersWithoutValidLicense : [PlayerRegistration] = playersWithoutValidLicense(in: players, isImported: isImported) + let playersMissing : [TeamRegistration] = selectedTeams.filter({ $0.unsortedPlayers().count < 2 }) + let waitingList : [TeamRegistration] = waitingListTeams(in: selectedTeams, includingWalkOuts: true) + let waitingListInBracket = waitingList.filter({ $0.bracketPosition != nil }) + let waitingListInGroupStage = waitingList.filter({ $0.groupStage != nil }) + + return callDateIssue.count + duplicates.count + problematicPlayers.count + inadequatePlayers.count + playersWithoutValidLicense.count + playersMissing.count + waitingListInBracket.count + waitingListInGroupStage.count + ageInadequatePlayers.count + homonyms.count + } + + func updateRank(to newDate: Date?, forceRefreshLockWeight: Bool, providedSources: [CSVParser]?) async throws { + refreshRanking = true + #if DEBUG_TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif + + guard let newDate else { return } + rankSourceDate = newDate + + // Fetch current month data only once + var monthData = currentMonthData() + + if monthData == nil { + async let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate) + async let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate) + + let formatted = URL.importDateFormatter.string(from: newDate) + let newMonthData = MonthData(monthKey: formatted) + + newMonthData.maleUnrankedValue = await lastRankMan + newMonthData.femaleUnrankedValue = await lastRankWoman + + do { + try DataStore.shared.monthData.addOrUpdate(instance: newMonthData) + } catch { + Logger.error(error) + } + + monthData = newMonthData + } + + let lastRankMan = monthData?.maleUnrankedValue + let lastRankWoman = monthData?.femaleUnrankedValue + + var chunkedParsers: [CSVParser] = [] + if let providedSources { + chunkedParsers = providedSources + } else { + // Fetch only the required files + let dataURLs = SourceFileManager.shared.allFiles.filter { $0.dateFromPath == newDate } + guard !dataURLs.isEmpty else { return } // Early return if no files found + + let sources = dataURLs.map { CSVParser(url: $0) } + chunkedParsers = try await chunkAllSources(sources: sources, size: 10000) + } + + let players = unsortedPlayers() + try await players.concurrentForEach { player in + let lastRank = (player.sex == .female) ? lastRankWoman : lastRankMan + try await player.updateRank(from: chunkedParsers, lastRank: lastRank) + player.setComputedRank(in: self) + } + + if providedSources == nil { + try chunkedParsers.forEach { chunk in + try FileManager.default.removeItem(at: chunk.url) + } + } + + try tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players) + + let unsortedTeams = unsortedTeams() + unsortedTeams.forEach { team in + team.setWeight(from: team.players(), inTournamentCategory: tournamentCategory) + if forceRefreshLockWeight { + team.lockedWeight = team.weight + } + } + try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams) + refreshRanking = false + } + +} + +extension Tournament { + static func newEmptyInstance() -> Tournament { + let lastDataSource: String? = DataStore.shared.appSettings.lastDataSource + var _mostRecentDateAvailable: Date? { + guard let lastDataSource else { return nil } + return URL.importDateFormatter.date(from: lastDataSource) + } + + let rankSourceDate = _mostRecentDateAvailable + return Tournament(rankSourceDate: rankSourceDate) + } + +} + +extension Tournament: FederalTournamentHolder { + + func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String { + if isAnimation() { + if let name { + return name.trunc(length: DeviceHelper.charLength()) + } else if build.age == .unlisted, build.category == .unlisted { + return build.level.localizedLevelLabel(.title) + } else { + return build.level.localizedLevelLabel(displayStyle) + } + } + return build.level.localizedLevelLabel(displayStyle) + } + + var codeClub: String? { + club()?.code + } + + var holderId: String { id } + + func clubLabel() -> String { + locationLabel() + } + + func subtitleLabel(forBuild build: any TournamentBuildHolder) -> String { + if isAnimation() { + if displayAgeAndCategory(forBuild: build) == false { + return [build.category.localizedCategoryLabel(ageCategory: build.age), build.age.localizedFederalAgeLabel()].filter({ $0.isEmpty == false }).joined(separator: " ") + } else if name != nil { + return build.level.localizedLevelLabel(.title) + } else { + return "" + } + } else { + return subtitle() + } + } + + var tournaments: [any TournamentBuildHolder] { + [ + self + ] + } + + var dayPeriod: DayPeriod { + let day = startDate.get(.weekday) + switch day { + case 2...6: + return .week + default: + return .weekend + } + } + + func displayAgeAndCategory(forBuild build: any TournamentBuildHolder) -> Bool { + if isAnimation() { + if let name, name.count < DeviceHelper.maxCharacter() { + return true + } else if build.age == .unlisted, build.category == .unlisted { + return true + } else { + return DeviceHelper.isBigScreen() + } + } + return true + } +} + +extension Tournament: TournamentBuildHolder { + public func buildHolderTitle(_ displayStyle: DisplayStyle) -> String { + tournamentTitle(.short) + } + + public var category: TournamentCategory { + tournamentCategory + } + + public var level: TournamentLevel { + tournamentLevel + } + + public var age: FederalTournamentAge { + federalTournamentAge + } +} + + +//extension Tournament { +// func deadline(for type: TournamentDeadlineType) -> Date? { +// guard [.p500, .p1000, .p1500, .p2000].contains(tournamentLevel) else { return nil } +// +// let daysOffset = type.daysOffset(level: tournamentLevel) +// if let date = Calendar.current.date(byAdding: .day, value: daysOffset, to: startDate) { +// let startOfDay = Calendar.current.startOfDay(for: date) +// return Calendar.current.date(byAdding: type.timeOffset, to: startOfDay) +// } +// return nil +// } +//} diff --git a/PadelClub/Extensions/URL+Extensions.swift b/PadelClub/Extensions/URL+Extensions.swift deleted file mode 100644 index f3cce59..0000000 --- a/PadelClub/Extensions/URL+Extensions.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// URL+Extensions.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/03/2024. -// - -import Foundation - -extension URL { - - static var savedDateFormatter: DateFormatter = { - let df = DateFormatter() - df.dateFormat = "DD/MM/yyyy" - return df - }() - - static var importDateFormatter: DateFormatter = { - let df = DateFormatter() - df.dateFormat = "MM-yyyy" - return df - }() - - var dateFromPath: Date { - let found = deletingPathExtension().path().components(separatedBy: "-").suffix(2).joined(separator: "-") - if let date = URL.importDateFormatter.date(from: found) { - return date - } else { - return Date() - } - } - - var index: Int { - if let i = path().dropLast(12).last?.wholeNumberValue { - return i - } - return 0 - } - - var manData: Bool { - path().contains("MESSIEURS") - } - - var womanData: Bool { - path().contains("DAMES") - } - - static var seed: URL? { - Bundle.main.url(forResource: "SeedData", withExtension: nil) - } -} - -extension URL { - func creationDate() -> Date? { - // Use FileManager to retrieve the file attributes - do { - let fileAttributes = try FileManager.default.attributesOfItem(atPath: self.path()) - - // Access the creationDate from the file attributes - if let creationDate = fileAttributes[.creationDate] as? Date { - print("File creationDate: \(creationDate)") - return creationDate - } else { - print("creationDate not found.") - } - } catch { - print("Error retrieving file attributes: \(error.localizedDescription)") - } - - return nil - } - - func fftImportingStatus() -> Int? { - // Read the contents of the file - guard let fileContents = try? String(contentsOfFile: path(), encoding: .utf8) else { - return nil - } - - // Split the contents by newline characters - let lines = fileContents.components(separatedBy: .newlines) - //0 means no need to reimport, just recalc - //1 or missing means re-import - if let line = lines.first(where: { - $0.hasPrefix("import-status:") - }) { - return Int(line.replacingOccurrences(of: "import-status:", with: "")) - } - - return nil - } - - func fftImportingMaleUnrankValue() -> Int? { - // Read the contents of the file - guard let fileContents = try? String(contentsOfFile: path(), encoding: .utf8) else { - return nil - } - - // Split the contents by newline characters - let lines = fileContents.components(separatedBy: .newlines) - - if let line = lines.first(where: { - $0.hasPrefix("unrank-male-value:") - }) { - return Int(line.replacingOccurrences(of: "unrank-male-value:", with: "")) - } - - return nil - } - - func fileModelIdentifier() -> String? { - // Read the contents of the file - guard let fileContents = try? String(contentsOfFile: path(), encoding: .utf8) else { - return nil - } - - // Split the contents by newline characters - let lines = fileContents.components(separatedBy: .newlines) - - if let line = lines.first(where: { - $0.hasPrefix("file-model-version:") - }) { - return line.replacingOccurrences(of: "file-model-version:", with: "") - } - - return nil - } - - func fftImportingUncomplete() -> Int? { - // Read the contents of the file - guard let fileContents = try? String(contentsOfFile: path(), encoding: .utf8) else { - return nil - } - - // Split the contents by newline characters - let lines = fileContents.components(separatedBy: .newlines) - - if let line = lines.first(where: { - $0.hasPrefix("max-players:") - }) { - return Int(line.replacingOccurrences(of: "max-players:", with: "")) - } - - return nil - } - - func getUnrankedValue() -> Int? { - // Read the contents of the file - guard let fileContents = try? String(contentsOfFile: path(), encoding: .utf8) else { - return nil - } - - // Split the contents by newline characters - let lines = fileContents.components(separatedBy: .newlines) - - // Get the last non-empty line - var lastLine: String? - for line in lines.reversed() { - let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines) - if !trimmedLine.isEmpty { - lastLine = trimmedLine - break - } - } - - guard let rankString = lastLine?.components(separatedBy: ";").dropFirst().first, let rank = Int(rankString) else { - return nil - } - // Define the regular expression pattern - let pattern = "\\b\(NSRegularExpression.escapedPattern(for: rankString))\\b" - - // Create the regular expression object - guard let regex = try? NSRegularExpression(pattern: pattern) else { - return nil - } - - // Get the matches - let matches = regex.matches(in: fileContents, range: NSRange(fileContents.startIndex..., in: fileContents)) - - // Return the count of matches - return matches.count + rank - 1 - } -} diff --git a/PadelClub/PadelClubApp.swift b/PadelClub/PadelClubApp.swift index 5033f08..f63d8b4 100644 --- a/PadelClub/PadelClubApp.swift +++ b/PadelClub/PadelClubApp.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import TipKit +import PadelClubData @main struct PadelClubApp: App { diff --git a/PadelClub/Utils/ContactManager.swift b/PadelClub/Utils/ContactManager.swift deleted file mode 100644 index 512a939..0000000 --- a/PadelClub/Utils/ContactManager.swift +++ /dev/null @@ -1,254 +0,0 @@ -// -// ContactManager.swift -// Padel Tournament -// -// Created by Razmig Sarkissian on 19/09/2023. -// - -import Foundation -import SwiftUI -import MessageUI -import LeStorage - -enum ContactManagerError: LocalizedError { - case mailFailed - case mailNotSent //no network no error - case messageFailed - case messageNotSent //no network no error - case calendarAccessDenied - case calendarEventSaveFailed - case noCalendarAvailable - case uncalledTeams([TeamRegistration]) - - var localizedDescription: String { - switch self { - case .mailFailed: - return "Le mail n'a pas été envoyé" - case .mailNotSent: - return "Le mail est dans la boîte d'envoi de l'app Mail. Vérifiez son état dans l'app Mail avant d'essayer de le renvoyer." - case .messageFailed: - return "Le SMS n'a pas été envoyé" - case .messageNotSent: - return "Le SMS n'a pas été envoyé" - case .uncalledTeams(let array): - let verb = array.count > 1 ? "peuvent" : "peut" - return "Attention, \(array.count) équipe\(array.count.pluralSuffix) ne \(verb) pas être contacté par la méthode choisie" - case .calendarAccessDenied: - return "Padel Club n'a pas accès à votre calendrier" - case .calendarEventSaveFailed: - return "Padel Club n'a pas réussi à sauver ce tournoi dans votre calendrier" - case .noCalendarAvailable: - return "Padel Club n'a pas réussi à trouver un calendrier pour y inscrire ce tournoi" - } - } - - static func getNetworkErrorMessage(sentError: ContactManagerError?, networkMonitorConnected: Bool) -> String { - var errors: [String] = [] - - if networkMonitorConnected == false { - errors.append("L'appareil n'est pas connecté à internet.") - } - if let sentError { - errors.append(sentError.localizedDescription) - } - return errors.joined(separator: "\n") - } -} - -enum ContactType: Identifiable { - case mail(date: Date?, recipients: [String]?, bccRecipients: [String]?, body: String?, subject: String?, tournamentBuild: TournamentBuild?) - case message(date: Date?, recipients: [String]?, body: String?, tournamentBuild: TournamentBuild?) - - var id: Int { - switch self { - case .message: return 0 - case .mail: return 1 - } - } -} - -extension ContactType { - static let defaultCustomMessage: String = -""" -Il est conseillé de vous présenter 10 minutes avant de jouer.\n\nMerci de me confirmer votre présence avec votre nom et de prévenir votre partenaire. -""" - static let defaultAvailablePaymentMethods: String = "Règlement possible par chèque ou espèces." - - static func callingCustomMessage(source: String? = nil, tournament: Tournament?, startDate: Date?, roundLabel: String) -> String { - let tournamentCustomMessage = source ?? DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage - let clubName = tournament?.clubName ?? "" - - var text = tournamentCustomMessage - let date = startDate ?? tournament?.startDate ?? Date() - - if let tournament { - text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle(.title, hideSenior: true)) - text = text.replacingOccurrences(of: "#prix", with: tournament.entryFeeMessage) - } - - text = text.replacingOccurrences(of: "#club", with: clubName) - text = text.replacingOccurrences(of: "#manche", with: roundLabel.lowercased()) - text = text.replacingOccurrences(of: "#jour", with: "\(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide)))") - text = text.replacingOccurrences(of: "#horaire", with: "\(date.formatted(Date.FormatStyle().hour().minute()))") - - let signature = DataStore.shared.user.getSummonsMessageSignature() ?? DataStore.shared.user.defaultSignature(tournament) - - text = text.replacingOccurrences(of: "#signature", with: signature) - return text - } - - static func callingMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?, reSummon: Bool = false) -> String { - - let useFullCustomMessage = DataStore.shared.user.summonsUseFullCustomMessage - - if useFullCustomMessage { - return callingCustomMessage(tournament: tournament, startDate: startDate, roundLabel: roundLabel) - } - - let date = startDate ?? tournament?.startDate ?? Date() - - let clubName = tournament?.clubName ?? "" - let message = DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage - let signature = DataStore.shared.user.getSummonsMessageSignature() ?? DataStore.shared.user.defaultSignature(tournament) - - let localizedCalled = "convoqué" + (tournament?.tournamentCategory == .women ? "e" : "") + "s" - - var entryFeeMessage: String? { - (DataStore.shared.user.summonsDisplayEntryFee) ? tournament?.entryFeeMessage : nil - } - - var linkMessage: String? { - if let tournament, tournament.isPrivate == false, let shareLink = tournament.shareURL(.matches)?.absoluteString { - return "Vous pourrez suivre tous les résultats de ce tournoi sur le site :\n\n".appending(shareLink) - } else { - return nil - } - } - - var computedMessage: String { - [entryFeeMessage, message, linkMessage].compacted().map { $0.trimmedMultiline }.joined(separator: "\n\n") - } - - let intro = reSummon ? "Suite à des forfaits, vous êtes finalement" : "Vous êtes" - - if let tournament { - return "Bonjour,\n\n\(intro) \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.title, hideSenior: true)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" - } else { - return "Bonjour,\n\n\(intro) \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)" - } - } -} - - -struct MessageComposeView: UIViewControllerRepresentable { - typealias Completion = (_ result: MessageComposeResult) -> Void - - static var canSendText: Bool { MFMessageComposeViewController.canSendText() } - - let recipients: [String]? - let body: String? - let completion: Completion? - - func makeUIViewController(context: Context) -> UIViewController { - guard Self.canSendText else { - let errorView = ContentUnavailableView("Aucun compte de messagerie", systemImage: "xmark", description: Text("Aucun compte de messagerie n'est configuré sur cet appareil.")) - return UIHostingController(rootView: errorView) - } - - let controller = MFMessageComposeViewController() - controller.messageComposeDelegate = context.coordinator - controller.recipients = recipients - controller.body = body - - return controller - } - - func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} - - func makeCoordinator() -> Coordinator { - Coordinator(completion: self.completion) - } - - class Coordinator: NSObject, MFMessageComposeViewControllerDelegate { - private let completion: Completion? - - public init(completion: Completion?) { - self.completion = completion - } - - public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) { - controller.dismiss(animated: true, completion: { - self.completion?(result) - }) - } - } -} - -struct MailComposeView: UIViewControllerRepresentable { - typealias Completion = (_ result: MFMailComposeResult) -> Void - - static var canSendMail: Bool { - if let mailURL = URL(string: "mailto:?to=jap@padelclub.com") { - let mailConfigured = UIApplication.shared.canOpenURL(mailURL) - return mailConfigured && MFMailComposeViewController.canSendMail() - } else { - return MFMailComposeViewController.canSendMail() - } - } - - let recipients: [String]? - let bccRecipients: [String]? - let body: String? - let subject: String? - var attachmentURL: URL? - let completion: Completion? - - func makeUIViewController(context: Context) -> UIViewController { - guard Self.canSendMail else { - let errorView = ContentUnavailableView("Aucun compte mail", systemImage: "xmark", description: Text("Aucun compte mail n'est configuré sur cet appareil.")) - return UIHostingController(rootView: errorView) - } - - let controller = MFMailComposeViewController() - controller.mailComposeDelegate = context.coordinator - controller.setToRecipients(recipients) - controller.setBccRecipients(bccRecipients) - if let attachmentURL { - do { - let attachmentData = try Data(contentsOf: attachmentURL) - controller.addAttachmentData(attachmentData, mimeType: "application/zip", fileName: "backup.zip") - } catch { - print("Could not attach file: \(error)") - } - } - - if let body { - controller.setMessageBody(body, isHTML: false) - } - if let subject { - controller.setSubject(subject) - } - - return controller - } - - func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} - - func makeCoordinator() -> Coordinator { - Coordinator(completion: self.completion) - } - - class Coordinator: NSObject, MFMailComposeViewControllerDelegate { - private let completion: Completion? - - public init(completion: Completion?) { - self.completion = completion - } - - public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { - controller.dismiss(animated: true, completion: { - self.completion?(result) - }) - } - } -} diff --git a/PadelClub/Utils/CryptoKey.swift b/PadelClub/Utils/CryptoKey.swift deleted file mode 100644 index 324ada1..0000000 --- a/PadelClub/Utils/CryptoKey.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Key.swift -// PadelClub -// -// Created by Laurent Morvillier on 30/04/2024. -// - -import Foundation - -enum CryptoKey: String { - case pass = "Aa9QDV1G5MP9ijF2FTFasibNbS/Zun4qXrubIL2P+Ik=" -} diff --git a/PadelClub/Utils/DisplayContext.swift b/PadelClub/Utils/DisplayContext.swift deleted file mode 100644 index e9d0338..0000000 --- a/PadelClub/Utils/DisplayContext.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// DisplayContext.swift -// PadelClub -// -// Created by Razmig Sarkissian on 20/03/2024. -// - -import Foundation -import UIKit - -enum DisplayContext { - case addition - case edition - case lockedForEditing - case selection -} - -enum DisplayStyle { - case title - case wide - case short -} - -enum SummoningDisplayContext { - case footer - case menu -} - -struct DeviceHelper { - static func isBigScreen() -> Bool { - switch UIDevice.current.userInterfaceIdiom { - case .pad: // iPads - return true - case .phone: // iPhones (you can add more cases here for large vs small phones) - if UIScreen.main.bounds.size.width > 375 { // iPhone X, 11, 12, 13 Pro Max etc. - return true // large phones - } else { - return false // smaller phones - } - default: - return false // Other devices (Apple Watch, TV, etc.) - } - - } - - static func maxCharacter() -> Int { - switch UIDevice.current.userInterfaceIdiom { - case .pad: // iPads - return 30 - case .phone: // iPhones (you can add more cases here for large vs small phones) - if UIScreen.main.bounds.size.width > 375 { // iPhone X, 11, 12, 13 Pro Max etc. - return 15 // large phones - } else { - return 9 // smaller phones - } - default: - return 9 // Other devices (Apple Watch, TV, etc.) - } - } - - static func charLength() -> Int { - isBigScreen() ? 0 : 15 - } -} diff --git a/PadelClub/Utils/ExportFormat.swift b/PadelClub/Utils/ExportFormat.swift deleted file mode 100644 index 4a2cb61..0000000 --- a/PadelClub/Utils/ExportFormat.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ExportFormat.swift -// PadelClub -// -// Created by Razmig Sarkissian on 19/07/2024. -// - -import Foundation - -enum ExportFormat: Int, Identifiable, CaseIterable { - var id: Int { self.rawValue } - - case rawText - case csv - - var suffix: String { - switch self { - case .rawText: - return "txt" - case .csv: - return "csv" - } - } - - func separator() -> String { - switch self { - case .rawText: - return " " - case .csv: - return ";" - } - } - - func newLineSeparator(_ count: Int = 1) -> String { - return Array(repeating: "\n", count: count).joined() - } -} diff --git a/PadelClub/Utils/FileImportManager.swift b/PadelClub/Utils/FileImportManager.swift index bc87eb6..01d2286 100644 --- a/PadelClub/Utils/FileImportManager.swift +++ b/PadelClub/Utils/FileImportManager.swift @@ -8,6 +8,7 @@ import Foundation import LeStorage import SwiftUI +import PadelClubData enum FileImportManagerError: LocalizedError { case unknownFormat diff --git a/PadelClub/Utils/HtmlGenerator.swift b/PadelClub/Utils/HtmlGenerator.swift index 09d5ab1..a790cb4 100644 --- a/PadelClub/Utils/HtmlGenerator.swift +++ b/PadelClub/Utils/HtmlGenerator.swift @@ -9,6 +9,7 @@ import Foundation import UIKit import WebKit import PDFKit +import PadelClubData class HtmlGenerator: ObservableObject { diff --git a/PadelClub/Utils/HtmlService.swift b/PadelClub/Utils/HtmlService.swift index 3ebd892..dbe7808 100644 --- a/PadelClub/Utils/HtmlService.swift +++ b/PadelClub/Utils/HtmlService.swift @@ -6,6 +6,7 @@ // import Foundation +import PadelClubData enum HtmlService { diff --git a/PadelClub/Utils/Network/NetworkFederalService.swift b/PadelClub/Utils/Network/NetworkFederalService.swift index 07ac04c..526190a 100644 --- a/PadelClub/Utils/Network/NetworkFederalService.swift +++ b/PadelClub/Utils/Network/NetworkFederalService.swift @@ -7,6 +7,7 @@ import Foundation import CoreLocation +import PadelClubData class NetworkFederalService { struct HttpCommand: Decodable { diff --git a/PadelClub/Utils/Network/NetworkManager.swift b/PadelClub/Utils/Network/NetworkManager.swift index 0f32b96..f7231d2 100644 --- a/PadelClub/Utils/Network/NetworkManager.swift +++ b/PadelClub/Utils/Network/NetworkManager.swift @@ -6,6 +6,7 @@ // import Foundation +import PadelClubData class NetworkManager { static let shared: NetworkManager = NetworkManager() diff --git a/PadelClub/Utils/Network/NetworkManagerError.swift b/PadelClub/Utils/Network/NetworkManagerError.swift deleted file mode 100644 index 970af60..0000000 --- a/PadelClub/Utils/Network/NetworkManagerError.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// NetworkManagerError.swift -// PadelClub -// -// Created by Razmig Sarkissian on 03/03/2024. -// - -import Foundation - -enum NetworkManagerError: LocalizedError { - case maintenance - case fileNotYetAvailable - case mailFailed - case mailNotSent //no network no error - case messageFailed - case messageNotSent //no network no error - case fileNotModified - case fileNotDownloaded(Int) - - var errorDescription: String? { - switch self { - case .maintenance: - return "Le site de la FFT est en maintenance" - default: - return String(describing: self) - } - } -} diff --git a/PadelClub/Utils/Network/RefundService.swift b/PadelClub/Utils/Network/RefundService.swift index 576d5aa..40696fa 100644 --- a/PadelClub/Utils/Network/RefundService.swift +++ b/PadelClub/Utils/Network/RefundService.swift @@ -7,6 +7,7 @@ import Foundation import LeStorage +import PadelClubData class RefundService { static func processRefund(teamRegistrationId: String) async throws -> RefundResponse { diff --git a/PadelClub/Utils/PListReader.swift b/PadelClub/Utils/PListReader.swift deleted file mode 100644 index 0ada740..0000000 --- a/PadelClub/Utils/PListReader.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// PListReader.swift -// PadelClub -// -// Created by Laurent Morvillier on 06/05/2024. -// - -import Foundation - -class PListReader { - - static func dictionary(plist: String) -> [String: Any]? { - if let plistPath = Bundle.main.path(forResource: plist, ofType: "plist") { - // Read plist file into Data - if let plistData = FileManager.default.contents(atPath: plistPath) { - do { - // Deserialize plist data into a dictionary - if let plistDictionary = try PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String: Any] { - return plistDictionary - } - } catch { - print("Error reading plist data: \(error)") - } - } else { - print("Failed to read plist file at path: \(plistPath)") - } - } else { - print("Plist file 'Data.plist' not found in bundle") - } - return nil - - } - - static func readString(plist: String, key: String) -> String? { - if let dictionary = self.dictionary(plist: plist) { - return dictionary[key] as? String - } - return nil - } - - static func readBool(plist: String, key: String) -> Bool? { - if let dictionary = self.dictionary(plist: plist) { - return dictionary[key] as? Bool - } - return nil - } -} diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift deleted file mode 100644 index 55b3d08..0000000 --- a/PadelClub/Utils/PadelRule.swift +++ /dev/null @@ -1,2116 +0,0 @@ -// -// PadelRule.swift -// Padel Tournament -// -// Created by razmig on 27/02/2023. -// - -import Foundation -import LeStorage - -enum RankSource: Hashable { - case national - case ligue - case club(assimilation: Bool) - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .national: - return "Classement National" - case .ligue: - return "Classement Ligue" - case .club: - return "Classement Club" - } - } -} - -protocol TournamentBuildHolder: Identifiable { - var id: String { get } - var category: TournamentCategory { get } - var level: TournamentLevel { get } - var age: FederalTournamentAge { get } - func buildHolderTitle(_ displayStyle: DisplayStyle) -> String -} - -struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable { - var uniqueId: String = Store.randomId() - var id: String { uniqueId } - let category: TournamentCategory - let level: TournamentLevel - let age: FederalTournamentAge -// var japIdentifier: Int? = nil -// var japFirstName: String? = nil -// var japLastName: String? = nil - - func buildHolderTitle(_ displayStyle: DisplayStyle) -> String { - computedLabel(displayStyle) - } - - var identifier: String { - level.localizedLevelLabel()+":"+category.localizedCategoryLabel(ageCategory: age)+":"+age.localizedFederalAgeLabel() - } - - func computedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if age == .senior { return localizedLabel(displayStyle) } - return localizedLabel(displayStyle) + " " + localizedAge(displayStyle) - } - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - level.localizedLevelLabel(displayStyle) + " " + category.localizedCategoryLabel(displayStyle, ageCategory: age) - } - - func localizedTitle(_ displayStyle: DisplayStyle = .wide) -> String { - level.localizedLevelLabel(displayStyle) + " " + category.localizedCategoryLabel(displayStyle, ageCategory: age) - } - - func localizedAge(_ displayStyle: DisplayStyle = .wide) -> String { - age.localizedFederalAgeLabel(displayStyle) - } -} - -extension TournamentBuild { - - init?(category: String, level: String, age: FederalTournamentAge = .senior) { - guard let levelFound = TournamentLevel.allCases.first(where: { $0.localizedLevelLabel() == level }) else { return nil } - - var c = category - if c.hasPrefix("ME") { - c = "H" - } - - if c.hasPrefix("F") { - c = "D" - } - - guard let categoryFound = TournamentCategory.allCases.first(where: { c.canonicalVersion.hasPrefix($0.buildLabel.canonicalVersion) }) else { return nil } - self.level = levelFound - self.category = categoryFound - self.age = age - } -} - -enum FederalTournamentType: String, Hashable, Codable, CaseIterable, Identifiable { - case tournoi = "P" - case championnatParEquipe = "S" - case championnatParPaire = "L" - - var id: String { self.rawValue } - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .tournoi: - return "Tournois" - case .championnatParEquipe: - return "Championnats par équipes" - case .championnatParPaire: - return "Championnats par paires" - } - } -} - -enum TournamentDifficulty { - - case rankS - case rankA - case rankB - case rankC - - init?(gameDifference: Double) { - switch gameDifference { - case ..<3: - self = .rankS - case ..<5: - self = .rankA - case ..<7: - self = .rankB - case 7...: - self = .rankC - default: - return nil - } - } - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .rankS: - return "S" - case .rankA: - return "A" - case .rankB: - return "B" - case .rankC: - return "C" - } - } - - var backgroundColor: String { - switch self { - case .rankS: - return "#d4af37" - case .rankA: - return "#c0c0c0" - case .rankB: - return "#cd7f32" - case .rankC: - return "#DCC2E0" - } - } -} - -enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable { - case unlisted = 0 - case a11_12 = 120 - case a13_14 = 140 - case a15_16 = 160 - case a17_18 = 180 - case senior = 200 - case a45 = 450 - case a55 = 550 - - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } - - func computedBirthYear() -> (Int?, Int?) { - let year = Calendar.current.getSportAge() - switch self { - case .unlisted: - return (nil, nil) - case .a11_12: - return (year - 12, year - 11) - case .a13_14: - return (year - 14, year - 13) - case .a15_16: - return (year - 16, year - 15) - case .a17_18: - return (year - 18, year - 17) - case .senior: - return (nil, year - 19) - case .a45: - return (nil, year - 45) - case .a55: - return (nil, year - 55) - } - } - - var importingRawValue: String { - switch self { - case .unlisted: - return "Animation" - case .a11_12: - return "11/12 ans" - case .a13_14: - return "13/14 ans" - case .a15_16: - return "15/16 ans" - case .a17_18: - return "17/18 ans" - case .senior: - return "Senior" - case .a45: - return "45 ans" - case .a55: - return "55 ans" - } - } - - static func mostRecent(inTournaments tournaments: [Tournament]) -> Self { - return tournaments.first?.federalTournamentAge ?? .senior - } - - static func mostUsed(inTournaments tournaments: [Tournament]) -> Self { - let countedSet = NSCountedSet(array: tournaments.map { $0.federalTournamentAge }) - let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) } - if mostFrequent != nil { - return mostFrequent as! FederalTournamentAge - } else { - return mostRecent(inTournaments: tournaments) - } - } - - var id: Int { self.rawValue } - - var order: Int { - switch self { - case .unlisted: - return 7 - case .a11_12: - return 6 - case .a13_14: - return 5 - case .a15_16: - return 4 - case .a17_18: - return 3 - case .senior: - return 0 - case .a45: - return 1 - case .a55: - return 2 - } - } - - func localizedFederalAgeLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .unlisted: - return displayStyle == .title ? "Aucune" : "" - case .a11_12: - return "U12" - case .a13_14: - return "U14" - case .a15_16: - return "U16" - case .a17_18: - return "U18" - case .senior: - return displayStyle == .short ? "" : "Senior" - case .a45: - return "+45 ans" - case .a55: - return "+55 ans" - } - } - - var tournamentDescriptionLabel: String { - return localizedFederalAgeLabel() - } - - func isAgeValid(age: Int?) -> Bool { - guard let age else { return true } - switch self { - case .unlisted: - return true - case .a11_12: - return age < 13 - case .a13_14: - return age < 15 - case .a15_16: - return age < 17 - case .a17_18: - return age < 19 - case .senior: - return age >= 11 - case .a45: - return age >= 45 - case .a55: - return age >= 55 - } - } - - func isChildCategory() -> Bool { - switch self { - case .unlisted: - return false - case .a11_12: - return true - case .a13_14: - return true - case .a15_16: - return true - case .a17_18: - return true - case .senior: - return false - case .a45: - return false - case .a55: - return false - } - } -} - -enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable { - case unlisted = 0 - case p25 = 25 - case p100 = 100 - case p250 = 250 - case p500 = 500 - case p1000 = 1000 - case p1500 = 1500 - case p2000 = 2000 - case championship = 1 - - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } - - static var assimilationAllCases: [TournamentLevel] = { - return [.p25, .p100, .p250, .p500, .p1000, .p1500, .p2000] - }() - - var entryFee: Double? { - switch self { - case .unlisted, .championship: - return nil - case .p25: - return 15 - default: - return 20 - } - } - - func isAnimation() -> Bool { - switch self { - case .unlisted: - return true - case .championship: - return false - default: - return false - } - } - - func searchRawValue() -> String { - String(describing: self) - } - - func pointsRange(first: Int, last: Int, teamsCount: Int) -> String { - let range = [points(for: first - 1, count: teamsCount), - points(for: last - 1, count: teamsCount)] - return range.map { $0.formatted(.number.sign(strategy: .always())) }.joined(separator: " / ") + " pts" - } - - func hideWeight() -> Bool { - switch self { - case .unlisted: - return true - default: - return false - } - } - - func shouldShareTeams() -> Bool { - switch self { - case .p500, .p1000, .p1500, .p2000: - return true - default: - return false - } - } - - static func mostRecent(inTournaments tournaments: [Tournament]) -> Self { - return tournaments.first?.tournamentLevel ?? .p100 - } - - static func mostUsed(inTournaments tournaments: [Tournament]) -> Self { - let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentLevel }) - let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) } - if mostFrequent != nil { - return mostFrequent as! TournamentLevel - } else { - return mostRecent(inTournaments: tournaments) - } - } - - var id: Int { self.rawValue } - - func wildcardArePossible() -> Bool { - switch self { - case .p500, .p1000, .p1500, .p2000: - return true - default: - return false - } - } - - func haveDeadlines() -> Bool { - switch self { - case .p500, .p1000, .p1500, .p2000: - return true - default: - return false - } - } - - func minimumPlayerRank(category: TournamentCategory, ageCategory: FederalTournamentAge) -> Int { - switch self { - case .p25: - switch ageCategory { - case .senior, .a45, .a55: - return category == .men ? 20000 : 1000 - default: - return 0 - } - case .p100: - switch ageCategory { - case .senior, .a45, .a55: - return category == .men ? 2000 : 300 - default: - return 0 - } - - case .p250: - switch ageCategory { - case .senior, .a45, .a55: - if category == .mix { return 0 } - return category == .men ? 500 : 100 - default: - return 0 - } - - default: - return 0 - } - } - - func federalFormatForGroupStage() -> MatchFormat { - federalFormatForBracketRound(5) - } - - func federalFormatForBracketRound(_ roundIndex: Int) -> MatchFormat { - switch self { - case .p25: - return .superTie - case .p100: - return .nineGamesDecisivePoint - case .p250: - if roundIndex == 0 { //finale - return .twoSetsDecisivePointSuperTie - } else { - return .nineGamesDecisivePoint - } - - case .p500: - if roundIndex == 0 { //finale - return .twoSetsDecisivePointSuperTie - } else if roundIndex == 1 { //demi-finale - return .twoSetsDecisivePointSuperTie - } else { - return .nineGamesDecisivePoint - } - case .p1000: - if roundIndex <= 3 { //demi / finale / quart / 8eme - return .twoSetsDecisivePoint - } else { - return .twoSetsDecisivePointSuperTie - } - case .p1500, .p2000: - if roundIndex <= 3 { //demi / finale / quart / 8eme - return .twoSetsDecisivePoint - } else { - return .twoSetsSuperTie - } - default: - return .superTie - } - } - - func decisivePointRequired(ageCategory: FederalTournamentAge) -> Bool { - switch ageCategory { - case .a11_12, .a13_14, .a15_16, .a17_18: - return true - default: - return false - } - } - - func federalFormatForLoserBracketRound(_ roundIndex: Int) -> MatchFormat { - switch self { - case .p25: - return .superTie - case .p100, .p250, .p500: - return .nineGamesDecisivePoint - case .p1000: - return .nineGamesDecisivePoint - case .p1500, .p2000: - return .twoSetsSuperTie - default: - return .nineGamesDecisivePoint - } - } - - - var defaultTeamSortingType: TeamSortingType { - switch self { - case .championship: - return .inscriptionDate - case .unlisted: - return .inscriptionDate - case .p25, .p100, .p250: - return .inscriptionDate - default: - return .rank - } - } - - var order: Int { - switch self { - case .unlisted: - return 7 - case .p25: - return 6 - case .p100: - return 5 - case .p250: - return 4 - case .p500: - return 3 - case .p1000: - return 2 - case .p1500: - return 1 - case .p2000: - return 0 - case .championship: - return 8 - } - } - - func localizedLevelLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if self == .unlisted { - if DeviceHelper.isBigScreen() { - return "Animation" - } else { - return displayStyle == .title ? "Animation" : "Anim." - } - } - if self == .championship { - if DeviceHelper.isBigScreen() { - return "Championnat" - } else { - return displayStyle == .title ? "Championnat" : "CHPT" - } - } - - return String(describing: self).capitalized - } - - var coachingIsAuthorized: Bool { - switch self { - case .p500, .p1000, .p1500, .p2000, .championship: - return true - default: - return false - } - } - - func points(for rank: Int, count: Int) -> Int { - if self == .unlisted { return 0 } - if self == .championship { return 0 } - let points = points(for: count) - if rank >= points.count { - return points.last! - } else if rank == 0 { - return Int(self.rawValue) - } else { - return points[rank-1] - } - } - - func allPoints(for count: Int) -> [Int] { - [Int(self.rawValue)] + points(for: count) - } - - func points(for count: Int) -> [Int] { - switch self { - case .unlisted, .championship: - return [] - case .p25: - switch count { - case 9...12: - return [17, 15, 13, 11, 9, 7, 5, 4, 3, 2, 1] - case 13...16: - return [18,16,15,14,13,12,11,10,9,7,5,4,3,2, 1] - case 17...20: - return [20,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1] - case 21...24: - return [20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1] - case 25...28: - return [21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1] - case _ where count > 28: - return [23,21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1] - default: - return [15, 12, 9, 6, 4, 2, 1] - } - case .p100: - switch count { - case 9...12: - return [65,55, 50, 35, 25, 20, 15, 10, 5, 3, 1] - case 13...16: - return [70, 60, 55, 45, 40, 35, 30, 25, 21, 18, 15, 10, 5, 3, 1] - case 17...20: - return [75,65,60,55,50,45,40,35,30,25,23,20,18,15,12,10,5,3, 1] - case 21...24: - return [75,70,65,60,55,50,47,43,40,37,33,30,28,25,23,20,18,15,12,10,5,3, 1] - case 25...28: - return [80,75,70,65,60,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,5,3, 1] - case _ where count > 28: - return [80,75,72,70,65,63,60,58,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,8,5,3, 1] - default: - return [60,50,40,25,10,5] - } - case .p250: - switch count { - case 9...12: - return [163,138,125,88,63,50,38,25,13,8,3] - case 13...16: - return [175,150,138,113,100,88,75,63,53,45,38,25,13,8,3] - case 17...20: - return [188,163,150,138,123,113,100,88,75,63,58,50,45,38,30,25,13,8,3] - case 21...24: - return [188,175,163,150,138,125,118,108,100,93,83,75,70,63,58,50,45,38,30,25,13,8,3] - case 25...28: - return [200,188,175,163,150,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,13,8,3] - case _ where count > 28: - return [200,188,180,175,163,158,150,145,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,20,13,8,3] - default: - return [150,125,100,63,25,13,3] - } - case .p500: - switch count { - case 9...12: - return [325,275,250,175,125,100,75,50,25,15,5] - case 13...16: - return [350,300,275,225,200,175,150,125,105,90,75,50,25,15,5] - case 17...20: - return [375,325,300,275,250,225,200,175,150,125,115,100,90,75,60,50,25,15,5] - case 21...24: - return [375,350,325,300,275,250,235,215,200,185,165,150,140,125,115,100,80,75,60,50,25,15,5] - case 25...28: - return [400,375,350,325,300,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,25,15,5] - case _ where count > 28: - return [400,375,360,350,325,315,300,290,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,40,25,15,5] - default: - return [300,250,200,125,50,25,5] - } - case .p1000: - switch count { - case 9...12: - return [650,550, 500, 350, 250, 200, 150, 100, 50, 30, 10] - case 13...16: - return [700, 600, 550, 450, 400, 350, 300, 250, 210, 180, 150, 100, 50, 30, 10] - case 17...20: - return [750,650,600,550,500,450,400,350,300,250,230,200,180,150,120,100,50,30, 10] - case 21...24: - return [750,700,650,600,550,500,470,430,400,370,330,300,280,250,230,200,180,150,120,100,50,30, 10] - case 25...28: - return [800,750,700,650,600,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,50,30, 10] - case _ where count > 28: - return [800,750,720,700,650,630,600,580,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,80,50,30, 10] - default: - return [600,500,400,250,100,50] - } - case .p1500: - switch count { - case 21...24: - return [1125,1050,975,900,825,750,705,645,600,555,495,450,420,375,345,300,270,225,180,150,75,45,15] - case 25...28: - return [1200,1125,1050,975,900,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,75,45,15] - case _ where count > 28: - return [1200,1125,1080,1050,975,945,900,870,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,120,75,45,15] - default: - return [1125,975,900,825,750,675,600,525,450,375,345,300,270,225,180,150,75,45,15] - } - case .p2000: - return [1600,1440,1300,1180,1120,1060,1000,880,840,800,760,720,680,640,600,540,520,500,480,460,440,420,400,260,200,40] - } - } - - var ranges: [PlayersCountRange] { - switch self { - case .p1500: - return [.N16, .N24, .N28, .N32] - case .p2000: - return [.N32] - default: - return PlayersCountRange.allCases - } - } - -// enum NewBallSystem { -// case perField -// case perMatch(fromRound: Int?) -// -// func localizedLabel(loserBracket: Bool = false) -> String { -// switch self { -// case .perField: -// return "3 / piste" -// case .perMatch(let fromRound): -// if fromRound != nil { -// if loserBracket { -// return "3 / match pour les perdants des \(RoundLabel.shortLabels[fromRound!].lowercased())s" -// } else { -// return "3 / match à partir des \(RoundLabel.shortLabels[fromRound!].lowercased())s" -// } -// } else { -// return "3 / match" -// } -// } -// } -// } -// - func minimumFormatFinalTableAndQualifier(roundIndex: Int) -> MatchFormat? { - switch self { - case .p25, .unlisted, .championship: - return nil - case .p100: - return .nineGamesDecisivePoint - case .p250: - if roundIndex == 0 { //final - return .twoSetsDecisivePointSuperTie - } else { - return .nineGamesDecisivePoint - } - case .p500: - if roundIndex == 0 { //final - return .twoSetsDecisivePoint - } else if roundIndex == 1 { //demi - return .twoSetsDecisivePointSuperTie - } else { - return .nineGamesDecisivePoint - } - case .p1000, .p1500, .p2000: - if roundIndex == 3 { //16eme - return .twoSetsDecisivePoint - } else { - return .twoSetsDecisivePointSuperTie - } - } - } - - func minimumFormatLoserBracket(roundIndex: Int) -> MatchFormat? { - switch self { - case .p25, .unlisted, .championship: - return nil - case .p100, .p250, .p500: - return .nineGamesDecisivePoint - case .p1000, .p1500, .p2000: - if roundIndex == 1 { //demi - return .twoSetsDecisivePoint - } else { - return .nineGamesDecisivePoint - } - } - } - - -// func newBallsFinalTable() -> NewBallSystem? { -// switch self { -// case .p25, .p100: -// return .perField -// case .p250: -// return .perMatch(fromRound: 1) //demi -// case .p500: -// return .perMatch(fromRound: 2) //quart -// case .p1000, .p1500, .p2000: -// return .perMatch(fromRound: nil) -// } -// } -// -// func newBallsLoserBracket() -> NewBallSystem? { -// switch self { -// case .p25, .p100: -// return nil -// case .p250: -// return .perMatch(fromRound: 1) //demi -// case .p500, .p1000, .p1500, .p2000: -// return .perMatch(fromRound: 2) //quart -// } -// } -// -} - -enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable { - case men - case women - case mix - case unlisted - - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } - - func mandatoryPlayerType() -> [Int] { - switch self { - case .unlisted: - return [] - case .mix: - return [0, 1] - case .women: - return [0, 0] - case .men: - return [1, 1] - } - } - - var localizedPlayerLabel: String { - switch self { - case .women: - return "joueuse" - default: - return "joueur" - } - } - - var showFemaleInMaleAssimilation: Bool { - switch self { - case .men: - return true - default: - return false - } - } - - static func femaleInMaleAssimilationAddition(_ rank: Int) -> Int { - switch rank { - case 1...10: return 400 - case 11...30: return 1000 - case 31...60: return 2000 - case 61...100: return 3500 - case 101...200: return 10000 - case 201...500: return 15000 - case 501...1000: return 25000 - case 1001...2000: return 35000 - case 2001...3000: return 45000 - default: - return 50000 - } - } - - static func mostRecent(inTournaments tournaments: [Tournament]) -> Self { - return tournaments.first?.tournamentCategory ?? .men - } - - static func mostUsed(inTournaments tournaments: [Tournament]) -> Self { - let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentCategory }) - let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) } - if mostFrequent != nil { - return mostFrequent as! TournamentCategory - } else { - return mostRecent(inTournaments: tournaments) - } - } - - var id: Int { self.rawValue } - - var order: Int { - switch self { - case .unlisted: - return 0 - case .men: - return 1 - case .women: - return 2 - case .mix: - return 3 - } - } - - var buildLabel: String { - switch self { - case .unlisted: - return "" - case .men: - return "H" - case .women: - return "D" - case .mix: - return "M" - } - } - - var requestLabel: String { - switch self { - case .unlisted: - return "" - case .men: - return "DM" - case .women: - return "DD" - case .mix: - return "DX" - } - } - - var importingRawValue: String { - switch self { - case .unlisted: - return "messieurs" - case .men: - return "messieurs" - case .women: - return "dames" - case .mix: - return "mixte" - } - } - - func localizedCategoryLabel(_ displayStyle: DisplayStyle = .wide, ageCategory: FederalTournamentAge? = nil) -> String { - switch self { - case .unlisted: - return displayStyle == .title ? "Aucune" : "" - case .men: - switch displayStyle { - case .title: - if ageCategory?.isChildCategory() == true { - return "Garçons" - } - return "Hommes" - case .wide: - if ageCategory?.isChildCategory() == true { - return "Garçons" - } - return "Hommes" - case .short: - if ageCategory?.isChildCategory() == true { - return "G" - } - - return "H" - } - case .women: - switch displayStyle { - case .title: - if ageCategory?.isChildCategory() == true { - return "Filles" - } - return "Dames" - case .wide: - if ageCategory?.isChildCategory() == true { - return "Filles" - } - return "Dames" - case .short: - if ageCategory?.isChildCategory() == true { - return "F" - } - - return "D" - } - case .mix: - switch displayStyle { - case .title: - return "Mixte" - case .wide: - return "Mixte" - case .short: - return "MX" - } - } - } - - var playerFilterOption: PlayerFilterOption { - switch self { - case .men, .unlisted: - return .all - case .women: - return .female - case .mix: - return .all - } - } -} - - -enum GroupStageOrderingMode: Int, Hashable, Codable, CaseIterable, Identifiable { - case random - case snake - case swiss - - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } - - var id: Int { self.rawValue } - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .random: - return "Au hasard" - case .snake: - return "En serpentin" - case .swiss: - return "Suisse" - } - } - - var systemImage: String { - switch self { - case .random: - return "dice.fill" - case .snake: - return "arrow.triangle.swap" - case .swiss: - return "cross.fill" - } - } -} - -enum TournamentType: Int, Hashable, Codable, CaseIterable, Identifiable { - case classic - case doubleBrackets - - var id: Int { self.rawValue } - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .classic: - return "Classique" - case .doubleBrackets: - return "Double Poules" - } - } -} - -enum TeamPosition: Int, Identifiable, Hashable, Codable, CaseIterable { - case one - case two - - var id: Int { self.rawValue } - - var otherTeam: TeamPosition { - switch self { - case .one: - return .two - case .two: - return .one - } - } - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - var shortName: String { - switch self { - case .one: - return "#1" - case .two: - return "#2" - } - } - - switch displayStyle { - case .wide, .title: - return "Équipe " + shortName - case .short: - return shortName - } - } - - func localizedBranchLabel() -> String { - switch self { - case .one: - return "Branche du haut" - case .two: - return "Branche du bas" - } - } -} - -enum SetFormat: Int, Hashable, Codable { - case nine - case four - case six - case superTieBreak - case megaTieBreak - - func shouldTiebreak(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool { - if let tieBreak { - return (scoreTeamOne + scoreTeamTwo) >= (2 * tieBreak) && (scoreTeamOne == tieBreak || scoreTeamTwo == tieBreak) - } else { - return false - } - } - - func winner(teamOne: Int, teamTwo: Int) -> TeamPosition { - return teamOne > teamTwo ? .one : .two - } - - func hasEnded(teamOne: Int, teamTwo: Int) -> Bool { - switch self { - case .nine: - if teamOne == 9 || teamTwo == 9 { - return true - } - case .four: - if teamOne == 5 || teamTwo == 5 { - return true - } - case .six: - if teamOne == 7 || teamTwo == 7 { - return true - } - case .superTieBreak, .megaTieBreak: - if teamOne == 1 || teamTwo == 1 { - return true - } - } - return (teamOne >= scoreToWin && teamOne >= teamTwo + 2) || (teamTwo >= scoreToWin && teamTwo >= teamOne + 2) - } - - var tieBreak: Int? { - switch self { - case .nine: - return 8 - case .four: - return 4 - case .six: - return 6 - case .superTieBreak, .megaTieBreak: - return nil - } - } - - func disableValuesForTeamTwo(with teamOneScore: Int) -> [Int] { - switch self { - case .nine: - if teamOneScore == 9 { - return [9] - } - case .four: - if teamOneScore == 4 { - return [] - } - case .six: - if teamOneScore == 6 { - return [] - } - case .superTieBreak: - if teamOneScore == 10 { - return [] - } - case .megaTieBreak: - if teamOneScore == 15 { - return [] - } - } - return [] - } - - var possibleValues: [Int] { - switch self { - case .nine: - return [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - case .four: - return [5, 4, 3, 2, 1, 0] - case .six: - return [7, 6, 5, 4, 3, 2, 1, 0] - case .superTieBreak: - return [10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - case .megaTieBreak: - return [15, 14, 13, 12, 11, 10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - } - } - - var scoreToWin: Int { - switch self { - case .nine: - return 9 - case .four: - return 4 - case .six: - return 6 - case .superTieBreak: - return 10 - case .megaTieBreak: - return 15 - } - } - - var firstGameFormat: Format { - switch self { - case .megaTieBreak: - return .tiebreakFifteen - case .superTieBreak: - return .tiebreakTen - default: - return .normal - } - } -} - -enum MatchType: String { - case bracket = "bracket" - case groupStage = "groupStage" - case loserBracket = "loserBracket" -} - -enum MatchFormat: Int, Hashable, Codable, CaseIterable, Identifiable { - var id: Int { self.rawValue } - case twoSets - case twoSetsSuperTie - case twoSetsOfFourGames - case nineGames - case superTie - case megaTie - - case twoSetsDecisivePoint - case twoSetsDecisivePointSuperTie - case twoSetsOfFourGamesDecisivePoint - case nineGamesDecisivePoint - - case twoSetsOfSuperTie - case singleSet - case singleSetDecisivePoint - case singleSetOfFourGames - case singleSetOfFourGamesDecisivePoint - - - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } - - func defaultWalkOutScore(_ asWalkOutTeam: Bool) -> [Int] { - Array(repeating: asWalkOutTeam ? 0 : setFormat.scoreToWin, count: setsToWin) - } - - var weight: Int { - switch self { - case .twoSets, .twoSetsDecisivePoint: - return 0 - case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie: - return 1 - case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint: - return 2 - case .nineGames, .nineGamesDecisivePoint: - return 3 - case .superTie: - return 4 - case .megaTie: - return 5 - case .twoSetsOfSuperTie: - return 6 - case .singleSet, .singleSetDecisivePoint: - return 7 - case .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint: - return 8 - } - } - - var rank: Int { - switch self { - case .twoSets: - return 0 - case .twoSetsDecisivePoint: - return 0 - case .twoSetsSuperTie: - return 1 - case .twoSetsDecisivePointSuperTie: - return 1 - case .twoSetsOfFourGames: - return 2 - case .twoSetsOfFourGamesDecisivePoint: - return 2 - case .nineGames: - return 3 - case .nineGamesDecisivePoint: - return 3 - case .superTie: - return 4 - case .megaTie: - return 5 - case .twoSetsOfSuperTie: - return 6 - case .singleSet, .singleSetDecisivePoint: - return 7 - case .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint: - return 8 - } - } - - static func defaultFormatForMatchType(_ matchType: MatchType) -> MatchFormat { - switch matchType { - case .bracket: - DataStore.shared.user.bracketMatchFormatPreference ?? .nineGamesDecisivePoint - case .groupStage: - DataStore.shared.user.groupStageMatchFormatPreference ?? .nineGamesDecisivePoint - case .loserBracket: - DataStore.shared.user.loserBracketMatchFormatPreference ?? .nineGamesDecisivePoint - } - } - - static var allCases: [MatchFormat] = { - [.twoSets, .twoSetsDecisivePoint, .twoSetsSuperTie, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie, .twoSetsOfSuperTie, .singleSet, .singleSetDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint] - }() - - func winner(scoreTeamOne: Int, scoreTeamTwo: Int) -> TeamPosition { - scoreTeamOne >= scoreTeamTwo ? .one : .two - } - - func hasEnded(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool { - scoreTeamOne == setsToWin || scoreTeamTwo == setsToWin - } - - var canSuperTie: Bool { - switch self { - case .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint: - return true - default: - return false - } - } - - func getEstimatedDuration(_ additionalDuration: Int = 0) -> Int { - estimatedDuration + additionalDuration - } - - private var estimatedDuration: Int { - DataStore.shared.user.matchFormatsDefaultDuration?[self] ?? defaultEstimatedDuration - } - - func formattedEstimatedDuration(_ additionalDuration: Int = 0) -> String { - Duration.seconds((estimatedDuration + additionalDuration) * 60).formatted(.units(allowed: [.minutes])) - } - - func formattedEstimatedBreakDuration() -> String { - var label = Duration.seconds(breakTime.breakTime * 60).formatted(.units(allowed: [.minutes])) - if breakTime.matchCount > 1 { - label += " de pause après \(breakTime.matchCount) match" - label += breakTime.matchCount.pluralSuffix - } else { - label += " de pause" - } - return label - } - - var defaultEstimatedDuration: Int { - switch self { - case .twoSets: - return 105 - case .twoSetsDecisivePoint: - return 90 - case .twoSetsSuperTie: - return 80 - case .twoSetsDecisivePointSuperTie: - return 70 - case .twoSetsOfFourGames: - return 60 - case .twoSetsOfFourGamesDecisivePoint: - return 50 - case .nineGames: - return 45 - case .nineGamesDecisivePoint: - return 40 - case .megaTie: - return 20 - case .superTie: - return 15 - case .twoSetsOfSuperTie: - return 25 - case .singleSet: - return 30 - case .singleSetDecisivePoint: - return 25 - case .singleSetOfFourGames: - return 15 - case .singleSetOfFourGamesDecisivePoint: - return 10 - } - } - - - var estimatedTimeWithBreak: Int { - estimatedDuration + breakTime.breakTime - } - - var breakTime: (breakTime: Int, matchCount: Int) { - switch self { - case .twoSets, .twoSetsDecisivePoint: - return (90, 1) - case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie: - return (60, 1) - case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint: - return (30, 1) - case .superTie: - return (15, 3) - default: - return (5, 1) - } - } - - func maximumMatchPerDay(for matchCount: Int) -> Int { - switch self { - case .twoSets, .twoSetsDecisivePoint: - return matchCount < 5 ? 2 : 0 - case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie: - return matchCount < 6 ? 3 : 0 - case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint: - return matchCount < 7 ? 6 : 2 - case .superTie: - return 7 - default: - return 10 - } - } - - var hasDecisivePoint: Bool { - switch self { - case .nineGamesDecisivePoint, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie, .singleSetDecisivePoint, .singleSetOfFourGamesDecisivePoint: - return true - default: - return false - } - } - - func newSetFormat(setCount: Int) -> SetFormat { - if setCount == 2 && canSuperTie { - return .superTieBreak - } - return setFormat - } - - func formatTitle(_ displayStyle: DisplayStyle = .wide) -> String { - switch displayStyle { - case .short: - return ["Format ", shortFormat].joined() - default: - return ["Format ", shortFormat, suffix].joined() - } - } - - var suffix: String { - switch self { - case .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGamesDecisivePoint, .nineGamesDecisivePoint, .singleSetDecisivePoint: - return " [Point Décisif]" - default: - return "" - } - } - - var longPrefix: String { - return "Format \(format) : " - } - - var shortPrefix: String { - return "\(format) : " - } - - var isFederal: Bool { - switch self { - case .megaTie, .twoSetsOfSuperTie, .singleSet, .singleSetDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint: - return false - default: - return true - } - } - - var format: String { - shortFormat + (isFederal ? "" : " (non officiel)") - } - var shortFormat: String { - switch self { - case .twoSets: - return "A1" - case .twoSetsSuperTie: - return "B1" - case .twoSetsOfFourGames: - return "C1" - case .nineGames: - return "D1" - case .superTie: - return "E" - case .twoSetsOfSuperTie: - return "G" - case .megaTie: - return "F" - case .singleSet: - return "H1" - case .singleSetDecisivePoint: - return "H2" - case .twoSetsDecisivePoint: - return "A2" - case .twoSetsDecisivePointSuperTie: - return "B2" - case .twoSetsOfFourGamesDecisivePoint: - return "C2" - case .nineGamesDecisivePoint: - return "D2" - case .singleSetOfFourGames: - return "I1" - case .singleSetOfFourGamesDecisivePoint: - return "I2" - } - } - - var longLabel: String { - switch self { - case .singleSet, .singleSetDecisivePoint: - return "1 set de 6" - case .twoSets, .twoSetsDecisivePoint: - return "2 sets de 6" - case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie: - return "2 sets de 6, supertie au 3ème" - case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint: - return "2 sets de 4, tiebreak à 4/4, supertie au 3ème" - case .nineGames, .nineGamesDecisivePoint: - return "9 jeux, tiebreak à 8/8" - case .twoSetsOfSuperTie: - return "2 sets de supertie de 10 points" - case .superTie: - return "supertie de 10 points" - case .megaTie: - return "supertie de 15 points" - case .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint: - return "1 set de 4 jeux, tiebreak à 4/4" - } - } - - var computedShortLabelWithoutPrefix: String { - longLabel + suffix - } - - var computedShortLabel: String { - shortPrefix + longLabel + suffix - } - - var computedLongLabel: String { - longPrefix + longLabel + suffix - } - - var setsToWin: Int { - switch self { - case .twoSets, .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie, .twoSetsOfSuperTie: - return 2 - case .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie, .singleSet, .singleSetDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint: - return 1 - } - } - - var setFormat: SetFormat { - switch self { - case .twoSets, .twoSetsSuperTie, .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie, .singleSet, .singleSetDecisivePoint: - return .six - case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .singleSetOfFourGames, .singleSetOfFourGamesDecisivePoint: - return .four - case .nineGames, .nineGamesDecisivePoint: - return .nine - case .superTie, .twoSetsOfSuperTie: - return .superTieBreak - case .megaTie: - return .megaTieBreak - } - } -} - -enum Format: Int, Hashable, Codable { - case normal - case tiebreakSeven - case tiebreakTen - case tiebreakFifteen - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .normal: - return "normal" - case .tiebreakSeven: - return "tie-break en 7" - case .tiebreakTen: - return "tie-break en 10" - case .tiebreakFifteen: - return "tie-break en 15" - } - } - var isTiebreak: Bool { - switch self { - case .normal: - return false - case .tiebreakSeven, .tiebreakTen, .tiebreakFifteen: - return true - } - } - - var scoreToWin: Int? { - switch self { - case .normal: - return nil - case .tiebreakSeven: - return 7 - case .tiebreakTen: - return 10 - case .tiebreakFifteen: - return 15 - } - } -} - -enum ActionType: Int, Identifiable { - case fault - case winner - - var id: Self { - self - } - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .fault: - return "Faute" - case .winner: - return "Point" - } - } - /* - Break points won - Break points - Errors - Smash winners - Smashes - Winners - Volley winners - - Total points won - % points won - Total break points - Break points won - Gold points won - Gold points won returning - Gold points won with service - Most consecutive points won - Total set points - Set points won - Match points - */ -} - -enum EventType: Int, CaseIterable, Identifiable { - case approvedTournament - case friendlyTournament - case simulation - case animation - - var id: Self { - self - } - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .approvedTournament: - return "Tournoi homologué" - case .friendlyTournament: - return "Tournoi amical" - case .simulation: - return "Simulation" - case .animation: - return "Animation" - } - } -} - -enum TeamSortingType: Int, Identifiable, CaseIterable, Hashable, Codable { - case rank = 1 - case inscriptionDate = 2 - - var id: Int { rawValue } - - init?(rawValue: Int?) { - guard let value = rawValue else { return nil } - self.init(rawValue: value) - } - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .rank: - return "Rang" - case .inscriptionDate: - return "Date d'inscription" - } - } -} - -enum PlayersCountRange: Int, CaseIterable { - case N8 = 8 - case N12 = 12 - case N16 = 16 - case N20 = 20 - case N24 = 24 - case N28 = 28 - case N32 = 32 - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .N8: - return "4 à 8" - case .N12: - return "9 à 12" - case .N16: - return "13 à 16" - case .N20: - return "17 à 20" - case .N24: - return "21 à 24" - case .N28: - return "25 à 28" - case .N32: - return "29 à 32" - } - } -} - -enum RoundRule { - static let colors = ["#99ff99", "#66ff66", "#33cc33", "#009900", "#006600", "#336633", "#DD6600", "#EE6633", "#EE6633", "#EE6633"] - - static func loserBrackets(index: Int) -> [String] { - switch index { - case 1: - return ["#3/#4"] - case 2: - return ["#5/#6", "#7/#8"] - default: - return ["#9/#10", "#11/#12", "#13/#14", "#15/#16", "#17/#18", "#19/#20", "#21/#22", "#23/#24", "#25/#26", "#27/#28", "#29/#30", "#31/#32"] - } - } - - static func teamsInFirstRound(forTeams teams: Int) -> Int { - Int(pow(2.0, ceil(log2(Double(teams))))) - } - - static func numberOfMatches(forTeams teams: Int) -> Int { - teamsInFirstRound(forTeams: teams) - 1 - } - - static func numberOfRounds(forTeams teams: Int) -> Int { - if teams == 0 { return 0 } - return Int(log2(Double(teamsInFirstRound(forTeams: teams)))) - } - - static func matchIndex(fromRoundIndex roundIndex: Int) -> Int { - guard roundIndex >= 0 else { - return -1 // Invalid round index - } - - return (1 << roundIndex) - 1 - } - - static func matchIndex(fromBracketPosition: Int) -> Int { - roundIndex(fromMatchIndex: fromBracketPosition / 2) + fromBracketPosition%2 - } - - static func roundIndex(fromMatchIndex matchIndex: Int) -> Int { - Int(log2(Double(matchIndex + 1))) - } - - static func numberOfMatches(forRoundIndex roundIndex: Int) -> Int { - Int(pow(2.0, Double(roundIndex))) - } - - static func matchIndexWithinRound(fromMatchIndex matchIndex: Int) -> Int { - let roundIndex = roundIndex(fromMatchIndex: matchIndex) - let matchIndexWithinRound = matchIndex - (Int(pow(2.0, Double(roundIndex))) - 1) - return matchIndexWithinRound - } - - static func roundName(fromMatchIndex matchIndex: Int) -> String { - let roundIndex = roundIndex(fromMatchIndex: matchIndex) - return roundName(fromRoundIndex: roundIndex) - } - - static func roundName(fromRoundIndex roundIndex: Int, displayStyle: DisplayStyle = .wide) -> String { - switch roundIndex { - case 0: - return "Finale" - case 1: - if displayStyle == .short { - return "Demi" - } - return "Demi-finale" - case 2: - if displayStyle == .short { - return "Quart" - } - return "Quart de finale" - default: - return "\(Int(pow(2.0, Double(roundIndex))))ème" - } - } -} - -enum AnimationType: Int, CaseIterable, Hashable, Identifiable { - case playerAnimation - case upAndDown - case brawl - - var id: Int { rawValue } - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .playerAnimation: - return "Par joueur" - case .upAndDown: - return "Montante / Descendante" - case .brawl: - return "Brawl" - } - } - - var descriptionLabel: String { - switch self { - case .playerAnimation: - return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)" - case .upAndDown: - return "Les gagnants montent sur le terrain d'à côté, les perdants descendent" - case .brawl: - return "A chaque rotation, les gagnants de la rotation précédente se jouent entre eux" - } - } -} - -enum PadelTournamentStructurePreset: Int, Identifiable, CaseIterable { - var id: Int { self.rawValue } - - case manual - case doubleGroupStage - case federalStructure_8 - case federalStructure_12 - case federalStructure_16 - case federalStructure_20 - case federalStructure_24 - case federalStructure_32 - case federalStructure_48 - case federalStructure_64 - - // Maximum qualified pairs based on the structure preset - func tableDimension() -> Int { - switch self { - case .federalStructure_8: - return 8 - case .federalStructure_12: - return 12 - case .federalStructure_16: - return 16 - case .federalStructure_20: - return 20 - case .federalStructure_24: - return 24 - case .federalStructure_32: - return 32 - case .federalStructure_48: - return 48 - case .federalStructure_64: - return 64 - case .manual: - return 24 - case .doubleGroupStage: - return 9 - } - } - - // Wildcards allowed in the Qualifiers - func wildcardBrackets() -> Int { - switch self { - case .federalStructure_8: - return 0 - case .federalStructure_12: - return 1 - case .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32: - return 2 - case .federalStructure_48, .federalStructure_64: - return 4 - case .manual, .doubleGroupStage: - return 0 - } - } - // Wildcards allowed in the Qualifiers - func wildcardQualifiers() -> Int { - switch self { - case .federalStructure_8: - return 0 - case .federalStructure_12, .federalStructure_16: - return 1 - case .federalStructure_20, .federalStructure_24: - return 2 - case .federalStructure_32: - return 4 - case .federalStructure_48: - return 6 - case .federalStructure_64: - return 8 - case .manual, .doubleGroupStage: - return 0 - } - } - - // Number of teams admitted to the Qualifiers - func teamsInQualifiers() -> Int { - switch self { - case .federalStructure_8: - return 8 - case .federalStructure_12: - return 12 - case .federalStructure_16: - return 16 - case .federalStructure_20: - return 20 - case .federalStructure_24: - return 24 - case .federalStructure_32: - return 32 - case .federalStructure_48: - return 48 - case .federalStructure_64: - return 64 - case .manual, .doubleGroupStage: - return 0 - } - } - - // Maximum teams that can qualify from the Qualifiers to the Final Table - func maxTeamsFromQualifiers() -> Int { - switch self { - case .federalStructure_8, .federalStructure_12: - return 2 - case .federalStructure_16, .federalStructure_20, .federalStructure_24: - return 4 - case .federalStructure_32, .federalStructure_48, .federalStructure_64: - return 8 - case .manual, .doubleGroupStage: - return 0 - } - } - - func localizedStructurePresetTitle() -> String { - switch self { - case .manual: - return "Défaut" - case .doubleGroupStage: - return "2 phases de poules" - case .federalStructure_8: - return "Structure fédérale 8" - case .federalStructure_12: - return "Structure fédérale 12" - case .federalStructure_16: - return "Structure fédérale 16" - case .federalStructure_20: - return "Structure fédérale 20" - case .federalStructure_24: - return "Structure fédérale 24" - case .federalStructure_32: - return "Structure fédérale 32" - case .federalStructure_48: - return "Structure fédérale 48" - case .federalStructure_64: - return "Structure fédérale 64" - } - } - - func localizedDescriptionStructurePresetTitle() -> String { - switch self { - case .manual: - return "24 équipes, 4 poules de 4, 1 qualifié par poule" - case .doubleGroupStage: - return "Poules qui enchaînent sur une autre phase de poules: les premiers de chaque se retrouvent ensemble, puis les deuxièmes, etc." - case .federalStructure_8: - return "Tableau final à 8 paires, dont 2 qualifiées sortant de qualifications à 8 paires maximum. Aucune wildcard." - case .federalStructure_12, .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32, .federalStructure_48, .federalStructure_64: - return "Tableau final à \(tableDimension()) paires, dont \(maxTeamsFromQualifiers()) qualifiées sortant de qualifications à \(teamsInQualifiers()) paires maximum. \(wildcardBrackets()) wildcard\(wildcardBrackets().pluralSuffix) en tableau et \(wildcardQualifiers()) wildcard\(wildcardQualifiers().pluralSuffix) en qualifications." - } - } - - func groupStageCount() -> Int { - switch self { - case .manual: - 4 - case .doubleGroupStage: - 3 - case .federalStructure_8: - 2 - case .federalStructure_12: - 2 - case .federalStructure_16: - 4 - case .federalStructure_20: - 4 - case .federalStructure_24: - 4 - case .federalStructure_32: - 8 - case .federalStructure_48: - 8 - case .federalStructure_64: - 8 - } - } - - func teamsPerGroupStage() -> Int { - switch self { - case .manual: - 4 - case .doubleGroupStage: - 3 - case .federalStructure_8: - 4 - case .federalStructure_12: - 6 - case .federalStructure_16: - 4 - case .federalStructure_20: - 5 - case .federalStructure_24: - 6 - case .federalStructure_32: - 4 - case .federalStructure_48: - 6 - case .federalStructure_64: - 8 - } - } - - func qualifiedPerGroupStage() -> Int { - switch self { - case .doubleGroupStage: - 0 - default: - 1 - } - } - - func hasWildcards() -> Bool { - wildcardBrackets() > 0 || wildcardQualifiers() > 0 - } - - func isFederalPreset() -> Bool { - switch self { - case .manual: - return false - case .doubleGroupStage: - return false - case .federalStructure_8, .federalStructure_12, .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32, .federalStructure_48, .federalStructure_64: - return true - } - } -} - -enum TournamentDeadlineType: String, CaseIterable { - case inscription = "Inscription" - case broadcastList = "Publication de la liste" - case wildcardRequest = "Demande de WC" - case wildcardLicensePurchase = "Prise de licence des WC" - case definitiveBroadcastList = "Publication définitive" - - func daysOffset(level: TournamentLevel) -> Int { - if level == .p500 { - switch self { - case .inscription: - return -6 - case .broadcastList: - return -6 - case .wildcardRequest: - return -4 - case .wildcardLicensePurchase, .definitiveBroadcastList: - return -4 - } - } else { - switch self { - case .inscription: - return -13 - case .broadcastList: - return -12 - case .wildcardRequest: - return -9 - case .wildcardLicensePurchase, .definitiveBroadcastList: - return -8 - } - - } - } - - var timeOffset: DateComponents { - switch self { - case .broadcastList, .definitiveBroadcastList: - return DateComponents(hour: 12) - case .inscription, .wildcardRequest, .wildcardLicensePurchase: - return DateComponents(minute: -1) - } - } -} diff --git a/PadelClub/Utils/Patcher.swift b/PadelClub/Utils/Patcher.swift deleted file mode 100644 index f5812cf..0000000 --- a/PadelClub/Utils/Patcher.swift +++ /dev/null @@ -1,143 +0,0 @@ -// -// Patcher.swift -// PadelClub -// -// Created by Laurent Morvillier on 21/06/2024. -// - -import Foundation -import LeStorage - -enum ManualPatch: String { - case disconnect - - var id: String { - return "padelclub.app.manual.patch.\(self.rawValue)" - } -} - -class ManualPatcher { - - static func patchIfPossible(_ patch: ManualPatch) -> Bool { - if UserDefaults.standard.value(forKey: patch.id) == nil { - do { - Logger.log(">>> Patches \(patch.rawValue)...") - let result = try self._applyPatch(patch) - UserDefaults.standard.setValue(true, forKey: patch.id) - return result - } catch { - Logger.error(error) - } - } - return false - } - - fileprivate static func _applyPatch(_ patch: ManualPatch) throws -> Bool { - switch patch { - case .disconnect: - let rawToken = try? StoreCenter.main.rawTokenShouldNotBeUsed() - if StoreCenter.main.userName != nil || StoreCenter.main.userId != nil || rawToken != nil { - DataStore.shared.disconnect() - return true - } else { - return false - } - } - } -} - -enum PatchError: Error { - case patchError(message: String) -} - -enum Patch: String, CaseIterable { - case cleanLogs - case syncUpgrade - case updateTournaments - case cleanPurchaseApiCalls = "cleanPurchaseApiCalls_2" - - var id: String { - return "padelclub.app.patch.\(self.rawValue)" - } -} - -class AutomaticPatcher { - - static func applyAllWhenApplicable() { - for patch in Patch.allCases { - self.patchIfPossible(patch) - } - } - - static func patchIfPossible(_ patch: Patch) { - if UserDefaults.standard.value(forKey: patch.id) == nil { - do { - Logger.log(">>> Patches \(patch.rawValue)...") - try self._applyPatch(patch) - UserDefaults.standard.setValue(true, forKey: patch.id) - } catch { - Logger.error(error) - } - } - } - - fileprivate static func _applyPatch(_ patch: Patch) throws { - switch patch { - case .cleanLogs: self._cleanLogs() - case .syncUpgrade: self._syncUpgrade() - case .updateTournaments: self._updateTournaments() - case .cleanPurchaseApiCalls: self._cleanPurchaseApiCalls() - } - } - - fileprivate static func _cleanLogs() { - StoreCenter.main.resetLoggingCollections() - } - - fileprivate static func _syncUpgrade() { - for tournament in DataStore.shared.tournaments { - let id = tournament.id - - guard let store = TournamentLibrary.shared.store(tournamentId: tournament.id) else { continue } - - 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 _updateTournaments() { - DataStore.shared.tournaments.addOrUpdate(contentOfs: DataStore.shared.tournaments) - } - - fileprivate static func _cleanPurchaseApiCalls() { - StoreCenter.main.resetApiCalls(type: Purchase.self) - } - -} diff --git a/PadelClub/Utils/SourceFileManager.swift b/PadelClub/Utils/SourceFileManager.swift deleted file mode 100644 index 83ec4b7..0000000 --- a/PadelClub/Utils/SourceFileManager.swift +++ /dev/null @@ -1,270 +0,0 @@ -// -// SourceFileManager.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/03/2024. -// - -import Foundation -import LeStorage - -class SourceFileManager { - static let shared = SourceFileManager() - - init() { - createDirectoryIfNeeded(directoryURL: rankingSourceDirectory) -#if targetEnvironment(simulator) - createDirectoryIfNeeded(directoryURL: anonymousSourceDirectory) -#endif - } - - let rankingSourceDirectory : URL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appending(path: "rankings") - let anonymousSourceDirectory : URL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appending(path: "anonymous") - - func createDirectoryIfNeeded(directoryURL: URL) { - let fileManager = FileManager.default - do { - // Check if the directory exists - 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)") - } else { -// print("Directory already exists at: \(directoryURL)") - } - } catch { - print("Error: \(error)") - } - } - - var lastDataSource: String? { - DataStore.shared.appSettings.lastDataSource - } - - func lastDataSourceDate() -> Date? { - guard let lastDataSource else { return nil } - return URL.importDateFormatter.date(from: lastDataSource) - } - - func fetchData() async { - await fetchData(fromDate: Date()) -// if let mostRecent = mostRecentDateAvailable, let current = Calendar.current.date(byAdding: .month, value: 1, to: mostRecent), current > mostRecent { -// await fetchData(fromDate: current) -// } else { -// } - } - - func _removeAllData(fromDate current: Date) { - let lastStringDate = URL.importDateFormatter.string(from: current) - let files = ["MESSIEURS", "MESSIEURS-2", "MESSIEURS-3", "MESSIEURS-4", "DAMES"] - files.forEach { fileName in - NetworkManager.shared.removeRankingData(lastDateString: lastStringDate, fileName: fileName) - } - } - - func exportToCSV(_ prefix: String = "", players: [FederalPlayer], sourceFileType: SourceFile, date: Date) { - let lastDateString = URL.importDateFormatter.string(from: date) - let dateString = [prefix, "CLASSEMENT-PADEL", sourceFileType.rawValue, lastDateString].filter({ $0.isEmpty == false }).joined(separator: "-") + "." + "csv" - - let documentsUrl:URL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)! - let destinationFileUrl = documentsUrl.appendingPathComponent("\(dateString)") - var csvText : String = "" - for player in players { - csvText.append(player.exportToCSV() + "\n") - } - - do { - try csvText.write(to: destinationFileUrl, atomically: true, encoding: .utf8) - print("CSV file exported successfully.") - } catch { - print("Error writing CSV file:", error) - Logger.error(error) - } - } - - actor SourceFileDownloadTracker { - var _downloadedFileStatus : Int? = nil - - func updateIfNecessary(with successState: Int?) { - if successState != nil && (_downloadedFileStatus == nil || _downloadedFileStatus == 0) { - _downloadedFileStatus = successState - } - } - - func getDownloadedFileStatus() -> Int? { - return _downloadedFileStatus - } - - } - - //return nil if no new files - //return 1 if new file to import - //return 0 if new file just to re-calc static data, no need to re-import - @discardableResult - func fetchData(fromDate current: Date) async -> Int? { - let lastStringDate = URL.importDateFormatter.string(from: current) - - let files = ["MESSIEURS", "MESSIEURS-2", "MESSIEURS-3", "MESSIEURS-4", "DAMES"] - - let sourceFileDownloadTracker = SourceFileDownloadTracker() - - do { - try await withThrowingTaskGroup(of: Void.self) { group in // Mark 1 - - for file in files { - group.addTask { [sourceFileDownloadTracker] in - let success = try await NetworkManager.shared.downloadRankingData(lastDateString: lastStringDate, fileName: file) - await sourceFileDownloadTracker.updateIfNecessary(with: success) - } - } - - try await group.waitForAll() - } - -// if current < Date() { -// if let nextCurrent = Calendar.current.date(byAdding: .month, value: 1, to: current) { -// await fetchData(fromDate: nextCurrent) -// } -// } - } catch { - print("downloadRankingData", error) - - if mostRecentDateAvailable == nil { - if let previousDate = Calendar.current.date(byAdding: .month, value: -1, to: current) { - await fetchData(fromDate: previousDate) - } - } - } - let downloadedFileStatus = await sourceFileDownloadTracker.getDownloadedFileStatus() - - return downloadedFileStatus - } - - func getAllFiles(initialDate: String = "08-2022") async { - let dates = monthsBetweenDates(startDateString: initialDate, endDateString: Date().monthYearFormatted) - .compactMap { - URL.importDateFormatter.date(from: $0) - } - .filter { date in - allFiles.contains(where: { $0.dateFromPath == date }) == false - } - - try? await dates.concurrentForEach { date in - await self.fetchData(fromDate: date) - } - } - - func monthsBetweenDates(startDateString: String, endDateString: String) -> [String] { - let dateFormatter = URL.importDateFormatter - - guard let startDate = dateFormatter.date(from: startDateString), - let endDate = dateFormatter.date(from: endDateString) else { - return [] - } - - var months: [String] = [] - var currentDate = startDate - let calendar = Calendar.current - - while currentDate <= endDate { - let monthString = dateFormatter.string(from: currentDate) - months.append(monthString) - - guard let nextMonthDate = calendar.date(byAdding: .month, value: 1, to: currentDate) else { - break - } - currentDate = nextMonthDate - } - return months - } - - func getUnrankValue(forMale: Bool, rankSourceDate: Date?) -> Int? { - let _rankSourceDate = rankSourceDate ?? mostRecentDateAvailable - let urls = allFiles(forMale).filter { $0.dateFromPath == _rankSourceDate } - return urls.compactMap { $0.getUnrankedValue() }.sorted().last - } - - var mostRecentDateAvailable: Date? { - allFiles(false).first?.dateFromPath - } - - func removeAllFilesFromServer() { - let allFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil) - allFiles.filter { $0.pathExtension == "csv" }.forEach { url in - try? FileManager.default.removeItem(at: url) - } - } - - func anonymousFiles() -> [URL] { - let allJSONFiles = try! FileManager.default.contentsOfDirectory(at: anonymousSourceDirectory, includingPropertiesForKeys: nil).filter({ url in - url.pathExtension == "csv" - }) - return allJSONFiles - } - - func jsonFiles() -> [URL] { - let allJSONFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil).filter({ url in - url.pathExtension == "json" - }) - return allJSONFiles - } - - var allFiles: [URL] { - let allFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil).filter({ url in - url.pathExtension == "csv" - }) - - return (allFiles + (Bundle.main.urls(forResourcesWithExtension: "csv", subdirectory: nil) ?? [])).sorted { $0.dateFromPath == $1.dateFromPath ? $0.index < $1.index : $0.dateFromPath > $1.dateFromPath } - } - - func allFiles(_ isManPlayer: Bool) -> [URL] { - allFiles.filter({ url in - url.path().contains(isManPlayer ? SourceFile.messieurs.rawValue : SourceFile.dames.rawValue) - }) - } - - func allFilesSortedByDate(_ isManPlayer: Bool) -> [URL] { - return allFiles(isManPlayer) - } - - static func isDateAfterUrlImportDate(date: Date, dateString: String) -> Bool { - guard let importDate = URL.importDateFormatter.date(from: dateString) else { - return false - } - - return importDate.isEarlierThan(date) - } - - static func getSortOption() -> [SortOption] { - return SortOption.allCases - } -} - -enum SourceFile: String, CaseIterable { - case dames = "DAMES" - case messieurs = "MESSIEURS" - - var filesFromServer: [URL] { - let rankingSourceDirectory = SourceFileManager.shared.rankingSourceDirectory - let allFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil) - return allFiles.filter{$0.pathExtension == "csv" && $0.path().contains(rawValue)} - } - - func currentURLs(importingDate: Date) -> [URL] { - var files = Bundle.main.urls(forResourcesWithExtension: "csv", subdirectory: nil)?.filter({ url in - url.path().contains(rawValue) - }) ?? [] - - files.append(contentsOf: filesFromServer) - return files.filter({ $0.dateFromPath == importingDate }) - } - - var isMan: Bool { - switch self { - case .dames: - return false - default: - return true - } - } -} diff --git a/PadelClub/Utils/SwiftParser.swift b/PadelClub/Utils/SwiftParser.swift index 7aab761..d796bda 100644 --- a/PadelClub/Utils/SwiftParser.swift +++ b/PadelClub/Utils/SwiftParser.swift @@ -6,6 +6,7 @@ // import Foundation +import PadelClubData typealias LineIterator = AsyncLineSequence.AsyncIterator struct Line: Identifiable { diff --git a/PadelClub/Utils/Tips.swift b/PadelClub/Utils/Tips.swift index c44c9b3..6daf217 100644 --- a/PadelClub/Utils/Tips.swift +++ b/PadelClub/Utils/Tips.swift @@ -7,6 +7,7 @@ import Foundation import TipKit +import PadelClubData struct PadelBeachExportTip: Tip { var title: Text { diff --git a/PadelClub/Utils/URLs.swift b/PadelClub/Utils/URLs.swift deleted file mode 100644 index 12a6429..0000000 --- a/PadelClub/Utils/URLs.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// URLs.swift -// PadelClub -// -// Created by Laurent Morvillier on 22/04/2024. -// - -import Foundation - -enum URLs: String, Identifiable { -// case httpScheme = "https://" -#if DEBUG - case activationHost = "http://127.0.0.1:8000" - case main = "http://127.0.0.1:8000/" -// 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/" -#elseif PRODTEST - case activationHost = "padelclub.app" - case main = "https://padelclub.app/" -// case api = "https://padelclub.app/roads/" -#else - case activationHost = "padelclub.app" - case main = "https://padelclub.app/" -// case api = "https://padelclub.app/roads/" -#endif - - case subscriptions = "https://apple.co/2Th4vqI" - case beachPadel = "https://beach-padel.app.fft.fr/beachja/index/" - //case padelClub = "https://padelclub.app" - case tenup = "https://tenup.fft.fr" - case padelCompetitionGeneralGuide = "https://padelclub.app/static/rules/padel-guide-general.pdf" - case padelCompetitionSpecificGuide = "https://padelclub.app/static/rules/padel-guide-cdc.pdf" - case padelCompetitionRankingGuide = "https://padelclub.app/static/rules/padel-guide-rankings.pdf" - case padelRules = "https://padelclub.app/static/rules/padel-rules.pdf" - case restingDischarge = "https://club.fft.fr/tennisfirmidecazeville/60120370_d/data_1/pdf/fo/formlairededechargederesponsabilitetournoidepadel.pdf" - case appReview = "https://apps.apple.com/app/padel-club/id6484163558?mt=8&action=write-review" - case appDescription = "https://padelclub.app/download/" - case instagram = "https://www.instagram.com/padelclub.app?igsh=bmticnV5YWhpMnBn" - case appStore = "https://apps.apple.com/app/padel-club/id6484163558" - case eula = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/" - case privacy = "https://padelclub.app/terms-of-use" - - var id: String { return self.rawValue } - - var url: URL { - return URL(string: self.rawValue)! - } - - func extend(path: String) -> URL { - return URL(string: self.rawValue + path)! - } - -} - -enum PageLink: String, Identifiable, CaseIterable { - case info = "Informations" - case teams = "Équipes" - case summons = "Convocations" - case groupStages = "Poules" - case matches = "Tournoi" - case rankings = "Classement" - case broadcast = "Mode TV (Tournoi)" - case clubBroadcast = "Mode TV (Club)" - - var id: String { self.rawValue } - - func localizedLabel() -> String { - rawValue - } - - var path: String { - switch self { - case .matches: - return "" - case .info: - return "info" - case .teams: - return "teams" - case .summons: - return "summons" - case .rankings: - return "rankings" - case .groupStages: - return "group-stages" - case .broadcast: - return "broadcast" - case .clubBroadcast: - return "" - } - } -} - diff --git a/PadelClub/ViewModel/AgendaDestination.swift b/PadelClub/ViewModel/AgendaDestination.swift index 2f08364..d1c56ee 100644 --- a/PadelClub/ViewModel/AgendaDestination.swift +++ b/PadelClub/ViewModel/AgendaDestination.swift @@ -8,6 +8,7 @@ import Foundation import SwiftUI import TipKit +import PadelClubData enum AgendaDestination: Int, CaseIterable, Identifiable, Selectable, Equatable { var id: Int { self.rawValue } diff --git a/PadelClub/ViewModel/FederalDataViewModel.swift b/PadelClub/ViewModel/FederalDataViewModel.swift index ad8a560..9bc23a0 100644 --- a/PadelClub/ViewModel/FederalDataViewModel.swift +++ b/PadelClub/ViewModel/FederalDataViewModel.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData @Observable class FederalDataViewModel { diff --git a/PadelClub/ViewModel/MatchDescriptor.swift b/PadelClub/ViewModel/MatchDescriptor.swift deleted file mode 100644 index 6bea89e..0000000 --- a/PadelClub/ViewModel/MatchDescriptor.swift +++ /dev/null @@ -1,154 +0,0 @@ -// -// swift -// PadelClub -// -// Created by Razmig Sarkissian on 02/04/2024. -// - -import Foundation -import SwiftUI - -class MatchDescriptor: ObservableObject { - @Published var matchFormat: MatchFormat - @Published var setDescriptors: [SetDescriptor] - var court: Int = 1 - var title: String = "Titre du match" - var teamLabelOne: String = "" - var teamLabelTwo: String = "" - var startDate: Date = Date() - var match: Match? - let colorTeamOne: Color = .teal - let colorTeamTwo: Color = .indigo - - var teamOneSetupIsActive: Bool { - if hasEnded && showSetInputView == false && showTieBreakInputView == false { - return false - } - - guard let setDescriptor = setDescriptors.last else { - return false - } - if setDescriptor.valueTeamOne == nil { - return true - } else if setDescriptor.valueTeamTwo == nil { - return false - } else if setDescriptor.tieBreakValueTeamOne == nil, setDescriptor.shouldTieBreak { - return true - } else if setDescriptor.tieBreakValueTeamTwo == nil, setDescriptor.shouldTieBreak { - return false - } - - return false - } - - var teamTwoSetupIsActive: Bool { - if hasEnded && showSetInputView == false && showTieBreakInputView == false { - return false - } - guard let setDescriptor = setDescriptors.last else { - return false - } - - if setDescriptor.valueTeamOne == nil { - return false - } else if setDescriptor.valueTeamTwo == nil { - return true - } else if setDescriptor.tieBreakValueTeamOne == nil, setDescriptor.shouldTieBreak { - return false - } else if setDescriptor.tieBreakValueTeamTwo == nil, setDescriptor.shouldTieBreak { - return true - } - - return true - } - - var showSetInputView: Bool { - return setDescriptors.anySatisfy({ $0.showSetInputView }) - } - - var showTieBreakInputView: Bool { - return setDescriptors.anySatisfy({ $0.showTieBreakInputView }) - } - - init(match: Match? = nil) { - self.match = match - if let groupStage = match?.groupStageObject { - self.matchFormat = groupStage.matchFormat - self.setDescriptors = [SetDescriptor(setFormat: groupStage.matchFormat.setFormat)] - } else { - let format = match?.matchFormat ?? match?.currentTournament()?.matchFormat ?? .defaultFormatForMatchType(.groupStage) - self.matchFormat = format - self.setDescriptors = [SetDescriptor(setFormat: format.setFormat)] - } - let teamOne = match?.team(.one) - let teamTwo = match?.team(.two) - self.teamLabelOne = teamOne?.teamLabel(.wide, twoLines: true) ?? "" - self.teamLabelTwo = teamTwo?.teamLabel(.wide, twoLines: true) ?? "" - - if let match, let scoresTeamOne = match.teamScore(ofTeam: teamOne)?.score, let scoresTeamTwo = match.teamScore(ofTeam: teamTwo)?.score { - - self.setDescriptors = combineArraysIntoTuples(scoresTeamOne.components(separatedBy: ","), scoresTeamTwo.components(separatedBy: ",")).map({ (a:String?, b:String?) in - SetDescriptor(valueTeamOne: a != nil ? Int(a!) : nil, valueTeamTwo: b != nil ? Int(b!) : nil, setFormat: match.matchFormat.setFormat) - }) - } - } - - var teamOneScores: [String] { - setDescriptors.compactMap { $0.getValue(teamPosition: .one) } - } - - var teamTwoScores: [String] { - setDescriptors.compactMap { $0.getValue(teamPosition: .two) } - } - - var scoreTeamOne: Int { setDescriptors.compactMap { $0.winner }.filter { $0 == .one }.count } - var scoreTeamTwo: Int { setDescriptors.compactMap { $0.winner }.filter { $0 == .two }.count } - - var hasEnded: Bool { - return matchFormat.hasEnded(scoreTeamOne: scoreTeamOne, scoreTeamTwo: scoreTeamTwo) - } - - func addNewSet() { - if hasEnded == false { - setDescriptors.append(SetDescriptor(setFormat: matchFormat.newSetFormat(setCount: setDescriptors.count))) - } - } - - var winner: TeamPosition { - matchFormat.winner(scoreTeamOne: scoreTeamOne, scoreTeamTwo: scoreTeamTwo) - } - - var winnerLabel: String { - if winner == .one { - return teamLabelOne - } else { - return teamLabelTwo - } - } -} - -fileprivate func combineArraysIntoTuples(_ array1: [String], _ array2: [String]) -> [(String?, String?)] { - // Zip the two arrays together and map them to tuples of optional strings - let combined = zip(array1, array2).map { (element1, element2) in - return (element1, element2) - } - - // If one array is longer than the other, append the remaining elements - let remainingElements: [(String?, String?)] - if array1.count > array2.count { - let remaining = Array(array1[array2.count...]).map { (element) in - return (element, nil as String?) - } - remainingElements = remaining - } else if array2.count > array1.count { - let remaining = Array(array2[array1.count...]).map { (element) in - return (nil as String?, element) - } - remainingElements = remaining - } else { - remainingElements = [] - } - - // Concatenate the two arrays - return combined + remainingElements -} diff --git a/PadelClub/ViewModel/MatchSpot.swift b/PadelClub/ViewModel/MatchSpot.swift deleted file mode 100644 index 61df6c7..0000000 --- a/PadelClub/ViewModel/MatchSpot.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// MatchSpot.swift -// PadelClub -// -// Created by razmig on 22/03/2025. -// - - -struct MatchSpot: SpinDrawable { - let match: Match - let teamPosition: TeamPosition - - func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { - [match.roundTitle(), matchTitle(displayStyle: displayStyle)].compactMap { $0 } - } - - func matchTitle(displayStyle: DisplayStyle) -> String { - [match.matchTitle(displayStyle), teamPositionLabel()].joined(separator: " - ") - } - - func teamPositionLabel() -> String { - switch teamPosition { - case .one: - return "haut" - case .two: - return "bas" - } - } -} diff --git a/PadelClub/ViewModel/NavigationViewModel.swift b/PadelClub/ViewModel/NavigationViewModel.swift index ce05d89..e53e2d5 100644 --- a/PadelClub/ViewModel/NavigationViewModel.swift +++ b/PadelClub/ViewModel/NavigationViewModel.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData @Observable class NavigationViewModel { diff --git a/PadelClub/ViewModel/Screen.swift b/PadelClub/ViewModel/Screen.swift deleted file mode 100644 index 38a02b0..0000000 --- a/PadelClub/ViewModel/Screen.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Screen.swift -// PadelClub -// -// Created by Razmig Sarkissian on 03/03/2024. -// - -import Foundation - -enum Screen: String, Codable { - case inscription - case groupStage - case round - case settings - case structure - case schedule - case cashier - case call - case rankings - case broadcast - case event - case print - case share - case restingTime - case stateSettings -} diff --git a/PadelClub/ViewModel/SearchViewModel.swift b/PadelClub/ViewModel/SearchViewModel.swift index e3c43e5..e6ffc3b 100644 --- a/PadelClub/ViewModel/SearchViewModel.swift +++ b/PadelClub/ViewModel/SearchViewModel.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData class DebouncableViewModel: ObservableObject { @Published var debouncableText: String = "" @@ -784,29 +785,8 @@ enum DataSet: Int, Identifiable { } } -enum SortOption: Int, CaseIterable, Identifiable { - case name - case rank - case tournamentCount - case points - case progression - - var id: Int { self.rawValue } - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - switch self { - case .name: - return "Nom" - case .rank: - return "Rang" - case .tournamentCount: - return "Tournoi" - case .points: - return "Points" - case .progression: - return "Progression" - } - } - +extension SortOption { + func sortDescriptors(_ ascending: Bool, dataSet: DataSet) -> [SortDescriptor] { switch self { case .name: @@ -849,31 +829,67 @@ enum SortOption: Int, CaseIterable, Identifiable { } } -enum PlayerFilterOption: Int, Hashable, CaseIterable, Identifiable { - case all = -1 - case male = 1 - case female = 0 - - var id: Int { rawValue } - - func icon() -> String { - switch self { - case .all: - return "Tous" - case .male: - return "Homme" - case .female: - return "Femme" - } - } - - var localizedPlayerLabel: String { - switch self { - case .female: - return "joueuse" - default: - return "joueur" - } - } - -} +//enum SortOption: Int, CaseIterable, Identifiable { +// case name +// case rank +// case tournamentCount +// case points +// case progression +// +// var id: Int { self.rawValue } +// func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { +// switch self { +// case .name: +// return "Nom" +// case .rank: +// return "Rang" +// case .tournamentCount: +// return "Tournoi" +// case .points: +// return "Points" +// case .progression: +// return "Progression" +// } +// } +// +// func sortDescriptors(_ ascending: Bool, dataSet: DataSet) -> [SortDescriptor] { +// switch self { +// case .name: +// return [ +// SortDescriptor(\ImportedPlayer.lastName, order: ascending ? .forward : .reverse), +// SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation), +// ] +// case .rank: +// if dataSet == .national || dataSet == .ligue { +// return [ +// SortDescriptor(\ImportedPlayer.rank, order: ascending ? .forward : .reverse) +// ] +// } else { +// return [ +// SortDescriptor(\ImportedPlayer.rank, order: ascending ? .forward : .reverse), +// SortDescriptor(\ImportedPlayer.assimilation), +// SortDescriptor(\ImportedPlayer.lastName), +// ] +// } +// case .tournamentCount: +// return [ +// SortDescriptor( +// \ImportedPlayer.tournamentCount, order: ascending ? .forward : .reverse), +// SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation), +// SortDescriptor(\ImportedPlayer.lastName), +// ] +// case .points: +// return [ +// SortDescriptor(\ImportedPlayer.points, order: ascending ? .forward : .reverse), +// SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation), +// SortDescriptor(\ImportedPlayer.lastName), +// ] +// case .progression: +// return [ +// SortDescriptor(\ImportedPlayer.progression, order: ascending ? .forward : .reverse), +// SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation), +// SortDescriptor(\ImportedPlayer.lastName), +// ] +// } +// } +//} diff --git a/PadelClub/ViewModel/SeedInterval.swift b/PadelClub/ViewModel/SeedInterval.swift deleted file mode 100644 index 32ab726..0000000 --- a/PadelClub/ViewModel/SeedInterval.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// SeedInterval.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/04/2024. -// - -import Foundation - -struct SeedInterval: Hashable, Comparable { - let first: Int - let last: Int - - func pointsRange(tournamentLevel: TournamentLevel, teamsCount: Int) -> String { - tournamentLevel.pointsRange(first: first, last: last, teamsCount: teamsCount) - } - - static func <(lhs: SeedInterval, rhs: SeedInterval) -> Bool { - return lhs.first < rhs.first - } - - func isFixed() -> Bool { - first == 1 && last == 2 - } - - var count: Int { - dimension - } - - private var dimension: Int { - (last - (first - 1)) - } - - func chunks() -> [SeedInterval]? { - if dimension > 3 { - let split = dimension / 2 - if split%2 == 0 { - let firstHalf = SeedInterval(first: first, last: first + split - 1) - let secondHalf = SeedInterval(first: first + split, last: last) - return [firstHalf, secondHalf] - } else { - let firstHalf = SeedInterval(first: first, last: first + split) - let secondHalf = SeedInterval(first: first + split + 1, last: last) - return [firstHalf, secondHalf] - } - } else { - return nil - } - } - - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if dimension < 3 { - return "\(first)\(first.ordinalFormattedSuffix()) place" - } else { - return "Place \(first) à \(last)" - } - } - - func localizedInterval(_ displayStyle: DisplayStyle = .wide) -> String { - if dimension < 3 { - return "#\(first) / #\(last)" - } else { - return "#\(first) à #\(last)" - } - } -} diff --git a/PadelClub/ViewModel/Selectable.swift b/PadelClub/ViewModel/Selectable.swift deleted file mode 100644 index 66309f9..0000000 --- a/PadelClub/ViewModel/Selectable.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// Selectable.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/04/2024. -// - -import Foundation -import SwiftUI -import TipKit - -protocol Selectable { - func selectionLabel(index: Int) -> String - func badgeValue() -> Int? - func badgeImage() -> Badge? - func badgeValueColor() -> Color? - func displayImageIfValueZero() -> Bool - func systemImage() -> String? - func associatedTip() -> (any Tip)? -} - -extension Selectable { - func associatedTip() -> (any Tip)? { - return nil - } - - func systemImage() -> String? { - return nil - } - - func displayImageIfValueZero() -> Bool { - return false - } -} - -enum Badge { - case checkmark - case xmark - case custom(systemName: String, color: Color) - - func systemName() -> String { - switch self { - case .checkmark: - return "checkmark.circle.fill" - case .xmark: - return "xmark.circle.fill" - case .custom(let systemName, _): - return systemName - } - } - - func color() -> Color { - switch self { - case .checkmark: - .green - case .xmark: - .logoRed - case .custom(_, let color): - color - } - } -} - -struct SelectionTipViewModifier: ViewModifier { - let selectable: Selectable - let action: () -> Void - func body(content: Content) -> some View { - if let tip = selectable.associatedTip() { - if #available(iOS 18.0, *) { - content - .popoverTip(tip, arrowEdge: .top) { _ in - action() - tip.invalidate(reason: .tipClosed) - } - } else { - content - } - } else { - content - } - } -} - -extension View { - func selectableTipViewModifier(selectable: Selectable, action: @escaping () -> Void) -> some View { - modifier(SelectionTipViewModifier(selectable: selectable, action: action)) - } -} - diff --git a/PadelClub/ViewModel/SetDescriptor.swift b/PadelClub/ViewModel/SetDescriptor.swift deleted file mode 100644 index 7ab5e42..0000000 --- a/PadelClub/ViewModel/SetDescriptor.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// SetDescriptor.swift -// PadelClub -// -// Created by Razmig Sarkissian on 02/04/2024. -// - -import Foundation - -struct SetDescriptor: Identifiable, Equatable { - let id: UUID = UUID() - var valueTeamOne: Int? - var valueTeamTwo: Int? - var tieBreakValueTeamOne: Int? - var tieBreakValueTeamTwo: Int? - var setFormat: SetFormat - var showSetInputView: Bool = true - var showTieBreakInputView: Bool = false - - var isTeamOneSet: Bool { - return valueTeamOne != nil || tieBreakValueTeamOne != nil - } - - var hasEnded: Bool { - if let valueTeamTwo, let valueTeamOne { - return setFormat.hasEnded(teamOne: valueTeamOne, teamTwo: valueTeamTwo) - } else { - return false - } - } - - var winner: TeamPosition? { - if let valueTeamTwo, let valueTeamOne, valueTeamOne != valueTeamTwo { - return setFormat.winner(teamOne: valueTeamOne, teamTwo: valueTeamTwo) - } else { - return nil - } - } - - var shouldTieBreak: Bool { - setFormat.shouldTiebreak(scoreTeamOne: valueTeamOne ?? 0, scoreTeamTwo: valueTeamTwo ?? 0) - } - - func getValue(teamPosition: TeamPosition) -> String? { - switch teamPosition { - case .one: - if let valueTeamOne { - if let tieBreakValueTeamOne { - return "\(valueTeamOne)-\(tieBreakValueTeamOne)" - } else { - return "\(valueTeamOne)" - } - } - case .two: - if let valueTeamTwo { - if let tieBreakValueTeamTwo { - return "\(valueTeamTwo)-\(tieBreakValueTeamTwo)" - } else { - return "\(valueTeamTwo)" - } - } - } - - return nil - } -} diff --git a/PadelClub/Views/Calling/BracketCallingView.swift b/PadelClub/Views/Calling/BracketCallingView.swift index 810919e..50e1583 100644 --- a/PadelClub/Views/Calling/BracketCallingView.swift +++ b/PadelClub/Views/Calling/BracketCallingView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct BracketCallingView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Calling/CallMessageCustomizationView.swift b/PadelClub/Views/Calling/CallMessageCustomizationView.swift index c1979b8..8d2ebe9 100644 --- a/PadelClub/Views/Calling/CallMessageCustomizationView.swift +++ b/PadelClub/Views/Calling/CallMessageCustomizationView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CallMessageCustomizationView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Calling/CallSettingsView.swift b/PadelClub/Views/Calling/CallSettingsView.swift index 49db7c5..af40ec8 100644 --- a/PadelClub/Views/Calling/CallSettingsView.swift +++ b/PadelClub/Views/Calling/CallSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CallSettingsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index 472cdc0..821cdd4 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CallView: View { diff --git a/PadelClub/Views/Calling/Components/MenuWarningView.swift b/PadelClub/Views/Calling/Components/MenuWarningView.swift index 5222650..3f3bcc5 100644 --- a/PadelClub/Views/Calling/Components/MenuWarningView.swift +++ b/PadelClub/Views/Calling/Components/MenuWarningView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct MenuWarningView: View { let tournament: Tournament diff --git a/PadelClub/Views/Calling/Components/PlayersWithoutContactView.swift b/PadelClub/Views/Calling/Components/PlayersWithoutContactView.swift index 92b38be..07b2747 100644 --- a/PadelClub/Views/Calling/Components/PlayersWithoutContactView.swift +++ b/PadelClub/Views/Calling/Components/PlayersWithoutContactView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PlayersWithoutContactView: View { @Environment(Tournament.self) var tournament: Tournament diff --git a/PadelClub/Views/Calling/GroupStageCallingView.swift b/PadelClub/Views/Calling/GroupStageCallingView.swift index 020d603..bfade2a 100644 --- a/PadelClub/Views/Calling/GroupStageCallingView.swift +++ b/PadelClub/Views/Calling/GroupStageCallingView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct GroupStageCallingView: View { @Environment(Tournament.self) var tournament: Tournament diff --git a/PadelClub/Views/Calling/SeedsCallingView.swift b/PadelClub/Views/Calling/SeedsCallingView.swift index d13f4b3..7e42641 100644 --- a/PadelClub/Views/Calling/SeedsCallingView.swift +++ b/PadelClub/Views/Calling/SeedsCallingView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct SeedsCallingView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Calling/SendToAllView.swift b/PadelClub/Views/Calling/SendToAllView.swift index 55a326e..8547947 100644 --- a/PadelClub/Views/Calling/SendToAllView.swift +++ b/PadelClub/Views/Calling/SendToAllView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct SendToAllView: View { diff --git a/PadelClub/Views/Calling/TeamsCallingView.swift b/PadelClub/Views/Calling/TeamsCallingView.swift index 017c5fa..b8987c4 100644 --- a/PadelClub/Views/Calling/TeamsCallingView.swift +++ b/PadelClub/Views/Calling/TeamsCallingView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TeamsCallingView: View { @Environment(Tournament.self) var tournament: Tournament diff --git a/PadelClub/Views/Cashier/CashierDetailView.swift b/PadelClub/Views/Cashier/CashierDetailView.swift index 0f1c8a1..fb975a4 100644 --- a/PadelClub/Views/Cashier/CashierDetailView.swift +++ b/PadelClub/Views/Cashier/CashierDetailView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct CashierDetailView: View { var tournaments : [Tournament] diff --git a/PadelClub/Views/Cashier/CashierSettingsView.swift b/PadelClub/Views/Cashier/CashierSettingsView.swift index 2090e0d..f97e7ac 100644 --- a/PadelClub/Views/Cashier/CashierSettingsView.swift +++ b/PadelClub/Views/Cashier/CashierSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CashierSettingsView: View { diff --git a/PadelClub/Views/Cashier/CashierView.swift b/PadelClub/Views/Cashier/CashierView.swift index ef95e65..5433655 100644 --- a/PadelClub/Views/Cashier/CashierView.swift +++ b/PadelClub/Views/Cashier/CashierView.swift @@ -7,6 +7,7 @@ import SwiftUI import Combine +import PadelClubData struct ShareableObject { @@ -42,23 +43,12 @@ extension ShareableObject: Transferable { } } -extension Array { - func sorted(by keyPath: KeyPath, order: SortOrder) -> [Element] { - switch order { - case .ascending: - return self.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] } - case .descending: - return self.sorted { $0[keyPath: keyPath] > $1[keyPath: keyPath] } - } - } -} - class CashierViewModel: ObservableObject { let id: UUID = UUID() @Published var sortOption: SortOption = .callDate @Published var filterOption: FilterOption = .didNotPay @Published var presenceFilterOption: PresenceFilterOption = .all - @Published var sortOrder: SortOrder = .ascending + @Published var sortOrder: PadelClubData.SortOrder = .ascending @Published var searchText: String = "" @Published var isSearching: Bool = false @@ -105,7 +95,7 @@ class CashierViewModel: ObservableObject { } } - func compare(_ lhs: PlayerRegistration, _ rhs: PlayerRegistration, order: SortOrder) -> Bool { + func compare(_ lhs: PlayerRegistration, _ rhs: PlayerRegistration, order: PadelClubData.SortOrder) -> Bool { switch self { case .alphabeticalLastName: return compare(lhs[keyPath: \.lastName], rhs[keyPath: \.lastName], order: order) @@ -118,7 +108,7 @@ class CashierViewModel: ObservableObject { } } - private func compare(_ lhs: T, _ rhs: T, order: SortOrder) -> Bool { + private func compare(_ lhs: T, _ rhs: T, order: PadelClubData.SortOrder) -> Bool { switch order { case .ascending: return lhs < rhs diff --git a/PadelClub/Views/Cashier/Event/EventCreationView.swift b/PadelClub/Views/Cashier/Event/EventCreationView.swift index 80e812d..fcef799 100644 --- a/PadelClub/Views/Cashier/Event/EventCreationView.swift +++ b/PadelClub/Views/Cashier/Event/EventCreationView.swift @@ -8,6 +8,7 @@ import SwiftUI import TipKit import LeStorage +import PadelClubData struct EventCreationView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Cashier/Event/EventLinksView.swift b/PadelClub/Views/Cashier/Event/EventLinksView.swift index 5ae957f..7128e11 100644 --- a/PadelClub/Views/Cashier/Event/EventLinksView.swift +++ b/PadelClub/Views/Cashier/Event/EventLinksView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct EventLinksView: View { let event: Event diff --git a/PadelClub/Views/Cashier/Event/EventSettingsView.swift b/PadelClub/Views/Cashier/Event/EventSettingsView.swift index 6fc7bf8..876262c 100644 --- a/PadelClub/Views/Cashier/Event/EventSettingsView.swift +++ b/PadelClub/Views/Cashier/Event/EventSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct EventSettingsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Cashier/Event/EventTournamentsView.swift b/PadelClub/Views/Cashier/Event/EventTournamentsView.swift index 1b70684..3901c77 100644 --- a/PadelClub/Views/Cashier/Event/EventTournamentsView.swift +++ b/PadelClub/Views/Cashier/Event/EventTournamentsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct EventTournamentsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Cashier/Event/EventView.swift b/PadelClub/Views/Cashier/Event/EventView.swift index 9ae5c7e..576bb50 100644 --- a/PadelClub/Views/Cashier/Event/EventView.swift +++ b/PadelClub/Views/Cashier/Event/EventView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData enum EventDestination: Identifiable, Selectable, Equatable { static func == (lhs: EventDestination, rhs: EventDestination) -> Bool { diff --git a/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift b/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift index faa6b7d..9f5b111 100644 --- a/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift +++ b/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentConfigurationView: View { @Bindable var tournament: Tournament diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index f74d143..8fa26c2 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct ClubDetailView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Club/ClubImportView.swift b/PadelClub/Views/Club/ClubImportView.swift index faf2099..af8a9bd 100644 --- a/PadelClub/Views/Club/ClubImportView.swift +++ b/PadelClub/Views/Club/ClubImportView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct ClubImportView: View { @Environment(\.dismiss) var dismiss diff --git a/PadelClub/Views/Club/ClubRowView.swift b/PadelClub/Views/Club/ClubRowView.swift index 1c087f2..d4d312a 100644 --- a/PadelClub/Views/Club/ClubRowView.swift +++ b/PadelClub/Views/Club/ClubRowView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct ClubRowView: View { let club: Club diff --git a/PadelClub/Views/Club/ClubSearchView.swift b/PadelClub/Views/Club/ClubSearchView.swift index 9ae177a..72c880b 100644 --- a/PadelClub/Views/Club/ClubSearchView.swift +++ b/PadelClub/Views/Club/ClubSearchView.swift @@ -10,6 +10,7 @@ import CoreLocation import CoreLocationUI import TipKit import LeStorage +import PadelClubData struct ClubSearchView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Club/ClubsView.swift b/PadelClub/Views/Club/ClubsView.swift index b5ea93e..85839f8 100644 --- a/PadelClub/Views/Club/ClubsView.swift +++ b/PadelClub/Views/Club/ClubsView.swift @@ -8,6 +8,7 @@ import SwiftUI import TipKit import LeStorage +import PadelClubData struct ClubsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Club/CourtView.swift b/PadelClub/Views/Club/CourtView.swift index 6b37fc8..bb1b3dd 100644 --- a/PadelClub/Views/Club/CourtView.swift +++ b/PadelClub/Views/Club/CourtView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CourtView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Club/CreateClubView.swift b/PadelClub/Views/Club/CreateClubView.swift index 5f21ece..1ac754f 100644 --- a/PadelClub/Views/Club/CreateClubView.swift +++ b/PadelClub/Views/Club/CreateClubView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CreateClubView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Club/Shared/ClubCourtSetupView.swift b/PadelClub/Views/Club/Shared/ClubCourtSetupView.swift index cd6ec8a..540f829 100644 --- a/PadelClub/Views/Club/Shared/ClubCourtSetupView.swift +++ b/PadelClub/Views/Club/Shared/ClubCourtSetupView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct ClubCourtSetupView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Components/ComposeViews.swift b/PadelClub/Views/Components/ComposeViews.swift new file mode 100644 index 0000000..43e7215 --- /dev/null +++ b/PadelClub/Views/Components/ComposeViews.swift @@ -0,0 +1,123 @@ +// +// ComposeViews.swift +// PadelClub +// +// Created by Laurent Morvillier on 30/04/2025. +// + +import Foundation +import SwiftUI +import MessageUI + +struct MessageComposeView: UIViewControllerRepresentable { + typealias Completion = (_ result: MessageComposeResult) -> Void + + static var canSendText: Bool { MFMessageComposeViewController.canSendText() } + + let recipients: [String]? + let body: String? + let completion: Completion? + + func makeUIViewController(context: Context) -> UIViewController { + guard Self.canSendText else { + let errorView = ContentUnavailableView("Aucun compte de messagerie", systemImage: "xmark", description: Text("Aucun compte de messagerie n'est configuré sur cet appareil.")) + return UIHostingController(rootView: errorView) + } + + let controller = MFMessageComposeViewController() + controller.messageComposeDelegate = context.coordinator + controller.recipients = recipients + controller.body = body + + return controller + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator(completion: self.completion) + } + + class Coordinator: NSObject, MFMessageComposeViewControllerDelegate { + private let completion: Completion? + + public init(completion: Completion?) { + self.completion = completion + } + + public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) { + controller.dismiss(animated: true, completion: { + self.completion?(result) + }) + } + } +} + +struct MailComposeView: UIViewControllerRepresentable { + typealias Completion = (_ result: MFMailComposeResult) -> Void + + static var canSendMail: Bool { + if let mailURL = URL(string: "mailto:?to=jap@padelclub.com") { + let mailConfigured = UIApplication.shared.canOpenURL(mailURL) + return mailConfigured && MFMailComposeViewController.canSendMail() + } else { + return MFMailComposeViewController.canSendMail() + } + } + + let recipients: [String]? + let bccRecipients: [String]? + let body: String? + let subject: String? + var attachmentURL: URL? + let completion: Completion? + + func makeUIViewController(context: Context) -> UIViewController { + guard Self.canSendMail else { + let errorView = ContentUnavailableView("Aucun compte mail", systemImage: "xmark", description: Text("Aucun compte mail n'est configuré sur cet appareil.")) + return UIHostingController(rootView: errorView) + } + + let controller = MFMailComposeViewController() + controller.mailComposeDelegate = context.coordinator + controller.setToRecipients(recipients) + controller.setBccRecipients(bccRecipients) + if let attachmentURL { + do { + let attachmentData = try Data(contentsOf: attachmentURL) + controller.addAttachmentData(attachmentData, mimeType: "application/zip", fileName: "backup.zip") + } catch { + print("Could not attach file: \(error)") + } + } + + if let body { + controller.setMessageBody(body, isHTML: false) + } + if let subject { + controller.setSubject(subject) + } + + return controller + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator(completion: self.completion) + } + + class Coordinator: NSObject, MFMailComposeViewControllerDelegate { + private let completion: Completion? + + public init(completion: Completion?) { + self.completion = completion + } + + public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { + controller.dismiss(animated: true, completion: { + self.completion?(result) + }) + } + } +} diff --git a/PadelClub/Views/Components/FortuneWheelView.swift b/PadelClub/Views/Components/FortuneWheelView.swift index 795773a..f880f35 100644 --- a/PadelClub/Views/Components/FortuneWheelView.swift +++ b/PadelClub/Views/Components/FortuneWheelView.swift @@ -6,43 +6,7 @@ // import SwiftUI - -protocol SpinDrawable { - func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] -} - -extension String: SpinDrawable { - func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { - [self] - } -} - -extension Match: SpinDrawable { - func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { - let teams = teams() - if teams.count == 1, hideNames == false { - return teams.first!.segmentLabel(displayStyle, hideNames: hideNames) - } else { - return [roundTitle(), matchTitle(displayStyle)].compactMap { $0 } - } - } -} - -extension TeamRegistration: SpinDrawable { - func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { - var strings: [String] = [] - let indexLabel = tournamentObject()?.labelIndexOf(team: self) - if let indexLabel { - strings.append(indexLabel) - if hideNames { - return strings - } - } - - strings.append(contentsOf: self.players().map { $0.playerLabel(displayStyle) }) - return strings - } -} +import PadelClubData struct DrawResult: Identifiable { let id: UUID = UUID() diff --git a/PadelClub/Views/Components/GenericDestinationPickerView.swift b/PadelClub/Views/Components/GenericDestinationPickerView.swift index 86e23c8..16876e3 100644 --- a/PadelClub/Views/Components/GenericDestinationPickerView.swift +++ b/PadelClub/Views/Components/GenericDestinationPickerView.swift @@ -7,6 +7,7 @@ import SwiftUI import TipKit +import PadelClubData struct GenericDestinationPickerView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Components/MatchListView.swift b/PadelClub/Views/Components/MatchListView.swift index e5e96a4..17689e1 100644 --- a/PadelClub/Views/Components/MatchListView.swift +++ b/PadelClub/Views/Components/MatchListView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchListView: View { diff --git a/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift b/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift index 50e5155..a7968eb 100644 --- a/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift +++ b/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct GroupStageSettingsView: View { diff --git a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift index 74a181f..2ed3de8 100644 --- a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift +++ b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct GroupStageTeamView: View { diff --git a/PadelClub/Views/GroupStage/GroupStageView.swift b/PadelClub/Views/GroupStage/GroupStageView.swift index 6baf09c..f0bff80 100644 --- a/PadelClub/Views/GroupStage/GroupStageView.swift +++ b/PadelClub/Views/GroupStage/GroupStageView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct GroupStageView: View { diff --git a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift index ae854f7..67dff1c 100644 --- a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct GroupStagesSettingsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/GroupStage/GroupStagesView.swift b/PadelClub/Views/GroupStage/GroupStagesView.swift index c89f7bc..ddf5d81 100644 --- a/PadelClub/Views/GroupStage/GroupStagesView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct GroupStagesView: View { @State var tournament: Tournament diff --git a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift index b5e2096..b779b6c 100644 --- a/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift +++ b/PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct LoserBracketFromGroupStageView: View { diff --git a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift index dbc5673..1442233 100644 --- a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift +++ b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct GroupStageTeamReplacementView: View { var team: TeamRegistration diff --git a/PadelClub/Views/Match/Components/MatchDateView.swift b/PadelClub/Views/Match/Components/MatchDateView.swift index c1095e3..9b6ba94 100644 --- a/PadelClub/Views/Match/Components/MatchDateView.swift +++ b/PadelClub/Views/Match/Components/MatchDateView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct MatchDateView: View { diff --git a/PadelClub/Views/Match/Components/MatchTeamDetailView.swift b/PadelClub/Views/Match/Components/MatchTeamDetailView.swift index 3ee208b..c81a6c7 100644 --- a/PadelClub/Views/Match/Components/MatchTeamDetailView.swift +++ b/PadelClub/Views/Match/Components/MatchTeamDetailView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchTeamDetailView: View { @EnvironmentObject var tournamentStore: TournamentStore diff --git a/PadelClub/Views/Match/Components/PlayerBlockView.swift b/PadelClub/Views/Match/Components/PlayerBlockView.swift index 7db8925..c1a6a5c 100644 --- a/PadelClub/Views/Match/Components/PlayerBlockView.swift +++ b/PadelClub/Views/Match/Components/PlayerBlockView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PlayerBlockView: View { @Environment(\.matchViewStyle) private var matchViewStyle diff --git a/PadelClub/Views/Match/EditSharingView.swift b/PadelClub/Views/Match/EditSharingView.swift index 651e419..b4d2bfa 100644 --- a/PadelClub/Views/Match/EditSharingView.swift +++ b/PadelClub/Views/Match/EditSharingView.swift @@ -8,6 +8,7 @@ import SwiftUI import TipKit import CoreTransferable +import PadelClubData struct EditSharingView: View { var match: Match diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index e172b21..7f0fa2a 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct MatchDetailView: View { @@ -308,15 +309,6 @@ struct MatchDetailView: View { .toolbar { ToolbarItem(placement: .topBarTrailing) { Menu { -// Button("Créer les scores") { -// let teamsScores = match.getOrCreateTeamScores() -// do { -// try dataStore.teamScores.addOrUpdate(contentOfs: teamsScores) -// } catch { -// Logger.error(error) -// } -// } - Toggle(isOn: .init(get: { return match.confirmed }, set: { value in diff --git a/PadelClub/Views/Match/MatchRowView.swift b/PadelClub/Views/Match/MatchRowView.swift index 7d8d4fb..e11a0f6 100644 --- a/PadelClub/Views/Match/MatchRowView.swift +++ b/PadelClub/Views/Match/MatchRowView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct MatchRowView: View { diff --git a/PadelClub/Views/Match/MatchSetupView.swift b/PadelClub/Views/Match/MatchSetupView.swift index 8170ae0..97094b7 100644 --- a/PadelClub/Views/Match/MatchSetupView.swift +++ b/PadelClub/Views/Match/MatchSetupView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct MatchSetupView: View { static let confirmationMessage = "Au moins une tête de série a été placée dans la branche de ce match dans les tours précédents. En plaçant une équipe ici, les équipes déjà placées dans la même branche seront retirées du tableau et devront être replacées." diff --git a/PadelClub/Views/Match/MatchSummaryView.swift b/PadelClub/Views/Match/MatchSummaryView.swift index d0aa674..6aa03a4 100644 --- a/PadelClub/Views/Match/MatchSummaryView.swift +++ b/PadelClub/Views/Match/MatchSummaryView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchSummaryView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Agenda/ActivityView.swift b/PadelClub/Views/Navigation/Agenda/ActivityView.swift index 9c222ce..81c9bbf 100644 --- a/PadelClub/Views/Navigation/Agenda/ActivityView.swift +++ b/PadelClub/Views/Navigation/Agenda/ActivityView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct ActivityView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Agenda/CalendarView.swift b/PadelClub/Views/Navigation/Agenda/CalendarView.swift index 7025f9a..a3a7d81 100644 --- a/PadelClub/Views/Navigation/Agenda/CalendarView.swift +++ b/PadelClub/Views/Navigation/Agenda/CalendarView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CalendarView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 4fda5c0..b2a20f5 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct EventListView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift index 699f6f1..7cecadc 100644 --- a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift +++ b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift @@ -8,6 +8,7 @@ import SwiftUI import CoreLocation import CoreLocationUI +import PadelClubData struct TournamentLookUpView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift b/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift index 51fe95e..a07a145 100644 --- a/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift +++ b/PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift @@ -7,6 +7,7 @@ import SwiftUI import EventKit +import PadelClubData struct TournamentSubscriptionView: View { @EnvironmentObject var networkMonitor: NetworkMonitor diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 4db867e..8d282fe 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import StoreKit +import PadelClubData struct MainView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingContainerView.swift b/PadelClub/Views/Navigation/Ongoing/OngoingContainerView.swift index 15e4bb9..dfcc058 100644 --- a/PadelClub/Views/Navigation/Ongoing/OngoingContainerView.swift +++ b/PadelClub/Views/Navigation/Ongoing/OngoingContainerView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData @Observable class OngoingViewModel { diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingDestination.swift b/PadelClub/Views/Navigation/Ongoing/OngoingDestination.swift index ebf38f4..3018ec0 100644 --- a/PadelClub/Views/Navigation/Ongoing/OngoingDestination.swift +++ b/PadelClub/Views/Navigation/Ongoing/OngoingDestination.swift @@ -5,6 +5,7 @@ // Created by razmig on 07/11/2024. // import SwiftUI +import PadelClubData enum OngoingDestination: Int, CaseIterable, Identifiable, Selectable, Equatable { var id: Int { self.rawValue } diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift index 309885a..b6d1f8f 100644 --- a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift +++ b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData extension Int: @retroactive Identifiable { public var id: Int { diff --git a/PadelClub/Views/Navigation/Organizer/OrganizedTournamentView.swift b/PadelClub/Views/Navigation/Organizer/OrganizedTournamentView.swift index 75e6a82..2600c1d 100644 --- a/PadelClub/Views/Navigation/Organizer/OrganizedTournamentView.swift +++ b/PadelClub/Views/Navigation/Organizer/OrganizedTournamentView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct OrganizedTournamentView: View { @Bindable var tournament: Tournament diff --git a/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift b/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift index 05f4eb5..e619486 100644 --- a/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift +++ b/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentButtonView: View { @Environment(\.colorScheme) var colorScheme diff --git a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift index b97a2b3..8358e10 100644 --- a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift +++ b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentOrganizerView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift index 6706d47..0382d5f 100644 --- a/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift +++ b/PadelClub/Views/Navigation/Toolbox/APICallsListView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct APICallsListView: View { diff --git a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift index 7e52ec3..8e40f3d 100644 --- a/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct DebugSettingsView: View { var body: some View { diff --git a/PadelClub/Views/Navigation/Toolbox/DurationSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/DurationSettingsView.swift index 8dd7fc4..d4fb0f0 100644 --- a/PadelClub/Views/Navigation/Toolbox/DurationSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/DurationSettingsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct DurationSettingsView: View { var body: some View { diff --git a/PadelClub/Views/Navigation/Toolbox/GlobalSettingsView.swift b/PadelClub/Views/Navigation/Toolbox/GlobalSettingsView.swift index ed0e831..2b47702 100644 --- a/PadelClub/Views/Navigation/Toolbox/GlobalSettingsView.swift +++ b/PadelClub/Views/Navigation/Toolbox/GlobalSettingsView.swift @@ -7,9 +7,10 @@ import SwiftUI import LeStorage +import PadelClubData struct GlobalSettingsView: View { - @EnvironmentObject var dataStore : DataStore + @EnvironmentObject var dataStore: DataStore var groupStageMatchFormat: Binding { Binding { diff --git a/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift b/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift index 9565658..1be7b6e 100644 --- a/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift +++ b/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct MatchFormatStorageView: View { @State private var estimatedDuration: Int diff --git a/PadelClub/Views/Navigation/Toolbox/RankCalculatorView.swift b/PadelClub/Views/Navigation/Toolbox/RankCalculatorView.swift index 28b4050..5c48769 100644 --- a/PadelClub/Views/Navigation/Toolbox/RankCalculatorView.swift +++ b/PadelClub/Views/Navigation/Toolbox/RankCalculatorView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct RankCalculatorView: View { @State private var rank: Int = 1 diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 1b16842..c867526 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import Zip +import PadelClubData struct ToolboxView: View { @EnvironmentObject var dataStore: DataStore @@ -309,7 +310,7 @@ struct DebugView: View { struct ZipLog: Transferable { private func _getZip() -> URL? { do { - let filePath = try Club.storageDirectoryPath() + let filePath = try StoreCenter.main.directoryURL() return try Zip.quickZipFiles([filePath], fileName: "backup") // Zip } catch { Logger.error(error) diff --git a/PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift b/PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift index 07f8ca1..6cdef7d 100644 --- a/PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift +++ b/PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct NetworkStatusView: View { diff --git a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift index b7d1508..ba8d0ee 100644 --- a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift +++ b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import Foundation +import PadelClubData struct PadelClubView: View { @State private var uuid: UUID = UUID() diff --git a/PadelClub/Views/Navigation/Umpire/UmpireStatisticView.swift b/PadelClub/Views/Navigation/Umpire/UmpireStatisticView.swift index a82674b..5535f73 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireStatisticView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireStatisticView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct UmpireStatisticView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index ef144bb..cdc6e93 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -9,6 +9,7 @@ import SwiftUI import CoreLocation import LeStorage import StoreKit +import PadelClubData struct UmpireView: View { diff --git a/PadelClub/Views/Planning/Components/DatePickingView.swift b/PadelClub/Views/Planning/Components/DatePickingView.swift index 9c865e3..e8219c9 100644 --- a/PadelClub/Views/Planning/Components/DatePickingView.swift +++ b/PadelClub/Views/Planning/Components/DatePickingView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct DatePickingView: View { let title: String diff --git a/PadelClub/Views/Planning/Components/DatePickingViewWithFormat.swift b/PadelClub/Views/Planning/Components/DatePickingViewWithFormat.swift index 6eca161..b9d5e06 100644 --- a/PadelClub/Views/Planning/Components/DatePickingViewWithFormat.swift +++ b/PadelClub/Views/Planning/Components/DatePickingViewWithFormat.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct DatePickingViewWithFormat: View { @Environment(Tournament.self) var tournament diff --git a/PadelClub/Views/Planning/Components/GroupStageDatePickingView.swift b/PadelClub/Views/Planning/Components/GroupStageDatePickingView.swift index 29c380f..38bea1d 100644 --- a/PadelClub/Views/Planning/Components/GroupStageDatePickingView.swift +++ b/PadelClub/Views/Planning/Components/GroupStageDatePickingView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct GroupStageDatePickingView: View { let title: String diff --git a/PadelClub/Views/Planning/Components/MatchFormatPickingView.swift b/PadelClub/Views/Planning/Components/MatchFormatPickingView.swift index 8b36c13..4ab3b21 100644 --- a/PadelClub/Views/Planning/Components/MatchFormatPickingView.swift +++ b/PadelClub/Views/Planning/Components/MatchFormatPickingView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchFormatPickingView: View { @Environment(Tournament.self) var tournament diff --git a/PadelClub/Views/Planning/Components/MultiCourtPickerView.swift b/PadelClub/Views/Planning/Components/MultiCourtPickerView.swift index 55c2b26..8cbe429 100644 --- a/PadelClub/Views/Planning/Components/MultiCourtPickerView.swift +++ b/PadelClub/Views/Planning/Components/MultiCourtPickerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MultiCourtPickerView: View { @Bindable var matchScheduler: MatchScheduler diff --git a/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift b/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift index 8f05d8a..4223373 100644 --- a/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift +++ b/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CourtAvailabilitySettingsView: View { @Environment(Tournament.self) var tournament: Tournament @@ -15,9 +16,9 @@ struct CourtAvailabilitySettingsView: View { let event: Event @State private var showingPopover: Bool = false - @State private var editingSlot: DateInterval? + @State private var editingSlot: PadelClubData.DateInterval? - var courtsUnavailability: [Int: [DateInterval]] { + var courtsUnavailability: [Int: [PadelClubData.DateInterval]] { let groupedBy = Dictionary(grouping: event.courtsUnavailability, by: { dateInterval in return dateInterval.courtIndex }) @@ -147,14 +148,14 @@ struct CourtAvailabilityEditorView: View { @Environment(Tournament.self) var tournament: Tournament @EnvironmentObject var dataStore: DataStore @Environment(\.dismiss) private var dismiss - var editingSlot: DateInterval? + var editingSlot: PadelClubData.DateInterval? let event: Event @State private var courtIndex: Int @State private var startDate: Date @State private var endDate: Date - init(editingSlot: DateInterval, event: Event) { + init(editingSlot: PadelClubData.DateInterval, event: Event) { self.editingSlot = editingSlot self.event = event _courtIndex = .init(wrappedValue: editingSlot.courtIndex) diff --git a/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift b/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift index a0e6866..df3ce9f 100644 --- a/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift +++ b/PadelClub/Views/Planning/GroupStageScheduleEditorView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct GroupStageScheduleEditorView: View { diff --git a/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift b/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift index 0d5eafb..80bb94f 100644 --- a/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift +++ b/PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct LoserRoundScheduleEditorView: View { diff --git a/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift b/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift index 567c182..69ca2df 100644 --- a/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift +++ b/PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct LoserRoundStepScheduleEditorView: View { diff --git a/PadelClub/Views/Planning/MatchFormatGuideView.swift b/PadelClub/Views/Planning/MatchFormatGuideView.swift index 4596a77..d60e84d 100644 --- a/PadelClub/Views/Planning/MatchFormatGuideView.swift +++ b/PadelClub/Views/Planning/MatchFormatGuideView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchFormatGuideView: View { let matchCounts = Array(2...7) diff --git a/PadelClub/Views/Planning/MatchScheduleEditorView.swift b/PadelClub/Views/Planning/MatchScheduleEditorView.swift index 24f534a..2ba81bf 100644 --- a/PadelClub/Views/Planning/MatchScheduleEditorView.swift +++ b/PadelClub/Views/Planning/MatchScheduleEditorView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct MatchScheduleEditorView: View { @Bindable var match: Match diff --git a/PadelClub/Views/Planning/PlanningByCourtView.swift b/PadelClub/Views/Planning/PlanningByCourtView.swift index 10b4f92..f9ac7b8 100644 --- a/PadelClub/Views/Planning/PlanningByCourtView.swift +++ b/PadelClub/Views/Planning/PlanningByCourtView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PlanningByCourtView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index 2ced50d..74bf764 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct PlanningSettingsView: View { diff --git a/PadelClub/Views/Planning/PlanningView.swift b/PadelClub/Views/Planning/PlanningView.swift index 7d8914c..b72441d 100644 --- a/PadelClub/Views/Planning/PlanningView.swift +++ b/PadelClub/Views/Planning/PlanningView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import TipKit +import PadelClubData struct PlanningView: View { diff --git a/PadelClub/Views/Planning/RoundScheduleEditorView.swift b/PadelClub/Views/Planning/RoundScheduleEditorView.swift index 2c42ac7..ae469f8 100644 --- a/PadelClub/Views/Planning/RoundScheduleEditorView.swift +++ b/PadelClub/Views/Planning/RoundScheduleEditorView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct RoundScheduleEditorView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Planning/SchedulerView.swift b/PadelClub/Views/Planning/SchedulerView.swift index a4e83b6..ed33436 100644 --- a/PadelClub/Views/Planning/SchedulerView.swift +++ b/PadelClub/Views/Planning/SchedulerView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData extension GroupStage: Schedulable { func titleLabel() -> String { diff --git a/PadelClub/Views/Player/Components/EditablePlayerView.swift b/PadelClub/Views/Player/Components/EditablePlayerView.swift index 1f5c291..c0ac878 100644 --- a/PadelClub/Views/Player/Components/EditablePlayerView.swift +++ b/PadelClub/Views/Player/Components/EditablePlayerView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct EditablePlayerView: View { diff --git a/PadelClub/Views/Player/Components/PlayerPayView.swift b/PadelClub/Views/Player/Components/PlayerPayView.swift index 0c989b6..1114120 100644 --- a/PadelClub/Views/Player/Components/PlayerPayView.swift +++ b/PadelClub/Views/Player/Components/PlayerPayView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct PlayerPayView: View { diff --git a/PadelClub/Views/Player/Components/PlayerPopoverView.swift b/PadelClub/Views/Player/Components/PlayerPopoverView.swift index ccad7b1..76758fe 100644 --- a/PadelClub/Views/Player/Components/PlayerPopoverView.swift +++ b/PadelClub/Views/Player/Components/PlayerPopoverView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PlayerPopoverView: View { enum PlayerCreationField { diff --git a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift index 022e037..a315f9d 100644 --- a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift +++ b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PlayerSexPickerView: View { diff --git a/PadelClub/Views/Player/PlayerDetailView.swift b/PadelClub/Views/Player/PlayerDetailView.swift index 4da59fd..9a07ef1 100644 --- a/PadelClub/Views/Player/PlayerDetailView.swift +++ b/PadelClub/Views/Player/PlayerDetailView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct PlayerDetailView: View { diff --git a/PadelClub/Views/Player/PlayerStatisticView.swift b/PadelClub/Views/Player/PlayerStatisticView.swift index 5a2faf3..23a0e20 100644 --- a/PadelClub/Views/Player/PlayerStatisticView.swift +++ b/PadelClub/Views/Player/PlayerStatisticView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct PlayerStatisticView: View { let player: PlayerRegistration diff --git a/PadelClub/Views/Player/PlayerView.swift b/PadelClub/Views/Player/PlayerView.swift index 7dbda7d..c7785bf 100644 --- a/PadelClub/Views/Player/PlayerView.swift +++ b/PadelClub/Views/Player/PlayerView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct PlayerView: View { diff --git a/PadelClub/Views/Round/DrawLogsView.swift b/PadelClub/Views/Round/DrawLogsView.swift index cd16e96..f89c3a7 100644 --- a/PadelClub/Views/Round/DrawLogsView.swift +++ b/PadelClub/Views/Round/DrawLogsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct DrawLogsView: View { @Environment(Tournament.self) var tournament diff --git a/PadelClub/Views/Round/LoserRoundSettingsView.swift b/PadelClub/Views/Round/LoserRoundSettingsView.swift index 5f1ad8a..33e4a0e 100644 --- a/PadelClub/Views/Round/LoserRoundSettingsView.swift +++ b/PadelClub/Views/Round/LoserRoundSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct LoserRoundSettingsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Round/LoserRoundView.swift b/PadelClub/Views/Round/LoserRoundView.swift index 46eed9f..dce0ef7 100644 --- a/PadelClub/Views/Round/LoserRoundView.swift +++ b/PadelClub/Views/Round/LoserRoundView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct LoserRoundView: View { @Environment(Tournament.self) var tournament: Tournament diff --git a/PadelClub/Views/Round/LoserRoundsView.swift b/PadelClub/Views/Round/LoserRoundsView.swift index 5cd2199..fd2e837 100644 --- a/PadelClub/Views/Round/LoserRoundsView.swift +++ b/PadelClub/Views/Round/LoserRoundsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData class UpperRound: Identifiable, Selectable { var id: String { round.id } diff --git a/PadelClub/Views/Round/PreviewBracketPositionView.swift b/PadelClub/Views/Round/PreviewBracketPositionView.swift index f906247..c915d3d 100644 --- a/PadelClub/Views/Round/PreviewBracketPositionView.swift +++ b/PadelClub/Views/Round/PreviewBracketPositionView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PreviewBracketPositionView: View { let seeds: [TeamRegistration] diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 5216ff4..d742100 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct RoundSettingsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index c85cadf..1f8892d 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import TipKit +import PadelClubData struct RoundView: View { diff --git a/PadelClub/Views/Round/RoundsView.swift b/PadelClub/Views/Round/RoundsView.swift index fa49dc2..63a471e 100644 --- a/PadelClub/Views/Round/RoundsView.swift +++ b/PadelClub/Views/Round/RoundsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct RoundsView: View { var tournament: Tournament diff --git a/PadelClub/Views/Score/EditScoreView.swift b/PadelClub/Views/Score/EditScoreView.swift index 63661d8..624f2c5 100644 --- a/PadelClub/Views/Score/EditScoreView.swift +++ b/PadelClub/Views/Score/EditScoreView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct EditScoreView: View { diff --git a/PadelClub/Views/Score/FollowUpMatchView.swift b/PadelClub/Views/Score/FollowUpMatchView.swift index 27a88a1..9e0fdab 100644 --- a/PadelClub/Views/Score/FollowUpMatchView.swift +++ b/PadelClub/Views/Score/FollowUpMatchView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct FollowUpMatchView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Score/PointSelectionView.swift b/PadelClub/Views/Score/PointSelectionView.swift index 1b70405..a716c57 100644 --- a/PadelClub/Views/Score/PointSelectionView.swift +++ b/PadelClub/Views/Score/PointSelectionView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PointSelectionView: View { @Binding var valueSelected: Int? diff --git a/PadelClub/Views/Score/PointView.swift b/PadelClub/Views/Score/PointView.swift index c72fb1d..b254086 100644 --- a/PadelClub/Views/Score/PointView.swift +++ b/PadelClub/Views/Score/PointView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PointView: View { let value: String diff --git a/PadelClub/Views/Score/SetInputView.swift b/PadelClub/Views/Score/SetInputView.swift index ff87f39..4a2a5e3 100644 --- a/PadelClub/Views/Score/SetInputView.swift +++ b/PadelClub/Views/Score/SetInputView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct SetInputView: View { @Binding var setDescriptor: SetDescriptor diff --git a/PadelClub/Views/Score/SetLabelView.swift b/PadelClub/Views/Score/SetLabelView.swift index abe6c28..beec014 100644 --- a/PadelClub/Views/Score/SetLabelView.swift +++ b/PadelClub/Views/Score/SetLabelView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct SetLabelView: View { @Binding var initialValueLeft: Int? diff --git a/PadelClub/Views/Shared/DateMenuView.swift b/PadelClub/Views/Shared/DateMenuView.swift index 5e3ed15..13ea453 100644 --- a/PadelClub/Views/Shared/DateMenuView.swift +++ b/PadelClub/Views/Shared/DateMenuView.swift @@ -7,6 +7,7 @@ import SwiftUI +import PadelClubData struct DateMenuView: View { @Binding var date: Date diff --git a/PadelClub/Views/Shared/ImportedPlayerView.swift b/PadelClub/Views/Shared/ImportedPlayerView.swift index c46b7b2..c476ce0 100644 --- a/PadelClub/Views/Shared/ImportedPlayerView.swift +++ b/PadelClub/Views/Shared/ImportedPlayerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct ImportedPlayerView: View { let player: PlayerHolder diff --git a/PadelClub/Views/Shared/InscriptionLegendView.swift b/PadelClub/Views/Shared/InscriptionLegendView.swift index 8bc6fbd..b8c75bb 100644 --- a/PadelClub/Views/Shared/InscriptionLegendView.swift +++ b/PadelClub/Views/Shared/InscriptionLegendView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct InscriptionLegendView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Shared/LearnMoreSheetView.swift b/PadelClub/Views/Shared/LearnMoreSheetView.swift index a8bf0b9..47ab4ef 100644 --- a/PadelClub/Views/Shared/LearnMoreSheetView.swift +++ b/PadelClub/Views/Shared/LearnMoreSheetView.swift @@ -6,6 +6,7 @@ A view that gets displayed when the learn more action buttons is tapped. */ import SwiftUI +import PadelClubData struct LearnMoreSheetView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Shared/MatchFormatRowView.swift b/PadelClub/Views/Shared/MatchFormatRowView.swift index b253e63..c7e1ca4 100644 --- a/PadelClub/Views/Shared/MatchFormatRowView.swift +++ b/PadelClub/Views/Shared/MatchFormatRowView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchFormatRowView: View { let matchFormat: MatchFormat diff --git a/PadelClub/Views/Shared/MatchFormatSelectionView.swift b/PadelClub/Views/Shared/MatchFormatSelectionView.swift index 3249090..cda435f 100644 --- a/PadelClub/Views/Shared/MatchFormatSelectionView.swift +++ b/PadelClub/Views/Shared/MatchFormatSelectionView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchFormatSelectionView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Shared/MatchTypeSelectionView.swift b/PadelClub/Views/Shared/MatchTypeSelectionView.swift index 2801067..bf6696b 100644 --- a/PadelClub/Views/Shared/MatchTypeSelectionView.swift +++ b/PadelClub/Views/Shared/MatchTypeSelectionView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct MatchTypeSelectionView: View { @Binding var selectedFormat: MatchFormat diff --git a/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift b/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift index d143d9b..235d9c1 100644 --- a/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift +++ b/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct OnlineWaitingListFaqSheetView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Shared/PaymentInfoSheetView.swift b/PadelClub/Views/Shared/PaymentInfoSheetView.swift index 1a79c5e..4f4a440 100644 --- a/PadelClub/Views/Shared/PaymentInfoSheetView.swift +++ b/PadelClub/Views/Shared/PaymentInfoSheetView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct PaymentInfoSheetView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Shared/RegistrationInfoSheetView.swift b/PadelClub/Views/Shared/RegistrationInfoSheetView.swift index a6dd9a4..5319666 100644 --- a/PadelClub/Views/Shared/RegistrationInfoSheetView.swift +++ b/PadelClub/Views/Shared/RegistrationInfoSheetView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct RegistrationInfoSheetView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index 16b7d7e..e03b35f 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -9,10 +9,36 @@ import SwiftUI import CoreData import Combine import LeStorage +import PadelClubData typealias PlayerSelectionAction = ((Set) -> ()) typealias ContentUnavailableAction = ((SearchViewModel) -> ()) +struct SomeSectionView: View { + + @StateObject private var searchViewModel: SearchViewModel + + var body: some View { + Section { + ForEach(SourceFileManager.getSortOption()) { option in + Toggle(isOn: .init(get: { + return searchViewModel.sortOption == option + }, set: { value in + if searchViewModel.sortOption == option { + searchViewModel.ascending.toggle() + } + searchViewModel.sortOption = option + })) { + Label(option.localizedLabel(), systemImage: searchViewModel.sortOption == option ? (searchViewModel.ascending ? "chevron.up" : "chevron.down") : "") + } + } + } header: { + Text("Trier par") + } + + } +} + struct SelectablePlayerListView: View { let allowSelection: Int let playerSelectionAction: PlayerSelectionAction? diff --git a/PadelClub/Views/Shared/SupportButtonView.swift b/PadelClub/Views/Shared/SupportButtonView.swift index b715356..9304c8a 100644 --- a/PadelClub/Views/Shared/SupportButtonView.swift +++ b/PadelClub/Views/Shared/SupportButtonView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import Zip +import PadelClubData extension URL: Identifiable { public var id: String { @@ -96,7 +97,7 @@ struct SupportButtonView: View { private func _zip() { do { - let filePath = try Club.storageDirectoryPath() + let filePath = try StoreCenter.main.directoryURL() self.zipFilePath = try Zip.quickZipFiles([filePath], fileName: "backup") // Zip } catch { Logger.error(error) diff --git a/PadelClub/Views/Shared/TournamentFilterView.swift b/PadelClub/Views/Shared/TournamentFilterView.swift index c926192..3367e0f 100644 --- a/PadelClub/Views/Shared/TournamentFilterView.swift +++ b/PadelClub/Views/Shared/TournamentFilterView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentFilterView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Team/CoachListView.swift b/PadelClub/Views/Team/CoachListView.swift index f5a771e..06fca54 100644 --- a/PadelClub/Views/Team/CoachListView.swift +++ b/PadelClub/Views/Team/CoachListView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CoachListView: View { @Environment(Tournament.self) var tournament diff --git a/PadelClub/Views/Team/Components/TeamHeaderView.swift b/PadelClub/Views/Team/Components/TeamHeaderView.swift index 69b2013..4228994 100644 --- a/PadelClub/Views/Team/Components/TeamHeaderView.swift +++ b/PadelClub/Views/Team/Components/TeamHeaderView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TeamHeaderView: View { var team: TeamRegistration diff --git a/PadelClub/Views/Team/Components/TeamWeightView.swift b/PadelClub/Views/Team/Components/TeamWeightView.swift index a2dbdf5..8fcdf67 100644 --- a/PadelClub/Views/Team/Components/TeamWeightView.swift +++ b/PadelClub/Views/Team/Components/TeamWeightView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TeamWeightView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 7a5ca19..236ecfe 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct EditingTeamView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Team/TeamDetailView.swift b/PadelClub/Views/Team/TeamDetailView.swift index 969731d..cf14d52 100644 --- a/PadelClub/Views/Team/TeamDetailView.swift +++ b/PadelClub/Views/Team/TeamDetailView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TeamDetailView: View { @Environment(Tournament.self) var tournament: Tournament diff --git a/PadelClub/Views/Team/TeamPickerView.swift b/PadelClub/Views/Team/TeamPickerView.swift index d00d67a..15282da 100644 --- a/PadelClub/Views/Team/TeamPickerView.swift +++ b/PadelClub/Views/Team/TeamPickerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TeamPickerView: View { @EnvironmentObject var dataStore: DataStore @@ -14,7 +15,7 @@ struct TeamPickerView: View { @State private var confirmTeam: TeamRegistration? @State private var presentTeamPickerView: Bool = false @State private var searchField: String = "" - @State private var sortOrder: SortOrder = .ascending + @State private var sortOrder: PadelClubData.SortOrder = .ascending var shouldConfirm: Bool = false var groupStagePosition: Int? = nil diff --git a/PadelClub/Views/Team/TeamRestingView.swift b/PadelClub/Views/Team/TeamRestingView.swift index 712cb05..f828cf2 100644 --- a/PadelClub/Views/Team/TeamRestingView.swift +++ b/PadelClub/Views/Team/TeamRestingView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TeamRestingView: View { @Environment(Tournament.self) var tournament: Tournament diff --git a/PadelClub/Views/Team/TeamRowView.swift b/PadelClub/Views/Team/TeamRowView.swift index 13b5ee0..fe94214 100644 --- a/PadelClub/Views/Team/TeamRowView.swift +++ b/PadelClub/Views/Team/TeamRowView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TeamRowView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/ConsolationTournamentImportView.swift b/PadelClub/Views/Tournament/ConsolationTournamentImportView.swift index 835e416..50e5d1b 100644 --- a/PadelClub/Views/Tournament/ConsolationTournamentImportView.swift +++ b/PadelClub/Views/Tournament/ConsolationTournamentImportView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct ConsolationTournamentImportView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index 2646da5..d1d26f2 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -8,6 +8,7 @@ import SwiftUI import TipKit import LeStorage +import PadelClubData enum FileImportCustomField: Int, Identifiable, CaseIterable { var id: Int { self.rawValue } diff --git a/PadelClub/Views/Tournament/Screen/AddTeamView.swift b/PadelClub/Views/Tournament/Screen/AddTeamView.swift index 40c0cff..382fbed 100644 --- a/PadelClub/Views/Tournament/Screen/AddTeamView.swift +++ b/PadelClub/Views/Tournament/Screen/AddTeamView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import CoreData +import PadelClubData struct AddTeamView: View { diff --git a/PadelClub/Views/Tournament/Screen/BroadcastView.swift b/PadelClub/Views/Tournament/Screen/BroadcastView.swift index 4dcc1ba..fce7e44 100644 --- a/PadelClub/Views/Tournament/Screen/BroadcastView.swift +++ b/PadelClub/Views/Tournament/Screen/BroadcastView.swift @@ -9,6 +9,7 @@ import SwiftUI import CoreImage.CIFilterBuiltins import LeStorage import TipKit +import PadelClubData struct BroadcastView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/Screen/Components/CloseDatePicker.swift b/PadelClub/Views/Tournament/Screen/Components/CloseDatePicker.swift index 32082ac..5fc2eae 100644 --- a/PadelClub/Views/Tournament/Screen/Components/CloseDatePicker.swift +++ b/PadelClub/Views/Tournament/Screen/Components/CloseDatePicker.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct CloseDatePicker: View { @State var closedRegistrationDate: Date diff --git a/PadelClub/Views/Tournament/Screen/Components/EventClubSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/EventClubSettingsView.swift index 86df0ef..c7127cf 100644 --- a/PadelClub/Views/Tournament/Screen/Components/EventClubSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/EventClubSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct EventClubSettingsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift index b83cc68..d8e6561 100644 --- a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct InscriptionInfoView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/Screen/Components/RefundResultsView.swift b/PadelClub/Views/Tournament/Screen/Components/RefundResultsView.swift index f3e536a..35355e7 100644 --- a/PadelClub/Views/Tournament/Screen/Components/RefundResultsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/RefundResultsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct RefundResultsView: View { @Binding var results: [RefundResult] diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift index 14985a6..563d350 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentCategorySettingsView: View { @Bindable var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift index e5ef7e7..6d63c78 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentClubSettingsView: View { @Environment(Tournament.self) private var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentDatePickerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentDatePickerView.swift index b3f8ce8..a00c758 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentDatePickerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentDatePickerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentDatePickerView: View { @Environment(Tournament.self) private var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift index aded23e..93d3b5a 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentDurationManagerView: View { @Environment(Tournament.self) private var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift index 512288e..d613eca 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentFieldsManagerView: View { let localizedStringKey: String diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentFormatSelectionView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentFormatSelectionView.swift index 5f6f301..3cc37ea 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentFormatSelectionView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentFormatSelectionView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentFormatSelectionView: View { @Environment(Tournament.self) private var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 621ca55..ae55917 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentGeneralSettingsView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift index beeb89e..dc58592 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentLevelPickerView: View { @Environment(Tournament.self) private var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift index 06b57d8..bc3906d 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentMatchFormatsSettingsView: View { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift index d4b989e..150d8ac 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentStatusView: View { @Environment(\.dismiss) private var dismiss diff --git a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift index 0b8cfb0..35186b1 100644 --- a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct UpdateSourceRankDateView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 479781d..cde3a24 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -8,6 +8,7 @@ import SwiftUI import TipKit import LeStorage +import PadelClubData //let slideToDeleteTip = SlideToDeleteTip() let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip() diff --git a/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift b/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift index f9206bf..f5b351b 100644 --- a/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift @@ -7,6 +7,7 @@ import SwiftUI import WebKit +import PadelClubData struct PrintSettingsView: View { let tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index c59bfc4..2bfb573 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -7,6 +7,7 @@ import LeStorage import SwiftUI +import PadelClubData struct RegistrationSetupView: View { @EnvironmentObject var dataStore: DataStore @@ -614,56 +615,3 @@ struct RegistrationSetupView: View { } } - -enum OnlineRegistrationStatus: Int { - case open = 1 - case notEnabled = 2 - case notStarted = 3 - case ended = 4 - case waitingListPossible = 5 - case waitingListFull = 6 - case inProgress = 7 - case endedWithResults = 8 - - var displayName: String { - switch self { - case .open: - return "Open" - case .notEnabled: - return "Not Enabled" - case .notStarted: - return "Not Started" - case .ended: - return "Ended" - case .waitingListPossible: - return "Waiting List Possible" - case .waitingListFull: - return "Waiting List Full" - case .inProgress: - return "In Progress" - case .endedWithResults: - return "Ended with Results" - } - } - - func statusLocalized() -> String { - switch self { - case .open: - return "Inscription ouverte" - case .notEnabled: - return "Inscription désactivée" - case .notStarted: - return "Inscription pas encore ouverte" - case .ended: - return "Inscription terminée" - case .waitingListPossible: - return "Liste d'attente disponible" - case .waitingListFull: - return "Liste d'attente complète" - case .inProgress: - return "Tournoi en cours" - case .endedWithResults: - return "Tournoi terminé" - } - } -} diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index 027c00f..8d30e92 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TableStructureView: View { @Environment(Tournament.self) private var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/TournamentCallView.swift b/PadelClub/Views/Tournament/Screen/TournamentCallView.swift index 16a9af1..d6eb3c1 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentCallView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentCallView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData enum CallDestination: Identifiable, Selectable, Equatable { diff --git a/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift b/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift index c4c79ef..66b1fc5 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift @@ -7,6 +7,7 @@ import SwiftUI import Combine +import PadelClubData enum CashierDestination: Identifiable, Selectable, Equatable { diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index b1bcd21..cfd8944 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentRankView: View { @Environment(Tournament.self) var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift b/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift index 1fdc503..30da32a 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData protocol Schedulable: Identifiable { var startDate: Date? { get set } diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index c0d7b8b..b11cc37 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData enum TournamentSettings: Identifiable, Selectable, Equatable { static func == (lhs: TournamentSettings, rhs: TournamentSettings) -> Bool { diff --git a/PadelClub/Views/Tournament/Shared/TournamentBroadcastRowView.swift b/PadelClub/Views/Tournament/Shared/TournamentBroadcastRowView.swift index beece4c..2d8a1e3 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentBroadcastRowView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentBroadcastRowView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentBroadcastRowView: View { var tournament: Tournament diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 88ccd5d..d979a24 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentCellView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/Subscription/Guard.swift b/PadelClub/Views/Tournament/Subscription/Guard.swift deleted file mode 100644 index 9cbdf79..0000000 --- a/PadelClub/Views/Tournament/Subscription/Guard.swift +++ /dev/null @@ -1,336 +0,0 @@ -// -// Guard.swift -// Poker Analytics 6 -// -// Created by Laurent Morvillier on 20/04/2022. -// - -import Foundation -import StoreKit -import LeStorage - -@available(iOS 15, *) -@objc class Guard: NSObject { - - static var main: Guard = Guard() - - @Published private(set) var purchasedTransactions = Set() - - var currentBestPurchase: Purchase? = nil - - var updateListenerTask: Task? = nil - - let freeTournaments: Int = 3 - - override init() { - - super.init() - - self.updateListenerTask = self.listenForTransactions() - - Task { - do { - try await self.refreshPurchasedAppleProducts() - } catch { - Logger.error(error) - } - Logger.log("plan = \(String(describing: currentBestPurchase?.productId))") - } - - NotificationCenter.default.addObserver(self, selector: #selector(collectionDidLoad), name: NSNotification.Name.CollectionDidLoad, object: nil) - - } - - deinit { - self.updateListenerTask?.cancel() - NotificationCenter.default.removeObserver(self) - } - - @objc func collectionDidLoad(notification: Notification) { - if let _ = notification.object as? StoredCollection { - self._updateBestPlan() - } - } - - func productIds() async -> [String] { - var productIds: [String] = [] - for await result in Transaction.all { - do { - let verified = try self.checkVerified(result) - productIds.append(verified.productID) - } catch { - Logger.error(error) - } - } - return productIds - } - - func refreshPurchasedAppleProducts() async throws { - - // Iterate through the user's purchased products. - for await verificationResult in Transaction.currentEntitlements { - let transaction = try await self.processTransactionResult(verificationResult) - print("processs product id = \(transaction.productID)") - DispatchQueue.main.async { - NotificationCenter.default.post(name: Notification.Name.StoreEventHappened, object: nil) - } - await transaction.finish() - } - } - - func listenForTransactions() -> Task { - return Task(priority: .background) { - //Iterate through any transactions which didn't come from a direct call to `purchase()`. - for await result in Transaction.updates { -// Logger.log(">>> update = \(result)") - do { - let transaction = try self.checkVerified(result) - - //Deliver content to the user. - await self.updatePurchasedIdentifiers(transaction) - - //Always finish a transaction. - await transaction.finish() - } catch { - Logger.error(error) - //StoreKit has a receipt it can read but it failed verification. Don't deliver content to the user. - print("Transaction failed verification") - } - } - } - } - - func checkVerified(_ result: VerificationResult) throws -> T { - //Check if the transaction passes StoreKit verification. - switch result { - case .unverified: - //StoreKit has parsed the JWS but failed verification. Don't deliver content to the user. - throw StoreManagerError.failedVerification - case .verified(let safe): - //If the transaction is verified, unwrap and return it. - return safe - } - } - - @MainActor - func updatePurchasedIdentifiers(_ transaction: StoreKit.Transaction) async { - -// Logger.log("\(transaction.productID) > purchase = \(transaction.originalPurchaseDate), exp date= \(transaction.expirationDate), rev date = \(transaction.revocationDate)") - -// Logger.log("purchase date = \(transaction.purchaseDate)") - - do { - if transaction.revocationDate == nil { - // If the App Store has not revoked the transaction, add it to the list of `purchasedIdentifiers`. - purchasedTransactions.insert(transaction) - -// try self._addPurchaseIfPossible(transaction: transaction) - } else { - // If the App Store has revoked this transaction, remove it from the list of `purchasedIdentifiers`. - purchasedTransactions.remove(transaction) -// try self._updatePurchaseIfPossible(transaction: transaction) - } - - try self._updatePurchase(transaction: transaction) - - } catch { - Logger.error(error) - } - - self._updateBestPlan() - } - - fileprivate func _updatePurchase(transaction: StoreKit.Transaction) throws { - let purchases = DataStore.shared.purchases - if let purchase = self._purchaseByTransactionId(transaction.originalID) { - purchase.revocationDate = transaction.revocationDate - purchase.expirationDate = transaction.expirationDate - purchase.purchaseDate = transaction.purchaseDate - purchase.productId = transaction.productID - try purchases.addOrUpdate(instance: purchase) - } else { - let purchase: Purchase = try transaction.purchase() - try purchases.addOrUpdate(instance: purchase) - } - } - -// fileprivate func _addPurchaseIfPossible(transaction: StoreKit.Transaction) throws { -// -// let purchases = DataStore.shared.purchases -// -// if self._purchaseByTransactionId(transaction.originalID) == nil { -// let purchase: Purchase = try transaction.purchase() -// try purchases.addOrUpdate(instance: purchase) -// } -// } -// -// fileprivate func _updatePurchaseIfPossible(transaction: StoreKit.Transaction) throws { -// let purchases = DataStore.shared.purchases -// if let existing: Purchase = self._purchaseByTransactionId(transaction.originalID) { -// existing.revocationDate = transaction.revocationDate -// try purchases.addOrUpdate(instance: existing) -// } -// } - - fileprivate func _purchaseByTransactionId(_ transactionId: UInt64) -> Purchase? { - let purchases = DataStore.shared.purchases - return purchases.first(where: { $0.id == transactionId }) - } - - func processTransactionResult(_ result: VerificationResult) async throws -> StoreKit.Transaction { - - let transaction = try checkVerified(result) - - // Deliver content to the user. - await updatePurchasedIdentifiers(transaction) - - return transaction - } - - var currentPlan: StoreItem? { - #if DEBUG - if let plan = PListReader.readString(plist: "local", key: "plan"), !plan.isEmpty { - return StoreItem(rawValue: plan) - } - #elseif TESTFLIGHT - return .monthlyUnlimited - #elseif PRODTEST - return .monthlyUnlimited - #else - if let currentBestPurchase = self.currentBestPurchase, let plan = StoreItem(rawValue: currentBestPurchase.productId) { - return plan - } - #endif - return nil - } - - func userFilteredPurchases() -> [StoreKit.Transaction] { -// Logger.log("self.purchasedTransactions = \(self.purchasedTransactions.count)") - guard let userId = StoreCenter.main.userId, let currentUserUUID: UUID = UUID(uuidString: userId) else { - return [] - } - - let userTransactions = self.purchasedTransactions.filter { currentUserUUID == $0.appAccountToken || $0.appAccountToken == nil } - - let now: Date = Date() -// print("now = \(now)") - return userTransactions.filter { transaction in - if let expirationDate = transaction.expirationDate { -// print("exp = \(expirationDate)") - return expirationDate > now - } else { - return true - } - } - - } - - /// Update best plan by filtering Apple purchases with registered purchases by the user - fileprivate func _updateBestPlan() { - - var purchases: [Purchase] = [] - - // Make sure the purchase has been done with the logged user - let userPurchases = self.userFilteredPurchases().compactMap { try? $0.purchase() } - purchases.append(contentsOf: userPurchases) - - let validPurchases = DataStore.shared.purchases.filter { $0.isValid() } - Logger.log("valid purchases = \(validPurchases.count)") - purchases.append(contentsOf: validPurchases) - - if let purchase = purchases.first(where: { $0.productId == StoreItem.monthlyUnlimited.rawValue }) { - self.currentBestPurchase = purchase - } else if let purchase = purchases.first(where: { $0.productId == StoreItem.fivePerMonth.rawValue }) { - self.currentBestPurchase = purchase - } - - } - - fileprivate func _purchasedTournamentCount() -> Int { - - let purchases = DataStore.shared.purchases - - let units = purchases.filter { $0.productId == StoreItem.unit.rawValue } - return units.reduce(0) { $0 + ($1.quantity ?? 0) } - -// let units = self.userFilteredPurchases().filter { $0.productID == StoreItem.unit.rawValue } -// return units.reduce(0) { $0 + $1.purchasedQuantity } - } - - func paymentForNewTournament() -> TournamentPayment? { - - switch self.currentPlan { - case .monthlyUnlimited: - return TournamentPayment.unlimited - case .fivePerMonth: - if let purchaseDate = self.currentBestPurchase?.purchaseDate { - let count = DataStore.shared.subscriptionUnitlyPayedTournaments(after: purchaseDate) - if count < StoreItem.five { - return TournamentPayment.subscriptionUnit - } - } - return self._paymentWithoutSubscription() - default: - return self._paymentWithoutSubscription() - } - - } - - fileprivate func _paymentWithoutSubscription() -> TournamentPayment? { - let freelyPayed: Int = DataStore.shared.tournaments.filter { $0.payment == .free && $0.isCanceled == false }.count - if freelyPayed < self.freeTournaments { - 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 TournamentPayment.unit - } - return nil - } - - var remainingTournaments: Int { - let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == TournamentPayment.unit }.count - let tournamentCreditCount = self._purchasedTournamentCount() - return tournamentCreditCount - unitlyPayed - } - - var remainingFreeTournaments: Int { - let freelyPayed = DataStore.shared.tournaments.filter { $0.payment == TournamentPayment.free }.count - return self.freeTournaments - freelyPayed - } - - func disconnect() { - let purchases = DataStore.shared.purchases - purchases.reset() - } - -} - -struct PurchaseRow: Identifiable { - var id: UInt64 - var name: String - var item: StoreItem - var quantity: Int? - var expirationDate: Date? - var remainingCount: Int? = nil -} - -fileprivate extension StoreKit.Transaction { - - func purchase() throws -> Purchase { - guard let userId = StoreCenter.main.userId else { - throw StoreError.missingUserId - } - - return Purchase(transactionId: self.originalID, - user: userId, - purchaseDate: self.purchaseDate, - productId: self.productID, - quantity: self.purchasedQuantity, - revocationDate: self.revocationDate, - expirationDate: self.expirationDate) - - } - -} diff --git a/PadelClub/Views/Tournament/Subscription/PaymentStatusView.swift b/PadelClub/Views/Tournament/Subscription/PaymentStatusView.swift index 3e141e7..08c9db0 100644 --- a/PadelClub/Views/Tournament/Subscription/PaymentStatusView.swift +++ b/PadelClub/Views/Tournament/Subscription/PaymentStatusView.swift @@ -7,6 +7,7 @@ import SwiftUI import TipKit +import PadelClubData struct ImageInfoView: View { diff --git a/PadelClub/Views/Tournament/Subscription/Purchase.swift b/PadelClub/Views/Tournament/Subscription/Purchase.swift deleted file mode 100644 index d0fcd3c..0000000 --- a/PadelClub/Views/Tournament/Subscription/Purchase.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// Purchase.swift -// LeStorage -// -// Created by Laurent Morvillier on 12/04/2024. -// - -import Foundation -import LeStorage - -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 - - init(transactionId: UInt64, user: String, 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) - -// self.id = transactionId -// self.lastUpdate = Date() -// self.user = user -// self.purchaseDate = purchaseDate -// self.productId = productId -// self.quantity = quantity -// self.revocationDate = revocationDate -// self.expirationDate = expirationDate - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - -// 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 { - return false - } - guard let expiration = self.expirationDate else { - return false - } - 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) -// } - -} diff --git a/PadelClub/Views/Tournament/Subscription/PurchaseListView.swift b/PadelClub/Views/Tournament/Subscription/PurchaseListView.swift index cd7bd68..0549710 100644 --- a/PadelClub/Views/Tournament/Subscription/PurchaseListView.swift +++ b/PadelClub/Views/Tournament/Subscription/PurchaseListView.swift @@ -8,6 +8,7 @@ import SwiftUI import StoreKit import LeStorage +import PadelClubData class PurchaseManager: ObservableObject { diff --git a/PadelClub/Views/Tournament/Subscription/StoreItem.swift b/PadelClub/Views/Tournament/Subscription/StoreItem.swift deleted file mode 100644 index 79bab93..0000000 --- a/PadelClub/Views/Tournament/Subscription/StoreItem.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// StoreItem.swift -// Padel Club -// -// Created by Laurent Morvillier on 22/04/2024. -// - -import Foundation - -enum StoreItem: String, Identifiable, CaseIterable { - case monthlyUnlimited = "app.padelclub.tournament.subscription.unlimited" - case fivePerMonth = "app.padelclub.tournament.subscription.five.per.month" - case unit = "app.padelclub.tournament.unit" - - #if DEBUG - static let five: Int = 2 - #else - static let five: Int = 5 - #endif - - var id: String { return self.rawValue } - - var systemImage: String { - switch self { - case .monthlyUnlimited: return "infinity.circle.fill" - case .fivePerMonth: return "star.circle.fill" - case .unit: return "tennisball.circle.fill" - } - } - - var isConsumable: Bool { - switch self { - case .monthlyUnlimited, .fivePerMonth: return false - case .unit: return true - } - } - -} diff --git a/PadelClub/Views/Tournament/Subscription/StoreManager.swift b/PadelClub/Views/Tournament/Subscription/StoreManager.swift deleted file mode 100644 index c70446c..0000000 --- a/PadelClub/Views/Tournament/Subscription/StoreManager.swift +++ /dev/null @@ -1,140 +0,0 @@ -// -// Store.swift -// Poker Analytics 6 -// -// Created by Laurent Morvillier on 20/04/2022. -// - -import Foundation -import StoreKit -import LeStorage - -public enum StoreManagerError: Error { - case failedVerification - case missingPlan -} - -protocol StoreDelegate { - func productsReceived(products: [Product]) - func errorDidOccur(error: Error) -} - -extension Notification.Name { - static let StoreEventHappened = Notification.Name("storePurchaseSucceeded") -} - -class StoreManager { - - @Published private(set) var purchasedTransactions = Set() - - var delegate: StoreDelegate? = nil - - var updateListenerTask: Task? = nil - - init(delegate: StoreDelegate?) { - - self.delegate = delegate - self.updateListenerTask = listenForTransactions() - - Task { - //Initialize the store by starting a product request. - await self.requestProducts() - } - } - - deinit { - self.updateListenerTask?.cancel() - } - - @MainActor - func requestProducts() async { - do { -// let identifiers: [String] = StoreItem.allCases.map { $0.rawValue } - - var products: [Product] = try await Product.products(for: self._productIdentifiers()) - products = products.sorted { p1, p2 in - return p2.price > p1.price - } - - Logger.log("products = \(products.count)") - self.delegate?.productsReceived(products: products) - } catch { - self.delegate?.errorDidOccur(error: error) - Logger.error(error) - } - } - - fileprivate func _productIdentifiers() -> [String] { - var items: [StoreItem] = [] - switch Guard.main.currentPlan { - case .fivePerMonth: - items = [StoreItem.unit, StoreItem.monthlyUnlimited] - case .monthlyUnlimited: - break - default: - items = StoreItem.allCases - } - return items.map { $0.rawValue } - } - - func listenForTransactions() -> Task { - return Task.detached { - //Iterate through any transactions which didn't come from a direct call to `purchase()`. - for await result in Transaction.updates { - do { - - let transaction = try await Guard.main.processTransactionResult(result) - - //Always finish a transaction. - await transaction.finish() - } catch { - self.delegate?.errorDidOccur(error: error) - - //StoreKit has a receipt it can read but it failed verification. Don't deliver content to the user. - print("Transaction failed verification") - } - } - } - } - - func purchase(_ product: Product, quantity: Int? = nil) async throws -> StoreKit.Transaction? { - Logger.log("Store purchase started...") - - guard let userId = StoreCenter.main.userId, let uuid: UUID = UUID(uuidString: userId) else { - throw StoreError.missingUserId - } - - var options: Set = [] - let tokenOption = Product.PurchaseOption.appAccountToken(uuid) - options.insert(tokenOption) - - if let quantity = quantity { - let quantityOption = Product.PurchaseOption.quantity(quantity) - options.insert(quantityOption) - } - - let result = try await product.purchase(options: options) - - Logger.log("Store purchase ended with result: \(result)") - - switch result { - case .success(let verificationResult): - - let transaction = try await Guard.main.processTransactionResult(verificationResult) - - // Always finish a transaction. - await transaction.finish() - - DispatchQueue.main.asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 100000), execute: { - NotificationCenter.default.post(name: Notification.Name.StoreEventHappened, object: nil) - }) - - return transaction - case .userCancelled, .pending: - return nil - default: - return nil - } - } - -} diff --git a/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift b/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift index d501299..a94c903 100644 --- a/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift @@ -7,6 +7,7 @@ import SwiftUI import TipKit +import PadelClubData struct SubscriptionInfoView: View { diff --git a/PadelClub/Views/Tournament/Subscription/SubscriptionView.swift b/PadelClub/Views/Tournament/Subscription/SubscriptionView.swift index a4e109f..e51a67c 100644 --- a/PadelClub/Views/Tournament/Subscription/SubscriptionView.swift +++ b/PadelClub/Views/Tournament/Subscription/SubscriptionView.swift @@ -8,6 +8,7 @@ import SwiftUI import StoreKit import LeStorage +import PadelClubData extension Product.SubscriptionPeriod.Unit { var label: String { diff --git a/PadelClub/Views/Tournament/TournamentBuildView.swift b/PadelClub/Views/Tournament/TournamentBuildView.swift index 35c1a2c..00cee8d 100644 --- a/PadelClub/Views/Tournament/TournamentBuildView.swift +++ b/PadelClub/Views/Tournament/TournamentBuildView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentBuildView: View { var tournament: Tournament diff --git a/PadelClub/Views/Tournament/TournamentInitView.swift b/PadelClub/Views/Tournament/TournamentInitView.swift index 4530929..bb13869 100644 --- a/PadelClub/Views/Tournament/TournamentInitView.swift +++ b/PadelClub/Views/Tournament/TournamentInitView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentInitView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/TournamentInscriptionView.swift b/PadelClub/Views/Tournament/TournamentInscriptionView.swift index fcb66e7..4c8933f 100644 --- a/PadelClub/Views/Tournament/TournamentInscriptionView.swift +++ b/PadelClub/Views/Tournament/TournamentInscriptionView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct TournamentInscriptionView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/Tournament/TournamentRunningView.swift b/PadelClub/Views/Tournament/TournamentRunningView.swift index d28620a..fd6e575 100644 --- a/PadelClub/Views/Tournament/TournamentRunningView.swift +++ b/PadelClub/Views/Tournament/TournamentRunningView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import PadelClubData struct TournamentRunningView: View { var tournament: Tournament diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 56a4878..d0fdd52 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -8,6 +8,7 @@ import SwiftUI import LeStorage import TipKit +import PadelClubData struct TournamentView: View { @EnvironmentObject var dataStore: DataStore diff --git a/PadelClub/Views/User/AccountView.swift b/PadelClub/Views/User/AccountView.swift index 539a2d4..1a35ece 100644 --- a/PadelClub/Views/User/AccountView.swift +++ b/PadelClub/Views/User/AccountView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct AccountView: View { diff --git a/PadelClub/Views/User/ChangePasswordView.swift b/PadelClub/Views/User/ChangePasswordView.swift index 9c445f6..c7fa1cb 100644 --- a/PadelClub/Views/User/ChangePasswordView.swift +++ b/PadelClub/Views/User/ChangePasswordView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct ChangePasswordView: View { diff --git a/PadelClub/Views/User/LoginView.swift b/PadelClub/Views/User/LoginView.swift index 57e36d4..fd352f6 100644 --- a/PadelClub/Views/User/LoginView.swift +++ b/PadelClub/Views/User/LoginView.swift @@ -7,6 +7,8 @@ import SwiftUI import LeStorage +import PadelClubData +import Algorithms enum LoginReason { case loginRequiredForFeature diff --git a/PadelClub/Views/User/ShareModelView.swift b/PadelClub/Views/User/ShareModelView.swift index c281ccd..d1a1960 100644 --- a/PadelClub/Views/User/ShareModelView.swift +++ b/PadelClub/Views/User/ShareModelView.swift @@ -8,6 +8,7 @@ import Combine import LeStorage import SwiftUI +import PadelClubData class UserSearchViewModel: ObservableObject { diff --git a/PadelClub/Views/User/UserCreationView.swift b/PadelClub/Views/User/UserCreationView.swift index 60a5a31..f08e8a0 100644 --- a/PadelClub/Views/User/UserCreationView.swift +++ b/PadelClub/Views/User/UserCreationView.swift @@ -7,6 +7,7 @@ import SwiftUI import LeStorage +import PadelClubData struct UserCreationFormView: View {