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. 226
      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. 21
      PadelClub/ViewModel/SeedInterval.swift
  13. 2
      PadelClub/ViewModel/Selectable.swift
  14. 21
      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. 11
      PadelClub/Views/Match/Components/PlayerBlockView.swift
  20. 27
      PadelClub/Views/Match/MatchDetailView.swift
  21. 5
      PadelClub/Views/Match/MatchRowView.swift
  22. 10
      PadelClub/Views/Match/MatchSummaryView.swift
  23. 98
      PadelClub/Views/Round/LoserRoundView.swift
  24. 146
      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. 26
      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] {
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 {
@ -230,10 +230,10 @@ extension Club {
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 {
return clubs.first!
if let club {
return club
} else {
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
var tournaments: [Tournament] {
Store.main.filter { $0.event == self.id }
DataStore.shared.tournaments.filter { $0.event == self.id }
}
func clubObject() -> Club? {
@ -46,7 +46,7 @@ class Event: ModelObject, Storable {
}
var courtsUnavailability: [DateInterval] {
Store.main.filter(isIncluded: { $0.event == id })
DataStore.shared.dateIntervals.filter({ $0.event == id })
}
// MARK: -

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

@ -60,7 +60,7 @@ class Match: ModelObject, Storable {
// MARK: - Computed dependencies
var teamScores: [TeamScore] {
return Store.main.filter { $0.match == self.id }
return DataStore.shared.teamScores.filter { $0.match == self.id }
}
// MARK: -
@ -70,6 +70,13 @@ class Match: ModelObject, Storable {
}
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 {
return index
} 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 {
#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 {
return groupStageObject.localizedMatchUpLabel(for: index)
}
@ -285,13 +299,13 @@ class Match: ModelObject, Storable {
let bottomMatchIndex = (nextIndex + 1) * 2
let isTopMatch = topMatchIndex + 1 == index
let lookingForIndex = isTopMatch ? topMatchIndex : bottomMatchIndex
return Store.main.filter(isIncluded: { $0.round == round && $0.index == lookingForIndex }).first
return DataStore.shared.matches.first(where: { $0.round == round && $0.index == lookingForIndex })
}
private func _forwardMatch(inRound round: Round) -> Match? {
guard let roundObjectNextRound = round.nextRound() else { return nil }
let nextIndex = (index - 1) / 2
return Store.main.filter(isIncluded: { $0.round == roundObjectNextRound.id && $0.index == nextIndex }).first
return DataStore.shared.matches.first(where: { $0.round == roundObjectNextRound.id && $0.index == nextIndex })
}
func _toggleForwardMatchDisableState(_ state: Bool) {
@ -350,7 +364,7 @@ class Match: ModelObject, Storable {
}
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? {
@ -359,7 +373,7 @@ class Match: ModelObject, Storable {
}
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 {
@ -386,26 +400,20 @@ class Match: ModelObject, Storable {
func topPreviousRoundMatch() -> Match? {
guard let roundObject else { return nil }
let matches: [Match] = Store.main.filter { match in
match.index == topPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id
}
return matches.sorted(by: \.index).first
let topPreviousRoundMatchIndex = topPreviousRoundMatchIndex()
let roundObjectPreviousRoundId = roundObject.previousRound()?.id
return DataStore.shared.matches.first(where: { match in
match.round != nil && match.round == roundObjectPreviousRoundId && match.index == topPreviousRoundMatchIndex
})
}
func bottomPreviousRoundMatch() -> Match? {
guard let roundObject else { return nil }
let matches: [Match] = Store.main.filter { match in
match.index == bottomPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id
}
return matches.sorted(by: \.index).first
}
func upperBracketMatch(_ teamPosition: TeamPosition) -> Match? {
if teamPosition == .one {
return roundObject?.upperBracketTopMatch(ofMatchIndex: index)
} else {
return roundObject?.upperBracketBottomMatch(ofMatchIndex: index)
}
let bottomPreviousRoundMatchIndex = bottomPreviousRoundMatchIndex()
let roundObjectPreviousRoundId = roundObject.previousRound()?.id
return DataStore.shared.matches.first(where: { match in
match.round != nil && match.round == roundObjectPreviousRoundId && match.index == bottomPreviousRoundMatchIndex
})
}
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 {
guard let roundObject else { return index }
return roundObject.isLoserBracket() ? roundObject.index * 100 + indexInRound() : roundObject.index * 1000 + indexInRound()
@ -428,9 +431,9 @@ class Match: ModelObject, Storable {
func previousMatches() -> [Match] {
guard let roundObject else { return [] }
return Store.main.filter { match in
(match.index == topPreviousRoundMatchIndex() || match.index == bottomPreviousRoundMatchIndex())
&& match.round == roundObject.previousRound()?.id
let roundObjectPreviousRoundId = roundObject.previousRound()?.id
return DataStore.shared.matches.filter { match in
match.round == roundObjectPreviousRoundId && (match.index == topPreviousRoundMatchIndex() || match.index == bottomPreviousRoundMatchIndex())
}.sorted(by: \.index)
}
@ -660,14 +663,26 @@ class Match: ModelObject, Storable {
}
func scores() -> [TeamScore] {
return Store.main.filter(isIncluded: { $0.match == id })
return DataStore.shared.teamScores.filter { $0.match == id }
}
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 {
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)? {
@ -696,7 +711,8 @@ class Match: ModelObject, Storable {
func roundProjectedTeam(_ team: TeamPosition) -> TeamRegistration? {
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 {
@ -710,7 +726,7 @@ class Match: ModelObject, Storable {
}
func team(_ team: TeamPosition) -> TeamRegistration? {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -718,19 +734,9 @@ class Match: ModelObject, Storable {
}
#endif
if groupStage != nil {
switch team {
case .one:
return groupStageProjectedTeam(.one)
case .two:
return groupStageProjectedTeam(.two)
}
return groupStageProjectedTeam(team)
} else {
switch team {
case .one:
return roundProjectedTeam(.one)
case .two:
return roundProjectedTeam(.two)
}
return roundProjectedTeam(team)
}
}

@ -41,7 +41,7 @@ class MonthData : ModelObject, Storable {
let anonymousCount = await FederalPlayer.anonymousCount(mostRecentDateAvailable: mostRecentDateAvailable)
await MainActor.run {
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.maleCount = lastDataSourceMaleUnranked?.1
currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0

@ -36,11 +36,11 @@ class Round: ModelObject, Storable {
}
func _matches() -> [Match] {
return Store.main.filter { $0.round == self.id }
return DataStore.shared.matches.filter { $0.round == self.id }
}
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: -
@ -60,7 +60,11 @@ class Round: ModelObject, Storable {
}
func hasEnded() -> Bool {
return playedMatches().anySatisfy({ $0.hasEnded() == false }) == false
if parent == nil {
return playedMatches().anySatisfy({ $0.hasEnded() == false }) == false
} else {
return enabledMatches().anySatisfy({ $0.hasEnded() == false }) == false
}
}
func upperMatches(ofMatch match: Match) -> [Match] {
@ -74,8 +78,8 @@ class Round: ModelObject, Storable {
func previousMatches(ofMatch match: Match) -> [Match] {
guard let previousRound = previousRound() else { return [] }
return Store.main.filter {
($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex()) && $0.round == previousRound.id
return DataStore.shared.matches.filter {
$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? {
switch team {
case .one:
return roundProjectedTeam(.one, inMatch: match)
case .two:
return roundProjectedTeam(.two, inMatch: match)
}
func team(_ team: TeamPosition, inMatch match: Match, previousRound: Round?) -> TeamRegistration? {
return roundProjectedTeam(team, inMatch: match, previousRound: previousRound)
}
func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? {
return Store.main.filter(isIncluded: {
return DataStore.shared.teamRegistrations.first(where: {
$0.tournament == tournament
&& $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) == matchIndex
&& ($0.bracketPosition! % 2) == team.rawValue
}).first
})
}
func seeds(inMatchIndex matchIndex: Int) -> [TeamRegistration] {
return Store.main.filter(isIncluded: {
return DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament
&& $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) == matchIndex
@ -121,7 +120,7 @@ class Round: ModelObject, Storable {
func seeds() -> [TeamRegistration] {
let initialMatchIndex = RoundRule.matchIndex(fromRoundIndex: index)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index)
return Store.main.filter(isIncluded: {
return DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament
&& $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) >= initialMatchIndex
@ -137,7 +136,14 @@ class Round: ModelObject, Storable {
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) {
return seed
}
@ -146,76 +152,115 @@ class Round: ModelObject, Storable {
case .one:
if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 }) {
return luckyLoser.team
} else if let parent = upperBracketTopMatch(ofMatchIndex: match.index)?.losingTeamId {
return Store.main.findById(parent)
} else if let previousMatch = topPreviousRoundMatch(ofMatch: match) {
} else if let previousMatch = topPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
if let teamId = previousMatch.winningTeamId {
return Store.main.findById(teamId)
} else if previousMatch.disabled {
return previousMatch.teams().first
}
} else if let parent = upperBracketTopMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
return Store.main.findById(parent)
}
case .two:
if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 + 1 }) {
return luckyLoser.team
} else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index)?.losingTeamId {
return Store.main.findById(parent)
} else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match) {
} else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
if let teamId = previousMatch.winningTeamId {
return Store.main.findById(teamId)
} else if previousMatch.disabled {
return previousMatch.teams().first
}
} else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
return Store.main.findById(parent)
}
}
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)
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 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)
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 nil
}
func topPreviousRoundMatch(ofMatch match: Match) -> Match? {
guard let previousRound = previousRound() else { return nil }
let matches: [Match] = Store.main.filter {
$0.index == match.topPreviousRoundMatchIndex() && $0.round == previousRound.id
}
return matches.sorted(by: \.index).first
func topPreviousRoundMatch(ofMatch match: Match, previousRound: Round?) -> Match? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func topPreviousRoundMatch", match.id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#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? {
guard let previousRound = previousRound() else { return nil }
let matches: [Match] = Store.main.filter {
$0.index == match.bottomPreviousRoundMatchIndex() && $0.round == previousRound.id
}
return matches.sorted(by: \.index).first
func bottomPreviousRoundMatch(ofMatch match: Match, previousRound: Round?) -> Match? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func bottomPreviousRoundMatch", match.id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#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? {
Store.main.filter(isIncluded: {
DataStore.shared.matches.first(where: {
let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index)
return $0.round == id && index == matchIndexInRound
}).first
})
}
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] {
#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() {
var matches : [Match?] = [playedMatches().first]
matches.append(loserRounds().first?.playedMatches().first)
@ -234,17 +279,28 @@ class Round: ModelObject, Storable {
}
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? {
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] {
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 {
return _matches().allSatisfy({ $0.disabled })
}
@ -351,8 +407,15 @@ class Round: ModelObject, Storable {
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 seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: {
let seedsAfterThisRound : [TeamRegistration] = DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament
&& $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
@ -366,23 +429,38 @@ class Round: ModelObject, Storable {
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 {
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index + 1)
if index == 0 { return SeedInterval(first: 1, last: 2) }
let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index)
let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: {
let seedsAfterThisRound : [TeamRegistration] = DataStore.shared.teamRegistrations.filter({
$0.tournament == tournament
&& $0.bracketPosition != nil
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
})
let playedMatches = playedMatches()
let reduce = numberOfMatches / 2 - (playedMatches.count + seedsAfterThisRound.count)
return SeedInterval(first: 1, last: numberOfMatches, reduce: reduce)
let seedInterval = SeedInterval(first: playedMatches.count + seedsAfterThisRound.count + 1, last: playedMatches.count * 2 + seedsAfterThisRound.count)
return seedInterval
}
if let previousRound = previousRound() {
return previousRound.seedInterval()?.chunks()?.first
if previousRound.enabledMatches().isEmpty == false && expanded == false {
return previousRound.seedInterval()?.chunks()?.first
} else {
return previousRound.previousRound()?.seedInterval()
}
} else if let parentRound {
if parentRound.parent == nil && expanded == false {
return parentRound.seedInterval()
}
return parentRound.seedInterval()?.chunks()?.last
}
@ -395,20 +473,6 @@ class Round: ModelObject, Storable {
}
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() {
if let tournamentObject = tournamentObject(), index == 0, isUpperBracket(), hasEnded() {
@ -433,7 +497,15 @@ class Round: ModelObject, Storable {
}
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] {
@ -574,7 +646,7 @@ extension Round: Selectable, Equatable {
}
func selectionLabel() -> String {
func selectionLabel(index: Int) -> String {
if let parentRound {
return "Tour #\(parentRound.loserRounds().count - index)"
} else {
@ -583,6 +655,15 @@ extension Round: Selectable, Equatable {
}
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 {
return parentRound.loserRounds(forRoundIndex: index).flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count
} else {
@ -595,6 +676,13 @@ extension Round: Selectable, Equatable {
}
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
func unsortedPlayers() -> [PlayerRegistration] {
Store.main.filter { $0.teamRegistration == self.id }
DataStore.shared.playerRegistrations.filter { $0.teamRegistration == self.id }
}
// MARK: -
@ -138,19 +138,19 @@ class TeamRegistration: ModelObject, Storable {
}
func teamScores() -> [TeamScore] {
return Store.main.filter(isIncluded: { $0.teamRegistration == id })
return DataStore.shared.teamScores.filter({ $0.teamRegistration == id })
}
func wins() -> [Match] {
return Store.main.filter(isIncluded: { $0.winningTeamId == id })
return DataStore.shared.matches.filter({ $0.winningTeamId == id })
}
func loses() -> [Match] {
return Store.main.filter(isIncluded: { $0.losingTeamId == id })
return DataStore.shared.matches.filter({ $0.losingTeamId == id })
}
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 {
@ -323,7 +323,7 @@ class TeamRegistration: ModelObject, Storable {
typealias AreInIncreasingOrder = (PlayerRegistration, PlayerRegistration) -> Bool
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] = [
{ $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 },
{ $0.rank ?? 0 < $1.rank ?? 0 },
@ -377,13 +377,13 @@ class TeamRegistration: ModelObject, Storable {
func initialRound() -> Round? {
guard let bracketPosition else { return nil }
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? {
guard let bracketPosition 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
func unsortedTeams() -> [TeamRegistration] {
return Store.main.filter { $0.tournament == self.id }
return DataStore.shared.teamRegistrations.filter { $0.tournament == self.id }
}
func groupStages() -> [GroupStage] {
@ -353,7 +353,7 @@ class Tournament : ModelObject, Storable {
}
func allRounds() -> [Round] {
return Store.main.filter { $0.tournament == self.id }
return DataStore.shared.rounds.filter { $0.tournament == self.id }
}
// MARK: -
@ -476,7 +476,15 @@ class Tournament : ModelObject, Storable {
}
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()
}
@ -557,6 +565,15 @@ class Tournament : ModelObject, Storable {
}
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() }
}
@ -569,7 +586,7 @@ class Tournament : ModelObject, Storable {
}
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] {
@ -746,18 +763,18 @@ class Tournament : ModelObject, Storable {
}
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()
return matches.filter({ $0.disabled == false })
}
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() }
}
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] {
@ -766,7 +783,7 @@ class Tournament : ModelObject, Storable {
}
func selectedSortedTeams() -> [TeamRegistration] {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -842,11 +859,11 @@ class Tournament : ModelObject, Storable {
}
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] {
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] {
@ -1015,11 +1032,11 @@ class Tournament : ModelObject, Storable {
func groupStagesMatches() -> [Match] {
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] {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1030,7 +1047,7 @@ class Tournament : ModelObject, Storable {
}
func asyncRunningMatches(_ allMatches: [Match]) async -> [Match] {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1041,7 +1058,7 @@ class Tournament : ModelObject, Storable {
}
func runningMatches(_ allMatches: [Match]) -> [Match] {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1052,7 +1069,7 @@ class Tournament : ModelObject, Storable {
}
func readyMatches(_ allMatches: [Match]) async -> [Match] {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
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] {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -1268,6 +1285,14 @@ class Tournament : ModelObject, Storable {
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 })
}
@ -1494,8 +1519,8 @@ class Tournament : ModelObject, Storable {
guard let bracketPosition else { return nil }
let matchIndex = bracketPosition / 2
let roundIndex = RoundRule.roundIndex(fromMatchIndex: matchIndex)
if let round : Round = Store.main.filter(isIncluded: { $0.tournament == id && $0.index == roundIndex }).first {
return Store.main.filter(isIncluded: { $0.round == round.id && $0.index == matchIndex }).first
if let round : Round = DataStore.shared.rounds.first(where: { $0.tournament == id && $0.index == roundIndex }) {
return DataStore.shared.matches.first(where: { $0.round == round.id && $0.index == matchIndex })
}
return nil
}
@ -1759,7 +1784,7 @@ class Tournament : ModelObject, Storable {
func currentMonthData() -> MonthData? {
guard let rankSourceDate else { return nil }
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? {
@ -1779,9 +1804,9 @@ class Tournament : ModelObject, Storable {
}
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
return rounds.first?.playedMatches().first?.winner()
return round?.playedMatches().first?.winner()
}
func getGroupStageChunkValue() -> Int {

@ -86,11 +86,11 @@ class User: ModelObject, UserBase, Storable {
}
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] {
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) {

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

@ -25,7 +25,7 @@ class FederalDataViewModel {
labels.append(contentsOf: categories.map { $0.localizedLabel() })
labels.append(contentsOf: ageCategories.map { $0.localizedLabel() })
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)
}
@ -35,7 +35,7 @@ class FederalDataViewModel {
func selectedClub() -> Club? {
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 {
return nil
}

@ -25,15 +25,7 @@ struct SeedInterval: Hashable, Comparable {
func isFixed() -> Bool {
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 {
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 {
last - reduce
}

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

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

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

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

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

@ -26,22 +26,6 @@ struct MatchDateView: View {
var body: some View {
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()
if match.startDate == nil && isReady {
Button("Démarrer") {

@ -14,6 +14,7 @@ struct PlayerBlockView: View {
let color: Color
let width: CGFloat
let teamScore: TeamScore?
let isWalkOut: Bool
init(match: Match, teamPosition: TeamPosition, color: Color, width: CGFloat) {
self.match = match
@ -22,7 +23,9 @@ struct PlayerBlockView: View {
self.team = theTeam
self.color = color
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]? {
@ -36,11 +39,7 @@ struct PlayerBlockView: View {
var hideScore: Bool {
match.hasWalkoutTeam()
}
var isWalkOut: Bool {
match.teamWalkOut(team)
}
var scores: [String] {
teamScore?.score?.components(separatedBy: ",") ?? []
}

@ -93,11 +93,9 @@ struct MatchDetailView: View {
}
}
if match.isReady() {
Section {
RowButtonView("Saisir les résultats", systemImage: "list.clipboard") {
self._editScores()
}
Section {
RowButtonView("Saisir les résultats", systemImage: "list.clipboard") {
self._editScores()
}
}
@ -233,6 +231,15 @@ struct MatchDetailView: View {
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
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 {
Button(role: .destructive) {
match.removeCourt()
@ -433,7 +440,15 @@ struct MatchDetailView: View {
}
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._payTournamentAndExecute {
self.scoreType = .edition

@ -10,11 +10,12 @@ import SwiftUI
struct MatchRowView: View {
var match: Match
let matchViewStyle: MatchViewStyle
var title: String? = nil
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
@ViewBuilder
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)
} else {
// MatchSummaryView(match: match, matchViewStyle: matchViewStyle)
@ -56,7 +57,7 @@ struct MatchRowView: View {
NavigationLink {
MatchDetailView(match: match, matchViewStyle: matchViewStyle)
} label: {
MatchSummaryView(match: match, matchViewStyle: matchViewStyle)
MatchSummaryView(match: match, matchViewStyle: matchViewStyle, title: title)
}
//.modifier(BroadcastViewModifier(isBroadcasted: match.isBroadcasted()))
}

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

@ -10,73 +10,83 @@ import SwiftUI
struct LoserRoundView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
let loserBracket: LoserRound
let loserRounds: [Round]
@State private var isEditingTournamentSeed: Bool = false
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 {
List {
if isEditingTournamentSeed == true {
if isEditingTournamentSeed.wrappedValue == true {
_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(loserRounds) { loserRound in
let matches = loserRound.playedMatches().filter { isEditingTournamentSeed == true || (isEditingTournamentSeed == false && $0.disabled == false) }.sorted(by: \.index)
if matches.isEmpty == false {
Section {
ForEach(matches) { match in
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
.overlay {
if match.disabled && isEditingTournamentSeed {
Image(systemName: "xmark")
.resizable()
.scaledToFit()
.opacity(0.8)
}
}
.disabled(match.disabled)
if isEditingTournamentSeed {
RowButtonView(match.disabled ? "Jouer ce match" : "Ne pas jouer ce match", role: .destructive) {
match._toggleMatchDisableState(!match.disabled)
ForEach(loserBracket.rounds) { loserRound in
let matches = _matches(loserRoundId: loserRound.id)
if matches.isEmpty == false {
Section {
ForEach(matches) { match in
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
.overlay {
if match.disabled && isEditingTournamentSeed.wrappedValue == true {
Image(systemName: "xmark")
.resizable()
.scaledToFit()
.opacity(0.6)
}
}
}
} header: {
HStack {
let labels = loserRound.roundTitleAndPointsRange(.wide, expanded: isEditingTournamentSeed, inTournament: tournament)
if let seedIntervalLocalizedLabel = labels.0 {
Text(seedIntervalLocalizedLabel)
}
if let seedIntervalPointRange = labels.1 {
Spacer()
Text(seedIntervalPointRange)
.font(.caption)
.disabled(match.disabled)
if isEditingTournamentSeed.wrappedValue == true {
RowButtonView(match.disabled ? "Jouer ce match" : "Ne pas jouer ce match", role: .destructive) {
match._toggleMatchDisableState(!match.disabled)
}
}
}
} header: {
HStack {
if let seedInterval = loserRound.seedInterval() {
Text(seedInterval.localizedLabel(.wide))
let seedIntervalPointRange = seedInterval.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournament.teamCount)
Spacer()
Text(seedIntervalPointRange)
.font(.caption)
}
}
}
}
}
/*
let shouldDisplayLoserRounds : Bool = isEditingTournamentSeed.wrappedValue == true ? true : (allMatches.first(where: { $0.disabled == false }) != nil)
if shouldDisplayLoserRounds {
} else {
Section {
ContentUnavailableView("Aucun match joué", systemImage: "tennisball", description: Text("Il n'y aucun match à jouer dans ce tour de match de classement."))
}
}
*/
}
.headerProminence(.increased)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(isEditingTournamentSeed == true ? "Valider" : "Modifier") {
isEditingTournamentSeed.toggle()
Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") {
isEditingTournamentSeed.wrappedValue.toggle()
}
}
}
@ -85,13 +95,13 @@ struct LoserRoundView: View {
private func _editingView() -> some View {
if _roundDisabled() {
RowButtonView("Jouer ce tour", role: .destructive) {
loserRounds.forEach { round in
loserBracket.rounds.forEach { round in
round.enableRound()
}
}
} else {
RowButtonView("Ne pas jouer ce tour", role: .destructive) {
loserRounds.forEach { round in
loserBracket.rounds.forEach { round in
round.disableRound()
}
}

@ -7,27 +7,118 @@
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 {
let turnIndex: Int
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 {
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] {
#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]()
let allLoserRounds = upperBracketRound.loserRoundsAndChildren()
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
}
static func enabledLoserRounds(inLoserRounds loserRounds: [Round], inUpperBracketRound upperBracketRound: Round) -> [Round] {
let allLoserRounds = upperBracketRound.loserRoundsAndChildren()
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)"
}
func badgeValue() -> Int? {
let playedMatches: [Match] = self.rounds.flatMap { $0.playedMatches() }
let runningMatches: [Match] = playedMatches.filter { $0.isRunning() }
let runningMatches: [Match] = allMatches.filter { $0.disabled == false && $0.isRunning() }
return runningMatches.count
}
@ -55,32 +148,45 @@ extension LoserRound: Equatable {
}
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 {
var upperBracketRound: Round
var upperBracketRound: UpperRound
@State private var selectedRound: LoserRound?
let loserRounds: [Round]
@State private var allDestinations: [LoserRound]
init(upperBracketRound: Round) {
@State private var isEditingTournamentSeed = false
init(upperBracketRound: UpperRound) {
self.upperBracketRound = upperBracketRound
let _loserRounds = upperBracketRound.loserRounds()
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)
_selectedRound = State(wrappedValue: upperBracketRound.loserRounds.first(where: { $0.rounds.anySatisfy({ $0.getActiveLoserRound() != nil }) }) ?? upperBracketRound.loserRounds.first(where: { $0.shouldBeDisplayed }))
}
var destinations: [LoserRound] {
isEditingTournamentSeed ? upperBracketRound.loserRounds : upperBracketRound.loserRounds.filter({ $0.shouldBeDisplayed })
}
var body: some View {
VStack(spacing: 0) {
GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: allDestinations, nilDestinationIsValid: false)
LoserRoundView(loserRounds: selectedRound!.rounds)
GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: destinations, nilDestinationIsValid: false)
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)
.toolbarBackground(.visible, for: .navigationBar)
}

@ -21,16 +21,32 @@ struct RoundView: View {
@State private var availableSeedGroup: SeedInterval?
@State private var showPrintScreen: Bool = false
var round: Round
var upperRound: UpperRound
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 {
#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.seedSpaceLeft.removeAll()
let displayableMatches: [Match] = self.round.displayableMatches()
let displayableMatches: [Match] = self.upperRound.round.displayableMatches()
displayableMatches.forEach { match in
let count: Int = match.teamScores.count
if count == 0 {
@ -52,8 +68,8 @@ struct RoundView: View {
var body: some View {
List {
let displayableMatches = round.displayableMatches().sorted(by: \.index)
let loserRounds = round.loserRounds()
let displayableMatches = upperRound.round.displayableMatches().sorted(by: \.index)
let loserRounds = upperRound.loserRounds
if displayableMatches.isEmpty {
Section {
ContentUnavailableView("Aucun match dans cette manche", systemImage: "tennisball")
@ -66,30 +82,38 @@ struct RoundView: View {
}
.tipStyle(tint: .master, asSection: true)
if loserRounds.isEmpty == false {
let correspondingLoserRoundTitle = round.correspondingLoserRoundTitle()
if upperRound.loserRounds.isEmpty == false {
Section {
NavigationLink {
LoserRoundsView(upperBracketRound: round)
LoserRoundsView(upperBracketRound: upperRound)
.environment(tournament)
.navigationTitle(correspondingLoserRoundTitle)
.navigationTitle(upperRound.correspondingLoserRoundTitle)
} 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 {
let disabledMatchesCount = BracketEditTip.matchesHidden
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)
Section {
let leftToPlay = (RoundRule.numberOfMatches(forRoundIndex: round.index) - disabledMatchesCount)
let leftToPlay = (RoundRule.numberOfMatches(forRoundIndex: upperRound.round.index) - disabledMatchesCount)
LabeledContent {
Text(leftToPlay.formatted()).font(.largeTitle)
} 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")
}
}
@ -101,7 +125,7 @@ struct RoundView: View {
if availableSeeds.isEmpty == false, let availableSeedGroup {
Section {
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()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
@ -225,16 +249,17 @@ struct RoundView: View {
}
ForEach(displayableMatches) { match in
let matchTitle = match.matchTitle(.short, inMatches: displayableMatches)
Section {
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle, title: matchTitle)
} header: {
HStack {
Text(round.roundTitle(.wide))
if round.index > 0 {
Text(match.matchTitle(.short, inMatches: displayableMatches))
Text(upperRound.round.roundTitle(.wide))
if upperRound.round.index > 0 {
Text(matchTitle)
} else {
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()
Text(seedIntervalPointRange)
.font(.caption)
@ -257,15 +282,15 @@ struct RoundView: View {
Task {
await _prepareRound()
}
let seeds = round.seeds()
let seeds = upperRound.round.seeds()
SlideToDeleteSeedTip.seeds = seeds.count
PrintTip.seeds = seeds.count
BracketEditTip.matchesHidden = round.getDisabledMatches().count
BracketEditTip.matchesHidden = upperRound.round.getDisabledMatches().count
}
.fullScreenCover(isPresented: showVisualDrawView) {
if let availableSeedGroup = selectedSeedGroup {
let seeds = tournament.seeds(inSeedGroup: availableSeedGroup)
let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: round.index)
let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: upperRound.round.index)
NavigationStack {
SpinDrawView(drawees: seeds, segments: availableSeedSpot, autoMode: true) { draws in
Task {

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

@ -137,7 +137,7 @@ struct InscriptionManagerView: View {
}
private func _setHash() async {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -431,7 +431,7 @@ struct InscriptionManagerView: View {
}
private func _prepareStats() async {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -446,7 +446,7 @@ struct InscriptionManagerView: View {
}
private func _prepareTeams() {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
@ -472,7 +472,7 @@ struct InscriptionManagerView: View {
}
private func _getIssues() async {
#if DEBUG_TIME
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
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 {
case .seeds:
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 {
case .summary:
return "Bilan"
case .groupStage(let groupStage):
return groupStage.selectionLabel()
return groupStage.selectionLabel(index: index)
case .bracket(let round):
return round.selectionLabel()
return round.selectionLabel(index: index)
case .all:
return "Tous"
}

@ -15,7 +15,9 @@ struct TournamentRankView: View {
@State private var rankings: [Int: [TeamRegistration]] = [:]
@State private var calculating = false
@State private var selectedTeam: TeamRegistration?
@State private var runningMatches: [Match]?
@State private var matchesLeft: [Match]?
var isEditingTeam: Binding<Bool> {
Binding {
selectedTeam != nil
@ -26,11 +28,24 @@ struct TournamentRankView: View {
var body: some View {
List {
@Bindable var tournament = tournament
let matchs = tournament.runningMatches(tournament.allMatches())
let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil })
Section {
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: {
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) {
if let selectedTeam {
@Bindable var team = selectedTeam

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

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

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

@ -85,7 +85,7 @@ struct LoginView: View {
ContentUnavailableView {
Label("Vérifiez vos emails.", systemImage: "envelope.badge")
} 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: {
SupportButtonView(contentIsUnavailable: true)
}

Loading…
Cancel
Save