fix more stuff on loser round

multistore
Razmig Sarkissian 2 years ago
parent 38d05af6df
commit 34ceea6b2a
  1. 2
      PadelClub.xcodeproj/project.pbxproj
  2. 21
      PadelClub/Data/Match.swift
  3. 81
      PadelClub/Data/Round.swift
  4. 21
      PadelClub/Data/Tournament.swift
  5. 4
      PadelClub/Manager/PadelRule.swift
  6. 13
      PadelClub/ViewModel/SeedInterval.swift
  7. 42
      PadelClub/Views/Components/GenericDestinationPickerView.swift
  8. 5
      PadelClub/Views/Match/MatchRowView.swift
  9. 21
      PadelClub/Views/Match/MatchSetupView.swift
  10. 2
      PadelClub/Views/Round/LoserRoundView.swift
  11. 97
      PadelClub/Views/Round/LoserRoundsView.swift
  12. 14
      PadelClub/Views/Round/RoundView.swift
  13. 2
      PadelClub/Views/Team/TeamPickerView.swift
  14. 20
      PadelClub/Views/Tournament/TournamentRunningView.swift
  15. 2
      PadelClub/Views/Tournament/TournamentView.swift

@ -1731,6 +1731,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -1762,6 +1763,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;

@ -166,15 +166,14 @@ class Match: ModelObject, Storable {
return roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) return roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2)
} }
private func _toggleLoserMatchDisableState(_ state: Bool) { func _toggleLoserMatchDisableState(_ state: Bool) {
guard let loserMatch = _loserMatch() else { return } guard let loserMatch = _loserMatch() else { return }
loserMatch.byeState = state guard let next = _otherMatch() else { return }
loserMatch._toggleMatchDisableState(state, forward: true) loserMatch.byeState = true
if next.disabled {
guard let otherMatch = _otherMatch() else { return } loserMatch.byeState = false
if otherMatch.disabled == state {
loserMatch.byeState = !state
} }
loserMatch._toggleMatchDisableState(state, forward: true)
} }
fileprivate func _otherMatch() -> Match? { fileprivate func _otherMatch() -> Match? {
@ -202,9 +201,7 @@ class Match: ModelObject, Storable {
if next.disabled && byeState == false && next.byeState == false { if next.disabled && byeState == false && next.byeState == false {
forwardMatch.byeState = false forwardMatch.byeState = false
forwardMatch._toggleMatchDisableState(state, forward: true) forwardMatch._toggleMatchDisableState(state, forward: true)
} } else if byeState && next.byeState {
if byeState && next.byeState {
print("don't disable forward match") print("don't disable forward match")
forwardMatch.byeState = false forwardMatch.byeState = false
forwardMatch._toggleMatchDisableState(false, forward: true) forwardMatch._toggleMatchDisableState(false, forward: true)
@ -233,10 +230,10 @@ class Match: ModelObject, Storable {
} }
func _toggleMatchDisableState(_ state: Bool, forward: Bool = false) { func _toggleMatchDisableState(_ state: Bool, forward: Bool = false) {
if disabled == state { return } //if disabled == state { return }
disabled = state disabled = state
//byeState = false //byeState = false
try? DataStore.shared.matches.addOrUpdate(instance: self) //try? DataStore.shared.matches.addOrUpdate(instance: self)
_toggleLoserMatchDisableState(state) _toggleLoserMatchDisableState(state)
if forward { if forward {

@ -92,26 +92,29 @@ class Round: ModelObject, Storable {
func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? { func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? {
return Store.main.filter(isIncluded: { return Store.main.filter(isIncluded: {
$0.tournament == tournament && $0.bracketPosition != nil $0.tournament == tournament
}).first(where: { && $0.bracketPosition != nil
($0.bracketPosition! / 2) == matchIndex && ($0.bracketPosition! / 2) == matchIndex
&& ($0.bracketPosition! % 2) == team.rawValue && ($0.bracketPosition! % 2) == team.rawValue
}) }).first
} }
func seeds(inMatchIndex matchIndex: Int) -> [TeamRegistration] { func seeds(inMatchIndex matchIndex: Int) -> [TeamRegistration] {
return Store.main.filter(isIncluded: { return Store.main.filter(isIncluded: {
$0.tournament == tournament && $0.bracketPosition != nil $0.tournament == tournament
}).filter({ && $0.bracketPosition != nil
($0.bracketPosition! / 2) == matchIndex && ($0.bracketPosition! / 2) == matchIndex
}) })
} }
func seeds() -> [TeamRegistration] { func seeds() -> [TeamRegistration] {
let initialMatchIndex = RoundRule.matchIndex(fromRoundIndex: index)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index)
return Store.main.filter(isIncluded: { return Store.main.filter(isIncluded: {
$0.tournament == tournament && $0.bracketPosition != nil $0.tournament == tournament
}).filter({ && $0.bracketPosition != nil
($0.bracketPosition! / 2) >= RoundRule.matchIndex(fromRoundIndex: index) && ($0.bracketPosition! / 2) < RoundRule.matchIndex(fromRoundIndex: index) + RoundRule.numberOfMatches(forRoundIndex: index) && ($0.bracketPosition! / 2) >= initialMatchIndex
&& ($0.bracketPosition! / 2) < initialMatchIndex + numberOfMatches
}) })
} }
@ -246,8 +249,8 @@ class Round: ModelObject, Storable {
} }
func getActiveLoserRound() -> Round? { func getActiveLoserRound() -> Round? {
let rounds = loserRounds() let rounds = loserRounds().filter({ $0.isDisabled() == false }).sorted(by: \.index).reversed()
return rounds.filter({ $0.hasStarted() && $0.hasEnded() == false && $0.isDisabled() == false }).sorted(by: \.index).reversed().first ?? rounds.first(where: { $0.isDisabled() == false }) return rounds.first(where: { $0.hasStarted() && $0.hasEnded() == false }) ?? rounds.first
} }
func enableRound() { func enableRound() {
@ -299,31 +302,43 @@ class Round: ModelObject, Storable {
func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String { func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
if let parentRound, let initialRound = parentRound.initialRound() { let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index)
let parentMatchCount = parentRound.cumulativeMatchCount - initialRound.playedMatches().count let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: {
// print("initialRound", initialRound.roundTitle()) $0.tournament == tournament
if let initialRoundNextRound = initialRound.nextRound() { && $0.bracketPosition != nil
let total = initialRoundNextRound.playedMatches().count * 2 + initialRoundNextRound.disabledMatches().count && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
let previousRoundMatchCount = (previousRound() ?? parentRound).playedMatches().count })
let seedInterval = SeedInterval(first: parentMatchCount + total + 1, last: parentMatchCount + total + previousRoundMatchCount).localizedLabel(displayStyle) let playedMatches = playedMatches()
// print("seedInterval", seedInterval, parentMatchCount, total, previousRoundMatchCount) let seedInterval = SeedInterval(first: playedMatches.count + seedsAfterThisRound.count + 1, last: playedMatches.count * 2 + seedsAfterThisRound.count)
return seedInterval return seedInterval.localizedLabel(displayStyle)
} }
func seedInterval() -> SeedInterval? {
if loser == nil {
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index + 1)
let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index)
let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: {
$0.tournament == tournament
&& $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
})
let playedMatches = playedMatches()
let reduce = numberOfMatches / 2 - (playedMatches.count + seedsAfterThisRound.count)
return SeedInterval(first: 1, last: numberOfMatches, reduce: reduce)
} }
return RoundRule.roundName(fromRoundIndex: index)
if let previousRound = previousRound() {
return previousRound.seedInterval()?.chunks()?.first
} else if let parentRound = parentRound {
return parentRound.seedInterval()?.chunks()?.last
}
return nil
} }
func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String { func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
if let parentRound, let initialRound = parentRound.initialRound() { if loser != nil {
let parentMatchCount = parentRound.theoryCumulativeMatchCount - RoundRule.numberOfMatches(forRoundIndex: initialRound.index) return seedInterval()?.localizedLabel(displayStyle) ?? "Pas trouvé"
// 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) return RoundRule.roundName(fromRoundIndex: index)
} }

@ -342,9 +342,13 @@ class Tournament : ModelObject, Storable {
} }
} }
func allRoundMatches() -> [Match] {
allRounds().flatMap { $0._matches() }
}
func allMatches() -> [Match] { func allMatches() -> [Match] {
let unsortedGroupStages : [GroupStage] = Store.main.filter { $0.tournament == self.id } let unsortedGroupStages : [GroupStage] = Store.main.filter { $0.tournament == self.id }
let matches: [Match] = unsortedGroupStages.flatMap { $0._matches() } + allRounds().flatMap { $0._matches() } let matches: [Match] = unsortedGroupStages.flatMap { $0._matches() } + allRoundMatches()
return matches.filter({ $0.disabled == false }) return matches.filter({ $0.disabled == false })
} }
@ -742,24 +746,27 @@ class Tournament : ModelObject, Storable {
let selectedPlayers = selectedPlayers() let selectedPlayers = selectedPlayers()
let paid = selectedPlayers.filter({ $0.hasPaid() }) let paid = selectedPlayers.filter({ $0.hasPaid() })
let label = paid.count.formatted() + " / " + selectedPlayers.count.formatted() + " joueurs encaissés" let label = paid.count.formatted() + " / " + selectedPlayers.count.formatted() + " joueurs encaissés"
let completion = (Double(paid.count) / Double(selectedPlayers.count)).formatted(.percent.precision(.fractionLength(0))) let completion = (Double(paid.count) / Double(selectedPlayers.count))
return TournamentStatus(label: label, completion: completion) let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0)))
return TournamentStatus(label: label, completion: completionLabel)
} }
func scheduleStatus() -> TournamentStatus { func scheduleStatus() -> TournamentStatus {
let allMatches = allMatches() let allMatches = allMatches()
let ready = allMatches.filter({ $0.startDate != nil }) let ready = allMatches.filter({ $0.startDate != nil })
let label = ready.count.formatted() + " / " + allMatches.count.formatted() + " matchs programmés" let label = ready.count.formatted() + " / " + allMatches.count.formatted() + " matchs programmés"
let completion = (Double(ready.count) / Double(allMatches.count)).formatted(.percent.precision(.fractionLength(0))) let completion = (Double(ready.count) / Double(allMatches.count))
return TournamentStatus(label: label, completion: completion) let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0)))
return TournamentStatus(label: label, completion: completionLabel)
} }
func callStatus() -> TournamentStatus { func callStatus() -> TournamentStatus {
let selectedSortedTeams = selectedSortedTeams() let selectedSortedTeams = selectedSortedTeams()
let called = selectedSortedTeams.filter{ $0.called() } let called = selectedSortedTeams.filter{ $0.called() }
let label = called.count.formatted() + " / " + selectedSortedTeams.count.formatted() + " paires convoquées" let label = called.count.formatted() + " / " + selectedSortedTeams.count.formatted() + " paires convoquées"
let completion = (Double(called.count) / Double(selectedSortedTeams.count)).formatted(.percent.precision(.fractionLength(0))) let completion = (Double(called.count) / Double(selectedSortedTeams.count))
return TournamentStatus(label: label, completion: completion) let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0)))
return TournamentStatus(label: label, completion: completionLabel)
} }
func bracketStatus() -> String { func bracketStatus() -> String {

@ -797,10 +797,12 @@ enum TournamentType: Int, Hashable, Codable, CaseIterable, Identifiable {
} }
} }
enum TeamPosition: Int, Hashable, Codable, CaseIterable { enum TeamPosition: Int, Identifiable, Hashable, Codable, CaseIterable {
case one case one
case two case two
var id: Int { self.rawValue }
var otherTeam: TeamPosition { var otherTeam: TeamPosition {
switch self { switch self {
case .one: case .one:

@ -10,24 +10,25 @@ import Foundation
struct SeedInterval: Hashable, Comparable { struct SeedInterval: Hashable, Comparable {
let first: Int let first: Int
let last: Int let last: Int
var reduce: Int = 0
static func <(lhs: SeedInterval, rhs: SeedInterval) -> Bool { static func <(lhs: SeedInterval, rhs: SeedInterval) -> Bool {
return lhs.first < rhs.first return lhs.first < rhs.first
} }
var count: Int { var count: Int {
dimension + 1 dimension
} }
private var dimension: Int { private var dimension: Int {
(last - first) (last - (first - 1))
} }
func chunks() -> [SeedInterval]? { func chunks() -> [SeedInterval]? {
if dimension > 3 { if dimension > 3 {
let split = dimension / 2 let split = dimension / 2
let firstHalf = SeedInterval(first: first, last: first + split - 1) let firstHalf = SeedInterval(first: first, last: first + split - 1, reduce: reduce)
let secondHalf = SeedInterval(first: first + split, last: last) let secondHalf = SeedInterval(first: first + split, last: last, reduce: reduce)
return [firstHalf, secondHalf] return [firstHalf, secondHalf]
} else { } else {
return nil return nil
@ -36,9 +37,9 @@ struct SeedInterval: Hashable, Comparable {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if dimension < 2 { if dimension < 2 {
return "#\(first) / #\(last)" return "#\(first - reduce) / #\(last - reduce)"
} else { } else {
return "#\(first) à #\(last)" return "#\(first - reduce) à #\(last - reduce)"
} }
} }
} }

@ -44,27 +44,27 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable>: View {
.opacity(selectedDestination?.id == destination.id ? 1.0 : 0.4) .opacity(selectedDestination?.id == destination.id ? 1.0 : 0.4)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.overlay(alignment: .bottomTrailing) { // .overlay(alignment: .bottomTrailing) {
if let badge = destination.badgeImage() { // if let badge = destination.badgeImage() {
Image(systemName: badge.systemName()) // Image(systemName: badge.systemName())
.foregroundColor(badge.color()) // .foregroundColor(badge.color())
.imageScale(.medium) // .imageScale(.medium)
.background ( // .background (
Color(.systemBackground) // Color(.systemBackground)
.clipShape(.circle) // .clipShape(.circle)
) // )
.offset(x: 3, y: 3) // .offset(x: 3, y: 3)
} else if let count = destination.badgeValue(), count > 0 { // } else if let count = destination.badgeValue(), count > 0 {
Image(systemName: count <= 50 ? "\(count).circle.fill" : "plus.circle.fill") // Image(systemName: count <= 50 ? "\(count).circle.fill" : "plus.circle.fill")
.foregroundColor(.red) // .foregroundColor(.red)
.imageScale(.medium) // .imageScale(.medium)
.background ( // .background (
Color(.systemBackground) // Color(.systemBackground)
.clipShape(.circle) // .clipShape(.circle)
) // )
.offset(x: 3, y: 3) // .offset(x: 3, y: 3)
} // }
} // }
} }
} }
.fixedSize() .fixedSize()

@ -48,8 +48,11 @@ struct MatchRowView: View {
// Button("toggle fwrd match") { // Button("toggle fwrd match") {
// match._toggleForwardMatchDisableState(true) // match._toggleForwardMatchDisableState(true)
// } // }
// Button("toggle loser match") {
// match._toggleLoserMatchDisableState(true)
// }
// }) // })
//
NavigationLink { NavigationLink {
MatchDetailView(match: match, matchViewStyle: matchViewStyle) MatchDetailView(match: match, matchViewStyle: matchViewStyle)
} label: { } label: {

@ -14,8 +14,17 @@ struct MatchSetupView: View {
@ViewBuilder @ViewBuilder
var body: some View { var body: some View {
_teamView(inTeamPosition: .one) ForEach(TeamPosition.allCases) { teamPosition in
_teamView(inTeamPosition: .two) VStack(alignment: .leading) {
if teamPosition == .one {
Text("Branche du haut")
}
_teamView(inTeamPosition: teamPosition)
if teamPosition == .two {
Text("Branche du bas")
}
}
}
} }
@ViewBuilder @ViewBuilder
@ -33,6 +42,7 @@ struct MatchSetupView: View {
if match.isSeededBy(team: team, inTeamPosition: teamPosition) { if match.isSeededBy(team: team, inTeamPosition: teamPosition) {
team.bracketPosition = nil team.bracketPosition = nil
match.enableMatch() match.enableMatch()
try? dataStore.matches.addOrUpdate(instance: match)
try? dataStore.teamRegistrations.addOrUpdate(instance: team) try? dataStore.teamRegistrations.addOrUpdate(instance: team)
} else { } else {
match.teamWillBeWalkOut(team) match.teamWillBeWalkOut(team)
@ -88,7 +98,7 @@ struct MatchSetupView: View {
} }
} }
} label: { } label: {
Text("Tirage").tag(nil as SeedInterval?) Text("Tirer au sort").tag(nil as SeedInterval?)
} }
.disabled(availableSeedGroups.isEmpty && walkOutSpot == false) .disabled(availableSeedGroups.isEmpty && walkOutSpot == false)
@ -106,9 +116,8 @@ struct MatchSetupView: View {
} }
} }
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.buttonBorderShape(.capsule) .buttonStyle(.borderless)
.buttonStyle(.borderedProminent) .underline()
} }
} }
} }

