From 119d7e53c9825637d81f64629dcc58fe614e9c80 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 20 Feb 2025 10:59:31 +0100 Subject: [PATCH 01/18] fix wildcard management fix p500 dates and schedule add format aide memoire optimize selectedsorted teams calls --- PadelClub.xcodeproj/project.pbxproj | 12 +- PadelClub/Data/TeamRegistration.swift | 8 +- PadelClub/Data/Tournament.swift | 26 ++-- PadelClub/Utils/PadelRule.swift | 34 +++-- PadelClub/Views/Calling/SendToAllView.swift | 7 +- .../Toolbox/MatchFormatStorageView.swift | 2 +- .../Views/Planning/MatchFormatGuideView.swift | 72 +++++++++++ .../Views/Planning/PlanningSettingsView.swift | 25 +++- .../Team/Components/TeamWeightView.swift | 7 +- PadelClub/Views/Team/EditingTeamView.swift | 3 +- PadelClub/Views/Team/TeamRowView.swift | 3 +- .../Views/Tournament/FileImportView.swift | 2 +- .../TournamentMatchFormatsSettingsView.swift | 2 +- .../Screen/InscriptionManagerView.swift | 121 +++++++++--------- 14 files changed, 221 insertions(+), 103 deletions(-) create mode 100644 PadelClub/Views/Planning/MatchFormatGuideView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 6fbfa92..e1b24d3 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -798,6 +798,9 @@ FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; }; FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; }; FFB1C98B2C10255100B154A7 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; }; + 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 */; }; 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 */; }; @@ -1201,6 +1204,7 @@ FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudConvert.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -2073,6 +2077,7 @@ FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */, FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */, FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */, + FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */, FF1162882BD0523B000C4809 /* Components */, ); path = Planning; @@ -2494,6 +2499,7 @@ FFBFC3962CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */, FF70916E2B9108C600AB08DA /* InscriptionManagerView.swift in Sources */, + FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */, FF77CE542CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */, FFDB1C732BB2CFE900F1E467 /* MySortDescriptor.swift in Sources */, @@ -2785,6 +2791,7 @@ FFBFC3972CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FF4CBFDA2C996C0600151637 /* PlayerPopoverView.swift in Sources */, FF4CBFDB2C996C0600151637 /* InscriptionManagerView.swift in Sources */, + FFB378362D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */, FF77CE522CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF4CBFDC2C996C0600151637 /* ActivityView.swift in Sources */, FF4CBFDD2C996C0600151637 /* MySortDescriptor.swift in Sources */, @@ -3055,6 +3062,7 @@ FFBFC3952CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FF70FB592C90584900129CC2 /* PlayerPopoverView.swift in Sources */, FF70FB5A2C90584900129CC2 /* InscriptionManagerView.swift in Sources */, + FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */, FF77CE532CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF70FB5B2C90584900129CC2 /* ActivityView.swift in Sources */, FF70FB5C2C90584900129CC2 /* MySortDescriptor.swift in Sources */, @@ -3315,7 +3323,6 @@ DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; - GCC_OPTIMIZATION_LEVEL = 0; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PadelClub/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Padel Club"; @@ -3344,7 +3351,6 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -3362,7 +3368,6 @@ DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; - GCC_OPTIMIZATION_LEVEL = 0; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PadelClub/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Padel Club"; @@ -3391,7 +3396,6 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index cc7dd6d..e9337af 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -632,13 +632,17 @@ final class TeamRegistration: ModelObject, Storable { func isDifferentPosition(_ drawMatchIndex: Int?) -> Bool { if let bracketPosition, let drawMatchIndex { return drawMatchIndex != bracketPosition - } else if let bracketPosition { + } else if bracketPosition != nil { return true - } else if let drawMatchIndex { + } else if drawMatchIndex != nil { return true } return false } + + func shouldDisplayRankAndWeight() -> Bool { + unsortedPlayers().count > 0 + } enum CodingKeys: String, CodingKey { case _id = "id" diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 20c1a44..4470155 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -428,6 +428,11 @@ final class Tournament : ModelObject, Storable { return Array(self.tournamentStore.teamRegistrations) } + func unsortedTeamsCount() -> Int { + return self.tournamentStore.teamRegistrations.count + } + + func groupStages(atStep step: Int = 0) -> [GroupStage] { let groupStages: [GroupStage] = self.tournamentStore.groupStages.filter { $0.tournament == self.id && $0.step == step } return groupStages.sorted(by: \.index) @@ -581,7 +586,8 @@ defer { } func pasteDataForImporting(_ exportFormat: ExportFormat = .rawText) -> String { - let selectedSortedTeams = selectedSortedTeams() + waitingListSortedTeams() + 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)) @@ -878,13 +884,13 @@ defer { return rounds.sorted(by: \.index).reversed() } - func sortedTeams() -> [TeamRegistration] { - let teams = selectedSortedTeams() + func sortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] { + let teams = selectedSortedTeams return teams + waitingListTeams(in: teams, includingWalkOuts: true) } - func waitingListSortedTeams() -> [TeamRegistration] { - let teams = selectedSortedTeams() + func waitingListSortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] { + let teams = selectedSortedTeams return waitingListTeams(in: teams, includingWalkOuts: false) } @@ -1183,9 +1189,8 @@ defer { } } - func registrationIssues() -> Int { + func registrationIssues(selectedTeams: [TeamRegistration]) -> Int { let players : [PlayerRegistration] = unsortedPlayers() - let selectedTeams : [TeamRegistration] = selectedSortedTeams() let callDateIssue : [TeamRegistration] = selectedTeams.filter { $0.callDate != nil && isStartDateIsDifferentThanCallDate($0) } let duplicates : [PlayerRegistration] = duplicates(in: players) let problematicPlayers : [PlayerRegistration] = players.filter({ $0.sex == nil }) @@ -1852,6 +1857,7 @@ defer { team.wildCardGroupStage = true } + team.setWeight(from: [], inTournamentCategory: self.tournamentCategory) return team } @@ -1865,6 +1871,7 @@ defer { func addEmptyTeamRegistration(_ count: Int) { let teams = (0.. Date? { guard [.p500, .p1000, .p1500, .p2000].contains(tournamentLevel) else { return nil } - var daysOffset = type.daysOffset - if tournamentLevel == .p500 { - daysOffset += 7 - } + var 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) diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift index ff9b1d8..dd7cbd2 100644 --- a/PadelClub/Utils/PadelRule.swift +++ b/PadelClub/Utils/PadelRule.swift @@ -1996,16 +1996,30 @@ enum TournamentDeadlineType: String, CaseIterable { case wildcardLicensePurchase = "Prise de licence des WC" case definitiveBroadcastList = "Publication définitive" - var daysOffset: Int { - switch self { - case .inscription: - return -13 - case .broadcastList: - return -12 - case .wildcardRequest: - return -9 - case .wildcardLicensePurchase, .definitiveBroadcastList: - return -8 + 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 + } + } } diff --git a/PadelClub/Views/Calling/SendToAllView.swift b/PadelClub/Views/Calling/SendToAllView.swift index d261f35..8a2adc2 100644 --- a/PadelClub/Views/Calling/SendToAllView.swift +++ b/PadelClub/Views/Calling/SendToAllView.swift @@ -223,13 +223,14 @@ struct SendToAllView: View { } func _teams() -> [TeamRegistration] { + let selectedSortedTeams = tournament.selectedSortedTeams() if onlyWaitingList { - return tournament.waitingListSortedTeams() + return tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams) } if _roundTeams().isEmpty && _groupStagesTeams().isEmpty { - return tournament.selectedSortedTeams() + (includeWaitingList ? tournament.waitingListSortedTeams() : []) + return tournament.selectedSortedTeams() + (includeWaitingList ? tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams) : []) } - return _roundTeams() + _groupStagesTeams() + (includeWaitingList ? tournament.waitingListSortedTeams() : []) + return _roundTeams() + _groupStagesTeams() + (includeWaitingList ? tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams) : []) } func _roundTeams() -> [TeamRegistration] { diff --git a/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift b/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift index 7d90518..9565658 100644 --- a/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift +++ b/PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift @@ -22,7 +22,7 @@ struct MatchFormatStorageView: View { var body: some View { Section { LabeledContent { - StepperView(title: "minutes", count: $estimatedDuration, step: 5) + StepperView(title: "minute", count: $estimatedDuration, step: 5) } label: { MatchFormatRowView(matchFormat: matchFormat, hideDuration: true) } diff --git a/PadelClub/Views/Planning/MatchFormatGuideView.swift b/PadelClub/Views/Planning/MatchFormatGuideView.swift new file mode 100644 index 0000000..4596a77 --- /dev/null +++ b/PadelClub/Views/Planning/MatchFormatGuideView.swift @@ -0,0 +1,72 @@ +// +// MatchFormatGuideView.swift +// PadelClub +// +// Created by razmig on 20/02/2025. +// + +import SwiftUI + +struct MatchFormatGuideView: View { + let matchCounts = Array(2...7) + let formats: [MatchFormat] = [ + .twoSets, .twoSetsDecisivePoint, + .twoSetsSuperTie, .twoSetsDecisivePointSuperTie, + .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, + .nineGames, .nineGamesDecisivePoint, + .superTie + ] + + func getFormatDescription(for matchCount: Int) -> String { + var description = "" + + // Group formats by their behavior + let formatGroups = Dictionary(grouping: formats) { format in + format.maximumMatchPerDay(for: matchCount) + } + + // Sort by maximum matches allowed (descending) + let sortedMaxMatches = formatGroups.keys.sorted(by: >) + + for maxMatches in sortedMaxMatches { + if let formatsForMax = formatGroups[maxMatches] { + let formatStrings = formatsForMax.map { $0.format }.joined(separator: "/") + if maxMatches > 0 && maxMatches <= matchCount { + description += "Maximum \(maxMatches) matchs en format \(formatStrings)\n" + } else if maxMatches == 0 { + description += "Aucun match au format \(formatStrings)\n" + } + } + } + + if matchCount >= 7 { + description += "Format \(MatchFormat.superTie.format) principalement" + } + + return description.isEmpty ? "Aucun match possible" : description + } + + var body: some View { + List { + Section { + ForEach(matchCounts, id: \.self) { count in + VStack { + Text("\(count) matchs par jour") + .font(.headline) + Text(getFormatDescription(for: count)) + } + } + + // Special case for 7+ matches + VStack { + Text("7+ matchs par jour") + .font(.headline) + Text("Tournois P 25 uniquement (soirée/demi-journée/journée)") + } + } + } + .navigationTitle("Guide des Formats de Match") + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + } +} diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index 8a0d080..8a1d024 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -23,7 +23,8 @@ struct PlanningSettingsView: View { @State private var parallelType: Bool = false @State private var deletingDateMatchesDone: Bool = false @State private var deletingDone: Bool = false - + @State private var presentFormatHelperView: Bool = false + var tournamentStore: TournamentStore { return self.tournament.tournamentStore } @@ -145,6 +146,28 @@ struct PlanningSettingsView: View { _smartView() } + .navigationTitle("Réglages") + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Button { + presentFormatHelperView = true + } label: { + Text("Aide-mémoire") + } + } + } + .sheet(isPresented: $presentFormatHelperView) { + NavigationStack { + MatchFormatGuideView() + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button("Retour", role: .cancel) { + presentFormatHelperView = false + } + } + } + } + } .headerProminence(.increased) .onAppear { do { diff --git a/PadelClub/Views/Team/Components/TeamWeightView.swift b/PadelClub/Views/Team/Components/TeamWeightView.swift index f19b6e6..0868ed3 100644 --- a/PadelClub/Views/Team/Components/TeamWeightView.swift +++ b/PadelClub/Views/Team/Components/TeamWeightView.swift @@ -12,16 +12,15 @@ struct TeamWeightView: View { let team: TeamRegistration var teamPosition: TeamPosition? = nil - var teamIndex: Int? { - team.tournamentObject()?.indexOf(team: team) - } + var teamIndex: Int? var displayWeight: Bool { - team.tournamentObject()?.hideWeight() == false + team.shouldDisplayRankAndWeight() && team.tournamentObject()?.hideWeight() == false } var body: some View { VStack(alignment: .trailing, spacing: 0) { + let displayWeight = self.displayWeight if (teamPosition == .one || teamPosition == nil) && displayWeight { Text(team.weight.formatted()) .monospacedDigit() diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 76bbe32..8f06955 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -47,7 +47,8 @@ struct EditingTeamView: View { } private func _resetTeam() { - self.currentWaitingList = tournament.waitingListSortedTeams().filter({ $0.hasRegisteredOnline() }).first + let selectedSortedTeams = tournament.selectedSortedTeams() + self.currentWaitingList = tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams).filter({ $0.hasRegisteredOnline() }).first team.resetPositions() team.wildCardGroupStage = false team.walkOut = false diff --git a/PadelClub/Views/Team/TeamRowView.swift b/PadelClub/Views/Team/TeamRowView.swift index 966f903..4a1ba04 100644 --- a/PadelClub/Views/Team/TeamRowView.swift +++ b/PadelClub/Views/Team/TeamRowView.swift @@ -13,10 +13,11 @@ struct TeamRowView: View { var teamPosition: TeamPosition? = nil var displayCallDate: Bool = false var displayRestingTime: Bool = false + var teamIndex: Int? var body: some View { LabeledContent { - TeamWeightView(team: team, teamPosition: teamPosition) + TeamWeightView(team: team, teamPosition: teamPosition, teamIndex: teamIndex) } label: { VStack(alignment: .leading) { TeamHeadlineView(team: team) diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index 500d7dc..f72a8cc 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -312,7 +312,7 @@ struct FileImportView: View { } } else if didImport { let _filteredTeams = filteredTeams - let previousTeams = tournament.sortedTeams() + let previousTeams = tournament.sortedTeams(selectedSortedTeams: tournament.selectedSortedTeams()) if previousTeams.isEmpty == false { Section { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift index ebf4bde..2546787 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift @@ -34,7 +34,7 @@ struct TournamentMatchFormatsSettingsView: View { Section { LabeledContent { - StepperView(title: "minutes", count: $tournament.additionalEstimationDuration, step: 5, minimum: -10) + StepperView(title: "minute", count: $tournament.additionalEstimationDuration, step: 5, minimum: -10) } label: { Text("Modifier les durées moyennes") } diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index e95fade..d4d875a 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -180,7 +180,7 @@ struct InscriptionManagerView: View { return _simpleHash(ids: ids1) != _simpleHash(ids: ids2) } - private func _setHash() { + private func _setHash(currentSelectedSortedTeams: [TeamRegistration]? = nil) { #if _DEBUG_TIME //DEBUGING TIME let start = Date() defer { @@ -188,18 +188,17 @@ struct InscriptionManagerView: View { print("func _setHash", duration.formatted(.units(allowed: [.seconds, .milliseconds]))) } #endif - let selectedSortedTeams = tournament.selectedSortedTeams() + let selectedSortedTeams = currentSelectedSortedTeams == nil ? tournament.selectedSortedTeams() : currentSelectedSortedTeams! if self.teamsHash == nil, selectedSortedTeams.isEmpty == false { self.teamsHash = _simpleHash(ids: selectedSortedTeams.map { $0.id }) } self.registrationIssues = nil DispatchQueue.main.async { - self.registrationIssues = tournament.registrationIssues() + self.registrationIssues = tournament.registrationIssues(selectedTeams: selectedSortedTeams) } } - private func _handleHashDiff() { - let selectedSortedTeams = tournament.selectedSortedTeams() + private func _handleHashDiff(selectedSortedTeams: [TeamRegistration]) { let newHash = _simpleHash(ids: selectedSortedTeams.map { $0.id }) if (self.teamsHash != nil && newHash != teamsHash!) || (self.teamsHash == nil && selectedSortedTeams.isEmpty == false) { self.teamsHash = newHash @@ -225,9 +224,10 @@ struct InscriptionManagerView: View { } var body: some View { - Group { - if tournament.unsortedTeams().isEmpty == false { - _teamRegisteredView() + let selectedSortedTeams = tournament.selectedSortedTeams() + return Group { + if tournament.unsortedTeamsCount() > 0 { + _teamRegisteredView(selectedSortedTeams: selectedSortedTeams) } else { List { @@ -263,10 +263,10 @@ struct InscriptionManagerView: View { await _refreshList() } .onAppear { - _setHash() + _setHash(currentSelectedSortedTeams: selectedSortedTeams) } .onDisappear { - _handleHashDiff() + _handleHashDiff(selectedSortedTeams: selectedSortedTeams) } .sheet(isPresented: $isLearningMore) { LearnMoreSheetView(tournament: tournament) @@ -490,47 +490,43 @@ struct InscriptionManagerView: View { tournament.unsortedPlayers() } - var sortedTeams: [TeamRegistration] { + func sortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] { if filterMode == .waiting { - return tournament.waitingListSortedTeams() + return tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams) } else { - return tournament.sortedTeams() + return tournament.sortedTeams(selectedSortedTeams: selectedSortedTeams) } } - var filteredTeams: [TeamRegistration] { - - var teams = sortedTeams - switch filterMode { - case .wildcardBracket: - teams = teams.filter({ $0.wildCardBracket }) - case .wildcardGroupStage: - teams = teams.filter({ $0.wildCardGroupStage }) - case .walkOut: - teams = teams.filter({ $0.walkOut }) - case .bracket: - teams = teams.filter({ $0.inRound() && $0.inGroupStage() == false }) - case .groupStage: - teams = teams.filter({ $0.inGroupStage() }) - case .notImported: - teams = teams.filter({ $0.isImported() == false }) - case .registeredLocally: - teams = teams.filter({ $0.hasRegisteredOnline() == false }) - case .registeredOnline: - teams = teams.filter({ $0.hasRegisteredOnline() == true }) - default: - break - } - - if sortingMode == .registrationDate { - teams = teams.sorted(by: \.computedRegistrationDate) + func filteredTeams(sortedTeams: [TeamRegistration]) -> [TeamRegistration] { + let filtered = sortedTeams.lazy.filter { team in + switch filterMode { + case .wildcardBracket: + return team.wildCardBracket + case .wildcardGroupStage: + return team.wildCardGroupStage + case .walkOut: + return team.walkOut + case .bracket: + return team.inRound() && !team.inGroupStage() + case .groupStage: + return team.inGroupStage() + case .notImported: + return !team.isImported() + case .registeredLocally: + return !team.hasRegisteredOnline() + case .registeredOnline: + return team.hasRegisteredOnline() + default: + return true + } } - if byDecreasingOrdering { - return teams.reversed() - } else { - return teams - } + let sorted = sortingMode == .registrationDate + ? filtered.sorted(by: { $0.computedRegistrationDate < $1.computedRegistrationDate }) + : Array(filtered) + + return byDecreasingOrdering ? sorted.reversed() : sorted } // private func _fixModel() { @@ -572,12 +568,10 @@ struct InscriptionManagerView: View { } } - private func _teamRegisteredView() -> some View { + private func _teamRegisteredView(selectedSortedTeams: [TeamRegistration]) -> some View { List { - let selectedSortedTeams = tournament.selectedSortedTeams() - if presentSearch == false { - _informationView() + _informationView(for: selectedSortedTeams) if tournament.isAnimation() == false { _rankHandlerView() @@ -585,7 +579,8 @@ struct InscriptionManagerView: View { } } - let teams = searchField.isEmpty ? filteredTeams : filteredTeams.filter({ $0.contains(searchField.canonicalVersion) }) + let sortedTeams = sortedTeams(selectedSortedTeams: selectedSortedTeams) + let teams = searchField.isEmpty ? filteredTeams(sortedTeams: sortedTeams) : filteredTeams(sortedTeams: sortedTeams).filter({ $0.contains(searchField.canonicalVersion) }) if teams.isEmpty && searchField.isEmpty == false { ContentUnavailableView { @@ -622,7 +617,7 @@ struct InscriptionManagerView: View { EditingTeamView(team: team) .environment(tournament) } label: { - TeamRowView(team: team) + TeamRowView(team: team, teamIndex: teamIndex) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { if tournament.enableOnlineRegistration == false { @@ -735,18 +730,18 @@ struct InscriptionManagerView: View { } } - private func _teamCountForFilterMode(filterMode: FilterMode) -> String { + private func _teamCountForFilterMode(filterMode: FilterMode, in teams: [TeamRegistration]) -> String { switch filterMode { case .wildcardBracket: - return tournament.selectedSortedTeams().filter({ $0.wildCardBracket }).count.formatted() + return teams.filter({ $0.wildCardBracket }).count.formatted() case .wildcardGroupStage: - return tournament.selectedSortedTeams().filter({ $0.wildCardGroupStage }).count.formatted() + return teams.filter({ $0.wildCardGroupStage }).count.formatted() case .all: return unsortedTeamsWithoutWO.count.formatted() case .bracket: - return tournament.selectedSortedTeams().filter({ $0.inRound() && $0.inGroupStage() == false }).count.formatted() + return teams.filter({ $0.inRound() && $0.inGroupStage() == false }).count.formatted() case .groupStage: - return tournament.selectedSortedTeams().filter({ $0.inGroupStage() }).count.formatted() + return teams.filter({ $0.inGroupStage() }).count.formatted() case .walkOut: let wo = walkoutTeams.count.formatted() return wo @@ -754,20 +749,20 @@ struct InscriptionManagerView: View { let waiting: Int = max(0, unsortedTeamsWithoutWO.count - tournament.teamCount) return waiting.formatted() case .notImported: - let notImported: Int = max(0, sortedTeams.filter({ $0.isImported() == false }).count) + let notImported: Int = max(0, sortedTeams(selectedSortedTeams: teams).filter({ $0.isImported() == false }).count) return notImported.formatted() case .registeredLocally: - let registeredLocally: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() == false }).count) + let registeredLocally: Int = max(0, sortedTeams(selectedSortedTeams: teams).filter({ $0.hasRegisteredOnline() == false }).count) return registeredLocally.formatted() case .registeredOnline: - let registeredOnline: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() }).count) + let registeredOnline: Int = max(0, sortedTeams(selectedSortedTeams: teams).filter({ $0.hasRegisteredOnline() }).count) return registeredOnline.formatted() } } @ViewBuilder - private func _informationView() -> some View { + private func _informationView(for teams: [TeamRegistration]) -> some View { Section { HStack { // VStack(alignment: .leading, spacing: 0) { @@ -781,7 +776,7 @@ struct InscriptionManagerView: View { // } // ForEach([FilterMode.all, FilterMode.waiting, FilterMode.walkOut]) { filterMode in - _filterModeView(filterMode: filterMode) + _filterModeView(filterMode: filterMode, in: teams) } Button { @@ -809,7 +804,7 @@ struct InscriptionManagerView: View { .listRowSeparator(.hidden) HStack { ForEach([FilterMode.groupStage, FilterMode.bracket, FilterMode.wildcardGroupStage, FilterMode.wildcardBracket]) { filterMode in - _filterModeView(filterMode: filterMode) + _filterModeView(filterMode: filterMode, in: teams) } } .padding(.bottom, -4) @@ -883,7 +878,7 @@ struct InscriptionManagerView: View { } } - private func _filterModeView(filterMode: FilterMode) -> some View { + private func _filterModeView(filterMode: FilterMode, in teams: [TeamRegistration]) -> some View { Button { if self.filterMode == filterMode { @@ -894,7 +889,7 @@ struct InscriptionManagerView: View { } label: { VStack(alignment: .center, spacing: -2) { Text(filterMode.localizedLabel(.short)).font(.caption).padding(.horizontal, -8) - Text(_teamCountForFilterMode(filterMode: filterMode)).font(.largeTitle) + Text(_teamCountForFilterMode(filterMode: filterMode, in: teams)).font(.largeTitle) } .frame(maxWidth: .infinity) .contentShape(Rectangle()) From 0afde57d8ce4e6fd8f9d0a7415f51216f566d063 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 20 Feb 2025 11:21:33 +0100 Subject: [PATCH 02/18] v1.1.13 --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 6ad3437..c3b7e74 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3318,7 +3318,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3344,7 +3344,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.12; + MARKETING_VERSION = 1.1.13; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3364,7 +3364,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3389,7 +3389,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.12; + MARKETING_VERSION = 1.1.13; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From b3e9c80990ae46e1c88bd3ca3b6c0e145eb9645e Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 20 Feb 2025 15:57:48 +0100 Subject: [PATCH 03/18] fix cut display stuff --- PadelClub/Data/TeamRegistration.swift | 4 +++- PadelClub/Data/Tournament.swift | 20 ++++++++++++-------- PadelClub/InscriptionLegendView.swift | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index e9337af..6744812 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -338,9 +338,11 @@ final class TeamRegistration: ModelObject, Storable { func initialRoundColor() -> Color? { if walkOut { return Color.logoRed } - if groupStagePosition != nil { return Color.blue } + 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 } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 4470155..2521583 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -925,7 +925,7 @@ defer { let wcBracket = _teams.filter { $0.wildCardBracket }.sorted(using: _currentSelectionSorting, order: .ascending) let groupStageSpots: Int = self.groupStageSpots() - var bracketSeeds: Int = min(teamCount, _teams.count) - groupStageSpots - wcBracket.count + var bracketSeeds: Int = teamCount - groupStageSpots - wcBracket.count var groupStageTeamCount: Int = groupStageSpots - wcGroupStage.count if groupStageTeamCount < 0 { groupStageTeamCount = 0 } if bracketSeeds < 0 { bracketSeeds = 0 } @@ -969,8 +969,8 @@ defer { } } - func bracketCut(teamCount: Int) -> Int { - return max(0, teamCount - groupStageCut()) + func bracketCut(teamCount: Int, groupStageCut: Int) -> Int { + return self.teamCount - groupStageCut } func groupStageCut() -> Int { @@ -979,10 +979,12 @@ defer { func cutLabel(index: Int, teamCount: Int?) -> String { let _teamCount = teamCount ?? selectedSortedTeams().count - let bracketCut = bracketCut(teamCount: _teamCount) + let groupStageCut = groupStageCut() + let bracketCut = bracketCut(teamCount: _teamCount, groupStageCut: groupStageCut) + if index < bracketCut { return "Tableau" - } else if index - bracketCut < groupStageCut() && _teamCount > 0 { + } else if index - bracketCut < groupStageCut && _teamCount > 0 { return "Poule" } else { return "Attente" @@ -992,11 +994,12 @@ defer { func cutLabelColor(index: Int?, teamCount: Int?) -> Color { guard let index else { return Color.gray } let _teamCount = teamCount ?? selectedSortedTeams().count - let bracketCut = bracketCut(teamCount: _teamCount) + 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.cyan + } else if index - bracketCut < groupStageCut && _teamCount > 0 { + return Color.indigo } else { return Color.gray } @@ -1858,6 +1861,7 @@ defer { } team.setWeight(from: [], inTournamentCategory: self.tournamentCategory) + team.weight += 200_000 return team } diff --git a/PadelClub/InscriptionLegendView.swift b/PadelClub/InscriptionLegendView.swift index 031e206..9589fc6 100644 --- a/PadelClub/InscriptionLegendView.swift +++ b/PadelClub/InscriptionLegendView.swift @@ -36,7 +36,7 @@ struct InscriptionLegendView: View { Text("Équipe estimée en tableau") .listRowView(isActive: true, color: .mint, hideColorVariation: true, alignment: .leading) Text("Équipe estimée en poule") - .listRowView(isActive: true, color: .cyan, hideColorVariation: true, alignment: .leading) + .listRowView(isActive: true, color: .indigo, hideColorVariation: true, alignment: .leading) } From 5c1c61d518691dc8506b14bb585bb07ab153e588 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 20 Feb 2025 15:58:13 +0100 Subject: [PATCH 04/18] build 2 --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index c3b7e74..47d7702 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3318,7 +3318,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3364,7 +3364,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; From 981e0772e5e3c313c7a0e7ed3c66bf9c9d25e12e Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 21 Feb 2025 16:57:18 +0100 Subject: [PATCH 05/18] fix build settings --- PadelClub.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 47d7702..043a95b 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3486,7 +3486,6 @@ DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; - GCC_OPTIMIZATION_LEVEL = 0; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PadelClub/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (ProdTest)"; @@ -3515,7 +3514,6 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -3533,7 +3531,6 @@ DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; - GCC_OPTIMIZATION_LEVEL = 0; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PadelClub/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (ProdTest)"; @@ -3563,7 +3560,6 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = PRODTEST; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; From 018e77fda7814e87842aad8922298be6ffc78d0d Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 21 Feb 2025 16:57:45 +0100 Subject: [PATCH 06/18] v1.1.14 --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 043a95b..e9b9cbc 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3318,7 +3318,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3344,7 +3344,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.13; + MARKETING_VERSION = 1.1.14; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3364,7 +3364,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3389,7 +3389,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.13; + MARKETING_VERSION = 1.1.14; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From e029295b6ac4ce9cc58a1cbdc796d7076d96eb9a Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 26 Feb 2025 10:20:59 +0100 Subject: [PATCH 07/18] fix crash when when replacing heads fix undetected tournament fix re-ranking efficiency add option to handle re-ranks for all tournaments in a month add option to handle refresh online reg list in a month add automatic refresh in inscription manager and tournamentcell display online reg count in inscription manager reshow button to delete all teams and let the jap know it's locked due to online reg --- PadelClub.xcodeproj/project.pbxproj | 4 +- .../Data/Federal/FederalTournament.swift | 12 +-- PadelClub/Data/Match.swift | 15 +-- PadelClub/Data/PlayerRegistration.swift | 20 ++-- PadelClub/Data/Tournament.swift | 91 +++++++++++++++---- PadelClub/Utils/SwiftParser.swift | 71 +++++++++++++-- .../Navigation/Agenda/EventListView.swift | 67 +++++++++++++- PadelClub/Views/Round/RoundSettingsView.swift | 2 +- .../Views/Tournament/FileImportView.swift | 19 +++- .../Components/UpdateSourceRankDateView.swift | 19 +--- .../Screen/InscriptionManagerView.swift | 44 +++++---- .../Screen/RegistrationSetupView.swift | 9 +- .../Screen/TableStructureView.swift | 11 ++- .../Shared/TournamentCellView.swift | 22 ++++- 14 files changed, 312 insertions(+), 94 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index e9b9cbc..ece45dc 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3344,7 +3344,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.14; + MARKETING_VERSION = 1.1.15; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3389,7 +3389,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.14; + MARKETING_VERSION = 1.1.15; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Data/Federal/FederalTournament.swift b/PadelClub/Data/Federal/FederalTournament.swift index 003218e..202a5d7 100644 --- a/PadelClub/Data/Federal/FederalTournament.swift +++ b/PadelClub/Data/Federal/FederalTournament.swift @@ -236,12 +236,12 @@ struct CategorieAge: Codable { var tournamentAge: FederalTournamentAge? { if let id { - return FederalTournamentAge(rawValue: id) + return FederalTournamentAge(rawValue: id) ?? .senior } if let libelle { - return FederalTournamentAge.allCases.first(where: { $0.localizedFederalAgeLabel().localizedCaseInsensitiveContains(libelle) }) + return FederalTournamentAge.allCases.first(where: { $0.localizedFederalAgeLabel().localizedCaseInsensitiveContains(libelle) }) ?? .senior } - return nil + return .senior } } @@ -295,7 +295,7 @@ struct Serie: Codable { var sexe: String? var tournamentCategory: TournamentCategory? { - TournamentCategory.allCases.first(where: { $0.requestLabel == code }) + TournamentCategory.allCases.first(where: { $0.requestLabel == code }) ?? .men } } @@ -348,9 +348,9 @@ struct TypeEpreuve: Codable { var tournamentLevel: TournamentLevel? { if let code, let value = Int(code.removingFirstCharacter) { - return TournamentLevel(rawValue: value) + return TournamentLevel(rawValue: value) ?? .p100 } - return nil + return .p100 } } diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index a71580d..b80f645 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -410,12 +410,15 @@ defer { } } //byeState = false - roundObject?._cachedSeedInterval = nil - name = nil - do { - try self.tournamentStore.matches.addOrUpdate(instance: self) - } catch { - Logger.error(error) + + if state != currentState { + roundObject?._cachedSeedInterval = nil + name = nil + do { + try self.tournamentStore.matches.addOrUpdate(instance: self) + } catch { + Logger.error(error) + } } if single == false { diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 3d17346..ebb617b 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -301,21 +301,23 @@ final class PlayerRegistration: ModelObject, Storable { return await withTaskGroup(of: Line?.self) { group in for source in filteredSources { group.addTask { - guard !Task.isCancelled else { print("Cancelled"); return nil } + guard !Task.isCancelled else { return nil } return try? await source.first { $0.rawValue.contains(";\(license);") } } } - if let first = await group.first(where: { $0 != nil }) { - group.cancelAll() - return first + 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_TIME + #if DEBUG let start = Date() defer { let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) @@ -338,9 +340,11 @@ final class PlayerRegistration: ModelObject, Storable { } } - if let first = await group.first(where: { $0 != nil }) { - group.cancelAll() - return first + for await result in group { + if let result { + group.cancelAll() // Stop other tasks as soon as we find a match + return result + } } return nil } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 2521583..e927f14 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -70,6 +70,10 @@ final class Tournament : ModelObject, Storable { var maximumPlayerPerTeam: Int = 2 var information: String? = nil + //local variable + var refreshInProgress: Bool = false + var refreshRanking: Bool = false + @ObservationIgnored var navigationPath: [Screen] = [] @@ -1513,8 +1517,8 @@ defer { } } - func updateRank(to newDate: Date?) async throws { - + func updateRank(to newDate: Date?, forceRefreshLockWeight: Bool, providedSources: [CSVParser]?) async throws { + refreshRanking = true #if DEBUG_TIME let start = Date() defer { @@ -1549,16 +1553,42 @@ defer { let lastRankMan = monthData?.maleUnrankedValue ?? 0 let lastRankWoman = monthData?.femaleUnrankedValue ?? 0 - // 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 + 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 sources = dataURLs.map { CSVParser(url: $0) } let players = unsortedPlayers() try await players.concurrentForEach { player in let lastRank = (player.sex == .female) ? lastRankWoman : lastRankMan - try await player.updateRank(from: sources, lastRank: lastRank) + 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 } @@ -2424,12 +2454,15 @@ defer { func updateSeedsBracketPosition() async { - await removeAllSeeds() + await removeAllSeeds(saveTeamsAtTheEnd: false) let drawLogs = drawLogs().reversed() let seeds = seeds() - for (index, seed) in seeds.enumerated() { - if let drawLog = drawLogs.first(where: { $0.drawSeed == index }) { - drawLog.updateTeamBracketPosition(seed) + + await MainActor.run { + for (index, seed) in seeds.enumerated() { + if let drawLog = drawLogs.first(where: { $0.drawSeed == index }) { + drawLog.updateTeamBracketPosition(seed) + } } } @@ -2440,11 +2473,13 @@ defer { } } - func removeAllSeeds() async { + 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 @@ -2471,12 +2506,13 @@ defer { Logger.error(error) } - do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) + if saveTeamsAtTheEnd { + do { + try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + } catch { + Logger.error(error) + } } - updateTournamentState() } func addNewRound(_ roundIndex: Int) async { @@ -2608,6 +2644,27 @@ defer { 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 refreshTeamList() async throws { + guard enableOnlineRegistration, refreshInProgress == false, hasEnded() == false else { return } + refreshInProgress = true + 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 + } + // MARK: - func insertOnServer() throws { diff --git a/PadelClub/Utils/SwiftParser.swift b/PadelClub/Utils/SwiftParser.swift index de0bda2..7aab761 100644 --- a/PadelClub/Utils/SwiftParser.swift +++ b/PadelClub/Utils/SwiftParser.swift @@ -70,18 +70,18 @@ struct Line: Identifiable { struct CSVParser: AsyncSequence, AsyncIteratorProtocol { typealias Element = Line - private let url: URL + let url: URL private var lineIterator: LineIterator - private let seperator: Character + private let separator: Character private let quoteCharacter: Character = "\"" private var lineNumber = 0 private let date: Date let maleData: Bool - init(url: URL, seperator: Character = ";") { + init(url: URL, separator: Character = ";") { self.date = url.dateFromPath self.url = url - self.seperator = seperator + self.separator = separator self.lineIterator = url.lines.makeAsyncIterator() self.maleData = url.path().contains(SourceFile.messieurs.rawValue) } @@ -127,7 +127,7 @@ struct CSVParser: AsyncSequence, AsyncIteratorProtocol { func makeAsyncIterator() -> CSVParser { return self } - + private func split(line: String) -> [String?] { var data = [String?]() var inQuote = false @@ -139,7 +139,7 @@ struct CSVParser: AsyncSequence, AsyncIteratorProtocol { inQuote = !inQuote continue - case seperator: + case separator: if !inQuote { data.append(currentString.isEmpty ? nil : currentString) currentString = "" @@ -157,4 +157,63 @@ struct CSVParser: AsyncSequence, AsyncIteratorProtocol { return data } + + /// Splits the CSV file into multiple temporary CSV files, each containing `size` lines. + /// Returns an array of new `CSVParser` instances pointing to these chunked files. + func getChunkedParser(size: Int) async throws -> [CSVParser] { + var chunkedParsers: [CSVParser] = [] + var currentChunk: [String] = [] + var iterator = self.makeAsyncIterator() + var chunkIndex = 0 + + while let line = try await iterator.next()?.rawValue { + currentChunk.append(line) + + // When the chunk reaches the desired size, write it to a file + if currentChunk.count == size { + let chunkURL = try writeChunkToFile(chunk: currentChunk, index: chunkIndex) + chunkedParsers.append(CSVParser(url: chunkURL, separator: self.separator)) + chunkIndex += 1 + currentChunk.removeAll() + } + } + + // Handle remaining lines (if any) + if !currentChunk.isEmpty { + let chunkURL = try writeChunkToFile(chunk: currentChunk, index: chunkIndex) + chunkedParsers.append(CSVParser(url: chunkURL, separator: self.separator)) + } + + return chunkedParsers + } + + /// Writes a chunk of CSV lines to a temporary file and returns its URL. + private func writeChunkToFile(chunk: [String], index: Int) throws -> URL { + let tempDirectory = FileManager.default.temporaryDirectory + let chunkURL = tempDirectory.appendingPathComponent("\(url.lastPathComponent)-\(index).csv") + + let chunkData = chunk.joined(separator: "\n") + try chunkData.write(to: chunkURL, atomically: true, encoding: .utf8) + + return chunkURL + } +} + +/// Process all large CSV files concurrently and gather all mini CSVs. +func chunkAllSources(sources: [CSVParser], size: Int) async throws -> [CSVParser] { + var allChunks: [CSVParser] = [] + + await withTaskGroup(of: [CSVParser].self) { group in + for source in sources { + group.addTask { + return (try? await source.getChunkedParser(size: size)) ?? [] + } + } + + for await miniCSVs in group { + allChunks.append(contentsOf: miniCSVs) + } + } + + return allChunks } diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 7000131..f7de342 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -17,6 +17,11 @@ struct EventListView: View { let tournaments: [FederalTournamentHolder] let sortAscending: Bool + var lastDataSource: Date? { + guard let _lastDataSource = dataStore.appSettings.lastDataSource else { return nil } + return URL.importDateFormatter.date(from: _lastDataSource) + } + var body: some View { let groupedTournamentsByDate = Dictionary(grouping: federalDataViewModel.filteredFederalTournaments(from: tournaments)) { $0.startDate.startOfMonth } switch viewStyle { @@ -101,6 +106,41 @@ struct EventListView: View { @ViewBuilder private func _options(_ pcTournaments: [Tournament]) -> some View { + if let lastDataSource, pcTournaments.anySatisfy({ $0.rankSourceShouldBeRefreshed() != nil && $0.hasEnded() == false }) { + Section { + Button { + Task { + do { + + let dataURLs = SourceFileManager.shared.allFiles.filter { $0.dateFromPath == lastDataSource } + guard !dataURLs.isEmpty else { return } // Early return if no files found + + let sources = dataURLs.map { CSVParser(url: $0) } + let chunkedParsers = try await chunkAllSources(sources: sources, size: 10000) + + try await pcTournaments.concurrentForEach { tournament in + if let mostRecentDate = tournament.rankSourceShouldBeRefreshed() { + try await tournament.updateRank(to: mostRecentDate, forceRefreshLockWeight: false, providedSources: chunkedParsers) + } + } + + try chunkedParsers.forEach { chunk in + try FileManager.default.removeItem(at: chunk.url) + } + + try dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments) + } catch { + Logger.error(error) + } + } + } label: { + Text("Rafraîchir les classements") + } + } header: { + Text("Source disponible : \(lastDataSource.monthYearFormatted)") + } + Divider() + } Section { if pcTournaments.anySatisfy({ $0.isPrivate == true }) { Button { @@ -135,7 +175,7 @@ struct EventListView: View { Text("Visibilité sur Padel Club") } Divider() - if pcTournaments.anySatisfy({ $0.hasEnded() == false && $0.enableOnlineRegistration == false && $0.onlineRegistrationCanBeEnabled() }) || pcTournaments.anySatisfy({ $0.enableOnlineRegistration == true }) { + if pcTournaments.anySatisfy({ $0.hasEnded() == false && $0.enableOnlineRegistration == false && $0.onlineRegistrationCanBeEnabled() }) || pcTournaments.anySatisfy({ $0.enableOnlineRegistration == true && $0.hasEnded() == false }) { Section { if pcTournaments.anySatisfy({ $0.hasEnded() == false && $0.enableOnlineRegistration == false && $0.onlineRegistrationCanBeEnabled() }) { Button { @@ -152,7 +192,23 @@ struct EventListView: View { } } - if pcTournaments.anySatisfy({ $0.enableOnlineRegistration == true }) { + if pcTournaments.anySatisfy({ $0.enableOnlineRegistration == true && $0.hasEnded() == false }) { + + Button { + Task { + do { + try await pcTournaments.concurrentForEach { tournament in + try await tournament.refreshTeamList() + } + } catch { + Logger.error(error) + } + } + } label: { + Text("Rafraîchir la liste des équipes inscrites en ligne") + } + + Button { pcTournaments.forEach { tournament in tournament.enableOnlineRegistration = false @@ -207,6 +263,13 @@ struct EventListView: View { private func _tournamentView(_ tournament: Tournament) -> some View { NavigationLink(value: tournament) { TournamentCellView(tournament: tournament, shouldTournamentBeOver: tournament.shouldTournamentBeOver()) + .task { + do { + try await tournament.refreshTeamList() + } catch { + Logger.error(error) + } + } } .listRowView(isActive: tournament.enableOnlineRegistration, color: .green, hideColorVariation: true) .contextMenu { diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 2928a49..b785fec 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -158,7 +158,7 @@ struct RoundSettingsView: View { private func _removeAllSeeds() async { - await tournament.removeAllSeeds() + await tournament.removeAllSeeds(saveTeamsAtTheEnd: true) self.isEditingTournamentSeed.wrappedValue = true } diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index f72a8cc..fbbffa3 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -120,10 +120,10 @@ struct FileImportView: View { return teams.filter { $0.tournamentCategory == tournament.tournamentCategory && $0.tournamentAgeCategory == tournament.federalTournamentAge }.sorted(by: \.weight) } - private func _deleteTeams() async { + private func _deleteTeams(teams: [TeamRegistration]) async { await MainActor.run { do { - try tournamentStore.teamRegistrations.delete(contentOfs: tournament.unsortedTeams()) + try tournamentStore.teamRegistrations.delete(contentOfs: teams) } catch { Logger.error(error) } @@ -140,9 +140,18 @@ struct FileImportView: View { } } - if tournament.unsortedTeams().count > 0, tournament.enableOnlineRegistration == false { - RowButtonView("Effacer les équipes déjà inscrites", role: .destructive) { - await _deleteTeams() + let unsortedTeams = tournament.unsortedTeams() + let onlineTeams = unsortedTeams.filter({ $0.hasRegisteredOnline() }) + if unsortedTeams.count > 0 { + Section { + RowButtonView("Effacer les équipes déjà inscrites", role: .destructive) { + await _deleteTeams(teams: unsortedTeams) + } + .disabled(onlineTeams.isEmpty == false) + } footer: { + if onlineTeams.isEmpty == false { + Text("Ce tournoi contient des inscriptions en ligne, vous ne pouvez pas effacer toute votre liste d'inscription d'un coup.") + } } } diff --git a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift index ceed51f..f85eb6e 100644 --- a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift @@ -42,24 +42,7 @@ struct UpdateSourceRankDateView: View { updatingRank = true Task { do { - try await tournament.updateRank(to: currentRankSourceDate) - let unsortedPlayers = tournament.unsortedPlayers() - tournament.unsortedPlayers().forEach { player in - player.setComputedRank(in: tournament) - } - - try tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers) - - let unsortedTeams = tournament.unsortedTeams() - unsortedTeams.forEach { team in - team.setWeight(from: team.players(), inTournamentCategory: tournament.tournamentCategory) - if forceRefreshLockWeight { - team.lockedWeight = team.weight - } - } - - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams) - + try await tournament.updateRank(to: currentRankSourceDate, forceRefreshLockWeight: forceRefreshLockWeight, providedSources: nil) try dataStore.tournaments.addOrUpdate(instance: tournament) } catch { Logger.error(error) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index d4d875a..53f2001 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -51,7 +51,6 @@ struct InscriptionManagerView: View { @State private var pasteString: String? @State private var registrationIssues: Int? = nil @State private var refreshResult: String? = nil - @State private var refreshInProgress: Bool = false @State private var refreshStatus: Bool? @State private var showLegendView: Bool = false @@ -259,6 +258,9 @@ struct InscriptionManagerView: View { } } } + .task { + await _refreshList() + } .refreshable { await _refreshList() } @@ -543,28 +545,27 @@ struct InscriptionManagerView: View { // } // private func _refreshList() async { - if refreshInProgress { return } + if tournament.enableOnlineRegistration == false { return } + if tournament.hasEnded() { return } + if tournament.refreshInProgress { return } refreshResult = nil refreshStatus = nil - 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) + + try await self.tournament.refreshTeamList() _setHash() - self.refreshResult = "la synchronization a réussi" + self.refreshResult = "La synchronization a réussi" self.refreshStatus = true - refreshInProgress = false } catch { Logger.error(error) - self.refreshResult = "la synchronization a échoué" + self.refreshResult = "La synchronization a échoué" self.refreshStatus = false - refreshInProgress = false + tournament.refreshInProgress = false } } @@ -717,7 +718,7 @@ struct InscriptionManagerView: View { @ViewBuilder private func _rankHandlerView() -> some View { - if let mostRecentDate = SourceFileManager.shared.lastDataSourceDate(), let currentRankSourceDate, currentRankSourceDate < mostRecentDate, tournament.hasEnded() == false { + if let mostRecentDate = tournament.rankSourceShouldBeRefreshed() { Section { TipView(rankUpdateTip) { action in self.currentRankSourceDate = mostRecentDate @@ -845,19 +846,22 @@ struct InscriptionManagerView: View { } } label: { LabeledContent { - if refreshInProgress { + if tournament.refreshInProgress { ProgressView() - } else if let refreshStatus { - if refreshStatus { - Image(systemName: "checkmark").foregroundStyle(.green).font(.headline) - } else { - Image(systemName: "xmark").foregroundStyle(.logoRed).font(.headline) - } + } else { + Text(tournament.unsortedTeams().filter({ $0.hasRegisteredOnline() }).count.formatted()) + .fontWeight(.bold) } } label: { - Text("Récupérer les inscriptions en ligne") + if let refreshStatus { + Text("Inscriptions en ligne") + } else if tournament.refreshInProgress { + Text("Récupération des inscrits en ligne") + } else { + Text("Récupérer des inscrits en ligne") + } if let refreshResult { - Text(refreshResult) + Text(refreshResult).foregroundStyle(.secondary) } } } diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index 33f08f7..bc1445f 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -27,7 +27,8 @@ struct RegistrationSetupView: View { @State private var showMoreInfos: Bool = false @State private var hasChanges: Bool = false - + @State private var displayWarning: Bool = false + @Environment(\.dismiss) private var dismiss init(tournament: Tournament) { @@ -74,6 +75,11 @@ struct RegistrationSetupView: View { var body: some View { List { + if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false { + Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Pour l'instant Padel Club ne saura pas les prévenir automatiquement, vous devrez les contacter via l'écran de gestion des inscriptions.") + .foregroundStyle(.logoRed) + } + Section { Toggle(isOn: $enableOnlineRegistration) { Text("Activer") @@ -249,6 +255,7 @@ struct RegistrationSetupView: View { } .onChange(of: targetTeamCount) { + displayWarning = true _hasChanged() } diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index a72e616..19cfc4c 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -21,6 +21,8 @@ struct TableStructureView: View { @State private var updatedElements: Set = Set() @State private var structurePreset: PadelTournamentStructurePreset = .manual @State private var buildWildcards: Bool = true + @State private var displayWarning: Bool = false + @FocusState private var stepperFieldIsFocused: Bool var qualifiedFromGroupStage: Int { @@ -58,7 +60,11 @@ struct TableStructureView: View { @ViewBuilder var body: some View { List { - + if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false { + Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Pour l'instant Padel Club ne saura pas les prévenir automatiquement, vous devrez les contacter via l'écran de gestion des inscriptions.") + .foregroundStyle(.logoRed) + } + if tournament.state() != .build { Section { Picker(selection: $structurePreset) { @@ -91,6 +97,9 @@ struct TableStructureView: View { } label: { Text("Nombre d'équipes") } + .onChange(of: teamCount) { + displayWarning = true + } LabeledContent { StepperView(count: $groupStageCount, minimum: 0, maximum: maxGroupStages) { diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 9be8280..08cdae2 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -114,7 +114,9 @@ struct TournamentCellView: View { } Spacer() if let tournament = tournament as? Tournament, displayStyle == .wide { - if tournament.isCanceled { + if tournament.refreshInProgress || tournament.refreshRanking { + ProgressView() + } else if tournament.isCanceled { Text("Annulé".uppercased()) .capsule(foreground: .white, background: .logoRed) } else if shouldTournamentBeOver { @@ -164,6 +166,24 @@ struct TournamentCellView: View { Text(build.category.localizedLabel()) Text(build.age.localizedFederalAgeLabel()) } + if displayStyle == .wide, let tournament = tournament as? Tournament { + if tournament.enableOnlineRegistration { + let value: Int = tournament.onlineTeams().count + HStack { + Spacer() + if value == 0 { + Text("(dont aucune équipe inscrite en ligne)").foregroundStyle(.secondary).font(.footnote) + } else { + Text("(dont " + value.formatted() + " équipe\(value.pluralSuffix) inscrite\(value.pluralSuffix) en ligne)").foregroundStyle(.secondary).font(.footnote) + } + } + } + if tournament.refreshRanking { + Text("mise à jour des classements des joueurs").foregroundStyle(.secondary).font(.footnote) + } else if tournament.refreshInProgress { + Text("synchronisation des inscriptions en ligne").foregroundStyle(.secondary).font(.footnote) + } + } } } .font(.caption) From 1843ab58d1c6097519fcfa2a857ac2bc3d5d0372 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 28 Feb 2025 11:02:03 +0100 Subject: [PATCH 08/18] fix team label and lucky loser stuff --- PadelClub/Data/Match.swift | 4 +++- PadelClub/Data/TeamRegistration.swift | 8 ++++++++ PadelClub/Views/Match/Components/MatchDateView.swift | 2 +- .../Views/Match/Components/PlayerBlockView.swift | 2 +- PadelClub/Views/Score/FollowUpMatchView.swift | 5 +++++ .../Views/Shared/SelectablePlayerListView.swift | 2 ++ PadelClub/Views/Team/TeamPickerView.swift | 12 ++++++++++-- PadelClub/Views/Team/TeamRowView.swift | 6 +++++- 8 files changed, 35 insertions(+), 6 deletions(-) diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index b80f645..5afa194 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -517,7 +517,7 @@ defer { return (groupStageObject.index + 1) * 100 + groupStageObject.indexOf(index) } guard let roundObject else { return index } - return roundObject.isLoserBracket() ? (roundObject.index + 1) * 10000 + indexInRound() : (roundObject.index + 1) * 1000 + indexInRound() + return (300 - (roundObject.theoryCumulativeMatchCount * 10 + roundObject.index * 22)) * 10 + indexInRound() } func previousMatches() -> [Match] { @@ -540,8 +540,10 @@ defer { 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 { diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 6744812..8a9066e 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -645,6 +645,14 @@ final class TeamRegistration: ModelObject, Storable { func shouldDisplayRankAndWeight() -> Bool { unsortedPlayers().count > 0 } + + func bracketMatchTitleAndQualifiedStatus() -> String? { + let values = [qualified ? "Qualifié" : nil, initialMatch()?.roundAndMatchTitle()].compactMap({ $0 }) + if values.isEmpty { + return nil + } + return values.joined(separator: " -> ") + } enum CodingKeys: String, CodingKey { case _id = "id" diff --git a/PadelClub/Views/Match/Components/MatchDateView.swift b/PadelClub/Views/Match/Components/MatchDateView.swift index 51ccc19..5dd2187 100644 --- a/PadelClub/Views/Match/Components/MatchDateView.swift +++ b/PadelClub/Views/Match/Components/MatchDateView.swift @@ -30,7 +30,7 @@ struct MatchDateView: View { } var currentDate: Date { - Date().withoutSeconds() + Date() } var body: some View { diff --git a/PadelClub/Views/Match/Components/PlayerBlockView.swift b/PadelClub/Views/Match/Components/PlayerBlockView.swift index d4c8d73..e2a7e15 100644 --- a/PadelClub/Views/Match/Components/PlayerBlockView.swift +++ b/PadelClub/Views/Match/Components/PlayerBlockView.swift @@ -151,7 +151,7 @@ struct PlayerBlockView: View { } } } - } else if let team { + } else if let team, hasWon == false, isWalkOut == false { TeamWeightView(team: team, teamPosition: teamPosition) } } diff --git a/PadelClub/Views/Score/FollowUpMatchView.swift b/PadelClub/Views/Score/FollowUpMatchView.swift index 78cda7f..27a88a1 100644 --- a/PadelClub/Views/Score/FollowUpMatchView.swift +++ b/PadelClub/Views/Score/FollowUpMatchView.swift @@ -217,6 +217,11 @@ struct FollowUpMatchView: View { } #if DEBUG Spacer() + if let roundObject = match.roundObject { + Text(roundObject.index.formatted()) + Text(roundObject.theoryCumulativeMatchCount.formatted()) + } + Text(match.computedOrder.formatted()) FooterButtonView("copier l'id") { let pasteboard = UIPasteboard.general pasteboard.string = match.id diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index 4210ef9..16b7d7e 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -427,7 +427,9 @@ struct MySearchView: View { searchViewModel.selectedPlayers.insert(player) } label: { ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true) + .contentShape(Rectangle()) } + .frame(maxWidth: .infinity) .buttonStyle(.plain) } } header: { diff --git a/PadelClub/Views/Team/TeamPickerView.swift b/PadelClub/Views/Team/TeamPickerView.swift index 0515283..73b8d59 100644 --- a/PadelClub/Views/Team/TeamPickerView.swift +++ b/PadelClub/Views/Team/TeamPickerView.swift @@ -134,11 +134,19 @@ struct TeamPickerView: View { // presentTeamPickerView = false // } } label: { - TeamRowView(team: team) - .contentShape(Rectangle()) + VStack(alignment: .leading) { + if let roundAndMatchTitle = team.bracketMatchTitleAndQualifiedStatus() { + Text(roundAndMatchTitle) + .font(.headline) + .frame(maxWidth: .infinity, alignment: .leading) + } + TeamRowView(team: team) + } + .contentShape(Rectangle()) } .frame(maxWidth: .infinity) .buttonStyle(.plain) + .id(team.id) .listRowView(isActive: matchTypeContext == .loserBracket && round?.teams().map({ $0.id }).contains(team.id) == true, color: .green, hideColorVariation: true) // .confirmationDialog("Attention", isPresented: confirmationRequest, titleVisibility: .visible) { // Button("Retirer du tableau", role: .destructive) { diff --git a/PadelClub/Views/Team/TeamRowView.swift b/PadelClub/Views/Team/TeamRowView.swift index 4a1ba04..bb32774 100644 --- a/PadelClub/Views/Team/TeamRowView.swift +++ b/PadelClub/Views/Team/TeamRowView.swift @@ -9,6 +9,8 @@ import SwiftUI struct TeamRowView: View { @EnvironmentObject var dataStore: DataStore + @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed + var team: TeamRegistration var teamPosition: TeamPosition? = nil var displayCallDate: Bool = false @@ -20,7 +22,9 @@ struct TeamRowView: View { TeamWeightView(team: team, teamPosition: teamPosition, teamIndex: teamIndex) } label: { VStack(alignment: .leading) { - TeamHeadlineView(team: team) + if isEditingTournamentSeed.wrappedValue == false { + TeamHeadlineView(team: team) + } TeamView(team: team) } if displayCallDate { From 217b3dadf197864c35a4ad79e6a1efacd735c7b0 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 28 Feb 2025 11:02:34 +0100 Subject: [PATCH 09/18] v1.1.16 --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index ece45dc..db5ca97 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3344,7 +3344,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.15; + MARKETING_VERSION = 1.1.16; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3389,7 +3389,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.15; + MARKETING_VERSION = 1.1.16; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 025e22b47a10e4665f2000a7af686661dbbe6f3e Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 28 Feb 2025 11:25:56 +0100 Subject: [PATCH 10/18] fix tournament cell sttuf --- PadelClub/OnlineRegistrationWarningView.swift | 60 +++++++++++++++++++ .../Screen/InscriptionManagerView.swift | 34 ++++------- .../Shared/TournamentCellView.swift | 11 +--- 3 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 PadelClub/OnlineRegistrationWarningView.swift diff --git a/PadelClub/OnlineRegistrationWarningView.swift b/PadelClub/OnlineRegistrationWarningView.swift new file mode 100644 index 0000000..ad68e6a --- /dev/null +++ b/PadelClub/OnlineRegistrationWarningView.swift @@ -0,0 +1,60 @@ +// +// WaitingListView.swift +// PadelClub +// +// Created by razmig on 26/02/2025. +// + +import SwiftUI + +struct WaitingListView: View { + @Environment(Tournament.self) var tournament: Tournament + let teamCount: Int + + @ViewBuilder + var body: some View { + Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Pour l'instant Padel Club ne saura pas les prévenir automatiquement, vous devrez les contacter via l'écran de gestion des inscriptions.") + .foregroundStyle(.logoRed) + let selection = tournament.selectedSortedTeams() + if teamCount > tournament.teamCount { + Section { + let teams = tournament.waitingListSortedTeams(selectedSortedTeams: selection) + .prefix(teamCount - tournament.teamCount) + .filter { $0.hasRegisteredOnline() } + + ForEach(teams) { team in + NavigationLink { + EditingTeamView(team: team) + .environment(tournament) + } label: { + TeamRowView(team: team) + } + } + } header: { + Text("Équipes entrantes dans la sélection") + } footer: { + Text("Équipes inscrites en ligne à prévenir rentrant dans votre liste") + } + } + + if teamCount < tournament.teamCount { + Section { + let teams = selection.suffix(tournament.teamCount - teamCount) + .filter { $0.hasRegisteredOnline() } + + ForEach(teams) { team in + NavigationLink { + EditingTeamView(team: team) + .environment(tournament) + } label: { + TeamRowView(team: team) + } + } + } header: { + Text("Équipes sortantes de la sélection") + } footer: { + Text("Équipes inscrites en ligne à prévenir retirées de votre liste") + } + } + } +} diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 53f2001..9ffefae 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -840,31 +840,21 @@ struct InscriptionManagerView: View { // } if tournament.enableOnlineRegistration { - Button { - Task { - await _refreshList() - } + LabeledContent { + Text(tournament.unsortedTeams().filter({ $0.hasRegisteredOnline() }).count.formatted()) + .font(.largeTitle) } label: { - LabeledContent { - if tournament.refreshInProgress { - ProgressView() - } else { - Text(tournament.unsortedTeams().filter({ $0.hasRegisteredOnline() }).count.formatted()) - .fontWeight(.bold) - } - } label: { - if let refreshStatus { - Text("Inscriptions en ligne") - } else if tournament.refreshInProgress { - Text("Récupération des inscrits en ligne") - } else { - Text("Récupérer des inscrits en ligne") - } - if let refreshResult { - Text(refreshResult).foregroundStyle(.secondary) - } + Text("Inscriptions en ligne") + if let refreshResult { + Text(refreshResult).foregroundStyle(.secondary) + } else { + Text(" ") } } + + RowButtonView("Rafraîchir les inscriptions en ligne") { + await _refreshList() + } } } header: { HStack { diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 08cdae2..5914769 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -171,18 +171,11 @@ struct TournamentCellView: View { let value: Int = tournament.onlineTeams().count HStack { Spacer() - if value == 0 { - Text("(dont aucune équipe inscrite en ligne)").foregroundStyle(.secondary).font(.footnote) - } else { - Text("(dont " + value.formatted() + " équipe\(value.pluralSuffix) inscrite\(value.pluralSuffix) en ligne)").foregroundStyle(.secondary).font(.footnote) + if value > 0 { + Text("(dont " + value.formatted() + " équipe\(value.pluralSuffix) inscrite\(value.pluralSuffix) en ligne)") } } } - if tournament.refreshRanking { - Text("mise à jour des classements des joueurs").foregroundStyle(.secondary).font(.footnote) - } else if tournament.refreshInProgress { - Text("synchronisation des inscriptions en ligne").foregroundStyle(.secondary).font(.footnote) - } } } } From 41f026c013752c79723a7cfc305ddd957b625ce0 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 28 Feb 2025 14:24:32 +0100 Subject: [PATCH 11/18] fix start date seconds setup --- PadelClub/Views/Match/MatchDetailView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 45f0444..9e19053 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -495,7 +495,7 @@ struct MatchDetailView: View { Text("Horaire") } .onChange(of: startDateSetup) { - let date = Date().withoutSeconds() + let date = Date() switch startDateSetup { case .customDate: break From f8d41f23d0207215222bd9a44d923b92667d3ebf Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 28 Feb 2025 23:05:40 +0100 Subject: [PATCH 12/18] fix online reg stuff --- PadelClub/Views/Team/EditingTeamView.swift | 125 +++++++++--------- .../Screen/InscriptionManagerView.swift | 15 ++- .../Screen/RegistrationSetupView.swift | 4 +- .../Screen/TableStructureView.swift | 4 +- 4 files changed, 74 insertions(+), 74 deletions(-) diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 8f06955..7263dfd 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -20,11 +20,11 @@ struct EditingTeamView: View { @State private var sentError: ContactManagerError? = nil @State private var showSubscriptionView: Bool = false @State private var registrationDate : Date + @State private var walkOut : Bool + @State private var wildCardBracket : Bool + @State private var wildCardGroupStage : Bool @State private var name: String @FocusState private var focusedField: TeamRegistration.CodingKeys? - @State private var presentOnlineRegistrationWarning: Bool = false - @State private var currentWaitingList: TeamRegistration? - @State private var presentTeamToWarn: Bool = false var messageSentFailed: Binding { Binding { @@ -36,6 +36,22 @@ struct EditingTeamView: View { } } + var hasChanged: Binding { + Binding { + if tournament.enableOnlineRegistration == false { + return false + } + + return + registrationDate != team.registrationDate + || walkOut != team.walkOut + || wildCardBracket != team.wildCardBracket + || wildCardGroupStage != team.wildCardGroupStage + + } set: { _ in + } + } + var tournamentStore: TournamentStore { return self.tournament.tournamentStore } @@ -44,25 +60,18 @@ struct EditingTeamView: View { self.team = team _name = .init(wrappedValue: team.name ?? "") _registrationDate = State(wrappedValue: team.registrationDate ?? Date()) + _walkOut = State(wrappedValue: team.walkOut) + _wildCardBracket = State(wrappedValue: team.wildCardBracket) + _wildCardGroupStage = State(wrappedValue: team.wildCardGroupStage) } private func _resetTeam() { - let selectedSortedTeams = tournament.selectedSortedTeams() - self.currentWaitingList = tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams).filter({ $0.hasRegisteredOnline() }).first team.resetPositions() team.wildCardGroupStage = false team.walkOut = false team.wildCardBracket = false } - private func _checkOnlineRegistrationWarning() { - guard let currentWaitingList else { return } - let selectedSortedTeams = tournament.selectedSortedTeams().map({ $0.id }) - if selectedSortedTeams.contains(currentWaitingList.id) { - presentOnlineRegistrationWarning = true - } - } - var body: some View { List { Section { @@ -92,10 +101,6 @@ struct EditingTeamView: View { .headerProminence(.increased) Section { - DatePicker(selection: $registrationDate) { - Text("Inscription") - Text(registrationDate.localizedWeekDay().capitalized) - } if let callDate = team.callDate { LabeledContent() { Text(callDate.localizedDate()) @@ -117,34 +122,23 @@ struct EditingTeamView: View { Text("Équipe sur place") } } + } + + Section { + DatePicker(selection: $registrationDate) { + Text("Inscription") + Text(registrationDate.localizedWeekDay().capitalized) + } - Toggle(isOn: .init(get: { - return team.wildCardBracket - }, set: { value in - _resetTeam() - team.wildCardBracket = value - _save() - })) { + Toggle(isOn: $wildCardBracket) { Text("Wildcard Tableau") } - Toggle(isOn: .init(get: { - return team.wildCardGroupStage - }, set: { value in - _resetTeam() - team.wildCardGroupStage = value - _save() - })) { + Toggle(isOn: $wildCardGroupStage) { Text("Wildcard Poule") - } + }.disabled(tournament.groupStageCount == 0) - Toggle(isOn: .init(get: { - return team.walkOut - }, set: { value in - _resetTeam() - team.walkOut = value - _save() - })) { + Toggle(isOn: $walkOut) { Text("Forfait") } } @@ -216,30 +210,24 @@ struct EditingTeamView: View { } } } - .sheet(isPresented: $presentTeamToWarn) { - if let currentWaitingList { - NavigationStack { - EditingTeamView(team: currentWaitingList) - } - .tint(.master) + .alert("Attention", isPresented: hasChanged, actions: { + Button("Confirmer") { + _resetTeam() + team.registrationDate = registrationDate + team.wildCardBracket = wildCardBracket + team.wildCardGroupStage = wildCardGroupStage + team.walkOut = walkOut + _save() } - } - .alert("Attention", isPresented: $presentOnlineRegistrationWarning, actions: { - if currentWaitingList != nil { - - Button("Voir l'équipe") { - self.presentTeamToWarn = true - } - - Button("OK") { - self.currentWaitingList = nil - self.presentOnlineRegistrationWarning = false - } + + Button("Annuler", role: .cancel) { + registrationDate = team.registrationDate ?? Date() + walkOut = team.walkOut + wildCardBracket = team.wildCardBracket + wildCardGroupStage = team.wildCardGroupStage } }, message: { - if let currentWaitingList { - Text("L'équipe \(currentWaitingList.teamLabel(separator: "/")), inscrite en ligne, rentre dans votre sélection suite à la modification que vous venez de faire, voulez-vous les prévenir ?") - } + Text("Ce changement peut entraîner l'entrée ou la sortie d'une équipe de votre sélection. Padel Club préviendra automatiquement une équipe inscrite en ligne de son nouveau statut.") }) .navigationBarBackButtonHidden(focusedField != nil) .toolbar(content: { @@ -327,8 +315,19 @@ struct EditingTeamView: View { } } .onChange(of: registrationDate) { - team.registrationDate = registrationDate - _save() + if tournament.enableOnlineRegistration == false { + team.registrationDate = registrationDate + _save() + } + } + .onChange(of: [walkOut, wildCardBracket, wildCardGroupStage]) { + if tournament.enableOnlineRegistration == false { + _resetTeam() + team.walkOut = walkOut + team.wildCardBracket = wildCardBracket + team.wildCardGroupStage = wildCardGroupStage + _save() + } } .toolbarBackground(.visible, for: .navigationBar) .navigationTitle("Édition de l'équipe") @@ -369,8 +368,6 @@ struct EditingTeamView: View { } catch { Logger.error(error) } - - _checkOnlineRegistrationWarning() } private var _networkErrorMessage: String { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 53f2001..cd361a6 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -247,7 +247,7 @@ struct InscriptionManagerView: View { if tournament.enableOnlineRegistration { RowButtonView("Rafraîchir la liste", cornerRadius: 20) { - await _refreshList() + await _refreshList(forced: true) } } else if tournament.onlineRegistrationCanBeEnabled() { RowButtonView("Inscription en ligne") { @@ -259,13 +259,15 @@ struct InscriptionManagerView: View { } } .task { - await _refreshList() + await _refreshList(forced: false) } .refreshable { - await _refreshList() + await _refreshList(forced: true) } .onAppear { - _setHash(currentSelectedSortedTeams: selectedSortedTeams) + if tournament.enableOnlineRegistration == false || refreshStatus == true { + _setHash(currentSelectedSortedTeams: selectedSortedTeams) + } } .onDisappear { _handleHashDiff(selectedSortedTeams: selectedSortedTeams) @@ -544,7 +546,8 @@ struct InscriptionManagerView: View { // try? tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) // } // - private func _refreshList() async { + private func _refreshList(forced: Bool) async { + if refreshStatus == true, forced == false { return } if tournament.enableOnlineRegistration == false { return } if tournament.hasEnded() { return } if tournament.refreshInProgress { return } @@ -842,7 +845,7 @@ struct InscriptionManagerView: View { if tournament.enableOnlineRegistration { Button { Task { - await _refreshList() + await _refreshList(forced: true) } } label: { LabeledContent { diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index bc1445f..13c38d2 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -75,8 +75,8 @@ struct RegistrationSetupView: View { var body: some View { List { - if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false { - Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Pour l'instant Padel Club ne saura pas les prévenir automatiquement, vous devrez les contacter via l'écran de gestion des inscriptions.") + if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false, tournament.hasEnded() == false, tournament.hasStarted() == false { + Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Padel Club saura prévenir les équipes inscrites en ligne automatiquement.") .foregroundStyle(.logoRed) } diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index 19cfc4c..1d8b8a0 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -60,8 +60,8 @@ struct TableStructureView: View { @ViewBuilder var body: some View { List { - if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false { - Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Pour l'instant Padel Club ne saura pas les prévenir automatiquement, vous devrez les contacter via l'écran de gestion des inscriptions.") + if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false, tournament.hasEnded() == false, tournament.hasStarted() == false { + Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Padel Club saura prévenir les équipes inscrites en ligne automatiquement.") .foregroundStyle(.logoRed) } From f7c393fc072fb01b06598ce30447e6220352c6f6 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 28 Feb 2025 23:29:51 +0100 Subject: [PATCH 13/18] fix compilation issue post merge --- PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift | 2 +- PadelClub/Views/Tournament/Shared/TournamentCellView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index af7383c..af5f70c 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -856,7 +856,7 @@ struct InscriptionManagerView: View { } RowButtonView("Rafraîchir les inscriptions en ligne") { - await _refreshList() + await _refreshList(forced: true) } } } header: { diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 5914769..589ac6e 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -172,7 +172,7 @@ struct TournamentCellView: View { HStack { Spacer() if value > 0 { - Text("(dont " + value.formatted() + " équipe\(value.pluralSuffix) inscrite\(value.pluralSuffix) en ligne)") + Text("(dont " + value.formatted() + " inscrite\(value.pluralSuffix) en ligne)") } } } From fdfe9b92ee8be887b85117cf2e9178b0d247791b Mon Sep 17 00:00:00 2001 From: Raz Date: Sat, 1 Mar 2025 08:38:15 +0100 Subject: [PATCH 14/18] minor fix --- PadelClub/Data/Tournament.swift | 7 ++++--- .../Tournament/Screen/RegistrationSetupView.swift | 2 +- .../Views/Tournament/Screen/TableStructureView.swift | 12 +++++++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index e927f14..aa8d85a 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1941,9 +1941,11 @@ defer { return bracketTeamCount } - func buildBracket() { + func buildBracket(minimalBracketTeamCount: Int? = nil) { guard rounds().isEmpty else { return } - let roundCount = RoundRule.numberOfRounds(forTeams: bracketTeamCount()) + let roundCount = RoundRule.numberOfRounds(forTeams: minimalBracketTeamCount ?? bracketTeamCount()) + let matchCount = RoundRule.numberOfMatches(forTeams: minimalBracketTeamCount ?? bracketTeamCount()) + let rounds = (0.. Date: Sat, 1 Mar 2025 12:21:36 +0100 Subject: [PATCH 15/18] b2 --- PadelClub.xcodeproj/project.pbxproj | 4 +- PadelClub/Data/GroupStage.swift | 24 +++--- PadelClub/Views/Calling/CallView.swift | 12 ++- .../Views/GroupStage/GroupStagesView.swift | 81 ++++++++++++++++++- .../Screen/TableStructureView.swift | 2 +- 5 files changed, 104 insertions(+), 19 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index db5ca97..a501486 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3318,7 +3318,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3364,7 +3364,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index c84ab31..b60f139 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -438,7 +438,7 @@ final class GroupStage: ModelObject, Storable { unsortedTeams().flatMap({ $0.unsortedPlayers() }) } - fileprivate typealias TeamScoreAreInIncreasingOrder = (TeamGroupStageScore, TeamGroupStageScore) -> Bool + typealias TeamScoreAreInIncreasingOrder = (TeamGroupStageScore, TeamGroupStageScore) -> Bool typealias TeamGroupStageScore = (team: TeamRegistration, wins: Int, loses: Int, setDifference: Int, gameDifference: Int) @@ -492,20 +492,24 @@ final class GroupStage: ModelObject, Storable { var scoreCache: [Int: TeamGroupStageScore] = [:] + func computedScore(forTeam team: TeamRegistration, step: Int = 0) -> TeamGroupStageScore? { + if let cachedScore = scoreCache[team.groupStagePositionAtStep(step)!] { + return cachedScore + } else { + let score = _score(forGroupStagePosition: team.groupStagePositionAtStep(step)!) + if let score = score { + scoreCache[team.groupStagePositionAtStep(step)!] = 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 }) ?? { - if let cachedScore = scoreCache[team.groupStagePositionAtStep(step)!] { - return cachedScore - } else { - let score = _score(forGroupStagePosition: team.groupStagePositionAtStep(step)!) - if let score = score { - scoreCache[team.groupStagePositionAtStep(step)!] = score - } - return score - } + return computedScore(forTeam: team, step: step) }() }).sorted { (lhs, rhs) in let predicates: [TeamScoreAreInIncreasingOrder] = [ diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index 1be6236..8f8f8a7 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -199,14 +199,16 @@ struct CallView: View { let uncalledTeams = teams.filter { $0.getPhoneNumbers().isEmpty } if networkMonitor.connected == false { - if uncalledTeams.isEmpty == false { + if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false { self.sentError = .uncalledTeams(uncalledTeams) } else { self.sentError = .messageNotSent } } else { - if uncalledTeams.isEmpty == false { + if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false { self.sentError = .uncalledTeams(uncalledTeams) + } else if uncalledTeams.isEmpty == false, calledTeams.isEmpty { + self._called(uncalledTeams, true) } self._called(calledTeams, true) } @@ -228,14 +230,16 @@ struct CallView: View { if networkMonitor.connected == false { self.contactType = nil - if uncalledTeams.isEmpty == false { + if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false { self.sentError = .uncalledTeams(uncalledTeams) } else { self.sentError = .mailNotSent } } else { - if uncalledTeams.isEmpty == false { + if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false { self.sentError = .uncalledTeams(uncalledTeams) + } else if uncalledTeams.isEmpty == false, calledTeams.isEmpty { + self._called(uncalledTeams, true) } self._called(calledTeams, true) } diff --git a/PadelClub/Views/GroupStage/GroupStagesView.swift b/PadelClub/Views/GroupStage/GroupStagesView.swift index 13cd9e2..a8f9f33 100644 --- a/PadelClub/Views/GroupStage/GroupStagesView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesView.swift @@ -105,6 +105,80 @@ struct GroupStagesView: View { allDestinations.append(contentsOf: groupStageDestinations) return allDestinations } + + func sortedTeams(missingQualifiedFromGroupStages: [TeamRegistration]) -> [GroupStage.TeamGroupStageScore] { + let sortedTeams = missingQualifiedFromGroupStages.compactMap({ team in + team.groupStageObject()?.computedScore(forTeam: team) + }).sorted { (lhs, rhs) in + let predicates: [GroupStage.TeamScoreAreInIncreasingOrder] = [ + { $0.wins < $1.wins }, + { $0.setDifference < $1.setDifference }, + { $0.gameDifference < $1.gameDifference}, + ] + + for predicate in predicates { + if !predicate(lhs, rhs) && !predicate(rhs, lhs) { + continue + } + + return predicate(lhs, rhs) + } + + return false + } + return sortedTeams + } + + func _sortedAdditionnalTeams(missingQualifiedFromGroupStages: [TeamRegistration]) -> some View { + Section { + let teamGroupStageScores = self.sortedTeams(missingQualifiedFromGroupStages: missingQualifiedFromGroupStages).reversed() + ForEach(teamGroupStageScores, id: \.team.id) { teamGroupStageScore in + let team = teamGroupStageScore.team + let groupStage = team.groupStageObject()! + let groupStagePosition = team.groupStagePosition! + + NavigationLink { + GroupStageTeamView(groupStage: groupStage, team: team) + .environment(self.tournament) + } label: { + HStack { + VStack(alignment: .leading) { + if let teamName = team.name, teamName.isEmpty == false { + Text(teamName).foregroundStyle(.secondary).font(.footnote) + } + ForEach(team.players()) { player in + Text(player.playerLabel()).lineLimit(1) + } + } + Spacer() + if let score = groupStage.scoreLabel(forGroupStagePosition: groupStagePosition, score: teamGroupStageScore) { + VStack(alignment: .trailing) { + HStack(spacing: 0.0) { + Text(score.wins) + Text("/") + Text(score.losses) + }.font(.headline).monospacedDigit() + if let setsDifference = score.setsDifference { + HStack(spacing: 4.0) { + Text(setsDifference) + }.font(.footnote) + } + if let gamesDifference = score.gamesDifference { + HStack(spacing: 4.0) { + Text(gamesDifference) + }.font(.footnote) + } + } + } + } + } + } + } header: { + let name = "\((tournament.qualifiedPerGroupStage + 1).ordinalFormatted())" + Text("Meilleurs \(name) de poule") + } + + } var body: some View { VStack(spacing: 0) { @@ -116,9 +190,8 @@ struct GroupStagesView: View { List { if tournament.groupStageAdditionalQualified > 0 { let missingQualifiedFromGroupStages = tournament.missingQualifiedFromGroupStages() + let name = "\((tournament.qualifiedPerGroupStage + 1).ordinalFormatted())" Section { - let name = "\((tournament.qualifiedPerGroupStage + 1).ordinalFormatted())" - NavigationLink { SpinDrawView(drawees: ["Qualification d'un \(name) de poule"], segments: missingQualifiedFromGroupStages) { results in results.forEach { drawResult in @@ -134,6 +207,8 @@ struct GroupStagesView: View { Text("Qualifier un \(name) de poule par tirage au sort") } .disabled(tournament.moreQualifiedToDraw() == 0 || missingQualifiedFromGroupStages.isEmpty) + } header: { + Text("Tirage au sort d'un \(name) de poule") } footer: { if tournament.moreQualifiedToDraw() == 0 { Text("Aucune équipe supplémentaire à qualifier. Vous pouvez en rajouter en modifier le paramètre dans structure.") @@ -141,6 +216,8 @@ struct GroupStagesView: View { Text("Aucune équipe supplémentaire à tirer au sort. Attendez la fin des poules.") } } + + _sortedAdditionnalTeams(missingQualifiedFromGroupStages: missingQualifiedFromGroupStages) } let runningMatches = Tournament.runningMatches(allMatches) diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index 4f5f95d..a06979b 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -45,7 +45,7 @@ struct TableStructureView: View { var moreQualifiedLabel: String { if groupStageAdditionalQualified == 0 { return "Aucun" } - return (groupStageAdditionalQualified > 1 ? "les \(groupStageAdditionalQualified)" : "le") + " meilleur\(groupStageAdditionalQualified.pluralSuffix) " + (qualifiedPerGroupStage + 1).ordinalFormatted() + return (groupStageAdditionalQualified > 1 ? "les \(groupStageAdditionalQualified)" : "le") + " " + (qualifiedPerGroupStage + 1).ordinalFormatted() } var maxGroupStages: Int { From 4a522abed99fce74c63814075a81e70e188b3d58 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sun, 2 Mar 2025 12:50:01 +0100 Subject: [PATCH 16/18] fix stuff --- PadelClub/Data/Tournament.swift | 6 +++- .../GroupStage/GroupStagesSettingsView.swift | 29 +++++++++---------- PadelClub/Views/Team/EditingTeamView.swift | 11 +++++-- .../Screen/InscriptionManagerView.swift | 4 +-- .../Screen/RegistrationSetupView.swift | 9 ++++-- .../Screen/TableStructureView.swift | 12 ++++---- 6 files changed, 41 insertions(+), 30 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index aa8d85a..190d79f 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1196,7 +1196,7 @@ defer { } } - func registrationIssues(selectedTeams: [TeamRegistration]) -> Int { + 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) @@ -2657,6 +2657,10 @@ defer { unsortedTeams().filter({ $0.hasRegisteredOnline() }) } + func shouldWarnOnlineRegistrationUpdates() -> Bool { + onlineTeams().isEmpty == false && hasEnded() == false && hasStarted() == false + } + func refreshTeamList() async throws { guard enableOnlineRegistration, refreshInProgress == false, hasEnded() == false else { return } refreshInProgress = true diff --git a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift index 6961bae..ed98683 100644 --- a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift @@ -128,6 +128,18 @@ struct GroupStagesSettingsView: View { } } #endif + + Section { + menuGenerateGroupStage(.random) + } footer: { + Text("Redistribue les équipes par tirage au sort par chapeau") + } + + Section { + menuGenerateGroupStage(.snake) + } footer: { + Text("Redistribue les équipes par la méthode du serpentin") + } Section { RowButtonView("Retirer tous les horaires", role: .destructive) { @@ -144,30 +156,17 @@ struct GroupStagesSettingsView: View { } } } footer: { - Text("Retire les horaires pré-définis des matchs. Utile si vous avez convoqué mais que l'ordre des matchs à lancer n'est pas important.") + Text("Retire les horaires pré-définis des matchs. Utile si vous avez convoqué mais que l'ordre des matchs à lancer n'est pas important.").foregroundStyle(.logoRed).bold() } - if tournament.unsortedTeams().filter({ $0.groupStagePosition != nil }).isEmpty == false { Section { menuBuildAllGroupStages } footer: { - Text("Efface et recréé les poules, les horaires et les résultats existants seront perdus") + Text("Efface et recréé les poules, les horaires et les résultats existants seront perdus").foregroundStyle(.logoRed).bold() } } - Section { - menuGenerateGroupStage(.random) - } footer: { - Text("Redistribue les équipes par tirage au sort par chapeau") - } - - Section { - menuGenerateGroupStage(.snake) - } footer: { - Text("Redistribue les équipes par la méthode du serpentin") - } - let groupStages = tournament.groupStages() Section { diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 7263dfd..62e8909 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -38,7 +38,7 @@ struct EditingTeamView: View { var hasChanged: Binding { Binding { - if tournament.enableOnlineRegistration == false { + if canSaveWithoutWarning() { return false } @@ -67,6 +67,7 @@ struct EditingTeamView: View { private func _resetTeam() { team.resetPositions() + team.qualified = false team.wildCardGroupStage = false team.walkOut = false team.wildCardBracket = false @@ -315,13 +316,13 @@ struct EditingTeamView: View { } } .onChange(of: registrationDate) { - if tournament.enableOnlineRegistration == false { + if canSaveWithoutWarning() { team.registrationDate = registrationDate _save() } } .onChange(of: [walkOut, wildCardBracket, wildCardGroupStage]) { - if tournament.enableOnlineRegistration == false { + if canSaveWithoutWarning() { _resetTeam() team.walkOut = walkOut team.wildCardBracket = wildCardBracket @@ -334,6 +335,10 @@ struct EditingTeamView: View { .navigationBarTitleDisplayMode(.inline) } + func canSaveWithoutWarning() -> Bool { + tournament.shouldWarnOnlineRegistrationUpdates() && tournament.teamCount <= tournament.unsortedTeamsCount() + } + private var confirmationReceived: Binding { Binding { team.confirmed() diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index af5f70c..f6b0387 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -192,8 +192,8 @@ struct InscriptionManagerView: View { self.teamsHash = _simpleHash(ids: selectedSortedTeams.map { $0.id }) } self.registrationIssues = nil - DispatchQueue.main.async { - self.registrationIssues = tournament.registrationIssues(selectedTeams: selectedSortedTeams) + Task { + self.registrationIssues = await tournament.registrationIssues(selectedTeams: selectedSortedTeams) } } diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index 7287b1b..54de58a 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -27,7 +27,6 @@ struct RegistrationSetupView: View { @State private var showMoreInfos: Bool = false @State private var hasChanges: Bool = false - @State private var displayWarning: Bool = false @Environment(\.dismiss) private var dismiss @@ -73,9 +72,14 @@ struct RegistrationSetupView: View { } + func displayWarning() -> Bool { + let unsortedTeamsCount = tournament.unsortedTeamsCount() + return tournament.shouldWarnOnlineRegistrationUpdates() && targetTeamCount != tournament.teamCount && (tournament.teamCount <= unsortedTeamsCount || targetTeamCount <= unsortedTeamsCount) + } + var body: some View { List { - if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false, tournament.hasEnded() == false, tournament.hasStarted() == false { + if displayWarning() { Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Padel Club saura prévenir les équipes inscrites en ligne automatiquement.") .foregroundStyle(.logoRed) } @@ -255,7 +259,6 @@ struct RegistrationSetupView: View { } .onChange(of: targetTeamCount) { - displayWarning = targetTeamCount != tournament.teamCount _hasChanged() } diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index a06979b..e9876a0 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -21,10 +21,13 @@ struct TableStructureView: View { @State private var updatedElements: Set = Set() @State private var structurePreset: PadelTournamentStructurePreset = .manual @State private var buildWildcards: Bool = true - @State private var displayWarning: Bool = false - @FocusState private var stepperFieldIsFocused: Bool + func displayWarning() -> Bool { + let unsortedTeamsCount = tournament.unsortedTeamsCount() + return tournament.shouldWarnOnlineRegistrationUpdates() && teamCount != tournament.teamCount && (tournament.teamCount <= unsortedTeamsCount || teamCount <= unsortedTeamsCount) + } + var qualifiedFromGroupStage: Int { groupStageCount * qualifiedPerGroupStage } @@ -60,7 +63,7 @@ struct TableStructureView: View { @ViewBuilder var body: some View { List { - if displayWarning, tournament.enableOnlineRegistration, tournament.onlineTeams().isEmpty == false, tournament.hasEnded() == false, tournament.hasStarted() == false { + if displayWarning() { Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Padel Club saura prévenir les équipes inscrites en ligne automatiquement.") .foregroundStyle(.logoRed) } @@ -97,9 +100,6 @@ struct TableStructureView: View { } label: { Text("Nombre d'équipes") } - .onChange(of: teamCount) { - displayWarning = teamCount != tournament.teamCount - } LabeledContent { StepperView(count: $groupStageCount, minimum: 0, maximum: maxGroupStages) { From 14993d719e53f0632f1e352209779645647f7df1 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sun, 2 Mar 2025 12:50:21 +0100 Subject: [PATCH 17/18] v1.1.18 --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index a501486..0fff4b7 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3318,7 +3318,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3344,7 +3344,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.16; + MARKETING_VERSION = 1.1.18; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3364,7 +3364,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3389,7 +3389,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.16; + MARKETING_VERSION = 1.1.18; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 097290962c08bbb92da6e0a7e9eafb71bc829ede Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sun, 2 Mar 2025 12:56:41 +0100 Subject: [PATCH 18/18] clean up --- PadelClub/Views/Team/EditingTeamView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 62e8909..6f930ca 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -336,7 +336,7 @@ struct EditingTeamView: View { } func canSaveWithoutWarning() -> Bool { - tournament.shouldWarnOnlineRegistrationUpdates() && tournament.teamCount <= tournament.unsortedTeamsCount() + (tournament.shouldWarnOnlineRegistrationUpdates() && tournament.teamCount <= tournament.unsortedTeamsCount()) == false } private var confirmationReceived: Binding {