in progress loser bracket

multistore
Razmig Sarkissian 2 years ago
parent 29cc0df4a2
commit 82384e3049
  1. 26
      PadelClub/Data/Match.swift
  2. 38
      PadelClub/Data/Round.swift
  3. 1
      PadelClub/Data/TeamRegistration.swift
  4. 4
      PadelClub/Data/Tournament.swift
  5. 1
      PadelClub/Views/Match/MatchSetupView.swift
  6. 7
      PadelClub/Views/Round/LoserRoundsView.swift
  7. 92
      PadelClub/Views/Round/RoundSettingsView.swift
  8. 2
      PadelClub/Views/Round/RoundView.swift

@ -26,7 +26,7 @@ class Match: ModelObject, Storable {
var broadcasted: Bool var broadcasted: Bool
var name: String? var name: String?
var order: Int var order: Int
private(set) var disabled: Bool = false var disabled: Bool = false
internal init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, court: String? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, broadcasted: Bool = false, name: String? = nil, order: Int = 0) { internal init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, court: String? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, broadcasted: Bool = false, name: String? = nil, order: Int = 0) {
self.round = round self.round = round
@ -74,8 +74,26 @@ class Match: ModelObject, Storable {
_toggleMatchDisableState(false) _toggleMatchDisableState(false)
} }
func _toggleLoserMatchDisableState(_ state: Bool) {
if isLoserBracket == false {
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index)
if let loserMatch = roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) {
print("disabling first loserround", state, loserMatch.matchTitle(.wide))
loserMatch.disabled = state
try? DataStore.shared.matches.addOrUpdate(instance: loserMatch)
loserMatch._toggleLoserMatchDisableState(state)
}
} else {
roundObject?.loserRounds().forEach({ round in
print("disabling", state, round.roundTitle())
round.disableLoserRound(state)
})
}
}
fileprivate func _toggleMatchDisableState(_ state: Bool) { fileprivate func _toggleMatchDisableState(_ state: Bool) {
disabled = state disabled = state
_toggleLoserMatchDisableState(state)
topPreviousRoundMatch()?._toggleMatchDisableState(state) topPreviousRoundMatch()?._toggleMatchDisableState(state)
bottomPreviousRoundMatch()?._toggleMatchDisableState(state) bottomPreviousRoundMatch()?._toggleMatchDisableState(state)
try? DataStore.shared.matches.addOrUpdate(instance: self) try? DataStore.shared.matches.addOrUpdate(instance: self)
@ -319,7 +337,7 @@ class Match: ModelObject, Storable {
func isBye() -> Bool { func isBye() -> Bool {
guard let roundObject else { return false } guard let roundObject else { return false }
return (roundObject.upperBracketMatches(ofMatch: self) + roundObject.previousRoundMatches(ofMatch: self)).anySatisfy({ $0.disabled }) return topPreviousRoundMatch()?.disabled == true || bottomPreviousRoundMatch()?.disabled == true
} }
func upperBracketTopMatch() -> Match? { func upperBracketTopMatch() -> Match? {
@ -355,7 +373,7 @@ class Match: ModelObject, Storable {
} else if let match = topPreviousRoundMatch() { } else if let match = topPreviousRoundMatch() {
if let teamId = match.winningTeamId { if let teamId = match.winningTeamId {
return Store.main.findById(teamId) return Store.main.findById(teamId)
} else if match.isBye() { } else if match.disabled {
return match.teams().first return match.teams().first
} }
} }
@ -365,7 +383,7 @@ class Match: ModelObject, Storable {
} else if let match = bottomPreviousRoundMatch() { } else if let match = bottomPreviousRoundMatch() {
if let teamId = match.winningTeamId { if let teamId = match.winningTeamId {
return Store.main.findById(teamId) return Store.main.findById(teamId)
} else if match.isBye() { } else if match.disabled {
return match.teams().first return match.teams().first
} }
} }

@ -81,7 +81,7 @@ class Round: ModelObject, Storable {
} else if let previousMatch = topPreviousRoundMatch(ofMatch: match) { } else if let previousMatch = topPreviousRoundMatch(ofMatch: match) {
if let teamId = previousMatch.winningTeamId { if let teamId = previousMatch.winningTeamId {
return Store.main.findById(teamId) return Store.main.findById(teamId)
} else if previousMatch.isBye() { } else if previousMatch.disabled {
return previousMatch.teams().first return previousMatch.teams().first
} }
} }
@ -91,7 +91,7 @@ class Round: ModelObject, Storable {
} else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match) { } else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match) {
if let teamId = previousMatch.winningTeamId { if let teamId = previousMatch.winningTeamId {
return Store.main.findById(teamId) return Store.main.findById(teamId)
} else if previousMatch.isBye() { } else if previousMatch.disabled {
return previousMatch.teams().first return previousMatch.teams().first
} }
} }
@ -143,7 +143,11 @@ class Round: ModelObject, Storable {
} }
func playedMatches() -> [Match] { func playedMatches() -> [Match] {
if loser == nil {
Store.main.filter { $0.round == self.id && $0.disabled == false } Store.main.filter { $0.round == self.id && $0.disabled == false }
} else {
Store.main.filter { $0.round == self.id }
}
} }
func previousRound() -> Round? { func previousRound() -> Round? {
@ -159,7 +163,7 @@ class Round: ModelObject, Storable {
} }
func isDisabled() -> Bool { func isDisabled() -> Bool {
playedMatches().allSatisfy({ $0.disabled || $0.isBye() }) _matches().allSatisfy({ $0.disabled })
} }
func getActiveLoserRound() -> Round? { func getActiveLoserRound() -> Round? {
@ -167,6 +171,32 @@ class Round: ModelObject, Storable {
return rounds.filter({ $0.hasStarted() && $0.hasEnded() == false && $0.isDisabled() == false }).sorted(by: \.index).reversed().first ?? rounds.first(where: { $0.isDisabled() == false }) return rounds.filter({ $0.hasStarted() && $0.hasEnded() == false && $0.isDisabled() == false }).sorted(by: \.index).reversed().first ?? rounds.first(where: { $0.isDisabled() == false })
} }
func enableRound() {
let _matches = _matches()
_matches.forEach { match in
match.disabled = false
match.losingTeamId = nil
match.winningTeamId = nil
match.endDate = nil
match.court = nil
match.servingTeamId = nil
try? DataStore.shared.teamScores.delete(contentOfs: match.teamScores)
}
try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches)
}
func disableLoserRound(_ disable: Bool) {
let _matches = _matches()
_matches.forEach { match in
match.disabled = match.topPreviousRoundMatch()?.disabled == disable || match.bottomPreviousRoundMatch()?.disabled == disable
}
try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches)
loserRounds().forEach { round in
round.disableLoserRound(disable)
}
}
var cumulativeMatchCount: Int { var cumulativeMatchCount: Int {
var totalMatches = playedMatches().count var totalMatches = playedMatches().count
if let parent = parentRound { if let parent = parentRound {
@ -188,7 +218,7 @@ class Round: ModelObject, Storable {
let parentMatchCount = parentRound.cumulativeMatchCount - initialRound.playedMatches().count let parentMatchCount = parentRound.cumulativeMatchCount - initialRound.playedMatches().count
print("initialRound", initialRound.roundTitle()) print("initialRound", initialRound.roundTitle())
if let initialRoundNextRound = initialRound.nextRound()?.playedMatches() { if let initialRoundNextRound = initialRound.nextRound()?.playedMatches() {
return SeedInterval(first: parentMatchCount + initialRoundNextRound.count * 2 + 1, last: parentMatchCount + initialRoundNextRound.count * 2 + playedMatches().count * 2).localizedLabel(displayStyle) return SeedInterval(first: parentMatchCount + initialRoundNextRound.count * 2 + 1, last: parentMatchCount + initialRoundNextRound.count * 2 + (previousRound() ?? parentRound).playedMatches().count).localizedLabel(displayStyle)
} }
} }
return RoundRule.roundName(fromRoundIndex: index) return RoundRule.roundName(fromRoundIndex: index)

@ -63,6 +63,7 @@ class TeamRegistration: ModelObject, Storable {
teamPosition = upperBranch ?? (isUpper ? 1 : 0) teamPosition = upperBranch ?? (isUpper ? 1 : 0)
} }
match.previousMatch(teamPosition)?.disableMatch() match.previousMatch(teamPosition)?.disableMatch()
match._toggleLoserMatchDisableState(false)
bracketPosition = matchIndex * 2 + teamPosition bracketPosition = matchIndex * 2 + teamPosition
} }