@ -29,7 +29,7 @@ struct LoserRoundView: View {
ForEach(matches) { match in ForEach(matches) { match in
MatchRowView(match: match, matchViewStyle: .standardStyle) MatchRowView(match: match, matchViewStyle: .standardStyle)
.overlay { .overlay {
if match.disabled && isEditingTournamentSeed { if match.disabled /*&& isEditingTournamentSeed*/ {
Image(systemName: "xmark") Image(systemName: "xmark")
.resizable() .resizable()
.scaledToFit() .scaledToFit()

@ -7,37 +7,44 @@
import SwiftUI import SwiftUI
enum LoserRound: Identifiable, Selectable { struct LoserRound: Identifiable, Selectable {
case round(turnIndex: Int, rounds: [Round]) let turnIndex: Int
let rounds: [Round]
var id: Int { var id: Int {
switch self { return turnIndex
case .round(let turnIndex, _): }
return turnIndex
static func updateDestinations(fromLoserRounds loserRounds: [Round], inUpperBracketRound upperBracketRound: Round) -> [LoserRound] {
var rounds = [LoserRound]()
for (index, round) in loserRounds.enumerated() {
rounds.append(LoserRound(turnIndex: index, rounds: upperBracketRound.loserRounds(forRoundIndex: round.index)))
} }
return rounds
} }
static func enabledLoserRounds(inLoserRounds loserRounds: [Round], inUpperBracketRound upperBracketRound: Round) -> [Round] {
return loserRounds.filter { loserRound in
upperBracketRound.loserRounds(forRoundIndex: loserRound.index).anySatisfy({ $0.isDisabled() == false })
}
}
} }
extension LoserRound { extension LoserRound {
func selectionLabel() -> String { func selectionLabel() -> String {
switch self { return "Tour #\(turnIndex + 1)"
case .round(let turnIndex, _):
return "Tour #\(turnIndex + 1)"
}
} }
func badgeValue() -> Int? { func badgeValue() -> Int? {
switch self { return rounds.flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count
case .round(_, let rounds):
return rounds.flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count
}
} }
func badgeImage() -> Badge? { func badgeImage() -> Badge? {
switch self { return rounds.allSatisfy({ $0.hasEnded() }) ? .checkmark : nil
case .round(_, let rounds):
return rounds.allSatisfy({ $0.hasEnded() }) ? .checkmark : nil
}
} }
} }
@ -47,54 +54,34 @@ struct LoserRoundsView: View {
var upperBracketRound: Round var upperBracketRound: Round
@State private var selectedRound: LoserRound? @State private var selectedRound: LoserRound?
let loserRounds: [Round] let loserRounds: [Round]
@State private var allDestinations: [LoserRound]
init(upperBracketRound: Round) { init(upperBracketRound: Round) {
self.upperBracketRound = upperBracketRound self.upperBracketRound = upperBracketRound
self.loserRounds = upperBracketRound.loserRounds() let _loserRounds = upperBracketRound.loserRounds()
// _selectedRound = State(wrappedValue: upperBracketRound.getActiveLoserRound()) self.loserRounds = _loserRounds
} let enabledLoserRounds = LoserRound.enabledLoserRounds(inLoserRounds: _loserRounds, inUpperBracketRound: upperBracketRound)
let rounds = LoserRound.updateDestinations(fromLoserRounds: enabledLoserRounds, inUpperBracketRound: upperBracketRound)
_allDestinations = State(wrappedValue: rounds)
func allDestinations() -> [LoserRound] { _selectedRound = State(wrappedValue: rounds.first(where: { $0.rounds.anySatisfy({ $0.getActiveLoserRound() != nil }) }) ?? rounds.first)
// 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 { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: allDestinations(), nilDestinationIsValid: true) GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: allDestinations, nilDestinationIsValid: false)
switch selectedRound { LoserRoundView(loserRounds: selectedRound!.rounds)
case .none:
List {
RowButtonView("Effacer", role: .destructive) {
}
}
case .some(let selectedRound):
switch selectedRound {
case .round(_, let rounds):
LoserRoundView(loserRounds: rounds)
}
}
} }
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.onChange(of: isEditingTournamentSeed.wrappedValue) {
_updateDestinations()
}
}
private func _updateDestinations() {
let enabledLoserRounds = isEditingTournamentSeed.wrappedValue ? loserRounds : LoserRound.enabledLoserRounds(inLoserRounds: loserRounds, inUpperBracketRound: upperBracketRound)
self.allDestinations = LoserRound.updateDestinations(fromLoserRounds: enabledLoserRounds, inUpperBracketRound: upperBracketRound)
} }
} }

@ -21,7 +21,7 @@ struct RoundView: View {
let loserRounds = round.loserRounds() let loserRounds = round.loserRounds()
//(where: { $0.isDisabled() == false || isEditingTournamentSeed.wrappedValue }) //(where: { $0.isDisabled() == false || isEditingTournamentSeed.wrappedValue })
if loserRounds.isEmpty == false, let first = loserRounds.first { if loserRounds.isEmpty == false, let first = loserRounds.first {
let correspondingLoserRoundTitle = first.correspondingLoserRoundTitle() let correspondingLoserRoundTitle = round.correspondingLoserRoundTitle()
Section { Section {
NavigationLink { NavigationLink {
LoserRoundsView(upperBracketRound: round) LoserRoundsView(upperBracketRound: round)
@ -36,9 +36,8 @@ struct RoundView: View {
RowButtonView("Placer \(availableSeedGroup.localizedLabel())") { RowButtonView("Placer \(availableSeedGroup.localizedLabel())") {
tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup) tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup)
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.seeds())
if tournament.availableSeeds().isEmpty { if tournament.availableSeeds().isEmpty {
_save()
self.isEditingTournamentSeed.wrappedValue = false self.isEditingTournamentSeed.wrappedValue = false
} }
} }
@ -56,11 +55,20 @@ struct RoundView: View {
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") { Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") {
if isEditingTournamentSeed.wrappedValue {
_save()
}
isEditingTournamentSeed.wrappedValue.toggle() isEditingTournamentSeed.wrappedValue.toggle()
} }
} }
} }
} }
private func _save() {
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.seeds())
let allRoundMatches = tournament.allRoundMatches()
try? DataStore.shared.matches.addOrUpdate(contentOfs: allRoundMatches)
}
} }
#Preview { #Preview {

