From 38d05af6dfbc7cad2611658b9440b73feeff9090 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Mon, 22 Apr 2024 04:43:51 +0200 Subject: [PATCH] fix bugs --- PadelClub.xcodeproj/project.pbxproj | 8 +- PadelClub/Data/DataStore.swift | 2 +- PadelClub/Data/Match.swift | 98 ++++++++++--- PadelClub/Data/Round.swift | 74 +++++----- PadelClub/Data/Tournament.swift | 19 ++- PadelClub/ViewModel/SeedInterval.swift | 20 +-- .../Match/Components/PlayerBlockView.swift | 3 - PadelClub/Views/Match/MatchRowView.swift | 33 +++++ PadelClub/Views/Round/LoserBracketView.swift | 53 ------- PadelClub/Views/Round/LoserRoundView.swift | 72 ++++++++++ PadelClub/Views/Round/LoserRoundsView.swift | 132 +++++++++--------- PadelClub/Views/Round/RoundSettingsView.swift | 10 ++ PadelClub/Views/Round/RoundView.swift | 8 +- 13 files changed, 329 insertions(+), 203 deletions(-) delete mode 100644 PadelClub/Views/Round/LoserBracketView.swift create mode 100644 PadelClub/Views/Round/LoserRoundView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 3bd322d..067c40c 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -212,7 +212,7 @@ FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */; }; FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */; }; FFC1E10C2BAC7FB0008D6F59 /* ClubImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */; }; - FFC2DCB22BBE75D40046DB9F /* LoserBracketView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserBracketView.swift */; }; + FFC2DCB22BBE75D40046DB9F /* LoserRoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */; }; FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; }; FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; }; FFC83D512BB8087E00750834 /* RoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D502BB8087E00750834 /* RoundView.swift */; }; @@ -501,7 +501,7 @@ FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = ""; }; FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkFederalService.swift; sourceTree = ""; }; FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubImportView.swift; sourceTree = ""; }; - FFC2DCB12BBE75D40046DB9F /* LoserBracketView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserBracketView.swift; sourceTree = ""; }; + FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundView.swift; sourceTree = ""; }; FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundsView.swift; sourceTree = ""; }; FFC83D4E2BB807D100750834 /* RoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundsView.swift; sourceTree = ""; }; FFC83D502BB8087E00750834 /* RoundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundView.swift; sourceTree = ""; }; @@ -1086,7 +1086,7 @@ FFC83D4E2BB807D100750834 /* RoundsView.swift */, FFC83D502BB8087E00750834 /* RoundView.swift */, FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */, - FFC2DCB12BBE75D40046DB9F /* LoserBracketView.swift */, + FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */, FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */, ); path = Round; @@ -1489,7 +1489,7 @@ FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */, FFF8ACD92B923F3C008466FA /* String+Extensions.swift in Sources */, FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */, - FFC2DCB22BBE75D40046DB9F /* LoserBracketView.swift in Sources */, + FFC2DCB22BBE75D40046DB9F /* LoserRoundView.swift in Sources */, FF9267FC2BCE84870080F940 /* PlayerPayView.swift in Sources */, FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */, FF6EC8FB2B94788600EA7F5A /* TournamentButtonView.swift in Sources */, diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index f917d67..4d2bf0b 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -65,7 +65,7 @@ class DataStore: ObservableObject { // store.addMigration(Migration(version: 2)) // store.addMigration(Migration(version: 3)) - let indexed : Bool = false + let indexed : Bool = true self.clubs = store.registerCollection(synchronized: false, indexed: indexed) self.tournaments = store.registerCollection(synchronized: false, indexed: indexed) self.events = store.registerCollection(synchronized: false, indexed: indexed) diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 43c74aa..833b61b 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -11,6 +11,7 @@ import LeStorage @Observable class Match: ModelObject, Storable { static func resourceName() -> String { "matches" } + var byeState: Bool = false var id: String = Store.randomId() var round: String? @@ -160,27 +161,90 @@ class Match: ModelObject, Storable { _toggleMatchDisableState(false) } + private func _loserMatch() -> Match? { + let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index) + return roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) + } + private func _toggleLoserMatchDisableState(_ state: Bool) { - if isLoserBracket == false { - let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index) - if let loserMatch = roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) { - loserMatch.disabled = state - try? DataStore.shared.matches.addOrUpdate(instance: loserMatch) - loserMatch._toggleLoserMatchDisableState(state) - } - } else { - roundObject?.loserRounds().forEach({ round in - round.handleLoserRoundState() - }) + guard let loserMatch = _loserMatch() else { return } + loserMatch.byeState = state + loserMatch._toggleMatchDisableState(state, forward: true) + + guard let otherMatch = _otherMatch() else { return } + if otherMatch.disabled == state { + loserMatch.byeState = !state } } - fileprivate func _toggleMatchDisableState(_ state: Bool) { + fileprivate func _otherMatch() -> Match? { + guard let round else { return nil } + guard index > 0 else { return nil } + let nextIndex = (index - 1) / 2 + let topMatchIndex = (nextIndex * 2) + 1 + let bottomMatchIndex = (nextIndex + 1) * 2 + let isTopMatch = topMatchIndex + 1 == index + let lookingForIndex = isTopMatch ? topMatchIndex : bottomMatchIndex + return Store.main.filter(isIncluded: { $0.round == round && $0.index == lookingForIndex }).first + } + + private func _forwardMatch(inRound round: Round) -> Match? { + guard let roundObjectNextRound = round.nextRound() else { return nil } + let nextIndex = (index - 1) / 2 + return Store.main.filter(isIncluded: { $0.round == roundObjectNextRound.id && $0.index == nextIndex }).first + } + + func _toggleForwardMatchDisableState(_ state: Bool) { + guard let roundObject else { return } + guard roundObject.loser != nil else { return } + guard let forwardMatch = _forwardMatch(inRound: roundObject) else { return } + guard let next = _otherMatch() else { return } + if next.disabled && byeState == false && next.byeState == false { + forwardMatch.byeState = false + forwardMatch._toggleMatchDisableState(state, forward: true) + } + + if byeState && next.byeState { + print("don't disable forward match") + forwardMatch.byeState = false + forwardMatch._toggleMatchDisableState(false, forward: true) + } else { + forwardMatch.byeState = true + forwardMatch._toggleMatchDisableState(state, forward: true) + } + +// if next.disabled == false { +// forwardMatch.byeState = state +// } + + + +// +// if next.disabled == state { +// if next.byeState != byeState { +// //forwardMatch.byeState = state +// forwardMatch._toggleMatchDisableState(state) +// } else { +// forwardMatch._toggleByeState(state) +// } +// } else { +// } +// forwardMatch._toggleByeState(state) + } + + func _toggleMatchDisableState(_ state: Bool, forward: Bool = false) { + if disabled == state { return } disabled = state - _toggleLoserMatchDisableState(state) - topPreviousRoundMatch()?._toggleMatchDisableState(state) - bottomPreviousRoundMatch()?._toggleMatchDisableState(state) + //byeState = false try? DataStore.shared.matches.addOrUpdate(instance: self) + + _toggleLoserMatchDisableState(state) + if forward { + _toggleForwardMatchDisableState(state) + } else { + topPreviousRoundMatch()?._toggleMatchDisableState(state) + bottomPreviousRoundMatch()?._toggleMatchDisableState(state) + } } func next() -> Match? { @@ -212,14 +276,14 @@ class Match: ModelObject, Storable { func topPreviousRoundMatch() -> Match? { guard let roundObject else { return nil } return Store.main.filter { match in - match.index == topPreviousRoundMatchIndex() && match.round == roundObject.previousRound()?.id + match.index == topPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id }.sorted(by: \.index).first } func bottomPreviousRoundMatch() -> Match? { guard let roundObject else { return nil } return Store.main.filter { match in - match.index == bottomPreviousRoundMatchIndex() && match.round == roundObject.previousRound()?.id + match.index == bottomPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id }.sorted(by: \.index).first } diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index 4577bd4..66ad6bc 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -209,7 +209,7 @@ class Round: ModelObject, Storable { } func loserRounds(forRoundIndex roundIndex: Int) -> [Round] { - return loserRoundsAndChildren().filter({ $0.index == roundIndex }).sorted(by: \.cumulativeMatchCount) + return loserRoundsAndChildren().filter({ $0.index == roundIndex }).sorted(by: \.theoryCumulativeMatchCount) } func isDisabled() -> Bool { @@ -268,39 +268,6 @@ class Round: ModelObject, Storable { try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches) } - func handleLoserRoundState() { - let _matches = _matches() - _matches.forEach { match in - let previousRound = self.previousRound() - let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: match.index) - var parentMatches = [Match]() - if isLoserBracket(), previousRound == nil, let parentRound = parentRound { - let upperBracketTopMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2) - let upperBracketBottomMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1) - parentMatches = [upperBracketTopMatch, upperBracketBottomMatch].compactMap({ $0 }) - } else if let previousRound { - let previousRoundTopMatch : Match? = Store.main.filter { - $0.round == previousRound.id && $0.index == match.topPreviousRoundMatchIndex() - }.first - let previousRoundBottomMatch : Match? = Store.main.filter { - $0.round == previousRound.id && $0.index == match.bottomPreviousRoundMatchIndex() - }.first - parentMatches = [previousRoundTopMatch, previousRoundBottomMatch].compactMap({ $0 }) - } - - if parentMatches.anySatisfy({ $0.disabled }) { - match.disabled = true - } else if parentMatches.allSatisfy({ $0.disabled == false }) { - match.disabled = false - } - } - - try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches) - loserRounds().forEach { round in - round.handleLoserRoundState() - } - } - var cumulativeMatchCount: Int { var totalMatches = playedMatches().count if let parent = parentRound { @@ -317,12 +284,45 @@ class Round: ModelObject, Storable { } } - func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String { + + func disabledMatches() -> [Match] { + _matches().filter({ $0.disabled }) + } + + var theoryCumulativeMatchCount: Int { + var totalMatches = RoundRule.numberOfMatches(forRoundIndex: index) + if let parent = parentRound { + totalMatches += parent.theoryCumulativeMatchCount + } + return totalMatches + } + + + func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String { if let parentRound, let initialRound = parentRound.initialRound() { let parentMatchCount = parentRound.cumulativeMatchCount - initialRound.playedMatches().count // print("initialRound", initialRound.roundTitle()) - if let initialRoundNextRound = initialRound.nextRound()?.playedMatches() { - return SeedInterval(first: parentMatchCount + initialRoundNextRound.count * 2 + 1, last: parentMatchCount + initialRoundNextRound.count * 2 + (previousRound() ?? parentRound).playedMatches().count).localizedLabel(displayStyle) + if let initialRoundNextRound = initialRound.nextRound() { + let total = initialRoundNextRound.playedMatches().count * 2 + initialRoundNextRound.disabledMatches().count + let previousRoundMatchCount = (previousRound() ?? parentRound).playedMatches().count + let seedInterval = SeedInterval(first: parentMatchCount + total + 1, last: parentMatchCount + total + previousRoundMatchCount).localizedLabel(displayStyle) +// print("seedInterval", seedInterval, parentMatchCount, total, previousRoundMatchCount) + return seedInterval + } + } + return RoundRule.roundName(fromRoundIndex: index) + } + + func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String { + if let parentRound, let initialRound = parentRound.initialRound() { + let parentMatchCount = parentRound.theoryCumulativeMatchCount - RoundRule.numberOfMatches(forRoundIndex: initialRound.index) +// print("initialRound", initialRound.roundTitle()) + if let initialRoundNextRound = initialRound.nextRound() { + let total = initialRoundNextRound.playedMatches().count * 2 + initialRoundNextRound.disabledMatches().count + let previousRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: (previousRound() ?? parentRound).index) + let seedInterval = SeedInterval(first: parentMatchCount + total + 1, last: parentMatchCount + total + previousRoundMatchCount).localizedLabel(displayStyle) +// print("seedInterval", seedInterval, parentMatchCount, total, previousRoundMatchCount) + return seedInterval } } return RoundRule.roundName(fromRoundIndex: index) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 00bf56f..129cdd6 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -259,9 +259,9 @@ class Tournament : ModelObject, Storable { let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex) let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex) - if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.dimension == availableSeeds.count { + if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.count == availableSeeds.count { return availableSeedGroup - } else if (availableSeeds.count == availableSeedOpponentSpot.count && availableSeeds.count == self.availableSeeds().count) && availableSeedGroup.dimension == availableSeedOpponentSpot.count { + } else if (availableSeeds.count == availableSeedOpponentSpot.count && availableSeeds.count == self.availableSeeds().count) && availableSeedGroup.count == availableSeedOpponentSpot.count { return availableSeedGroup } else if let chunks = availableSeedGroup.chunks() { if let chunk = chunks.first(where: { seedInterval in @@ -303,8 +303,12 @@ class Tournament : ModelObject, Storable { for (index, seed) in availableSeeds.enumerated() { seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: true) } - } else if let chunk = seedGroup.chunk() { - setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk) + } else if let chunks = seedGroup.chunks() { + if let chunk = chunks.first(where: { seedInterval in + seedInterval.first >= self.seededTeams().count + }) { + setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk) + } } } } @@ -343,7 +347,12 @@ class Tournament : ModelObject, Storable { let matches: [Match] = unsortedGroupStages.flatMap { $0._matches() } + allRounds().flatMap { $0._matches() } return matches.filter({ $0.disabled == false }) } - + + func _allMatchesIncludingDisabled() -> [Match] { + let unsortedGroupStages : [GroupStage] = Store.main.filter { $0.tournament == self.id } + return unsortedGroupStages.flatMap { $0._matches() } + allRounds().flatMap { $0._matches() } + } + func allRounds() -> [Round] { Store.main.filter { $0.tournament == self.id } } diff --git a/PadelClub/ViewModel/SeedInterval.swift b/PadelClub/ViewModel/SeedInterval.swift index e9d397c..ff140df 100644 --- a/PadelClub/ViewModel/SeedInterval.swift +++ b/PadelClub/ViewModel/SeedInterval.swift @@ -15,7 +15,11 @@ struct SeedInterval: Hashable, Comparable { return lhs.first < rhs.first } - var dimension: Int { + var count: Int { + dimension + 1 + } + + private var dimension: Int { (last - first) } @@ -29,21 +33,9 @@ struct SeedInterval: Hashable, Comparable { return nil } } - - func chunk() -> SeedInterval? { - if dimension / 2 > 0 { - let halfDimension = last - dimension / 2 - if halfDimension > first { - return SeedInterval(first: first, last: halfDimension - 1) - } - } - return nil - } -} -extension SeedInterval { func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if last - first < 2 { + if dimension < 2 { return "#\(first) / #\(last)" } else { return "#\(first) à #\(last)" diff --git a/PadelClub/Views/Match/Components/PlayerBlockView.swift b/PadelClub/Views/Match/Components/PlayerBlockView.swift index 29b9127..070928e 100644 --- a/PadelClub/Views/Match/Components/PlayerBlockView.swift +++ b/PadelClub/Views/Match/Components/PlayerBlockView.swift @@ -43,9 +43,6 @@ struct PlayerBlockView: View { } private func _defaultLabel() -> String { - if match.upperBracketMatch(teamPosition)?.disabled == true { - return "Bye" - } return teamPosition.localizedLabel() } diff --git a/PadelClub/Views/Match/MatchRowView.swift b/PadelClub/Views/Match/MatchRowView.swift index 0251cbb..be37b09 100644 --- a/PadelClub/Views/Match/MatchRowView.swift +++ b/PadelClub/Views/Match/MatchRowView.swift @@ -17,6 +17,39 @@ struct MatchRowView: View { if isEditingTournamentSeed.wrappedValue == true && match.isGroupStage() == false && match.isLoserBracket == false { MatchSetupView(match: match) } else { +// MatchSummaryView(match: match, matchViewStyle: matchViewStyle) +// .overlay { +// if match.disabled { +// Image(systemName: "xmark") +// .resizable() +// .scaledToFit() +// .opacity(0.8) +// } +// } +// .contextMenu(menuItems: { +// Text("index: \(match.index)") +// Text("bye state : \(match.byeState)") +// Text("disable state : \(match.disabled)") +// Button("enable") { +// match._toggleMatchDisableState(false) +// } +// Button("disable") { +// match._toggleMatchDisableState(true) +// } +// Button("bye") { +// match.byeState = true +// } +// Button("not bye") { +// match.byeState = false +// } +// Button("solo toggle") { +// match.disabled.toggle() +// } +// Button("toggle fwrd match") { +// match._toggleForwardMatchDisableState(true) +// } +// }) +// NavigationLink { MatchDetailView(match: match, matchViewStyle: matchViewStyle) } label: { diff --git a/PadelClub/Views/Round/LoserBracketView.swift b/PadelClub/Views/Round/LoserBracketView.swift deleted file mode 100644 index 470c033..0000000 --- a/PadelClub/Views/Round/LoserBracketView.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// LoserBracketView.swift -// PadelClub -// -// Created by Razmig Sarkissian on 04/04/2024. -// - -import SwiftUI - -struct LoserBracketView: View { - @EnvironmentObject var dataStore: DataStore - let loserRounds: [Round] - - @ViewBuilder - var body: some View { - if let first = loserRounds.first { - List { - ForEach(loserRounds) { loserRound in - _loserRoundView(loserRound) - let childLoserRounds = loserRound.loserRounds() - if childLoserRounds.isEmpty == false { - let uniqueChildRound = childLoserRounds.first - if childLoserRounds.count == 1, let uniqueChildRound { - _loserRoundView(uniqueChildRound) - } else if let uniqueChildRound { - NavigationLink { - LoserBracketView(loserRounds: childLoserRounds) - } label: { - Text(uniqueChildRound.roundTitle()) - } - } - } - } - } - .navigationTitle(first.roundTitle()) - } - } - - private func _loserRoundView(_ loserRound: Round) -> some View { - Section { - ForEach(loserRound.playedMatches()) { match in - MatchRowView(match: match, matchViewStyle: .standardStyle) - } - } header: { - Text(loserRound.roundTitle()) - } - } -} - -#Preview { - LoserBracketView(loserRounds: [Round.mock()]) - .environmentObject(DataStore.shared) -} diff --git a/PadelClub/Views/Round/LoserRoundView.swift b/PadelClub/Views/Round/LoserRoundView.swift new file mode 100644 index 0000000..a744f07 --- /dev/null +++ b/PadelClub/Views/Round/LoserRoundView.swift @@ -0,0 +1,72 @@ +// +// LoserRoundView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 04/04/2024. +// + +import SwiftUI + +struct LoserRoundView: View { + @EnvironmentObject var dataStore: DataStore + let loserRounds: [Round] + @State private var isEditingTournamentSeed: Bool = false + + private func _roundDisabled() -> Bool { + loserRounds.allSatisfy({ $0.isDisabled() }) + } + + var body: some View { + List { + if isEditingTournamentSeed == true { + _editingView() + } + + ForEach(loserRounds) { loserRound in + if isEditingTournamentSeed || loserRound.isDisabled() == false { + Section { + let matches = isEditingTournamentSeed ? loserRound.playedMatches() : loserRound.playedMatches().filter({ $0.disabled == false }) + ForEach(matches) { match in + MatchRowView(match: match, matchViewStyle: .standardStyle) + .overlay { + if match.disabled && isEditingTournamentSeed { + Image(systemName: "xmark") + .resizable() + .scaledToFit() + .opacity(0.8) + } + } + .disabled(match.disabled) + } + } header: { + Text(loserRound.roundTitle(.wide)) + } + } + } + } + .headerProminence(.increased) + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Button(isEditingTournamentSeed == true ? "Valider" : "Modifier") { + isEditingTournamentSeed.toggle() + } + } + } + } + + private func _editingView() -> some View { + if _roundDisabled() { + RowButtonView("Jouer ce tour", role: .destructive) { + loserRounds.forEach { round in + round.enableRound() + } + } + } else { + RowButtonView("Ne pas jouer ce tour", role: .destructive) { + loserRounds.forEach { round in + round.disableRound() + } + } + } + } +} diff --git a/PadelClub/Views/Round/LoserRoundsView.swift b/PadelClub/Views/Round/LoserRoundsView.swift index 8a311a3..9020a1e 100644 --- a/PadelClub/Views/Round/LoserRoundsView.swift +++ b/PadelClub/Views/Round/LoserRoundsView.swift @@ -7,20 +7,79 @@ import SwiftUI +enum LoserRound: Identifiable, Selectable { + case round(turnIndex: Int, rounds: [Round]) + + var id: Int { + switch self { + case .round(let turnIndex, _): + return turnIndex + } + } +} + +extension LoserRound { + func selectionLabel() -> String { + switch self { + case .round(let turnIndex, _): + return "Tour #\(turnIndex + 1)" + } + } + + func badgeValue() -> Int? { + switch self { + case .round(_, let rounds): + return rounds.flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count + } + } + + func badgeImage() -> Badge? { + switch self { + case .round(_, let rounds): + return rounds.allSatisfy({ $0.hasEnded() }) ? .checkmark : nil + } + } +} + + struct LoserRoundsView: View { + @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed var upperBracketRound: Round - @State private var selectedRound: Round? + @State private var selectedRound: LoserRound? let loserRounds: [Round] init(upperBracketRound: Round) { self.upperBracketRound = upperBracketRound self.loserRounds = upperBracketRound.loserRounds() - _selectedRound = State(wrappedValue: upperBracketRound.getActiveLoserRound()) +// _selectedRound = State(wrappedValue: upperBracketRound.getActiveLoserRound()) } + func allDestinations() -> [LoserRound] { +// return loserRounds + if isEditingTournamentSeed.wrappedValue == false { + let loserRounds = loserRounds.filter { loserRound in + upperBracketRound.loserRounds(forRoundIndex: loserRound.index).anySatisfy({ $0.isDisabled() == false }) + } + + var rounds = [LoserRound]() + for (index, round) in loserRounds.enumerated() { + rounds.append(LoserRound.round(turnIndex: index, rounds: upperBracketRound.loserRounds(forRoundIndex: round.index))) + } + + return rounds + } else { + var rounds = [LoserRound]() + for (index, round) in loserRounds.enumerated() { + rounds.append(LoserRound.round(turnIndex: index, rounds: upperBracketRound.loserRounds(forRoundIndex: round.index))) + } + + return rounds + } + } + var body: some View { VStack(spacing: 0) { - GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: loserRounds, nilDestinationIsValid: true) + GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: allDestinations(), nilDestinationIsValid: true) switch selectedRound { case .none: List { @@ -29,72 +88,13 @@ struct LoserRoundsView: View { } } case .some(let selectedRound): - LoserRoundView(loserRounds: upperBracketRound.loserRounds(forRoundIndex: selectedRound.index)) + switch selectedRound { + case .round(_, let rounds): + LoserRoundView(loserRounds: rounds) + } } } .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) } } - -struct LoserRoundView: View { - @EnvironmentObject var dataStore: DataStore - let loserRounds: [Round] - @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed - - private func _roundDisabled() -> Bool { - loserRounds.allSatisfy({ $0.isDisabled() }) - } - - var body: some View { - List { - if isEditingTournamentSeed.wrappedValue == true { - _editingView() - } - - ForEach(loserRounds) { loserRound in - Section { - ForEach(loserRound.playedMatches()) { match in - MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle) - .overlay { - if match.disabled { - Image(systemName: "xmark") - .resizable() - .scaledToFit() - .opacity(0.8) - } - } - .disabled(match.disabled) - } - } header: { - Text(loserRound.roundTitle(.wide)) - } - } - } - .headerProminence(.increased) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") { - isEditingTournamentSeed.wrappedValue.toggle() - } - } - } - } - - private func _editingView() -> some View { - if _roundDisabled() { - RowButtonView("Jouer ce tour", role: .destructive) { - loserRounds.forEach { round in - round.enableRound() - round.handleLoserRoundState() - } - } - } else { - RowButtonView("Ne pas jouer ce tour", role: .destructive) { - loserRounds.forEach { round in - round.disableRound() - } - } - } - } -} diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 8fd0963..1b535ec 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -14,6 +14,16 @@ struct RoundSettingsView: View { var body: some View { List { + Section { + RowButtonView("Enabled", role: .destructive) { + let allMatches = tournament._allMatchesIncludingDisabled() + allMatches.forEach({ + $0.disabled = false + $0.byeState = false + }) + try? dataStore.matches.addOrUpdate(contentOfs: allMatches) + } + } Section { RowButtonView("Retirer toutes les têtes de séries", role: .destructive) { tournament.unsortedTeams().forEach({ $0.bracketPosition = nil }) diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index c9e0ab2..e78e1f2 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -19,14 +19,16 @@ struct RoundView: View { if isEditingTournamentSeed.wrappedValue == false { let loserRounds = round.loserRounds() - if loserRounds.isEmpty == false, let first = loserRounds.first(where: { $0.isDisabled() == false }) { + //(where: { $0.isDisabled() == false || isEditingTournamentSeed.wrappedValue }) + if loserRounds.isEmpty == false, let first = loserRounds.first { + let correspondingLoserRoundTitle = first.correspondingLoserRoundTitle() Section { NavigationLink { LoserRoundsView(upperBracketRound: round) .environment(tournament) - .navigationTitle(first.roundTitle()) + .navigationTitle(correspondingLoserRoundTitle) } label: { - Text(first.roundTitle()) + Text(correspondingLoserRoundTitle) } } }