performance boost

multistore
Razmig Sarkissian 1 year ago
parent 8508bedcca
commit b97a533a10
  1. 8
      PadelClub/Data/Club.swift
  2. 4
      PadelClub/Data/Event.swift
  3. 16
      PadelClub/Data/GroupStage.swift
  4. 96
      PadelClub/Data/Match.swift
  5. 2
      PadelClub/Data/MonthData.swift
  6. 216
      PadelClub/Data/Round.swift
  7. 16
      PadelClub/Data/TeamRegistration.swift
  8. 67
      PadelClub/Data/Tournament.swift
  9. 4
      PadelClub/Data/User.swift
  10. 2
      PadelClub/ViewModel/AgendaDestination.swift
  11. 4
      PadelClub/ViewModel/FederalDataViewModel.swift
  12. 19
      PadelClub/ViewModel/SeedInterval.swift
  13. 2
      PadelClub/ViewModel/Selectable.swift
  14. 19
      PadelClub/Views/Cashier/Event/EventCreationView.swift
  15. 2
      PadelClub/Views/Cashier/Event/EventView.swift
  16. 5
      PadelClub/Views/Components/GenericDestinationPickerView.swift
  17. 2
      PadelClub/Views/GroupStage/GroupStagesView.swift
  18. 16
      PadelClub/Views/Match/Components/MatchDateView.swift
  19. 9
      PadelClub/Views/Match/Components/PlayerBlockView.swift
  20. 19
      PadelClub/Views/Match/MatchDetailView.swift
  21. 5
      PadelClub/Views/Match/MatchRowView.swift
  22. 8
      PadelClub/Views/Match/MatchSummaryView.swift
  23. 56
      PadelClub/Views/Round/LoserRoundView.swift
  24. 140
      PadelClub/Views/Round/LoserRoundsView.swift
  25. 69
      PadelClub/Views/Round/RoundView.swift
  26. 14
      PadelClub/Views/Round/RoundsView.swift
  27. 8
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  28. 2
      PadelClub/Views/Tournament/Screen/TournamentCallView.swift
  29. 6
      PadelClub/Views/Tournament/Screen/TournamentCashierView.swift
  30. 24
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift
  31. 2
      PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift
  32. 2
      PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift
  33. 4
      PadelClub/Views/Tournament/TournamentView.swift
  34. 2
      PadelClub/Views/User/LoginView.swift

