diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index 53a3661..f18437c 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.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) } diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index bf73fcb..679bdad 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -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: - diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index 7fa5218..af6175a 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -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() } diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 7d94af6..26814b2 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -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) } } diff --git a/PadelClub/Data/MonthData.swift b/PadelClub/Data/MonthData.swift index 7580fb1..8ad05c8 100644 --- a/PadelClub/Data/MonthData.swift +++ b/PadelClub/Data/MonthData.swift @@ -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 diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index c8d95be..881f649 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -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 } } diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 824870d..d6df314 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -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 }) } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 3841997..5a0ea1d 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -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 { diff --git a/PadelClub/Data/User.swift b/PadelClub/Data/User.swift index ae24386..4087995 100644 --- a/PadelClub/Data/User.swift +++ b/PadelClub/Data/User.swift @@ -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) { diff --git a/PadelClub/ViewModel/AgendaDestination.swift b/PadelClub/ViewModel/AgendaDestination.swift index 7b9c77d..3708e3d 100644 --- a/PadelClub/ViewModel/AgendaDestination.swift +++ b/PadelClub/ViewModel/AgendaDestination.swift @@ -35,7 +35,7 @@ enum AgendaDestination: Int, CaseIterable, Identifiable, Selectable, Equatable { } } - func selectionLabel() -> String { + func selectionLabel(index: Int) -> String { localizedTitleKey } diff --git a/PadelClub/ViewModel/FederalDataViewModel.swift b/PadelClub/ViewModel/FederalDataViewModel.swift index 5d71add..387dc0a 100644 --- a/PadelClub/ViewModel/FederalDataViewModel.swift +++ b/PadelClub/ViewModel/FederalDataViewModel.swift @@ -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 } diff --git a/PadelClub/ViewModel/SeedInterval.swift b/PadelClub/ViewModel/SeedInterval.swift index e44455e..91adb31 100644 --- a/PadelClub/ViewModel/SeedInterval.swift +++ b/PadelClub/ViewModel/SeedInterval.swift @@ -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 } diff --git a/PadelClub/ViewModel/Selectable.swift b/PadelClub/ViewModel/Selectable.swift index 0953678..88658e8 100644 --- a/PadelClub/ViewModel/Selectable.swift +++ b/PadelClub/ViewModel/Selectable.swift @@ -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? diff --git a/PadelClub/Views/Cashier/Event/EventCreationView.swift b/PadelClub/Views/Cashier/Event/EventCreationView.swift index 2baf2bd..ab7e566 100644 --- a/PadelClub/Views/Cashier/Event/EventCreationView.swift +++ b/PadelClub/Views/Cashier/Event/EventCreationView.swift @@ -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!) diff --git a/PadelClub/Views/Cashier/Event/EventView.swift b/PadelClub/Views/Cashier/Event/EventView.swift index dee9614..3d73901 100644 --- a/PadelClub/Views/Cashier/Event/EventView.swift +++ b/PadelClub/Views/Cashier/Event/EventView.swift @@ -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" diff --git a/PadelClub/Views/Components/GenericDestinationPickerView.swift b/PadelClub/Views/Components/GenericDestinationPickerView.swift index 27c28ae..5e6e2af 100644 --- a/PadelClub/Views/Components/GenericDestinationPickerView.swift +++ b/PadelClub/Views/Components/GenericDestinationPickerView.swift @@ -33,11 +33,12 @@ struct GenericDestinationPickerView: .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() diff --git a/PadelClub/Views/GroupStage/GroupStagesView.swift b/PadelClub/Views/GroupStage/GroupStagesView.swift index 7827c32..4709c0d 100644 --- a/PadelClub/Views/GroupStage/GroupStagesView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesView.swift @@ -28,7 +28,7 @@ struct GroupStagesView: View { } } - func selectionLabel() -> String { + func selectionLabel(index: Int) -> String { switch self { case .all: return "Tout" diff --git a/PadelClub/Views/Match/Components/MatchDateView.swift b/PadelClub/Views/Match/Components/MatchDateView.swift index 9324f15..653c5b6 100644 --- a/PadelClub/Views/Match/Components/MatchDateView.swift +++ b/PadelClub/Views/Match/Components/MatchDateView.swift @@ -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") { diff --git a/PadelClub/Views/Match/Components/PlayerBlockView.swift b/PadelClub/Views/Match/Components/PlayerBlockView.swift index c5a6791..8ad7a9e 100644 --- a/PadelClub/Views/Match/Components/PlayerBlockView.swift +++ b/PadelClub/Views/Match/Components/PlayerBlockView.swift @@ -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: ",") ?? [] } diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 91a4b7a..bc6d00c 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -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 diff --git a/PadelClub/Views/Match/MatchRowView.swift b/PadelClub/Views/Match/MatchRowView.swift index e27e585..2012934 100644 --- a/PadelClub/Views/Match/MatchRowView.swift +++ b/PadelClub/Views/Match/MatchRowView.swift @@ -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())) } diff --git a/PadelClub/Views/Match/MatchSummaryView.swift b/PadelClub/Views/Match/MatchSummaryView.swift index 7c0529d..a62488a 100644 --- a/PadelClub/Views/Match/MatchSummaryView.swift +++ b/PadelClub/Views/Match/MatchSummaryView.swift @@ -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 { diff --git a/PadelClub/Views/Round/LoserRoundView.swift b/PadelClub/Views/Round/LoserRoundView.swift index d58dc52..08f8205 100644 --- a/PadelClub/Views/Round/LoserRoundView.swift +++ b/PadelClub/Views/Round/LoserRoundView.swift @@ -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() } } diff --git a/PadelClub/Views/Round/LoserRoundsView.swift b/PadelClub/Views/Round/LoserRoundsView.swift index c7643e2..948d2ea 100644 --- a/PadelClub/Views/Round/LoserRoundsView.swift +++ b/PadelClub/Views/Round/LoserRoundsView.swift @@ -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) } diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index fe3c15b..eaa84ba 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -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 { diff --git a/PadelClub/Views/Round/RoundsView.swift b/PadelClub/Views/Round/RoundsView.swift index 1647906..913893e 100644 --- a/PadelClub/Views/Round/RoundsView.swift +++ b/PadelClub/Views/Round/RoundsView.swift @@ -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) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 9795143..ad5b8ca 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -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) diff --git a/PadelClub/Views/Tournament/Screen/TournamentCallView.swift b/PadelClub/Views/Tournament/Screen/TournamentCallView.swift index 5e5024e..4afa906 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentCallView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentCallView.swift @@ -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" diff --git a/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift b/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift index 5e0aef3..01468f9 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentCashierView.swift @@ -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" } diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index 476353b..08c655b 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -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 { 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 diff --git a/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift b/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift index addd345..9e4c965 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift @@ -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" diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index 6a2574b..9a819bd 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -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" diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index c57c363..e99b7f6 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -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) } } } diff --git a/PadelClub/Views/User/LoginView.swift b/PadelClub/Views/User/LoginView.swift index cbfdf6a..937bd98 100644 --- a/PadelClub/Views/User/LoginView.swift +++ b/PadelClub/Views/User/LoginView.swift @@ -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) }