add special position management

sync^2
Raz 8 months ago
parent 563404d92b
commit 377f1eced4
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 3
      PadelClub/Data/Match.swift
  3. 23
      PadelClub/Data/Tournament.swift
  4. 29
      PadelClub/ViewModel/MatchSpot.swift
  5. 80
      PadelClub/Views/Round/RoundView.swift

@ -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 = "<group>"; };
FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = "<group>"; };
FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatGuideView.swift; sourceTree = "<group>"; };
FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchSpot.swift; sourceTree = "<group>"; };
FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = "<group>"; };
FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = "<group>"; };
FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchViewStyle.swift; sourceTree = "<group>"; };
@ -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 */,

@ -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"

@ -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()

@ -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"
}
}
}

@ -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 }
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.")
}
func matchTitle(displayStyle: DisplayStyle) -> String {
[match.matchTitle(displayStyle), teamPositionLabel()].joined(separator: " - ")
}
func teamPositionLabel() -> String {
switch teamPosition {
case .one:
return "haut"
case .two:
return "bas"
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.")
}
}
}
extension Match {
func matchSpots() -> [MatchSpot] {
[MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)]
}
}

Loading…
Cancel
Save