add a bracket calling view

fix lag when opening planning view with a lot of matches
paca_championship
Raz 1 year ago
parent 1d70070a68
commit 47198e9b88
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 152
      PadelClub/Views/Calling/BracketCallingView.swift
  3. 235
      PadelClub/Views/Planning/PlanningView.swift
  4. 22
      PadelClub/Views/Tournament/Screen/TournamentCallView.swift

@ -87,6 +87,9 @@
FF17CA4D2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; }; FF17CA4D2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; };
FF17CA4E2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; }; FF17CA4E2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; };
FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; }; FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; };
FF17CA532CBE4788003C7323 /* BracketCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA522CBE4788003C7323 /* BracketCallingView.swift */; };
FF17CA542CBE4788003C7323 /* BracketCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA522CBE4788003C7323 /* BracketCallingView.swift */; };
FF17CA552CBE4788003C7323 /* BracketCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA522CBE4788003C7323 /* BracketCallingView.swift */; };
FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; }; FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; };
FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; }; FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; };
FF1CBC1F2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; }; FF1CBC1F2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; };
@ -985,6 +988,7 @@
FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundStepScheduleEditorView.swift; sourceTree = "<group>"; }; FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundStepScheduleEditorView.swift; sourceTree = "<group>"; };
FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiCourtPickerView.swift; sourceTree = "<group>"; }; FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiCourtPickerView.swift; sourceTree = "<group>"; };
FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowUpMatchView.swift; sourceTree = "<group>"; }; FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowUpMatchView.swift; sourceTree = "<group>"; };
FF17CA522CBE4788003C7323 /* BracketCallingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BracketCallingView.swift; sourceTree = "<group>"; };
FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournament.swift; sourceTree = "<group>"; }; FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournament.swift; sourceTree = "<group>"; };
FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Extensions.swift"; sourceTree = "<group>"; }; FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Extensions.swift"; sourceTree = "<group>"; };
FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournamentSearchScope.swift; sourceTree = "<group>"; }; FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournamentSearchScope.swift; sourceTree = "<group>"; };
@ -1772,6 +1776,7 @@
FF9268082BCEDC2C0080F940 /* CallView.swift */, FF9268082BCEDC2C0080F940 /* CallView.swift */,
FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */, FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */,
FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */, FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */,
FF17CA522CBE4788003C7323 /* BracketCallingView.swift */,
FFEF7F4C2BDE68F80033D0F0 /* Components */, FFEF7F4C2BDE68F80033D0F0 /* Components */,
); );
path = Calling; path = Calling;
@ -2426,6 +2431,7 @@
FF5DA18F2BB9268800A33061 /* GroupStagesSettingsView.swift in Sources */, FF5DA18F2BB9268800A33061 /* GroupStagesSettingsView.swift in Sources */,
FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */, FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */,
FF1F4B752BFA00FC000B4573 /* HtmlGenerator.swift in Sources */, FF1F4B752BFA00FC000B4573 /* HtmlGenerator.swift in Sources */,
FF17CA532CBE4788003C7323 /* BracketCallingView.swift in Sources */,
FF8F26382BAD523300650388 /* PadelRule.swift in Sources */, FF8F26382BAD523300650388 /* PadelRule.swift in Sources */,
FF967CF42BAECC0B00A9A3BD /* TeamRegistration.swift in Sources */, FF967CF42BAECC0B00A9A3BD /* TeamRegistration.swift in Sources */,
FFF8ACDB2B923F48008466FA /* Date+Extensions.swift in Sources */, FFF8ACDB2B923F48008466FA /* Date+Extensions.swift in Sources */,
@ -2697,6 +2703,7 @@
FF4CC0022C996C0600151637 /* GroupStagesSettingsView.swift in Sources */, FF4CC0022C996C0600151637 /* GroupStagesSettingsView.swift in Sources */,
FF4CC0032C996C0600151637 /* TournamentFilterView.swift in Sources */, FF4CC0032C996C0600151637 /* TournamentFilterView.swift in Sources */,
FF4CC0042C996C0600151637 /* HtmlGenerator.swift in Sources */, FF4CC0042C996C0600151637 /* HtmlGenerator.swift in Sources */,
FF17CA542CBE4788003C7323 /* BracketCallingView.swift in Sources */,
FF4CC0052C996C0600151637 /* PadelRule.swift in Sources */, FF4CC0052C996C0600151637 /* PadelRule.swift in Sources */,
FF4CC0062C996C0600151637 /* TeamRegistration.swift in Sources */, FF4CC0062C996C0600151637 /* TeamRegistration.swift in Sources */,
FF4CC0072C996C0600151637 /* Date+Extensions.swift in Sources */, FF4CC0072C996C0600151637 /* Date+Extensions.swift in Sources */,
@ -2947,6 +2954,7 @@
FF70FB812C90584900129CC2 /* GroupStagesSettingsView.swift in Sources */, FF70FB812C90584900129CC2 /* GroupStagesSettingsView.swift in Sources */,
FF70FB822C90584900129CC2 /* TournamentFilterView.swift in Sources */, FF70FB822C90584900129CC2 /* TournamentFilterView.swift in Sources */,
FF70FB832C90584900129CC2 /* HtmlGenerator.swift in Sources */, FF70FB832C90584900129CC2 /* HtmlGenerator.swift in Sources */,
FF17CA552CBE4788003C7323 /* BracketCallingView.swift in Sources */,
FF70FB842C90584900129CC2 /* PadelRule.swift in Sources */, FF70FB842C90584900129CC2 /* PadelRule.swift in Sources */,
FF70FB852C90584900129CC2 /* TeamRegistration.swift in Sources */, FF70FB852C90584900129CC2 /* TeamRegistration.swift in Sources */,
FF70FB862C90584900129CC2 /* Date+Extensions.swift in Sources */, FF70FB862C90584900129CC2 /* Date+Extensions.swift in Sources */,