@ -75,7 +75,7 @@ class Club : ModelObject, Storable, Hashable {
} }
var customizedCourts: [Court] { var customizedCourts: [Court] {
Store.main.filter { $0.club == self.id }.sorted(by: \.index) DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index)
} }
override func deleteDependencies() throws { override func deleteDependencies() throws {
@ -230,10 +230,10 @@ extension Club {
identify a club : code, name, ?? identify a club : code, name, ??
*/ */
let clubs: [Club] = Store.main.filter(isIncluded: { (code == nil && $0.name == name && $0.city == city && $0.zipCode == zipCode) || code != nil && $0.code == code }) let club: Club? = DataStore.shared.clubs.first(where: { (code == nil && $0.name == name && $0.city == city && $0.zipCode == zipCode) || code != nil && $0.code == code })
if clubs.isEmpty == false { if let club {
return clubs.first! return club
} else { } else {
return Club(creator: Store.main.userId, name: name, code: code, city: city, zipCode: zipCode) return Club(creator: Store.main.userId, name: name, code: code, city: city, zipCode: zipCode)
} }

@ -37,7 +37,7 @@ class Event: ModelObject, Storable {
// MARK: - Computed dependencies // MARK: - Computed dependencies
var tournaments: [Tournament] { var tournaments: [Tournament] {
Store.main.filter { $0.event == self.id } DataStore.shared.tournaments.filter { $0.event == self.id }
} }
func clubObject() -> Club? { func clubObject() -> Club? {
@ -46,7 +46,7 @@ class Event: ModelObject, Storable {
} }
var courtsUnavailability: [DateInterval] { var courtsUnavailability: [DateInterval] {
Store.main.filter(isIncluded: { $0.event == id }) DataStore.shared.dateIntervals.filter({ $0.event == id })
} }
// MARK: - // MARK: -

@ -44,7 +44,7 @@ class GroupStage: ModelObject, Storable {
// MARK: - Computed dependencies // MARK: - Computed dependencies
func _matches() -> [Match] { func _matches() -> [Match] {
Store.main.filter { $0.groupStage == self.id } DataStore.shared.matches.filter { $0.groupStage == self.id }
} }
func tournamentObject() -> Tournament? { func tournamentObject() -> Tournament? {
@ -184,7 +184,7 @@ class GroupStage: ModelObject, Storable {
} }
func availableToStart(playedMatches: [Match], in runningMatches: [Match]) async -> [Match] { func availableToStart(playedMatches: [Match], in runningMatches: [Match]) async -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -195,7 +195,7 @@ class GroupStage: ModelObject, Storable {
} }
func runningMatches(playedMatches: [Match]) -> [Match] { func runningMatches(playedMatches: [Match]) -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -206,7 +206,7 @@ class GroupStage: ModelObject, Storable {
} }
func asyncRunningMatches(playedMatches: [Match]) async -> [Match] { func asyncRunningMatches(playedMatches: [Match]) async -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -218,7 +218,7 @@ class GroupStage: ModelObject, Storable {
func readyMatches(playedMatches: [Match]) async -> [Match] { func readyMatches(playedMatches: [Match]) async -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -229,7 +229,7 @@ class GroupStage: ModelObject, Storable {
} }
func finishedMatches(playedMatches: [Match]) -> [Match] { func finishedMatches(playedMatches: [Match]) -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -315,7 +315,7 @@ class GroupStage: ModelObject, Storable {
} }
func unsortedTeams() -> [TeamRegistration] { func unsortedTeams() -> [TeamRegistration] {
Store.main.filter { $0.groupStage == self.id && $0.groupStagePosition != nil } DataStore.shared.teamRegistrations.filter { $0.groupStage == self.id && $0.groupStagePosition != nil }
} }
func teams(_ sortedByScore: Bool = false, scores: [TeamGroupStageScore]? = nil) -> [TeamRegistration] { func teams(_ sortedByScore: Bool = false, scores: [TeamGroupStageScore]? = nil) -> [TeamRegistration] {
@ -416,7 +416,7 @@ extension GroupStage {
} }
extension GroupStage: Selectable { extension GroupStage: Selectable {
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
groupStageTitle() groupStageTitle()
} }

@ -60,7 +60,7 @@ class Match: ModelObject, Storable {
// MARK: - Computed dependencies // MARK: - Computed dependencies
var teamScores: [TeamScore] { var teamScores: [TeamScore] {
return Store.main.filter { $0.match == self.id } return DataStore.shared.teamScores.filter { $0.match == self.id }
} }
// MARK: - // MARK: -
@ -70,6 +70,13 @@ class Match: ModelObject, Storable {
} }
func indexInRound(in matches: [Match]? = nil) -> Int { func indexInRound(in matches: [Match]? = nil) -> Int {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func indexInRound(in", matches?.count, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
if groupStage != nil { if groupStage != nil {
return index return index
} else if let index = (matches ?? roundObject?.playedMatches().sorted(by: \.index))?.firstIndex(where: { $0.id == id }) { } else if let index = (matches ?? roundObject?.playedMatches().sorted(by: \.index))?.firstIndex(where: { $0.id == id }) {
@ -87,6 +94,13 @@ class Match: ModelObject, Storable {
} }
func matchTitle(_ displayStyle: DisplayStyle = .wide, inMatches matches: [Match]? = nil) -> String { func matchTitle(_ displayStyle: DisplayStyle = .wide, inMatches matches: [Match]? = nil) -> String {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func matchTitle", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
if let groupStageObject { if let groupStageObject {
return groupStageObject.localizedMatchUpLabel(for: index) return groupStageObject.localizedMatchUpLabel(for: index)
} }
@ -285,13 +299,13 @@ class Match: ModelObject, Storable {
let bottomMatchIndex = (nextIndex + 1) * 2 let bottomMatchIndex = (nextIndex + 1) * 2
let isTopMatch = topMatchIndex + 1 == index let isTopMatch = topMatchIndex + 1 == index
let lookingForIndex = isTopMatch ? topMatchIndex : bottomMatchIndex let lookingForIndex = isTopMatch ? topMatchIndex : bottomMatchIndex
return Store.main.filter(isIncluded: { $0.round == round && $0.index == lookingForIndex }).first return DataStore.shared.matches.first(where: { $0.round == round && $0.index == lookingForIndex })
} }
private func _forwardMatch(inRound round: Round) -> Match? { private func _forwardMatch(inRound round: Round) -> Match? {
guard let roundObjectNextRound = round.nextRound() else { return nil } guard let roundObjectNextRound = round.nextRound() else { return nil }
let nextIndex = (index - 1) / 2 let nextIndex = (index - 1) / 2
return Store.main.filter(isIncluded: { $0.round == roundObjectNextRound.id && $0.index == nextIndex }).first return DataStore.shared.matches.first(where: { $0.round == roundObjectNextRound.id && $0.index == nextIndex })
} }
func _toggleForwardMatchDisableState(_ state: Bool) { func _toggleForwardMatchDisableState(_ state: Bool) {
@ -350,7 +364,7 @@ class Match: ModelObject, Storable {
} }
func next() -> Match? { func next() -> Match? {
return Store.main.filter(isIncluded: { $0.round == round && $0.index > index }).sorted(by: \.index).first return DataStore.shared.matches.filter({ $0.round == round && $0.index > index }).sorted(by: \.index).first
} }
func followingMatch() -> Match? { func followingMatch() -> Match? {
@ -359,7 +373,7 @@ class Match: ModelObject, Storable {
} }
func getFollowingMatch(fromNextRoundId nextRoundId: String) -> Match? { func getFollowingMatch(fromNextRoundId nextRoundId: String) -> Match? {
return Store.main.filter(isIncluded: { $0.round == nextRoundId && $0.index == (index - 1) / 2 }).first return DataStore.shared.matches.first(where: { $0.round == nextRoundId && $0.index == (index - 1) / 2 })
} }
func getDuration() -> Int { func getDuration() -> Int {
@ -386,26 +400,20 @@ class Match: ModelObject, Storable {
func topPreviousRoundMatch() -> Match? { func topPreviousRoundMatch() -> Match? {
guard let roundObject else { return nil } guard let roundObject else { return nil }
let matches: [Match] = Store.main.filter { match in let topPreviousRoundMatchIndex = topPreviousRoundMatchIndex()
match.index == topPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id let roundObjectPreviousRoundId = roundObject.previousRound()?.id
} return DataStore.shared.matches.first(where: { match in
return matches.sorted(by: \.index).first match.round != nil && match.round == roundObjectPreviousRoundId && match.index == topPreviousRoundMatchIndex
})
} }
func bottomPreviousRoundMatch() -> Match? { func bottomPreviousRoundMatch() -> Match? {
guard let roundObject else { return nil } guard let roundObject else { return nil }
let matches: [Match] = Store.main.filter { match in let bottomPreviousRoundMatchIndex = bottomPreviousRoundMatchIndex()
match.index == bottomPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id let roundObjectPreviousRoundId = roundObject.previousRound()?.id
} return DataStore.shared.matches.first(where: { match in
return matches.sorted(by: \.index).first match.round != nil && match.round == roundObjectPreviousRoundId && match.index == bottomPreviousRoundMatchIndex
} })
func upperBracketMatch(_ teamPosition: TeamPosition) -> Match? {
if teamPosition == .one {
return roundObject?.upperBracketTopMatch(ofMatchIndex: index)
} else {
return roundObject?.upperBracketBottomMatch(ofMatchIndex: index)
}
} }
func previousMatch(_ teamPosition: TeamPosition) -> Match? { func previousMatch(_ teamPosition: TeamPosition) -> Match? {
@ -416,11 +424,6 @@ class Match: ModelObject, Storable {
} }
} }
func upperMatches() -> [Match] {
guard let roundObject else { return [] }
return [roundObject.upperBracketTopMatch(ofMatchIndex: index), roundObject.upperBracketBottomMatch(ofMatchIndex: index)].compactMap({ $0 })
}
var computedOrder: Int { var computedOrder: Int {
guard let roundObject else { return index } guard let roundObject else { return index }
return roundObject.isLoserBracket() ? roundObject.index * 100 + indexInRound() : roundObject.index * 1000 + indexInRound() return roundObject.isLoserBracket() ? roundObject.index * 100 + indexInRound() : roundObject.index * 1000 + indexInRound()
@ -428,9 +431,9 @@ class Match: ModelObject, Storable {
func previousMatches() -> [Match] { func previousMatches() -> [Match] {
guard let roundObject else { return [] } guard let roundObject else { return [] }
return Store.main.filter { match in let roundObjectPreviousRoundId = roundObject.previousRound()?.id
(match.index == topPreviousRoundMatchIndex() || match.index == bottomPreviousRoundMatchIndex()) return DataStore.shared.matches.filter { match in
&& match.round == roundObject.previousRound()?.id match.round == roundObjectPreviousRoundId && (match.index == topPreviousRoundMatchIndex() || match.index == bottomPreviousRoundMatchIndex())
}.sorted(by: \.index) }.sorted(by: \.index)
} }
@ -660,14 +663,26 @@ class Match: ModelObject, Storable {
} }
func scores() -> [TeamScore] { func scores() -> [TeamScore] {
return Store.main.filter(isIncluded: { $0.match == id }) return DataStore.shared.teamScores.filter { $0.match == id }
} }
func teams() -> [TeamRegistration] { func teams() -> [TeamRegistration] {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func teams()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
if groupStage != nil { if groupStage != nil {
return [groupStageProjectedTeam(.one), groupStageProjectedTeam(.two)].compactMap { $0 } return [groupStageProjectedTeam(.one), groupStageProjectedTeam(.two)].compactMap { $0 }
} }
return [roundProjectedTeam(.one), roundProjectedTeam(.two)].compactMap { $0 } guard let roundObject else { return [] }
let previousRound = roundObject.previousRound()
return [roundObject.roundProjectedTeam(.one, inMatch: self, previousRound: previousRound), roundObject.roundProjectedTeam(.two, inMatch: self, previousRound: previousRound)].compactMap { $0 }
// return [roundProjectedTeam(.one), roundProjectedTeam(.two)].compactMap { $0 }
} }
func scoreDifference(_ teamPosition: Int) -> (set: Int, game: Int)? { func scoreDifference(_ teamPosition: Int) -> (set: Int, game: Int)? {
@ -696,7 +711,8 @@ class Match: ModelObject, Storable {
func roundProjectedTeam(_ team: TeamPosition) -> TeamRegistration? { func roundProjectedTeam(_ team: TeamPosition) -> TeamRegistration? {
guard let roundObject else { return nil } guard let roundObject else { return nil }
return roundObject.roundProjectedTeam(team, inMatch: self) let previousRound = roundObject.previousRound()
return roundObject.roundProjectedTeam(team, inMatch: self, previousRound: previousRound)
} }
func teamWon(_ team: TeamRegistration?) -> Bool { func teamWon(_ team: TeamRegistration?) -> Bool {
@ -710,7 +726,7 @@ class Match: ModelObject, Storable {
} }
func team(_ team: TeamPosition) -> TeamRegistration? { func team(_ team: TeamPosition) -> TeamRegistration? {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -718,19 +734,9 @@ class Match: ModelObject, Storable {
} }
#endif #endif
if groupStage != nil { if groupStage != nil {
switch team { return groupStageProjectedTeam(team)
case .one:
return groupStageProjectedTeam(.one)
case .two:
return groupStageProjectedTeam(.two)
}
} else { } else {
switch team { return roundProjectedTeam(team)
case .one:
return roundProjectedTeam(.one)
case .two:
return roundProjectedTeam(.two)
}
} }
} }

@ -41,7 +41,7 @@ class MonthData : ModelObject, Storable {
let anonymousCount = await FederalPlayer.anonymousCount(mostRecentDateAvailable: mostRecentDateAvailable) let anonymousCount = await FederalPlayer.anonymousCount(mostRecentDateAvailable: mostRecentDateAvailable)
await MainActor.run { await MainActor.run {
if let lastDataSource = DataStore.shared.appSettings.lastDataSource { if let lastDataSource = DataStore.shared.appSettings.lastDataSource {
let currentMonthData : MonthData = Store.main.filter(isIncluded: { $0.monthKey == lastDataSource }).first ?? MonthData(monthKey: lastDataSource) let currentMonthData : MonthData = DataStore.shared.monthData.first(where: { $0.monthKey == lastDataSource }) ?? MonthData(monthKey: lastDataSource)
currentMonthData.maleUnrankedValue = lastDataSourceMaleUnranked?.0 currentMonthData.maleUnrankedValue = lastDataSourceMaleUnranked?.0
currentMonthData.maleCount = lastDataSourceMaleUnranked?.1 currentMonthData.maleCount = lastDataSourceMaleUnranked?.1
currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0 currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0

@ -36,11 +36,11 @@ class Round: ModelObject, Storable {
} }
func _matches() -> [Match] { func _matches() -> [Match] {
return Store.main.filter { $0.round == self.id } return DataStore.shared.matches.filter { $0.round == self.id }
} }
func getDisabledMatches() -> [Match] { func getDisabledMatches() -> [Match] {
return Store.main.filter { $0.round == self.id && $0.disabled == true } return DataStore.shared.matches.filter { $0.round == self.id && $0.disabled == true }
} }
// MARK: - // MARK: -
@ -60,7 +60,11 @@ class Round: ModelObject, Storable {
} }
func hasEnded() -> Bool { func hasEnded() -> Bool {
if parent == nil {
return playedMatches().anySatisfy({ $0.hasEnded() == false }) == false return playedMatches().anySatisfy({ $0.hasEnded() == false }) == false
} else {
return enabledMatches().anySatisfy({ $0.hasEnded() == false }) == false
}
} }
func upperMatches(ofMatch match: Match) -> [Match] { func upperMatches(ofMatch match: Match) -> [Match] {
@ -74,8 +78,8 @@ class Round: ModelObject, Storable {
func previousMatches(ofMatch match: Match) -> [Match] { func previousMatches(ofMatch match: Match) -> [Match] {
guard let previousRound = previousRound() else { return [] } guard let previousRound = previousRound() else { return [] }
return Store.main.filter { return DataStore.shared.matches.filter {
($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex()) && $0.round == previousRound.id $0.round == previousRound.id && ($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex())
} }
} }
@ -92,26 +96,21 @@ class Round: ModelObject, Storable {
} }
} }
func team(_ team: TeamPosition, inMatch match: Match) -> TeamRegistration? { func team(_ team: TeamPosition, inMatch match: Match, previousRound: Round?) -> TeamRegistration? {
switch team { return roundProjectedTeam(team, inMatch: match, previousRound: previousRound)
case .one:
return roundProjectedTeam(.one, inMatch: match)
case .two:
return roundProjectedTeam(.two, inMatch: match)
}
} }
func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? { func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? {
return Store.main.filter(isIncluded: { return DataStore.shared.teamRegistrations.first(where: {
$0.tournament == tournament $0.tournament == tournament
&& $0.bracketPosition != nil && $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 DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament $0.tournament == tournament
&& $0.bracketPosition != nil && $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) == matchIndex && ($0.bracketPosition! / 2) == matchIndex
@ -121,7 +120,7 @@ class Round: ModelObject, Storable {
func seeds() -> [TeamRegistration] { func seeds() -> [TeamRegistration] {
let initialMatchIndex = RoundRule.matchIndex(fromRoundIndex: index) let initialMatchIndex = RoundRule.matchIndex(fromRoundIndex: index)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index) let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index)
return Store.main.filter(isIncluded: { return DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament $0.tournament == tournament
&& $0.bracketPosition != nil && $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) >= initialMatchIndex && ($0.bracketPosition! / 2) >= initialMatchIndex
@ -137,7 +136,14 @@ class Round: ModelObject, Storable {
return playedMatches().flatMap({ $0.teams() }) return playedMatches().flatMap({ $0.teams() })
} }
func roundProjectedTeam(_ team: TeamPosition, inMatch match: Match) -> TeamRegistration? { func roundProjectedTeam(_ team: TeamPosition, inMatch match: Match, previousRound: Round?) -> TeamRegistration? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func roundProjectedTeam", team.rawValue, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
if isLoserBracket() == false, let seed = seed(team, inMatchIndex: match.index) { if isLoserBracket() == false, let seed = seed(team, inMatchIndex: match.index) {
return seed return seed
} }
@ -146,76 +152,115 @@ class Round: ModelObject, Storable {
case .one: case .one:
if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 }) { if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 }) {
return luckyLoser.team return luckyLoser.team
} else if let parent = upperBracketTopMatch(ofMatchIndex: match.index)?.losingTeamId { } else if let previousMatch = topPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
return Store.main.findById(parent)
} 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.disabled { } else if previousMatch.disabled {
return previousMatch.teams().first return previousMatch.teams().first
} }
} else if let parent = upperBracketTopMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
return Store.main.findById(parent)
} }
case .two: case .two:
if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 + 1 }) { if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 + 1 }) {
return luckyLoser.team return luckyLoser.team
} else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index)?.losingTeamId { } else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
return Store.main.findById(parent)
} 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.disabled { } else if previousMatch.disabled {
return previousMatch.teams().first return previousMatch.teams().first
} }
} else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
return Store.main.findById(parent)
} }
} }
return nil return nil
} }
func upperBracketTopMatch(ofMatchIndex matchIndex: Int) -> Match? { func upperBracketTopMatch(ofMatchIndex matchIndex: Int, previousRound: Round?) -> Match? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func upperBracketTopMatch", matchIndex, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex)
if isLoserBracket(), previousRound() == nil, let parentRound = parentRound, let upperBracketTopMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2) { if isLoserBracket(), previousRound == nil, let parentRound = parentRound, let upperBracketTopMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2) {
return upperBracketTopMatch return upperBracketTopMatch
} }
return nil return nil
} }
func upperBracketBottomMatch(ofMatchIndex matchIndex: Int) -> Match? { func upperBracketBottomMatch(ofMatchIndex matchIndex: Int, previousRound: Round?) -> Match? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func upperBracketBottomMatch", matchIndex, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex)
if isLoserBracket(), previousRound() == nil, let parentRound = parentRound, let upperBracketBottomMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1) { if isLoserBracket(), previousRound == nil, let parentRound = parentRound, let upperBracketBottomMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1) {
return upperBracketBottomMatch return upperBracketBottomMatch
} }
return nil return nil
} }
func topPreviousRoundMatch(ofMatch match: Match) -> Match? { func topPreviousRoundMatch(ofMatch match: Match, previousRound: Round?) -> Match? {
guard let previousRound = previousRound() else { return nil } #if DEBUG_TIME //DEBUGING TIME
let matches: [Match] = Store.main.filter { let start = Date()
$0.index == match.topPreviousRoundMatchIndex() && $0.round == previousRound.id defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func topPreviousRoundMatch", match.id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
} }
return matches.sorted(by: \.index).first #endif
guard let previousRound else { return nil }
let topPreviousRoundMatchIndex = match.topPreviousRoundMatchIndex()
return DataStore.shared.matches.first(where: {
$0.round == previousRound.id && $0.index == topPreviousRoundMatchIndex
})
} }
func bottomPreviousRoundMatch(ofMatch match: Match) -> Match? { func bottomPreviousRoundMatch(ofMatch match: Match, previousRound: Round?) -> Match? {
guard let previousRound = previousRound() else { return nil } #if DEBUG_TIME //DEBUGING TIME
let matches: [Match] = Store.main.filter { let start = Date()
$0.index == match.bottomPreviousRoundMatchIndex() && $0.round == previousRound.id defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func bottomPreviousRoundMatch", match.id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
} }
return matches.sorted(by: \.index).first #endif
guard let previousRound else { return nil }
let bottomPreviousRoundMatchIndex = match.bottomPreviousRoundMatchIndex()
return DataStore.shared.matches.first(where: {
$0.round == previousRound.id && $0.index == bottomPreviousRoundMatchIndex
})
} }
func getMatch(atMatchIndexInRound matchIndexInRound: Int) -> Match? { func getMatch(atMatchIndexInRound matchIndexInRound: Int) -> Match? {
Store.main.filter(isIncluded: { DataStore.shared.matches.first(where: {
let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index) let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index)
return $0.round == id && index == matchIndexInRound return $0.round == id && index == matchIndexInRound
}).first })
} }
func enabledMatches() -> [Match] { func enabledMatches() -> [Match] {
return Store.main.filter { $0.round == self.id && $0.disabled == false } return DataStore.shared.matches.filter { $0.round == self.id && $0.disabled == false }
} }
func displayableMatches() -> [Match] { func displayableMatches() -> [Match] {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func displayableMatches of round: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
if index == 0 && isUpperBracket() { if index == 0 && isUpperBracket() {
var matches : [Match?] = [playedMatches().first] var matches : [Match?] = [playedMatches().first]
matches.append(loserRounds().first?.playedMatches().first) matches.append(loserRounds().first?.playedMatches().first)
@ -234,17 +279,28 @@ class Round: ModelObject, Storable {
} }
func previousRound() -> Round? { func previousRound() -> Round? {
return Store.main.filter(isIncluded: { $0.tournament == tournament && $0.parent == parent && $0.index == index + 1 }).first #if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func previousRound of: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return DataStore.shared.rounds.first(where: { $0.tournament == tournament && $0.parent == parent && $0.index == index + 1 })
} }
func nextRound() -> Round? { func nextRound() -> Round? {
return Store.main.filter(isIncluded: { $0.tournament == tournament && $0.parent == parent && $0.index == index - 1 }).first return DataStore.shared.rounds.first(where: { $0.tournament == tournament && $0.parent == parent && $0.index == index - 1 })
} }
func loserRounds(forRoundIndex roundIndex: Int) -> [Round] { func loserRounds(forRoundIndex roundIndex: Int) -> [Round] {
return loserRoundsAndChildren().filter({ $0.index == roundIndex }).sorted(by: \.theoryCumulativeMatchCount) return loserRoundsAndChildren().filter({ $0.index == roundIndex }).sorted(by: \.theoryCumulativeMatchCount)
} }
func loserRounds(forRoundIndex roundIndex: Int, loserRoundsAndChildren: [Round]) -> [Round] {
return loserRoundsAndChildren.filter({ $0.index == roundIndex }).sorted(by: \.theoryCumulativeMatchCount)
}
func isDisabled() -> Bool { func isDisabled() -> Bool {
return _matches().allSatisfy({ $0.disabled }) return _matches().allSatisfy({ $0.disabled })
} }
@ -351,8 +407,15 @@ class Round: ModelObject, Storable {
func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String { func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func correspondingLoserRoundTitle()", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index) let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index)
let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: { let seedsAfterThisRound : [TeamRegistration] = DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament $0.tournament == tournament
&& $0.bracketPosition != nil && $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
@ -366,23 +429,38 @@ class Round: ModelObject, Storable {
return nextRound()?.isRankDisabled() == false return nextRound()?.isRankDisabled() == false
} }
func seedInterval() -> SeedInterval? { func seedInterval(expanded: Bool = false) -> SeedInterval? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func seedInterval(expanded: Bool = false)", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
if parent == nil { if parent == nil {
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index + 1) if index == 0 { return SeedInterval(first: 1, last: 2) }
let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index) let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index)
let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: { let seedsAfterThisRound : [TeamRegistration] = DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament $0.tournament == tournament
&& $0.bracketPosition != nil && $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
}) })
let playedMatches = playedMatches() let playedMatches = playedMatches()
let reduce = numberOfMatches / 2 - (playedMatches.count + seedsAfterThisRound.count) let seedInterval = SeedInterval(first: playedMatches.count + seedsAfterThisRound.count + 1, last: playedMatches.count * 2 + seedsAfterThisRound.count)
return SeedInterval(first: 1, last: numberOfMatches, reduce: reduce) return seedInterval
} }
if let previousRound = previousRound() { if let previousRound = previousRound() {
if previousRound.enabledMatches().isEmpty == false && expanded == false {
return previousRound.seedInterval()?.chunks()?.first return previousRound.seedInterval()?.chunks()?.first
} else {
return previousRound.previousRound()?.seedInterval()
}
} else if let parentRound { } else if let parentRound {
if parentRound.parent == nil && expanded == false {
return parentRound.seedInterval()
}
return parentRound.seedInterval()?.chunks()?.last return parentRound.seedInterval()?.chunks()?.last
} }
@ -396,20 +474,6 @@ class Round: ModelObject, Storable {
return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle) return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle)
} }
func roundTitleAndPointsRange(_ displayStyle: DisplayStyle = .wide, expanded: Bool = false, inTournament: Tournament) -> (String?, String?) {
let seedInterval = seedInterval()?.withLast(enabledMatches().count * 2 - 1)
var roundTitle : String? = nil
if parent != nil {
roundTitle = seedInterval?.localizedLabel(displayStyle) ?? "Round pas trouvé"
} else {
roundTitle = RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle)
}
let tournamentTeamCount = inTournament.teamCount
let pointsEarned: String? = seedInterval?.pointsRange(tournamentLevel: inTournament.tournamentLevel, teamsCount: tournamentTeamCount)
return (roundTitle, pointsEarned)
}
func updateTournamentState() { func updateTournamentState() {
if let tournamentObject = tournamentObject(), index == 0, isUpperBracket(), hasEnded() { if let tournamentObject = tournamentObject(), index == 0, isUpperBracket(), hasEnded() {
tournamentObject.endDate = Date() tournamentObject.endDate = Date()
@ -433,7 +497,15 @@ class Round: ModelObject, Storable {
} }
func loserRounds() -> [Round] { func loserRounds() -> [Round] {
return Store.main.filter(isIncluded: { $0.parent == id }).sorted(by: \.index).reversed() #if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func loserRounds: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return DataStore.shared.rounds.filter( { $0.parent == id }).sorted(by: \.index).reversed()
} }
func loserRoundsAndChildren() -> [Round] { func loserRoundsAndChildren() -> [Round] {
@ -574,7 +646,7 @@ extension Round: Selectable, Equatable {
} }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
if let parentRound { if let parentRound {
return "Tour #\(parentRound.loserRounds().count - index)" return "Tour #\(parentRound.loserRounds().count - index)"
} else { } else {
@ -583,6 +655,15 @@ extension Round: Selectable, Equatable {
} }
func badgeValue() -> Int? { func badgeValue() -> Int? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func badgeValue round of: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
if let parentRound { if let parentRound {
return parentRound.loserRounds(forRoundIndex: index).flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count return parentRound.loserRounds(forRoundIndex: index).flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count
} else { } else {
@ -595,6 +676,13 @@ extension Round: Selectable, Equatable {
} }
func badgeImage() -> Badge? { func badgeImage() -> Badge? {
hasEnded() ? .checkmark : nil #if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func badgeImage of round: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return hasEnded() ? .checkmark : nil
} }
} }

@ -60,7 +60,7 @@ class TeamRegistration: ModelObject, Storable {
// MARK: - Computed dependencies // MARK: - Computed dependencies
func unsortedPlayers() -> [PlayerRegistration] { func unsortedPlayers() -> [PlayerRegistration] {
Store.main.filter { $0.teamRegistration == self.id } DataStore.shared.playerRegistrations.filter { $0.teamRegistration == self.id }
} }
// MARK: - // MARK: -
@ -138,19 +138,19 @@ class TeamRegistration: ModelObject, Storable {
} }
func teamScores() -> [TeamScore] { func teamScores() -> [TeamScore] {
return Store.main.filter(isIncluded: { $0.teamRegistration == id }) return DataStore.shared.teamScores.filter({ $0.teamRegistration == id })
} }
func wins() -> [Match] { func wins() -> [Match] {
return Store.main.filter(isIncluded: { $0.winningTeamId == id }) return DataStore.shared.matches.filter({ $0.winningTeamId == id })
} }
func loses() -> [Match] { func loses() -> [Match] {
return Store.main.filter(isIncluded: { $0.losingTeamId == id }) return DataStore.shared.matches.filter({ $0.losingTeamId == id })
} }
func matches() -> [Match] { func matches() -> [Match] {
return Store.main.filter(isIncluded: { $0.losingTeamId == id || $0.winningTeamId == id }) return DataStore.shared.matches.filter({ $0.losingTeamId == id || $0.winningTeamId == id })
} }
var tournamentCategory: TournamentCategory { var tournamentCategory: TournamentCategory {
@ -323,7 +323,7 @@ class TeamRegistration: ModelObject, Storable {
typealias AreInIncreasingOrder = (PlayerRegistration, PlayerRegistration) -> Bool typealias AreInIncreasingOrder = (PlayerRegistration, PlayerRegistration) -> Bool
func players() -> [PlayerRegistration] { func players() -> [PlayerRegistration] {
Store.main.filter { $0.teamRegistration == self.id }.sorted { (lhs, rhs) in DataStore.shared.playerRegistrations.filter { $0.teamRegistration == self.id }.sorted { (lhs, rhs) in
let predicates: [AreInIncreasingOrder] = [ let predicates: [AreInIncreasingOrder] = [
{ $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 }, { $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 },
{ $0.rank ?? 0 < $1.rank ?? 0 }, { $0.rank ?? 0 < $1.rank ?? 0 },
@ -377,13 +377,13 @@ class TeamRegistration: ModelObject, Storable {
func initialRound() -> Round? { func initialRound() -> Round? {
guard let bracketPosition else { return nil } guard let bracketPosition else { return nil }
let roundIndex = RoundRule.roundIndex(fromMatchIndex: bracketPosition / 2) let roundIndex = RoundRule.roundIndex(fromMatchIndex: bracketPosition / 2)
return Store.main.filter(isIncluded: { $0.tournament == tournament && $0.index == roundIndex }).first return DataStore.shared.rounds.first(where: { $0.tournament == tournament && $0.index == roundIndex })
} }
func initialMatch() -> Match? { func initialMatch() -> Match? {
guard let bracketPosition else { return nil } guard let bracketPosition else { return nil }
guard let initialRoundObject = initialRound() else { return nil } guard let initialRoundObject = initialRound() else { return nil }
return Store.main.filter(isIncluded: { $0.round == initialRoundObject.id && $0.index == bracketPosition / 2 }).first return DataStore.shared.matches.first(where: { $0.round == initialRoundObject.id && $0.index == bracketPosition / 2 })
} }

@ -344,7 +344,7 @@ class Tournament : ModelObject, Storable {
// MARK: - Computed Dependencies // MARK: - Computed Dependencies
func unsortedTeams() -> [TeamRegistration] { func unsortedTeams() -> [TeamRegistration] {
return Store.main.filter { $0.tournament == self.id } return DataStore.shared.teamRegistrations.filter { $0.tournament == self.id }
} }
func groupStages() -> [GroupStage] { func groupStages() -> [GroupStage] {
@ -353,7 +353,7 @@ class Tournament : ModelObject, Storable {
} }
func allRounds() -> [Round] { func allRounds() -> [Round] {
return Store.main.filter { $0.tournament == self.id } return DataStore.shared.rounds.filter { $0.tournament == self.id }
} }
// MARK: - // MARK: -
@ -476,7 +476,15 @@ class Tournament : ModelObject, Storable {
} }
func courtUsed() -> [Int] { func courtUsed() -> [Int] {
let runningMatches : [Match] = Store.main.filter(isIncluded: { $0.isRunning() }).filter({ $0.tournamentId() == self.id }) #if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func courtUsed()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
let runningMatches : [Match] = DataStore.shared.matches.filter({ $0.isRunning() }).filter({ $0.tournamentId() == self.id })
return Set(runningMatches.compactMap { $0.courtIndex }).sorted() return Set(runningMatches.compactMap { $0.courtIndex }).sorted()
} }
@ -557,6 +565,15 @@ class Tournament : ModelObject, Storable {
} }
func availableSeeds() -> [TeamRegistration] { func availableSeeds() -> [TeamRegistration] {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func availableSeeds()", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return seeds().filter { $0.isSeedable() } return seeds().filter { $0.isSeedable() }
} }
@ -569,7 +586,7 @@ class Tournament : ModelObject, Storable {
} }
func getRound(atRoundIndex roundIndex: Int) -> Round? { func getRound(atRoundIndex roundIndex: Int) -> Round? {
return Store.main.filter(isIncluded: { $0.tournament == id && $0.index == roundIndex }).first return DataStore.shared.rounds.first(where: { $0.tournament == id && $0.index == roundIndex })
} }
func availableSeedSpot(inRoundIndex roundIndex: Int) -> [Match] { func availableSeedSpot(inRoundIndex roundIndex: Int) -> [Match] {
@ -746,18 +763,18 @@ class Tournament : ModelObject, Storable {
} }
func allMatches() -> [Match] { func allMatches() -> [Match] {
let unsortedGroupStages : [GroupStage] = Store.main.filter { $0.tournament == self.id } let unsortedGroupStages : [GroupStage] = DataStore.shared.groupStages.filter { $0.tournament == self.id }
let matches: [Match] = unsortedGroupStages.flatMap { $0._matches() } + allRoundMatches() let matches: [Match] = unsortedGroupStages.flatMap { $0._matches() } + allRoundMatches()
return matches.filter({ $0.disabled == false }) return matches.filter({ $0.disabled == false })
} }
func _allMatchesIncludingDisabled() -> [Match] { func _allMatchesIncludingDisabled() -> [Match] {
let unsortedGroupStages : [GroupStage] = Store.main.filter { $0.tournament == self.id } let unsortedGroupStages : [GroupStage] = DataStore.shared.groupStages.filter { $0.tournament == self.id }
return unsortedGroupStages.flatMap { $0._matches() } + allRounds().flatMap { $0._matches() } return unsortedGroupStages.flatMap { $0._matches() } + allRounds().flatMap { $0._matches() }
} }
func rounds() -> [Round] { func rounds() -> [Round] {
Store.main.filter { $0.tournament == self.id && $0.parent == nil }.sorted(by: \.index).reversed() DataStore.shared.rounds.filter { $0.tournament == self.id && $0.parent == nil }.sorted(by: \.index).reversed()
} }
func sortedTeams() -> [TeamRegistration] { func sortedTeams() -> [TeamRegistration] {
@ -766,7 +783,7 @@ class Tournament : ModelObject, Storable {
} }
func selectedSortedTeams() -> [TeamRegistration] { func selectedSortedTeams() -> [TeamRegistration] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -842,11 +859,11 @@ class Tournament : ModelObject, Storable {
} }
func unsortedTeamsWithoutWO() -> [TeamRegistration] { func unsortedTeamsWithoutWO() -> [TeamRegistration] {
return Store.main.filter { $0.tournament == self.id && $0.walkOut == false } return DataStore.shared.teamRegistrations.filter { $0.tournament == self.id && $0.walkOut == false }
} }
func walkoutTeams() -> [TeamRegistration] { func walkoutTeams() -> [TeamRegistration] {
return Store.main.filter { $0.tournament == self.id && $0.walkOut == true } return DataStore.shared.teamRegistrations.filter { $0.tournament == self.id && $0.walkOut == true }
} }
func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] { func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] {
@ -1015,11 +1032,11 @@ class Tournament : ModelObject, Storable {
func groupStagesMatches() -> [Match] { func groupStagesMatches() -> [Match] {
let groupStageIds = groupStages().map { $0.id } let groupStageIds = groupStages().map { $0.id }
return Store.main.filter(isIncluded: { $0.groupStage != nil && groupStageIds.contains($0.groupStage!) }) return DataStore.shared.matches.filter({ $0.groupStage != nil && groupStageIds.contains($0.groupStage!) })
} }
func availableToStart(_ allMatches: [Match], in runningMatches: [Match]) async -> [Match] { func availableToStart(_ allMatches: [Match], in runningMatches: [Match]) async -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1030,7 +1047,7 @@ class Tournament : ModelObject, Storable {
} }
func asyncRunningMatches(_ allMatches: [Match]) async -> [Match] { func asyncRunningMatches(_ allMatches: [Match]) async -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1041,7 +1058,7 @@ class Tournament : ModelObject, Storable {
} }
func runningMatches(_ allMatches: [Match]) -> [Match] { func runningMatches(_ allMatches: [Match]) -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1052,7 +1069,7 @@ class Tournament : ModelObject, Storable {
} }
func readyMatches(_ allMatches: [Match]) async -> [Match] { func readyMatches(_ allMatches: [Match]) async -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1063,7 +1080,7 @@ class Tournament : ModelObject, Storable {
} }
func finishedMatches(_ allMatches: [Match], limit: Int? = nil) -> [Match] { func finishedMatches(_ allMatches: [Match], limit: Int? = nil) -> [Match] {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1268,6 +1285,14 @@ class Tournament : ModelObject, Storable {
func availableQualifiedTeams() -> [TeamRegistration] { func availableQualifiedTeams() -> [TeamRegistration] {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func availableQualifiedTeams()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return unsortedTeams().filter({ $0.qualified && $0.bracketPosition == nil }) return unsortedTeams().filter({ $0.qualified && $0.bracketPosition == nil })
} }
@ -1494,8 +1519,8 @@ class Tournament : ModelObject, Storable {
guard let bracketPosition else { return nil } guard let bracketPosition else { return nil }
let matchIndex = bracketPosition / 2 let matchIndex = bracketPosition / 2
let roundIndex = RoundRule.roundIndex(fromMatchIndex: matchIndex) let roundIndex = RoundRule.roundIndex(fromMatchIndex: matchIndex)
if let round : Round = Store.main.filter(isIncluded: { $0.tournament == id && $0.index == roundIndex }).first { if let round : Round = DataStore.shared.rounds.first(where: { $0.tournament == id && $0.index == roundIndex }) {
return Store.main.filter(isIncluded: { $0.round == round.id && $0.index == matchIndex }).first return DataStore.shared.matches.first(where: { $0.round == round.id && $0.index == matchIndex })
} }
return nil return nil
} }
@ -1759,7 +1784,7 @@ class Tournament : ModelObject, Storable {
func currentMonthData() -> MonthData? { func currentMonthData() -> MonthData? {
guard let rankSourceDate else { return nil } guard let rankSourceDate else { return nil }
let dateString = URL.importDateFormatter.string(from: rankSourceDate) let dateString = URL.importDateFormatter.string(from: rankSourceDate)
return Store.main.filter(isIncluded: { $0.monthKey == dateString }).first return DataStore.shared.monthData.first(where: { $0.monthKey == dateString })
} }
var maleUnrankedValue: Int? { var maleUnrankedValue: Int? {
@ -1779,9 +1804,9 @@ class Tournament : ModelObject, Storable {
} }
func tournamentWinner() -> TeamRegistration? { func tournamentWinner() -> TeamRegistration? {
let rounds: [Round] = Store.main.filter(isIncluded: { $0.index == 0 && $0.tournament == id && $0.parent == nil }) let round: Round? = DataStore.shared.rounds.first(where: { $0.tournament == id && $0.parent == nil && $0.index == 0 })
// let final: Round? = .first // let final: Round? = .first
return rounds.first?.playedMatches().first?.winner() return round?.playedMatches().first?.winner()
} }
func getGroupStageChunkValue() -> Int { func getGroupStageChunkValue() -> Int {

@ -86,11 +86,11 @@ class User: ModelObject, UserBase, Storable {
} }
func clubsObjects(includeCreated: Bool = false) -> [Club] { func clubsObjects(includeCreated: Bool = false) -> [Club] {
return Store.main.filter(isIncluded: { (includeCreated && $0.creator == id) || clubs.contains($0.id) }) return DataStore.shared.clubs.filter({ (includeCreated && $0.creator == id) || clubs.contains($0.id) })
} }
func createdClubsObjectsNotFavorite() -> [Club] { func createdClubsObjectsNotFavorite() -> [Club] {
return Store.main.filter(isIncluded: { ($0.creator == id) && clubs.contains($0.id) == false }) return DataStore.shared.clubs.filter({ ($0.creator == id) && clubs.contains($0.id) == false })
} }
func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) { func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) {

@ -35,7 +35,7 @@ enum AgendaDestination: Int, CaseIterable, Identifiable, Selectable, Equatable {
} }
} }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
localizedTitleKey localizedTitleKey
} }

@ -25,7 +25,7 @@ class FederalDataViewModel {
labels.append(contentsOf: categories.map { $0.localizedLabel() }) labels.append(contentsOf: categories.map { $0.localizedLabel() })
labels.append(contentsOf: ageCategories.map { $0.localizedLabel() }) labels.append(contentsOf: ageCategories.map { $0.localizedLabel() })
let clubNames = selectedClubs.compactMap { codeClub in let clubNames = selectedClubs.compactMap { codeClub in
let club: Club? = Store.main.filter(isIncluded: { $0.code == codeClub }).first let club: Club? = DataStore.shared.clubs.first(where: { $0.code == codeClub })
return club?.clubTitle(.short) return club?.clubTitle(.short)
} }
@ -35,7 +35,7 @@ class FederalDataViewModel {
func selectedClub() -> Club? { func selectedClub() -> Club? {
if selectedClubs.isEmpty == false { if selectedClubs.isEmpty == false {
return Store.main.filter(isIncluded: { $0.code == selectedClubs.first! }).first return DataStore.shared.clubs.first(where: { $0.code == selectedClubs.first! })
} else { } else {
return nil return nil
} }

@ -26,14 +26,6 @@ struct SeedInterval: Hashable, Comparable {
first == 1 && last == 2 first == 1 && last == 2
} }
func reducedBy(_ count: Int, firstAlso: Bool = false) -> SeedInterval {
return SeedInterval(first: first - (firstAlso ? count : 0), last: last - count, reduce: reduce)
}
func withLast(_ lastValue: Int) -> SeedInterval {
return SeedInterval(first: first, last: first + lastValue, reduce: reduce)
}
var count: Int { var count: Int {
dimension dimension
} }
@ -53,17 +45,6 @@ struct SeedInterval: Hashable, Comparable {
} }
} }
func chunksOrSelf() -> [SeedInterval] {
if dimension > 3 {
let split = dimension / 2
let firstHalf = SeedInterval(first: first, last: first + split - 1, reduce: reduce)
let secondHalf = SeedInterval(first: first + split, last: last, reduce: reduce)
return [firstHalf, secondHalf]
} else {
return [self]
}
}
var computedLast: Int { var computedLast: Int {
last - reduce last - reduce
} }

@ -9,7 +9,7 @@ import Foundation
import SwiftUI import SwiftUI
protocol Selectable { protocol Selectable {
func selectionLabel() -> String func selectionLabel(index: Int) -> String
func badgeValue() -> Int? func badgeValue() -> Int?
func badgeImage() -> Badge? func badgeImage() -> Badge?
func badgeValueColor() -> Color? func badgeValueColor() -> Color?

@ -130,6 +130,7 @@ struct EventCreationView: View {
private func _validate() { private func _validate() {
let event = Event(creator: Store.main.userId, name: eventName) let event = Event(creator: Store.main.userId, name: eventName)
event.club = selectedClub?.id
do { do {
try dataStore.events.addOrUpdate(instance: event) try dataStore.events.addOrUpdate(instance: event)
@ -152,15 +153,15 @@ struct EventCreationView: View {
} }
if let selectedClub, let verifiedSelectedClubId = dataStore.clubs.first(where: { selectedClub.id == $0.id })?.id { // if let selectedClub, let verifiedSelectedClubId = dataStore.clubs.first(where: { selectedClub.id == $0.id })?.id {
event.club = verifiedSelectedClubId // event.club = verifiedSelectedClubId
do { // do {
try dataStore.events.addOrUpdate(instance: event) // try dataStore.events.addOrUpdate(instance: event)
} catch { // } catch {
Logger.error(error) // Logger.error(error)
} // }
} // }
//
dismiss() dismiss()
navigation.path.append(tournaments.first!) navigation.path.append(tournaments.first!)

@ -21,7 +21,7 @@ enum EventDestination: Identifiable, Selectable, Equatable {
return String(describing: self) return String(describing: self)
} }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
switch self { switch self {
case .links: case .links:
return "Liens" return "Liens"

@ -33,11 +33,12 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable & Equatable >:
.id("settings") .id("settings")
} }
ForEach(destinations) { destination in ForEach(destinations.indices, id: \.self) { index in
let destination = destinations[index]
Button { Button {
selectedDestination = destination selectedDestination = destination
} label: { } label: {
Text(destination.selectionLabel()) Text(destination.selectionLabel(index: index))
.foregroundStyle(selectedDestination?.id == destination.id ? .white : .black) .foregroundStyle(selectedDestination?.id == destination.id ? .white : .black)
} }
.padding() .padding()

@ -28,7 +28,7 @@ struct GroupStagesView: View {
} }
} }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
switch self { switch self {
case .all: case .all:
return "Tout" return "Tout"

@ -26,22 +26,6 @@ struct MatchDateView: View {
var body: some View { var body: some View {
Menu { Menu {
Button("Ne pas jouer ce match") {
match._toggleMatchDisableState(true)
}
Button("Jouer ce match") {
match._toggleMatchDisableState(false)
}
Button("Créer les scores") {
let teamsScores = match.getOrCreateTeamScores()
do {
try dataStore.teamScores.addOrUpdate(contentOfs: teamsScores)
} catch {
Logger.error(error)
}
}
let estimatedDuration = match.getDuration() let estimatedDuration = match.getDuration()
if match.startDate == nil && isReady { if match.startDate == nil && isReady {
Button("Démarrer") { Button("Démarrer") {

@ -14,6 +14,7 @@ struct PlayerBlockView: View {
let color: Color let color: Color
let width: CGFloat let width: CGFloat
let teamScore: TeamScore? let teamScore: TeamScore?
let isWalkOut: Bool
init(match: Match, teamPosition: TeamPosition, color: Color, width: CGFloat) { init(match: Match, teamPosition: TeamPosition, color: Color, width: CGFloat) {
self.match = match self.match = match
@ -22,7 +23,9 @@ struct PlayerBlockView: View {
self.team = theTeam self.team = theTeam
self.color = color self.color = color
self.width = width self.width = width
self.teamScore = match.teamScore(ofTeam: theTeam) let theTeamScore = match.teamScore(ofTeam: theTeam)
self.teamScore = theTeamScore
self.isWalkOut = theTeamScore?.isWalkOut() == true
} }
var names: [String]? { var names: [String]? {
@ -37,10 +40,6 @@ struct PlayerBlockView: View {
match.hasWalkoutTeam() match.hasWalkoutTeam()
} }
var isWalkOut: Bool {
match.teamWalkOut(team)
}
var scores: [String] { var scores: [String] {
teamScore?.score?.components(separatedBy: ",") ?? [] teamScore?.score?.components(separatedBy: ",") ?? []
} }

@ -93,13 +93,11 @@ struct MatchDetailView: View {
} }
} }
if match.isReady() {
Section { Section {
RowButtonView("Saisir les résultats", systemImage: "list.clipboard") { RowButtonView("Saisir les résultats", systemImage: "list.clipboard") {
self._editScores() self._editScores()
} }
} }
}
let players = self.match.teams().flatMap { $0.players() } let players = self.match.teams().flatMap { $0.players() }
let unpaid = players.filter({ $0.hasPaid() == false }) let unpaid = players.filter({ $0.hasPaid() == false })
@ -233,6 +231,15 @@ struct MatchDetailView: View {
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
Menu { Menu {
// Button("Créer les scores") {
// let teamsScores = match.getOrCreateTeamScores()
// do {
// try dataStore.teamScores.addOrUpdate(contentOfs: teamsScores)
// } catch {
// Logger.error(error)
// }
// }
if match.courtIndex != nil { if match.courtIndex != nil {
Button(role: .destructive) { Button(role: .destructive) {
match.removeCourt() match.removeCourt()
@ -433,6 +440,14 @@ struct MatchDetailView: View {
} }
fileprivate func _editScores() { fileprivate func _editScores() {
if match.isReady() == false && match.teams().count == 2 {
let teamsScores = match.getOrCreateTeamScores()
do {
try dataStore.teamScores.addOrUpdate(contentOfs: teamsScores)
} catch {
Logger.error(error)
}
}
self._verifyUser { self._verifyUser {
self._payTournamentAndExecute { self._payTournamentAndExecute {

@ -10,11 +10,12 @@ import SwiftUI
struct MatchRowView: View { struct MatchRowView: View {
var match: Match var match: Match
let matchViewStyle: MatchViewStyle let matchViewStyle: MatchViewStyle
var title: String? = nil
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
@ViewBuilder @ViewBuilder
var body: some View { var body: some View {
if isEditingTournamentSeed.wrappedValue == true && match.isGroupStage() == false && match.isLoserBracket == false { if isEditingTournamentSeed.wrappedValue == true && match.isGroupStage() == false && match.disabled == false {
MatchSetupView(match: match) MatchSetupView(match: match)
} else { } else {
// MatchSummaryView(match: match, matchViewStyle: matchViewStyle) // MatchSummaryView(match: match, matchViewStyle: matchViewStyle)
@ -56,7 +57,7 @@ struct MatchRowView: View {
NavigationLink { NavigationLink {
MatchDetailView(match: match, matchViewStyle: matchViewStyle) MatchDetailView(match: match, matchViewStyle: matchViewStyle)
} label: { } label: {
MatchSummaryView(match: match, matchViewStyle: matchViewStyle) MatchSummaryView(match: match, matchViewStyle: matchViewStyle, title: title)
} }
//.modifier(BroadcastViewModifier(isBroadcasted: match.isBroadcasted())) //.modifier(BroadcastViewModifier(isBroadcasted: match.isBroadcasted()))
} }

@ -18,7 +18,7 @@ struct MatchSummaryView: View {
let color: Color let color: Color
let width: CGFloat let width: CGFloat
init(match: Match, matchViewStyle: MatchViewStyle) { init(match: Match, matchViewStyle: MatchViewStyle, title: String? = nil) {
self.match = match self.match = match
self.matchViewStyle = matchViewStyle self.matchViewStyle = matchViewStyle
self.padding = matchViewStyle == .plainStyle ? 0 : 8 self.padding = matchViewStyle == .plainStyle ? 0 : 8
@ -34,9 +34,9 @@ struct MatchSummaryView: View {
self.roundTitle = nil self.roundTitle = nil
} }
self.matchTitle = match.matchTitle(.short) self.matchTitle = title ?? match.matchTitle(.short)
if let court = match.courtName(), match.hasEnded() == false { if match.hasEnded() == false, let court = match.courtName() {
self.courtName = court self.courtName = court
} else { } else {
self.courtName = nil self.courtName = nil
@ -54,8 +54,10 @@ struct MatchSummaryView: View {
if let roundTitle { if let roundTitle {
Text(roundTitle).fontWeight(.semibold) Text(roundTitle).fontWeight(.semibold)
} }
if match.index > 0 {
Text(matchTitle) Text(matchTitle)
} }
}
Spacer() Spacer()
if let courtName { if let courtName {
Spacer() Spacer()

@ -10,42 +10,48 @@ import SwiftUI
struct LoserRoundView: View { struct LoserRoundView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament @Environment(Tournament.self) var tournament: Tournament
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
let loserRounds: [Round] let loserBracket: LoserRound
@State private var isEditingTournamentSeed: Bool = false
private func _roundDisabled() -> Bool { private func _roundDisabled() -> Bool {
loserRounds.allSatisfy({ $0.isDisabled() }) #if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func _roundDisabled", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return loserBracket.allMatches.allSatisfy({ $0.disabled == false })
}
private func _matches(loserRoundId: String?) -> [Match] {
return loserBracket.allMatches.filter { $0.round == loserRoundId && (isEditingTournamentSeed.wrappedValue == true || (isEditingTournamentSeed.wrappedValue == false && $0.disabled == false)) }.sorted(by: \.index)
} }
var body: some View { var body: some View {
List { List {
if isEditingTournamentSeed == true { if isEditingTournamentSeed.wrappedValue == true {
_editingView() _editingView()
} }
let shouldDisplayLoserRounds = loserRounds.filter({
let matches = $0.playedMatches().filter { isEditingTournamentSeed == true || (isEditingTournamentSeed == false && $0.disabled == false) }
return matches.isEmpty == false
}).isEmpty == false
if shouldDisplayLoserRounds { ForEach(loserBracket.rounds) { loserRound in
ForEach(loserRounds) { loserRound in let matches = _matches(loserRoundId: loserRound.id)
let matches = loserRound.playedMatches().filter { isEditingTournamentSeed == true || (isEditingTournamentSeed == false && $0.disabled == false) }.sorted(by: \.index)
if matches.isEmpty == false { if matches.isEmpty == false {
Section { Section {
ForEach(matches) { match in ForEach(matches) { match in
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle) MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
.overlay { .overlay {
if match.disabled && isEditingTournamentSeed { if match.disabled && isEditingTournamentSeed.wrappedValue == true {
Image(systemName: "xmark") Image(systemName: "xmark")
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.opacity(0.8) .opacity(0.6)
} }
} }
.disabled(match.disabled) .disabled(match.disabled)
if isEditingTournamentSeed { if isEditingTournamentSeed.wrappedValue == true {
RowButtonView(match.disabled ? "Jouer ce match" : "Ne pas jouer ce match", role: .destructive) { RowButtonView(match.disabled ? "Jouer ce match" : "Ne pas jouer ce match", role: .destructive) {
match._toggleMatchDisableState(!match.disabled) match._toggleMatchDisableState(!match.disabled)
} }
@ -53,11 +59,9 @@ struct LoserRoundView: View {
} }
} header: { } header: {
HStack { HStack {
let labels = loserRound.roundTitleAndPointsRange(.wide, expanded: isEditingTournamentSeed, inTournament: tournament) if let seedInterval = loserRound.seedInterval() {
if let seedIntervalLocalizedLabel = labels.0 { Text(seedInterval.localizedLabel(.wide))
Text(seedIntervalLocalizedLabel) let seedIntervalPointRange = seedInterval.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournament.teamCount)
}
if let seedIntervalPointRange = labels.1 {
Spacer() Spacer()
Text(seedIntervalPointRange) Text(seedIntervalPointRange)
.font(.caption) .font(.caption)
@ -66,17 +70,23 @@ struct LoserRoundView: View {
} }
} }
} }
/*
let shouldDisplayLoserRounds : Bool = isEditingTournamentSeed.wrappedValue == true ? true : (allMatches.first(where: { $0.disabled == false }) != nil)
if shouldDisplayLoserRounds {
} else { } else {
Section { Section {
ContentUnavailableView("Aucun match joué", systemImage: "tennisball", description: Text("Il n'y aucun match à jouer dans ce tour de match de classement.")) ContentUnavailableView("Aucun match joué", systemImage: "tennisball", description: Text("Il n'y aucun match à jouer dans ce tour de match de classement."))
} }
} }
*/
} }
.headerProminence(.increased) .headerProminence(.increased)
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
Button(isEditingTournamentSeed == true ? "Valider" : "Modifier") { Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") {
isEditingTournamentSeed.toggle() isEditingTournamentSeed.wrappedValue.toggle()
} }
} }
} }
@ -85,13 +95,13 @@ struct LoserRoundView: View {
private func _editingView() -> some View { private func _editingView() -> some View {
if _roundDisabled() { if _roundDisabled() {
RowButtonView("Jouer ce tour", role: .destructive) { RowButtonView("Jouer ce tour", role: .destructive) {
loserRounds.forEach { round in loserBracket.rounds.forEach { round in
round.enableRound() round.enableRound()
} }
} }
} else { } else {
RowButtonView("Ne pas jouer ce tour", role: .destructive) { RowButtonView("Ne pas jouer ce tour", role: .destructive) {
loserRounds.forEach { round in loserBracket.rounds.forEach { round in
round.disableRound() round.disableRound()
} }
} }

@ -7,27 +7,118 @@
import SwiftUI import SwiftUI
class UpperRound: Identifiable, Selectable {
var id: String { round.id }
let round: Round
lazy var loserRounds: [LoserRound] = {
LoserRound.updateDestinations(fromLoserRounds: round.loserRounds(), inUpperBracketRound: round)
}()
let title: String
let playedMatches: [Match]
let correspondingLoserRoundTitle: String
init(round: Round) {
self.round = round
self.title = round.roundTitle(.short)
self.playedMatches = round.playedMatches()
self.correspondingLoserRoundTitle = round.correspondingLoserRoundTitle()
}
func loserMatches() -> [Match] {
loserRounds.flatMap({ $0.allMatches }).filter({ $0.disabled == false })
}
func status() -> (Int, Int) {
let loserMatches = loserMatches()
return (loserMatches.filter { $0.hasEnded() }.count, loserMatches.count)
}
}
extension UpperRound: Equatable {
static func == (lhs: UpperRound, rhs: UpperRound) -> Bool {
lhs.id == rhs.id
}
func selectionLabel(index: Int) -> String {
return title
}
func badgeValue() -> Int? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func badgeValue round of: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return playedMatches.filter({ $0.isRunning() }).count
}
func badgeValueColor() -> Color? {
return nil
}
func badgeImage() -> Badge? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func badgeImage of round: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return playedMatches.allSatisfy({ $0.hasEnded() }) ? .checkmark : nil
}
}
struct LoserRound: Identifiable, Selectable { struct LoserRound: Identifiable, Selectable {
let turnIndex: Int let turnIndex: Int
let rounds: [Round] let rounds: [Round]
let allMatches: [Match]
init(turnIndex: Int, rounds: [Round]) {
self.turnIndex = turnIndex
self.rounds = rounds
self.allMatches = rounds.flatMap { $0.playedMatches() }
}
var id: Int { var id: Int {
return turnIndex return turnIndex
} }
var shouldBeDisplayed: Bool {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func shouldBeDisplayed loserRound", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return allMatches.first(where: { $0.disabled == false }) != nil
}
static func updateDestinations(fromLoserRounds loserRounds: [Round], inUpperBracketRound upperBracketRound: Round) -> [LoserRound] { static func updateDestinations(fromLoserRounds loserRounds: [Round], inUpperBracketRound upperBracketRound: Round) -> [LoserRound] {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func updateDestinations(fromLoserRounds", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
var rounds = [LoserRound]() var rounds = [LoserRound]()
let allLoserRounds = upperBracketRound.loserRoundsAndChildren()
for (index, round) in loserRounds.enumerated() { for (index, round) in loserRounds.enumerated() {
rounds.append(LoserRound(turnIndex: index, rounds: upperBracketRound.loserRounds(forRoundIndex: round.index))) rounds.append(LoserRound(turnIndex: index, rounds: upperBracketRound.loserRounds(forRoundIndex: round.index, loserRoundsAndChildren: allLoserRounds)))
} }
return rounds return rounds
} }
static func enabledLoserRounds(inLoserRounds loserRounds: [Round], inUpperBracketRound upperBracketRound: Round) -> [Round] { static func enabledLoserRounds(inLoserRounds loserRounds: [Round], inUpperBracketRound upperBracketRound: Round) -> [Round] {
let allLoserRounds = upperBracketRound.loserRoundsAndChildren()
return loserRounds.filter { loserRound in return loserRounds.filter { loserRound in
upperBracketRound.loserRounds(forRoundIndex: loserRound.index).anySatisfy({ $0.isDisabled() == false }) upperBracketRound.loserRounds(forRoundIndex: loserRound.index, loserRoundsAndChildren: allLoserRounds).anySatisfy({ $0.isDisabled() == false })
} }
} }
@ -40,13 +131,15 @@ extension LoserRound: Equatable {
} }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
if index < turnIndex {
return "Tour #\(index + 1)"
}
return "Tour #\(turnIndex + 1)" return "Tour #\(turnIndex + 1)"
} }
func badgeValue() -> Int? { func badgeValue() -> Int? {
let playedMatches: [Match] = self.rounds.flatMap { $0.playedMatches() } let runningMatches: [Match] = allMatches.filter { $0.disabled == false && $0.isRunning() }
let runningMatches: [Match] = playedMatches.filter { $0.isRunning() }
return runningMatches.count return runningMatches.count
} }
@ -55,32 +148,45 @@ extension LoserRound: Equatable {
} }
func badgeImage() -> Badge? { func badgeImage() -> Badge? {
return rounds.allSatisfy({ $0.hasEnded() }) ? .checkmark : nil #if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func badgeImage loserRound", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return allMatches.filter { $0.disabled == false }.allSatisfy({ $0.hasEnded() }) ? .checkmark : nil
} }
} }
struct LoserRoundsView: View { struct LoserRoundsView: View {
var upperBracketRound: Round var upperBracketRound: UpperRound
@State private var selectedRound: LoserRound? @State private var selectedRound: LoserRound?
let loserRounds: [Round] @State private var isEditingTournamentSeed = false
@State private var allDestinations: [LoserRound]
init(upperBracketRound: Round) { init(upperBracketRound: UpperRound) {
self.upperBracketRound = upperBracketRound self.upperBracketRound = upperBracketRound
let _loserRounds = upperBracketRound.loserRounds() _selectedRound = State(wrappedValue: upperBracketRound.loserRounds.first(where: { $0.rounds.anySatisfy({ $0.getActiveLoserRound() != nil }) }) ?? upperBracketRound.loserRounds.first(where: { $0.shouldBeDisplayed }))
self.loserRounds = _loserRounds }
let rounds = LoserRound.updateDestinations(fromLoserRounds: _loserRounds, inUpperBracketRound: upperBracketRound)
_allDestinations = State(wrappedValue: rounds)
_selectedRound = State(wrappedValue: rounds.first(where: { $0.rounds.anySatisfy({ $0.getActiveLoserRound() != nil }) }) ?? rounds.first) var destinations: [LoserRound] {
isEditingTournamentSeed ? upperBracketRound.loserRounds : upperBracketRound.loserRounds.filter({ $0.shouldBeDisplayed })
} }
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: allDestinations, nilDestinationIsValid: false) GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: destinations, nilDestinationIsValid: false)
LoserRoundView(loserRounds: selectedRound!.rounds) if let selectedRound {
LoserRoundView(loserBracket: selectedRound)
} else {
Section {
ContentUnavailableView("Aucun tour à jouer", systemImage: "tennisball", description: Text("Il il n'y a aucun tour de match de classement prévu."))
}
}
} }
.environment(\.isEditingTournamentSeed, $isEditingTournamentSeed)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
} }

@ -21,16 +21,32 @@ struct RoundView: View {
@State private var availableSeedGroup: SeedInterval? @State private var availableSeedGroup: SeedInterval?
@State private var showPrintScreen: Bool = false @State private var showPrintScreen: Bool = false
var round: Round var upperRound: UpperRound
private func _getAvailableSeedGroup() async { private func _getAvailableSeedGroup() async {
availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: round.index) #if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func _getAvailableSeedGroup of: ", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: upperRound.round.index)
} }
private func _getSpaceLeft() async { private func _getSpaceLeft() async {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func _getSpaceLeft of: ", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
self.spaceLeft.removeAll() self.spaceLeft.removeAll()
self.seedSpaceLeft.removeAll() self.seedSpaceLeft.removeAll()
let displayableMatches: [Match] = self.round.displayableMatches() let displayableMatches: [Match] = self.upperRound.round.displayableMatches()
displayableMatches.forEach { match in displayableMatches.forEach { match in
let count: Int = match.teamScores.count let count: Int = match.teamScores.count
if count == 0 { if count == 0 {
@ -52,8 +68,8 @@ struct RoundView: View {
var body: some View { var body: some View {
List { List {
let displayableMatches = round.displayableMatches().sorted(by: \.index) let displayableMatches = upperRound.round.displayableMatches().sorted(by: \.index)
let loserRounds = round.loserRounds() let loserRounds = upperRound.loserRounds
if displayableMatches.isEmpty { if displayableMatches.isEmpty {
Section { Section {
ContentUnavailableView("Aucun match dans cette manche", systemImage: "tennisball") ContentUnavailableView("Aucun match dans cette manche", systemImage: "tennisball")
@ -66,30 +82,38 @@ struct RoundView: View {
} }
.tipStyle(tint: .master, asSection: true) .tipStyle(tint: .master, asSection: true)
if loserRounds.isEmpty == false { if upperRound.loserRounds.isEmpty == false {
let correspondingLoserRoundTitle = round.correspondingLoserRoundTitle()
Section { Section {
NavigationLink { NavigationLink {
LoserRoundsView(upperBracketRound: round) LoserRoundsView(upperBracketRound: upperRound)
.environment(tournament) .environment(tournament)
.navigationTitle(correspondingLoserRoundTitle) .navigationTitle(upperRound.correspondingLoserRoundTitle)
} label: { } label: {
Text(correspondingLoserRoundTitle) LabeledContent {
let status = upperRound.status()
if status.0 == status.1 {
Image(systemName: "checkmark").foregroundStyle(.green)
} else {
Text("\(status.0) terminé\(status.0.pluralSuffix) sur \(status.1)")
}
} label: {
Text(upperRound.correspondingLoserRoundTitle)
}
} }
} }
} }
} else { } else {
let disabledMatchesCount = BracketEditTip.matchesHidden let disabledMatchesCount = BracketEditTip.matchesHidden
if disabledMatchesCount > 0 { if disabledMatchesCount > 0 {
let bracketTip = BracketEditTip(nextRoundName: round.nextRound()?.roundTitle()) let bracketTip = BracketEditTip(nextRoundName: upperRound.round.nextRound()?.roundTitle())
TipView(bracketTip).tipStyle(tint: .green, asSection: true) TipView(bracketTip).tipStyle(tint: .green, asSection: true)
Section { Section {
let leftToPlay = (RoundRule.numberOfMatches(forRoundIndex: round.index) - disabledMatchesCount) let leftToPlay = (RoundRule.numberOfMatches(forRoundIndex: upperRound.round.index) - disabledMatchesCount)
LabeledContent { LabeledContent {
Text(leftToPlay.formatted()).font(.largeTitle) Text(leftToPlay.formatted()).font(.largeTitle)
} label: { } label: {
Text("Match\(leftToPlay.pluralSuffix) à jouer \(round.roundTitle(.short))") Text("Match\(leftToPlay.pluralSuffix) à jouer \(upperRound.title)")
Text("\(disabledMatchesCount) match\(disabledMatchesCount.pluralSuffix) désactivé\(disabledMatchesCount.pluralSuffix) automatiquement") Text("\(disabledMatchesCount) match\(disabledMatchesCount.pluralSuffix) désactivé\(disabledMatchesCount.pluralSuffix) automatiquement")
} }
} }
@ -101,7 +125,7 @@ struct RoundView: View {
if availableSeeds.isEmpty == false, let availableSeedGroup { if availableSeeds.isEmpty == false, let availableSeedGroup {
Section { Section {
RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) { RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) {
tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup) tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: availableSeedGroup)
await _save() await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false self.isEditingTournamentSeed.wrappedValue = false
@ -225,16 +249,17 @@ struct RoundView: View {
} }
ForEach(displayableMatches) { match in ForEach(displayableMatches) { match in
let matchTitle = match.matchTitle(.short, inMatches: displayableMatches)
Section { Section {
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle) MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle, title: matchTitle)
} header: { } header: {
HStack { HStack {
Text(round.roundTitle(.wide)) Text(upperRound.round.roundTitle(.wide))
if round.index > 0 { if upperRound.round.index > 0 {
Text(match.matchTitle(.short, inMatches: displayableMatches)) Text(matchTitle)
} else { } else {
let tournamentTeamCount = tournament.teamCount let tournamentTeamCount = tournament.teamCount
if let seedIntervalPointRange = round.seedInterval()?.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournamentTeamCount) { if let seedIntervalPointRange = upperRound.round.seedInterval()?.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournamentTeamCount) {
Spacer() Spacer()
Text(seedIntervalPointRange) Text(seedIntervalPointRange)
.font(.caption) .font(.caption)
@ -257,15 +282,15 @@ struct RoundView: View {
Task { Task {
await _prepareRound() await _prepareRound()
} }
let seeds = round.seeds() let seeds = upperRound.round.seeds()
SlideToDeleteSeedTip.seeds = seeds.count SlideToDeleteSeedTip.seeds = seeds.count
PrintTip.seeds = seeds.count PrintTip.seeds = seeds.count
BracketEditTip.matchesHidden = round.getDisabledMatches().count BracketEditTip.matchesHidden = upperRound.round.getDisabledMatches().count
} }
.fullScreenCover(isPresented: showVisualDrawView) { .fullScreenCover(isPresented: showVisualDrawView) {
if let availableSeedGroup = selectedSeedGroup { if let availableSeedGroup = selectedSeedGroup {
let seeds = tournament.seeds(inSeedGroup: availableSeedGroup) let seeds = tournament.seeds(inSeedGroup: availableSeedGroup)
let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: round.index) let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: upperRound.round.index)
NavigationStack { NavigationStack {
SpinDrawView(drawees: seeds, segments: availableSeedSpot, autoMode: true) { draws in SpinDrawView(drawees: seeds, segments: availableSeedSpot, autoMode: true) { draws in
Task { Task {

@ -9,16 +9,20 @@ import SwiftUI
struct RoundsView: View { struct RoundsView: View {
var tournament: Tournament var tournament: Tournament
@State private var selectedRound: Round? @State private var selectedRound: UpperRound?
@State private var isEditingTournamentSeed = false @State private var isEditingTournamentSeed = false
let destinations: [UpperRound]
init(tournament: Tournament) { init(tournament: Tournament) {
self.tournament = tournament self.tournament = tournament
let _destinations = tournament.rounds().map { UpperRound(round: $0) }
self.destinations = _destinations
let availableSeeds = tournament.availableSeeds() let availableSeeds = tournament.availableSeeds()
if tournament.shouldVerifyBracket && availableSeeds.isEmpty { if tournament.shouldVerifyBracket && availableSeeds.isEmpty {
_selectedRound = State(wrappedValue: nil) _selectedRound = State(wrappedValue: nil)
} else { } else {
_selectedRound = State(wrappedValue: tournament.getActiveRound()) _selectedRound = State(wrappedValue: _destinations.first(where: { $0.id == tournament.getActiveRound()?.id }))
} }
if availableSeeds.isEmpty == false || tournament.availableQualifiedTeams().isEmpty == false { if availableSeeds.isEmpty == false || tournament.availableQualifiedTeams().isEmpty == false {
_isEditingTournamentSeed = State(wrappedValue: true) _isEditingTournamentSeed = State(wrappedValue: true)
@ -27,14 +31,14 @@ struct RoundsView: View {
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: tournament.rounds(), nilDestinationIsValid: true) GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: destinations, nilDestinationIsValid: true)
switch selectedRound { switch selectedRound {
case .none: case .none:
RoundSettingsView() RoundSettingsView()
.navigationTitle("Réglages") .navigationTitle("Réglages")
case .some(let selectedRound): case .some(let selectedRound):
RoundView(round: selectedRound).id(selectedRound.id) RoundView(upperRound: selectedRound).id(selectedRound.id)
.navigationTitle(selectedRound.roundTitle()) .navigationTitle(selectedRound.round.roundTitle())
} }
} }
.environment(\.isEditingTournamentSeed, $isEditingTournamentSeed) .environment(\.isEditingTournamentSeed, $isEditingTournamentSeed)

@ -137,7 +137,7 @@ struct InscriptionManagerView: View {
} }
private func _setHash() async { private func _setHash() async {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -431,7 +431,7 @@ struct InscriptionManagerView: View {
} }
private func _prepareStats() async { private func _prepareStats() async {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -446,7 +446,7 @@ struct InscriptionManagerView: View {
} }
private func _prepareTeams() { private func _prepareTeams() {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -472,7 +472,7 @@ struct InscriptionManagerView: View {
} }
private func _getIssues() async { private func _getIssues() async {
#if DEBUG_TIME #if DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)

@ -26,7 +26,7 @@ enum CallDestination: Identifiable, Selectable, Equatable {
} }
} }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
switch self { switch self {
case .seeds: case .seeds:
return "Têtes de série" return "Têtes de série"

@ -32,14 +32,14 @@ enum CashierDestination: Identifiable, Selectable, Equatable {
} }
} }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
switch self { switch self {
case .summary: case .summary:
return "Bilan" return "Bilan"
case .groupStage(let groupStage): case .groupStage(let groupStage):
return groupStage.selectionLabel() return groupStage.selectionLabel(index: index)
case .bracket(let round): case .bracket(let round):
return round.selectionLabel() return round.selectionLabel(index: index)
case .all: case .all:
return "Tous" return "Tous"
} }

@ -15,6 +15,8 @@ struct TournamentRankView: View {
@State private var rankings: [Int: [TeamRegistration]] = [:] @State private var rankings: [Int: [TeamRegistration]] = [:]
@State private var calculating = false @State private var calculating = false
@State private var selectedTeam: TeamRegistration? @State private var selectedTeam: TeamRegistration?
@State private var runningMatches: [Match]?
@State private var matchesLeft: [Match]?
var isEditingTeam: Binding<Bool> { var isEditingTeam: Binding<Bool> {
Binding { Binding {
@ -26,11 +28,24 @@ struct TournamentRankView: View {
var body: some View { var body: some View {
List { List {
@Bindable var tournament = tournament @Bindable var tournament = tournament
let matchs = tournament.runningMatches(tournament.allMatches())
let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil }) let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil })
Section { Section {
LabeledContent { LabeledContent {
Text(matchs.count.formatted()) if let matchesLeft {
Text(matchesLeft.count.formatted())
} else {
ProgressView()
}
} label: {
Text("Matchs restant")
}
LabeledContent {
if let runningMatches {
Text(runningMatches.count.formatted())
} else {
ProgressView()
}
} label: { } label: {
Text("Matchs en cours") Text("Matchs en cours")
} }
@ -110,6 +125,11 @@ struct TournamentRankView: View {
} }
} }
} }
.task {
let all = tournament.allMatches()
self.runningMatches = await tournament.asyncRunningMatches(all)
self.matchesLeft = await tournament.readyMatches(all)
}
.alert("Position", isPresented: isEditingTeam) { .alert("Position", isPresented: isEditingTeam) {
if let selectedTeam { if let selectedTeam {
@Bindable var team = selectedTeam @Bindable var team = selectedTeam

@ -31,7 +31,7 @@ enum ScheduleDestination: String, Identifiable, Selectable, Equatable {
case scheduleGroupStage case scheduleGroupStage
case scheduleBracket case scheduleBracket
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
switch self { switch self {
case .scheduleGroupStage: case .scheduleGroupStage:
return "Poules" return "Poules"

@ -19,7 +19,7 @@ enum TournamentSettings: Identifiable, Selectable, Equatable {
var id: String { String(describing: self) } var id: String { String(describing: self) }
func selectionLabel() -> String { func selectionLabel(index: Int) -> String {
switch self { switch self {
case .status: case .status:
return "Statut" return "Statut"

@ -39,9 +39,6 @@ struct TournamentView: View {
var body: some View { var body: some View {
VStack(spacing: 0.0) { VStack(spacing: 0.0) {
List { List {
TipView(tournamentRunningTip)
.tipStyle(tint: nil)
if tournament.state() != .finished { if tournament.state() != .finished {
SubscriptionInfoView() SubscriptionInfoView()
} }
@ -188,6 +185,7 @@ struct TournamentView: View {
} }
} label: { } label: {
LabelOptions() LabelOptions()
.popoverTip(tournamentRunningTip)
} }
} }
} }

@ -85,7 +85,7 @@ struct LoginView: View {
ContentUnavailableView { ContentUnavailableView {
Label("Vérifiez vos emails.", systemImage: "envelope.badge") Label("Vérifiez vos emails.", systemImage: "envelope.badge")
} description: { } description: {
Text("Vous pouvez maintenant ouvrir votre boîte mail pour valider votre compte. Vous pourrez ensuite vous connecter ici. N'oubliez pas de vérifiez vos spams !") Text("Vous pourrez ensuite vous connecter ici. N'oubliez pas de vérifiez vos spams !")
} actions: { } actions: {
SupportButtonView(contentIsUnavailable: true) SupportButtonView(contentIsUnavailable: true)
} }

Loading…
Cancel
Save