@ -253,6 +253,10 @@ class Tournament : ModelObject, Storable {
return rounds.filter({ $0.hasStarted() && $0.hasEnded() == false }).sorted(by: \.index).reversed().first ?? rounds.first return rounds.filter({ $0.hasStarted() && $0.hasEnded() == false }).sorted(by: \.index).reversed().first ?? rounds.first
} }
func allRounds() -> [Round] {
Store.main.filter { $0.tournament == self.id }
}
func rounds() -> [Round] { func rounds() -> [Round] {
Store.main.filter { $0.tournament == self.id && $0.loser == nil }.sorted(by: \.index).reversed() Store.main.filter { $0.tournament == self.id && $0.loser == nil }.sorted(by: \.index).reversed()
} }

@ -25,6 +25,7 @@ struct MatchSetupView: View {
.swipeActions(edge: .trailing, allowsFullSwipe: false) { .swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button(role: .cancel) { Button(role: .cancel) {
team.bracketPosition = nil team.bracketPosition = nil
match._toggleLoserMatchDisableState(false)
try? dataStore.teamRegistrations.addOrUpdate(instance: team) try? dataStore.teamRegistrations.addOrUpdate(instance: team)
} label: { } label: {
Label("retirer", systemImage: "xmark") Label("retirer", systemImage: "xmark")

@ -44,13 +44,14 @@ struct LoserRoundView: View {
ForEach(loserRound.playedMatches()) { match in ForEach(loserRound.playedMatches()) { match in
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle) MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
.overlay { .overlay {
if match.isBye() { if match.disabled {
Image(systemName: "pencil.slash") Image(systemName: "xmark")
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.opacity(0.3) .opacity(0.8)
} }
} }
.disabled(match.disabled)
} }
} header: { } header: {
Text(loserRound.roundTitle(.wide)) Text(loserRound.roundTitle(.wide))

@ -22,49 +22,6 @@ struct RoundSettingsView: View {
List { List {
Toggle("Éditer les têtes de série", isOn: $isEditingTournamentSeed) Toggle("Éditer les têtes de série", isOn: $isEditingTournamentSeed)
Section {
RowButtonView("Effacer classement", role: .destructive) {
tournament.rounds().forEach { round in
try? dataStore.rounds.delete(contentOfs: round.loserRounds())
}
}
}
Section {
RowButtonView("Match de classement") {
tournament.rounds().forEach { round in
round.buildLoserBracket()
}
}
}
Section {
RowButtonView("Retirer toutes les têtes de séries", role: .destructive) {
tournament.unsortedTeams().forEach({ $0.bracketPosition = nil })
}
}
Section {
if let lastRound = tournament.rounds().first { // first is final, last round
RowButtonView("Supprimer " + lastRound.roundTitle(), role: .destructive) {
try? dataStore.rounds.delete(instance: lastRound)
}
}
}
Section {
let roundIndex = tournament.rounds().count
RowButtonView("Ajouter " + RoundRule.roundName(fromRoundIndex: roundIndex)) {
let round = Round(tournament: tournament.id, index: roundIndex, matchFormat: tournament.matchFormat)
let matchCount = RoundRule.numberOfMatches(forRoundIndex: roundIndex)
let matchStartIndex = RoundRule.matchIndex(fromRoundIndex: roundIndex)
let matches = (0..<matchCount).map { //0 is final match
return Match(round: round.id, index: $0 + matchStartIndex, matchFormat: round.matchFormat)
}
try? dataStore.rounds.addOrUpdate(instance: round)
try? dataStore.matches.addOrUpdate(contentOfs: matches)
round.buildLoserBracket()
}
}
if let availableSeedGroup = tournament.availableSeedGroup() { if let availableSeedGroup = tournament.availableSeedGroup() {
Section { Section {
@ -108,6 +65,55 @@ struct RoundSettingsView: View {
Text("Placement des têtes de série") Text("Placement des têtes de série")
} }
} }
Section {
RowButtonView("Effacer classement", role: .destructive) {
tournament.rounds().forEach { round in
try? dataStore.rounds.delete(contentOfs: round.loserRounds())
}
}
}
Section {
RowButtonView("Match de classement") {
tournament.rounds().forEach { round in
round.buildLoserBracket()
}
}
}
Section {
RowButtonView("Retirer toutes les têtes de séries", role: .destructive) {
tournament.unsortedTeams().forEach({ $0.bracketPosition = nil })
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
tournament.allRounds().forEach({ round in
round.enableRound()
})
}
}
Section {
if let lastRound = tournament.rounds().first { // first is final, last round
RowButtonView("Supprimer " + lastRound.roundTitle(), role: .destructive) {
try? dataStore.rounds.delete(instance: lastRound)
}
}
}
Section {
let roundIndex = tournament.rounds().count
RowButtonView("Ajouter " + RoundRule.roundName(fromRoundIndex: roundIndex)) {
let round = Round(tournament: tournament.id, index: roundIndex, matchFormat: tournament.matchFormat)
let matchCount = RoundRule.numberOfMatches(forRoundIndex: roundIndex)
let matchStartIndex = RoundRule.matchIndex(fromRoundIndex: roundIndex)
let matches = (0..<matchCount).map { //0 is final match
return Match(round: round.id, index: $0 + matchStartIndex, matchFormat: round.matchFormat)
}
try? dataStore.rounds.addOrUpdate(instance: round)
try? dataStore.matches.addOrUpdate(contentOfs: matches)
round.buildLoserBracket()
}
}
} }
} }
} }

@ -13,7 +13,7 @@ struct RoundView: View {
var body: some View { var body: some View {
List { List {
let loserRounds = round.loserRounds() let loserRounds = round.loserRounds()
if loserRounds.isEmpty == false, let first = loserRounds.first { if loserRounds.isEmpty == false, let first = loserRounds.first(where: { $0.isDisabled() == false }) {
Section { Section {
NavigationLink { NavigationLink {
LoserRoundsView(upperBracketRound: round) LoserRoundsView(upperBracketRound: round)

Loading…
Cancel
Save