@ -0,0 +1,152 @@
//
// BracketCallingView.swift
// PadelClub
//
// Created by razmig on 15/10/2024.
//
import SwiftUI
struct BracketCallingView: View {
@Environment(Tournament.self) var tournament: Tournament
@State private var displayByMatch: Bool = true
@State private var initialSeedRound: Int = 0
@State private var initialSeedCount: Int = 0
let tournamentRounds: [Round]
let teams: [TeamRegistration]
init(tournament: Tournament) {
let rounds = tournament.rounds()
self.tournamentRounds = rounds
self.teams = tournament.availableSeeds()
let index = rounds.count - 1
_initialSeedRound = .init(wrappedValue: index)
_initialSeedCount = .init(wrappedValue: RoundRule.numberOfMatches(forRoundIndex: index))
}
var initialRound: Round {
tournamentRounds.first(where: { $0.index == initialSeedRound })!
}
func filteredRounds() -> [Round] {
tournamentRounds.filter({ $0.index >= initialSeedRound }).reversed()
}
func seedCount(forRoundIndex roundIndex: Int) -> Int {
if roundIndex < initialSeedRound { return 0 }
if roundIndex == initialSeedRound {
return initialSeedCount
}
let seedCount = RoundRule.numberOfMatches(forRoundIndex: roundIndex)
let previousSeedCount = self.seedCount(forRoundIndex: roundIndex - 1)
let total = seedCount - previousSeedCount
if total < 0 { return 0 }
return total
}
func seeds(forRoundIndex roundIndex: Int) -> [TeamRegistration] {
let previousSeeds: Int = (initialSeedRound..<roundIndex).map { seedCount(forRoundIndex: $0) }.reduce(0, +)
if roundIndex == tournamentRounds.count - 1 {
return Array(teams.dropFirst(previousSeeds))
} else {
return Array(teams.dropFirst(previousSeeds).prefix(seedCount(forRoundIndex: roundIndex)))
}
}
var body: some View {
List {
NavigationLink {
TeamsCallingView(teams: teams.filter({ $0.callDate == nil }))
.environment(tournament)
} label: {
LabeledContent("Équipes non contactées", value: teams.filter({ $0.callDate == nil }).count.formatted())
}
PlayersWithoutContactView(players: teams.flatMap({ $0.unsortedPlayers() }).sorted(by: \.computedRank))
Section {
Picker(selection: $initialSeedRound) {
ForEach(tournamentRounds) {
Text($0.roundTitle()).tag($0.index)
}
} label: {
Text("Premier tour")
}
.onChange(of: initialSeedRound) {
initialSeedCount = RoundRule.numberOfMatches(forRoundIndex: initialSeedRound)
}
LabeledContent {
StepperView(count: $initialSeedCount, minimum: 0, maximum: RoundRule.numberOfMatches(forRoundIndex: initialSeedRound))
} label: {
Text("Têtes de série")
}
}
ForEach(filteredRounds()) { round in
let seeds = seeds(forRoundIndex: round.index)
let callSeeds = seeds.filter({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
if seeds.isEmpty == false {
Section {
NavigationLink {
_roundView(round: round, seeds: seeds)
.environment(tournament)
} label: {
CallView.CallStatusView(count: callSeeds.count, total: seeds.count, startDate: round.playedMatches().first?.startDate)
}
} header: {
Text(round.roundTitle())
} footer: {
if let startDate = round.startDate ?? round.playedMatches().first?.startDate {
CallView(teams: seeds, callDate: startDate, matchFormat: round.matchFormat, roundLabel: round.roundTitle())
}
}
}
}
}
.headerProminence(.increased)
.navigationTitle("Prévision")
}
@ViewBuilder
private func _roundView(round: Round, seeds: [TeamRegistration]) -> some View {
List {
NavigationLink("Équipes non contactées") {
TeamsCallingView(teams: seeds.filter({ $0.callDate == nil }))
}
Section {
ForEach(seeds) { team in
CallView.TeamView(team: team)
}
} header: {
Text(round.roundTitle())
}
}
.overlay {
if seeds.isEmpty {
ContentUnavailableView {
Label("Aucune équipe dans ce tour", systemImage: "clock.badge.questionmark")
} description: {
Text("Padel Club n'a pas réussi à déterminer quelles équipes jouent ce tour.")
} actions: {
// RowButtonView("Horaire intelligent") {
// selectedScheduleDestination = nil
// }
}
}
}
.headerProminence(.increased)
.navigationTitle(round.roundTitle())
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
}
//#Preview {
// SeedsCallingView()
//}

@ -30,11 +30,11 @@ struct PlanningView: View {
Dictionary(grouping: matches) { $0.startDate ?? .distantFuture } Dictionary(grouping: matches) { $0.startDate ?? .distantFuture }
} }
var days: [Date] { func days(timeSlots: [Date:[Match]]) -> [Date] {
Set(timeSlots.keys.map { $0.startOfDay }).sorted() Set(timeSlots.keys.map { $0.startOfDay }).sorted()
} }
var keys: [Date] { func keys(timeSlots: [Date:[Match]]) -> [Date] {
timeSlots.keys.sorted() timeSlots.keys.sorted()
} }
@ -54,7 +54,7 @@ struct PlanningView: View {
} }
} }
private func _computedTitle() -> String { private func _computedTitle(days: [Date]) -> String {
if let selectedDay { if let selectedDay {
return selectedDay.formatted(.dateTime.day().weekday().month()) return selectedDay.formatted(.dateTime.day().weekday().month())
} else { } else {
@ -67,143 +67,160 @@ struct PlanningView: View {
} }
var body: some View { var body: some View {
List { let timeSlots = self.timeSlots
_bySlotView() let keys = self.keys(timeSlots: timeSlots)
} let days = self.days(timeSlots: timeSlots)
.navigationTitle(Text(_computedTitle())) let matches = matches
.toolbar(content: { BySlotView(days: days, keys: keys, timeSlots: timeSlots, matches: matches, selectedDay: selectedDay, filterOption: filterOption, showFinishedMatches: showFinishedMatches)
if days.count > 1 { .navigationTitle(Text(_computedTitle(days: days)))
ToolbarTitleMenu { .toolbar(content: {
Picker(selection: $selectedDay) { if days.count > 1 {
Text("Tous les jours").tag(nil as Date?) ToolbarTitleMenu {
ForEach(days, id: \.self) { day in Picker(selection: $selectedDay) {
Text(day.formatted(.dateTime.day().weekday().month())).tag(day as Date?) Text("Tous les jours").tag(nil as Date?)
ForEach(days, id: \.self) { day in
Text(day.formatted(.dateTime.day().weekday().month())).tag(day as Date?)
}
} label: {
Text("Jour")
} }
} label: { .pickerStyle(.automatic)
Text("Jour")
} }
.pickerStyle(.automatic)
} }
}
ToolbarItemGroup(placement: .topBarTrailing) { ToolbarItemGroup(placement: .topBarTrailing) {
Menu { Menu {
Picker(selection: $showFinishedMatches) { Picker(selection: $showFinishedMatches) {
Text("Afficher tous les matchs").tag(true) Text("Afficher tous les matchs").tag(true)
Text("Masquer les matchs terminés").tag(false) Text("Masquer les matchs terminés").tag(false)
} label: {
Text("Option de filtrage")
}
.labelsHidden()
.pickerStyle(.inline)
} label: { } label: {
Text("Option de filtrage") Label("Filtrer", systemImage: "clock.badge.checkmark")
.symbolVariant(showFinishedMatches ? .fill : .none)
} }
.labelsHidden() Menu {
.pickerStyle(.inline) Picker(selection: $filterOption) {
} label: { ForEach(PlanningFilterOption.allCases) {
Label("Filtrer", systemImage: "clock.badge.checkmark") Text($0.localizedPlanningLabel()).tag($0)
.symbolVariant(showFinishedMatches ? .fill : .none) }
} } label: {
Menu { Text("Option de triage")
Picker(selection: $filterOption) {
ForEach(PlanningFilterOption.allCases) {
Text($0.localizedPlanningLabel()).tag($0)
} }
.labelsHidden()
.pickerStyle(.inline)
} label: { } label: {
Text("Option de triage") Label("Trier", systemImage: "line.3.horizontal.decrease.circle")
.symbolVariant(filterOption == .byCourt ? .fill : .none)
} }
.labelsHidden()
.pickerStyle(.inline)
} label: {
Label("Trier", systemImage: "line.3.horizontal.decrease.circle")
.symbolVariant(filterOption == .byCourt ? .fill : .none)
}
} }
}) })
.overlay { .overlay {
if matches.allSatisfy({ $0.startDate == nil }) { if matches.allSatisfy({ $0.startDate == nil }) {
ContentUnavailableView { ContentUnavailableView {
Label("Aucun horaire défini", systemImage: "clock.badge.questionmark") Label("Aucun horaire défini", systemImage: "clock.badge.questionmark")
} description: { } description: {
Text("Vous n'avez pas encore défini d'horaire pour les différentes phases du tournoi") Text("Vous n'avez pas encore défini d'horaire pour les différentes phases du tournoi")
} actions: { } actions: {
RowButtonView("Horaire intelligent") { RowButtonView("Horaire intelligent") {
selectedScheduleDestination = nil selectedScheduleDestination = nil
}
} }
} }
} }
}
} }
@ViewBuilder struct BySlotView: View {
func _bySlotView() -> some View { @Environment(Tournament.self) var tournament: Tournament
if matches.allSatisfy({ $0.startDate == nil }) == false { let days: [Date]
ForEach(days.filter({ selectedDay == nil || selectedDay == $0 }), id: \.self) { day in let keys: [Date]
Section { let timeSlots: [Date:[Match]]
ForEach(keys.filter({ $0.dayInt == day.dayInt }), id: \.self) { key in let matches: [Match]
if let _matches = timeSlots[key] { let selectedDay: Date?
DisclosureGroup { let filterOption: PlanningFilterOption
ForEach(_matches.sorted(by: filterOption == .byDefault ? \.computedOrder : \.courtIndexForSorting)) { match in let showFinishedMatches: Bool
NavigationLink {
MatchDetailView(match: match, matchViewStyle: .sectionedStandardStyle) var body: some View {
} label: { List {
LabeledContent { if matches.allSatisfy({ $0.startDate == nil }) == false {
if let courtName = match.courtName() { ForEach(days.filter({ selectedDay == nil || selectedDay == $0 }), id: \.self) { day in
Text(courtName) Section {
} ForEach(keys.filter({ $0.dayInt == day.dayInt }), id: \.self) { key in
} label: { if let _matches = timeSlots[key]?.sorted(by: filterOption == .byDefault ? \.computedOrder : \.courtIndexForSorting) {
if let groupStage = match.groupStageObject { DisclosureGroup {
Text(groupStage.groupStageTitle(.title)) ForEach(_matches) { match in
} else if let round = match.roundObject { NavigationLink {
Text(round.roundTitle()) MatchDetailView(match: match, matchViewStyle: .sectionedStandardStyle)
} label: {
LabeledContent {
if let courtName = match.courtName() {
Text(courtName)
}
} label: {
if let groupStage = match.groupStageObject {
Text(groupStage.groupStageTitle(.title))
} else if let round = match.roundObject {
Text(round.roundTitle())
}
Text(match.matchTitle())
}
} }
Text(match.matchTitle())
} }
} label: {
_timeSlotView(key: key, matches: _matches)
} }
} }
} label: { }
_timeSlotView(key: key, matches: _matches) } header: {
HStack {
Text(day.formatted(.dateTime.day().weekday().month()))
Spacer()
let count = _matchesCount(inDayInt: day.dayInt, timeSlots: timeSlots)
if showFinishedMatches {
Text(self._formattedMatchCount(count))
} else {
Text(self._formattedMatchCount(count) + " restant\(count.pluralSuffix)")
}
} }
} }
} .headerProminence(.increased)
} header: {
HStack {
Text(day.formatted(.dateTime.day().weekday().month()))
Spacer()
let count = _matchesCount(inDayInt: day.dayInt)
if showFinishedMatches {
Text(self._formattedMatchCount(count))
} else {
Text(self._formattedMatchCount(count) + " restant\(count.pluralSuffix)")
}
} }
} }
.headerProminence(.increased)
} }
} }
}
private func _matchesCount(inDayInt dayInt: Int) -> Int { private func _matchesCount(inDayInt dayInt: Int, timeSlots: [Date:[Match]]) -> Int {
timeSlots.filter { $0.key.dayInt == dayInt }.flatMap({ $0.value }).count timeSlots.filter { $0.key.dayInt == dayInt }.flatMap({ $0.value }).count
} }
private func _timeSlotView(key: Date, matches: [Match]) -> some View { private func _timeSlotView(key: Date, matches: [Match]) -> some View {
LabeledContent { LabeledContent {
Text(self._formattedMatchCount(matches.count)) Text(self._formattedMatchCount(matches.count))
} label: { } label: {
Text(key.formatted(date: .omitted, time: .shortened)).font(.title).fontWeight(.semibold) Text(key.formatted(date: .omitted, time: .shortened)).font(.title).fontWeight(.semibold)
let names = matches.sorted(by: \.computedOrder) if matches.count <= tournament.courtCount {
.compactMap({ $0.roundTitle() }) let names = matches.sorted(by: \.computedOrder)
.reduce(into: [String]()) { uniqueNames, name in .compactMap({ $0.roundTitle() })
if !uniqueNames.contains(name) { .reduce(into: [String]()) { uniqueNames, name in
uniqueNames.append(name) if !uniqueNames.contains(name) {
} uniqueNames.append(name)
}
}
Text(names.joined(separator: ", "))
} else {
Text(matches.count.formatted().appending(" matchs"))
} }
Text(names.joined(separator: ", ")) }
} }
}
fileprivate func _formattedMatchCount(_ count: Int) -> String { fileprivate func _formattedMatchCount(_ count: Int) -> String {
return "\(count.formatted()) match\(count.pluralSuffix)" return "\(count.formatted()) match\(count.pluralSuffix)"
}
} }
} }
//#Preview { //#Preview {

@ -16,6 +16,7 @@ enum CallDestination: Identifiable, Selectable, Equatable {
case teams(Tournament) case teams(Tournament)
case seeds(Tournament) case seeds(Tournament)
case groupStages(Tournament) case groupStages(Tournament)
case brackets(Tournament)
var id: String { var id: String {
switch self { switch self {
@ -25,6 +26,8 @@ enum CallDestination: Identifiable, Selectable, Equatable {
return "seed" return "seed"
case .groupStages: case .groupStages:
return "groupStage" return "groupStage"
case .brackets:
return "bracket"
} }
} }
@ -36,6 +39,8 @@ enum CallDestination: Identifiable, Selectable, Equatable {
return "Têtes de série" return "Têtes de série"
case .groupStages: case .groupStages:
return "Poules" return "Poules"
case .brackets:
return "Tableau"
} }
} }
@ -47,6 +52,9 @@ enum CallDestination: Identifiable, Selectable, Equatable {
case .seeds(let tournament): case .seeds(let tournament):
let allSeedCalled = tournament.seeds().filter({ tournament.isStartDateIsDifferentThanCallDate($0) || $0.callDate == nil }) let allSeedCalled = tournament.seeds().filter({ tournament.isStartDateIsDifferentThanCallDate($0) || $0.callDate == nil })
return allSeedCalled.count return allSeedCalled.count
case .brackets(let tournament):
let availableSeeds = tournament.availableSeeds().filter({ tournament.isStartDateIsDifferentThanCallDate($0) || $0.callDate == nil })
return availableSeeds.count
case .groupStages(let tournament): case .groupStages(let tournament):
let allSeedCalled = tournament.groupStageTeams().filter({ tournament.isStartDateIsDifferentThanCallDate($0) || $0.callDate == nil }) let allSeedCalled = tournament.groupStageTeams().filter({ tournament.isStartDateIsDifferentThanCallDate($0) || $0.callDate == nil })
return allSeedCalled.count return allSeedCalled.count
@ -65,6 +73,9 @@ enum CallDestination: Identifiable, Selectable, Equatable {
case .seeds(let tournament): case .seeds(let tournament):
let allSeedCalled = tournament.seeds().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false }) let allSeedCalled = tournament.seeds().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
return allSeedCalled ? .checkmark : nil return allSeedCalled ? .checkmark : nil
case .brackets(let tournament):
let availableSeeds = tournament.availableSeeds().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
return availableSeeds ? .checkmark : nil
case .groupStages(let tournament): case .groupStages(let tournament):
let allSeedCalled = tournament.groupStageTeams().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false }) let allSeedCalled = tournament.groupStageTeams().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
return allSeedCalled ? .checkmark : nil return allSeedCalled ? .checkmark : nil
@ -83,16 +94,23 @@ struct TournamentCallView: View {
self.tournament = tournament self.tournament = tournament
var destinations = [CallDestination]() var destinations = [CallDestination]()
let groupStageTeams = tournament.groupStageTeams() let groupStageTeams = tournament.groupStageTeams()
let seededTeams = tournament.seededTeams()
if groupStageTeams.isEmpty == false { if groupStageTeams.isEmpty == false {
destinations.append(.groupStages(tournament)) destinations.append(.groupStages(tournament))
self._selectedDestination = State(wrappedValue: .groupStages(tournament)) self._selectedDestination = State(wrappedValue: .groupStages(tournament))
} }
if tournament.seededTeams().isEmpty == false { if seededTeams.isEmpty == false {
destinations.append(.seeds(tournament)) destinations.append(.seeds(tournament))
if groupStageTeams.isEmpty { if groupStageTeams.isEmpty {
self._selectedDestination = State(wrappedValue: .seeds(tournament)) self._selectedDestination = State(wrappedValue: .seeds(tournament))
} }
} }
if tournament.availableSeeds().isEmpty == false {
destinations.append(.brackets(tournament))
if seededTeams.isEmpty {
self._selectedDestination = State(wrappedValue: .brackets(tournament))
}
}
destinations.append(.teams(tournament)) destinations.append(.teams(tournament))
self.allDestinations = destinations self.allDestinations = destinations
} }
@ -109,6 +127,8 @@ struct TournamentCallView: View {
TeamsCallingView(teams: tournament.selectedSortedTeams()) TeamsCallingView(teams: tournament.selectedSortedTeams())
case .groupStages: case .groupStages:
GroupStageCallingView() GroupStageCallingView()
case .brackets:
BracketCallingView(tournament: tournament)
case .seeds: case .seeds:
SeedsCallingView() SeedsCallingView()
} }

Loading…
Cancel
Save