@ -23,7 +23,7 @@ struct TeamPickerView: View {
.sheet(isPresented: $presentTeamPickerView) { .sheet(isPresented: $presentTeamPickerView) {
NavigationStack { NavigationStack {
List { List {
let teams = tournament.sortedTeams() let teams = tournament.selectedSortedTeams()
if luckyLosers.isEmpty == false { if luckyLosers.isEmpty == false {
Section { Section {
_teamListView(luckyLosers.sorted(by: \.weight)) _teamListView(luckyLosers.sorted(by: \.weight))

@ -8,7 +8,13 @@
import SwiftUI import SwiftUI
struct TournamentRunningView: View { struct TournamentRunningView: View {
@Environment(Tournament.self) private var tournament: Tournament var tournament: Tournament
var allMatches: [Match]
init(tournament: Tournament) {
self.tournament = tournament
self.allMatches = tournament.allMatches()
}
@ViewBuilder @ViewBuilder
var body: some View { var body: some View {
@ -68,15 +74,13 @@ struct TournamentRunningView: View {
} }
} }
let allMatches = tournament.allMatches() MatchListView(section: "en cours", matches: tournament.runningMatches(allMatches))
MatchListView(section: "en cours", matches: tournament.runningMatches(allMatches)).id(UUID()) //MatchListView(section: "disponible", matches: tournament.availableToStart(allMatches))
MatchListView(section: "disponible", matches: tournament.availableToStart(allMatches)).id(UUID()) //MatchListView(section: "à lancer", matches: tournament.readyMatches(allMatches))
MatchListView(section: "à lancer", matches: tournament.readyMatches(allMatches)).id(UUID()) MatchListView(section: "terminés", matches: tournament.finishedMatches(allMatches))
MatchListView(section: "terminés", matches: tournament.finishedMatches(allMatches)).id(UUID())
} }
} }
#Preview { #Preview {
TournamentRunningView() TournamentRunningView(tournament: Tournament.mock())
.environment(Tournament.mock())
} }

@ -66,7 +66,7 @@ struct TournamentView: View {
case .initial: case .initial:
TournamentInitView() TournamentInitView()
case .build: case .build:
TournamentRunningView() TournamentRunningView(tournament: tournament)
} }
} }
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)

Loading…
Cancel
Save