From 377f1eced46318ccf172aa1a590fe4e256351ca5 Mon Sep 17 00:00:00 2001 From: Raz Date: Sat, 22 Mar 2025 10:17:30 +0100 Subject: [PATCH] add special position management --- PadelClub.xcodeproj/project.pbxproj | 8 +++ PadelClub/Data/Match.swift | 3 + PadelClub/Data/Tournament.swift | 23 +++++-- PadelClub/ViewModel/MatchSpot.swift | 29 +++++++++ PadelClub/Views/Round/RoundView.swift | 90 +++++++++++---------------- 5 files changed, 96 insertions(+), 57 deletions(-) create mode 100644 PadelClub/ViewModel/MatchSpot.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 1dd8dcb..f470fec 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -801,6 +801,9 @@ FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; FFB378362D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; + FFB39B342D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; }; + FFB39B352D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; }; + FFB39B362D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; }; FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; @@ -1205,6 +1208,7 @@ FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = ""; }; FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatGuideView.swift; sourceTree = ""; }; + FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchSpot.swift; sourceTree = ""; }; FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = ""; }; FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = ""; }; FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchViewStyle.swift; sourceTree = ""; }; @@ -1755,6 +1759,7 @@ isa = PBXGroup; children = ( FF6EC8FD2B94792300EA7F5A /* Screen.swift */, + FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */, FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */, FF7091652B90F0B000AB08DA /* TabDestination.swift */, FF025AEC2BD1513700A86CF8 /* AppScreen.swift */, @@ -2517,6 +2522,7 @@ FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */, FFBF41842BF75ED7001B24CB /* EventTournamentsView.swift in Sources */, FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */, + FFB39B352D8E8B05008E0C89 /* MatchSpot.swift in Sources */, FF17CA4A2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */, FF9268072BCE94D90080F940 /* TournamentCallView.swift in Sources */, FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */, @@ -2809,6 +2815,7 @@ FF4CBFE92C996C0600151637 /* CloudConvert.swift in Sources */, FF4CBFEA2C996C0600151637 /* EventTournamentsView.swift in Sources */, FF4CBFEB2C996C0600151637 /* DisplayContext.swift in Sources */, + FFB39B342D8E8B05008E0C89 /* MatchSpot.swift in Sources */, FF17CA4B2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */, FF4CBFEC2C996C0600151637 /* TournamentCallView.swift in Sources */, FF4CBFED2C996C0600151637 /* LoserRoundsView.swift in Sources */, @@ -3080,6 +3087,7 @@ FF70FB682C90584900129CC2 /* CloudConvert.swift in Sources */, FF70FB692C90584900129CC2 /* EventTournamentsView.swift in Sources */, FF70FB6A2C90584900129CC2 /* DisplayContext.swift in Sources */, + FFB39B362D8E8B05008E0C89 /* MatchSpot.swift in Sources */, FF17CA492CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */, FF70FB6B2C90584900129CC2 /* TournamentCallView.swift in Sources */, FF70FB6C2C90584900129CC2 /* LoserRoundsView.swift in Sources */, diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 5afa194..f08ef76 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -1078,6 +1078,9 @@ defer { previousMatches() + loserMatches() } + func matchSpots() -> [MatchSpot] { + [MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)] + } enum CodingKeys: String, CodingKey { case _id = "id" diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 6f18e49..a7769eb 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -761,19 +761,19 @@ defer { } func seedGroupAvailable(atRoundIndex roundIndex: Int, availableSeedGroup: SeedInterval) -> SeedInterval? { - - if availableSeeds().isEmpty == false && roundIndex >= lastSeedRound() { + let fullLeftSeeds = availableSeeds() + if fullLeftSeeds.isEmpty == false && roundIndex >= lastSeedRound() { if availableSeedGroup == SeedInterval(first: 1, last: 2) { return availableSeedGroup } let availableSeeds = seeds(inSeedGroup: availableSeedGroup) let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex) let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex) - + let maxSpots = max(availableSeedSpot.count, availableSeedOpponentSpot.count) if availableSeedGroup == SeedInterval(first: 3, last: 4), availableSeedSpot.count == 6 { - print("availableSeedGroup == SeedInterval(first: 3, last: 4)") return availableSeedGroup + } else if availableSeedGroup == SeedInterval(first: 5, last: 8), maxSpots == 6, availableSeeds.count == 2 { + return SeedInterval(first: 7, last: 12) } - if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.count == availableSeeds.count { return availableSeedGroup } else if availableSeeds.count == availableSeedOpponentSpot.count && availableSeedGroup.count == availableSeedOpponentSpot.count { @@ -784,6 +784,11 @@ defer { }) { return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: chunk) } + } else if fullLeftSeeds.count % maxSpots == 0 { + let seeds = seeds() + if let firstIndex = seeds.firstIndex(where: { $0.isSeedable() }) { + return SeedInterval(first: firstIndex + 1, last: firstIndex + maxSpots) + } } } @@ -815,6 +820,14 @@ defer { for (index, seed) in availableSeeds.enumerated() { seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) } + } else if seedGroup == SeedInterval(first: 5, last: 6), availableSeedSpot.count == 4 { + var spots = [Match]() + spots.append(availableSeedSpot[1]) + spots.append(availableSeedSpot[2]) + spots = spots.shuffled() + for (index, seed) in availableSeeds.enumerated() { + seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) + } } else { if availableSeeds.count <= availableSeedSpot.count { let spots = availableSeedSpot.shuffled() diff --git a/PadelClub/ViewModel/MatchSpot.swift b/PadelClub/ViewModel/MatchSpot.swift new file mode 100644 index 0000000..61df6c7 --- /dev/null +++ b/PadelClub/ViewModel/MatchSpot.swift @@ -0,0 +1,29 @@ +// +// MatchSpot.swift +// PadelClub +// +// Created by razmig on 22/03/2025. +// + + +struct MatchSpot: SpinDrawable { + let match: Match + let teamPosition: TeamPosition + + func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { + [match.roundTitle(), matchTitle(displayStyle: displayStyle)].compactMap { $0 } + } + + func matchTitle(displayStyle: DisplayStyle) -> String { + [match.matchTitle(displayStyle), teamPositionLabel()].joined(separator: " - ") + } + + func teamPositionLabel() -> String { + switch teamPosition { + case .one: + return "haut" + case .two: + return "bas" + } + } +} diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index e5e0bd2..3ffe945 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -131,33 +131,9 @@ struct RoundView: View { let availableQualifiedTeams = tournament.availableQualifiedTeams() if availableSeeds.isEmpty == false, let availableSeedGroup { - Section { - RowButtonView("Placer \(availableSeedGroup.localizedInterval())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) { - Task { - tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: availableSeedGroup) - _save(seeds: availableSeeds) - } - } - } footer: { - if availableSeedGroup.isFixed() == false { - Text("Le tirage au sort ne sera pas visuel. Toutes les équipes de ce chapeau seront tirées.") - } - } - - if (availableSeedGroup.isFixed() == false) { - Section { - Toggle(isOn: $hideNames) { - Text("Masquer les noms") - if hideNames { - Text("Réalise un tirage des positions.") - } - } - RowButtonView("Tirage au sort \(availableSeedGroup.localizedInterval()) visuel") { - self.selectedSeedGroup = availableSeedGroup - } - } footer: { - Text("Le tirage au sort sera visuel et automatique, n'hésitez pas à enregistrer une vidéo de votre écran. Toutes les équipes de ce chapeau seront tirées les unes après les autres.") - } + _seedGroupSection(availableSeeds: availableSeeds, availableSeedGroup: availableSeedGroup) + if upperRound.round.index == 3, availableSeedGroup.first == 5, availableSeedGroup.last == 8, availableSeeds.count > 4, let half = availableSeedGroup.chunks()?.first { + _seedGroupSection(availableSeeds: Array(availableSeeds.prefix(2)), availableSeedGroup: half) } } @@ -319,6 +295,11 @@ struct RoundView: View { array.append(spots[1]) array.append(spots[4]) return array + } else if availableSeedGroup == SeedInterval(first: 5, last: 6), spots.count == 4 { + var array = [Match]() + array.append(spots[1]) + array.append(spots[2]) + return array } else { return spots } @@ -403,32 +384,37 @@ struct RoundView: View { } } } -} - -struct MatchSpot: SpinDrawable { - let match: Match - let teamPosition: TeamPosition - - func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] { - [match.roundTitle(), matchTitle(displayStyle: displayStyle)].compactMap { $0 } - } - func matchTitle(displayStyle: DisplayStyle) -> String { - [match.matchTitle(displayStyle), teamPositionLabel()].joined(separator: " - ") - } - - func teamPositionLabel() -> String { - switch teamPosition { - case .one: - return "haut" - case .two: - return "bas" - } - } -} + private func _seedGroupSection(availableSeeds: [TeamRegistration], availableSeedGroup: SeedInterval) -> some View { + Group { + Section { + RowButtonView("Placer \(availableSeedGroup.localizedInterval())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) { + Task { + tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: availableSeedGroup) + _save(seeds: availableSeeds) + } + } + } footer: { + if availableSeedGroup.isFixed() == false { + Text("Le tirage au sort ne sera pas visuel. Toutes les équipes de ce chapeau seront tirées.") + } + } -extension Match { - func matchSpots() -> [MatchSpot] { - [MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)] + if (availableSeedGroup.isFixed() == false) { + Section { + Toggle(isOn: $hideNames) { + Text("Masquer les noms") + if hideNames { + Text("Réalise un tirage des positions.") + } + } + RowButtonView("Tirage au sort \(availableSeedGroup.localizedInterval()) visuel") { + self.selectedSeedGroup = availableSeedGroup + } + } footer: { + Text("Le tirage au sort sera visuel et automatique, n'hésitez pas à enregistrer une vidéo de votre écran. Toutes les équipes de ce chapeau seront tirées les unes après les autres.") + } + } + } } }