multistore
Razmig Sarkissian 2 years ago
parent df56b384e0
commit 38d05af6df
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 2
      PadelClub/Data/DataStore.swift
  3. 98
      PadelClub/Data/Match.swift
  4. 74
      PadelClub/Data/Round.swift
  5. 19
      PadelClub/Data/Tournament.swift
  6. 20
      PadelClub/ViewModel/SeedInterval.swift
  7. 3
      PadelClub/Views/Match/Components/PlayerBlockView.swift
  8. 33
      PadelClub/Views/Match/MatchRowView.swift
  9. 53
      PadelClub/Views/Round/LoserBracketView.swift
  10. 72
      PadelClub/Views/Round/LoserRoundView.swift
  11. 132
      PadelClub/Views/Round/LoserRoundsView.swift
  12. 10
      PadelClub/Views/Round/RoundSettingsView.swift
  13. 8
      PadelClub/Views/Round/RoundView.swift

@ -212,7 +212,7 @@
FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */; };
FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */; };
FFC1E10C2BAC7FB0008D6F59 /* ClubImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */; };
FFC2DCB22BBE75D40046DB9F /* LoserBracketView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserBracketView.swift */; };
FFC2DCB22BBE75D40046DB9F /* LoserRoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */; };
FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; };
FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; };
FFC83D512BB8087E00750834 /* RoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D502BB8087E00750834 /* RoundView.swift */; };
@ -501,7 +501,7 @@
FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = "<group>"; };
FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkFederalService.swift; sourceTree = "<group>"; };
FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubImportView.swift; sourceTree = "<group>"; };
FFC2DCB12BBE75D40046DB9F /* LoserBracketView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserBracketView.swift; sourceTree = "<group>"; };
FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundView.swift; sourceTree = "<group>"; };
FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundsView.swift; sourceTree = "<group>"; };
FFC83D4E2BB807D100750834 /* RoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundsView.swift; sourceTree = "<group>"; };
FFC83D502BB8087E00750834 /* RoundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundView.swift; sourceTree = "<group>"; };
@ -1086,7 +1086,7 @@
FFC83D4E2BB807D100750834 /* RoundsView.swift */,
FFC83D502BB8087E00750834 /* RoundView.swift */,
FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */,
FFC2DCB12BBE75D40046DB9F /* LoserBracketView.swift */,
FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */,
FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */,
);
path = Round;
@ -1489,7 +1489,7 @@
FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */,
FFF8ACD92B923F3C008466FA /* String+Extensions.swift in Sources */,
FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */,
FFC2DCB22BBE75D40046DB9F /* LoserBracketView.swift in Sources */,
FFC2DCB22BBE75D40046DB9F /* LoserRoundView.swift in Sources */,
FF9267FC2BCE84870080F940 /* PlayerPayView.swift in Sources */,
FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */,
FF6EC8FB2B94788600EA7F5A /* TournamentButtonView.swift in Sources */,

@ -65,7 +65,7 @@ class DataStore: ObservableObject {
// store.addMigration(Migration<TournamentV1, TournamentV2>(version: 2))
// store.addMigration(Migration<TournamentV2, Tournament>(version: 3))
let indexed : Bool = false
let indexed : Bool = true
self.clubs = store.registerCollection(synchronized: false, indexed: indexed)
self.tournaments = store.registerCollection(synchronized: false, indexed: indexed)
self.events = store.registerCollection(synchronized: false, indexed: indexed)

@ -11,6 +11,7 @@ import LeStorage
@Observable
class Match: ModelObject, Storable {
static func resourceName() -> String { "matches" }
var byeState: Bool = false
var id: String = Store.randomId()
var round: String?
@ -160,27 +161,90 @@ class Match: ModelObject, Storable {
_toggleMatchDisableState(false)
}
private func _loserMatch() -> Match? {
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index)
return roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2)
}
private func _toggleLoserMatchDisableState(_ state: Bool) {
if isLoserBracket == false {
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: index)
if let loserMatch = roundObject?.loserRounds().first?.getMatch(atMatchIndexInRound: indexInRound / 2) {
loserMatch.disabled = state
try? DataStore.shared.matches.addOrUpdate(instance: loserMatch)
loserMatch._toggleLoserMatchDisableState(state)
}
} else {
roundObject?.loserRounds().forEach({ round in
round.handleLoserRoundState()
})
guard let loserMatch = _loserMatch() else { return }
loserMatch.byeState = state
loserMatch._toggleMatchDisableState(state, forward: true)
guard let otherMatch = _otherMatch() else { return }
if otherMatch.disabled == state {
loserMatch.byeState = !state
}
}
fileprivate func _toggleMatchDisableState(_ state: Bool) {
fileprivate func _otherMatch() -> Match? {
guard let round else { return nil }
guard index > 0 else { return nil }
let nextIndex = (index - 1) / 2
let topMatchIndex = (nextIndex * 2) + 1
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
}
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
}
func _toggleForwardMatchDisableState(_ state: Bool) {
guard let roundObject else { return }
guard roundObject.loser != nil else { return }
guard let forwardMatch = _forwardMatch(inRound: roundObject) else { return }
guard let next = _otherMatch() else { return }
if next.disabled && byeState == false && next.byeState == false {
forwardMatch.byeState = false
forwardMatch._toggleMatchDisableState(state, forward: true)
}
if byeState && next.byeState {
print("don't disable forward match")
forwardMatch.byeState = false
forwardMatch._toggleMatchDisableState(false, forward: true)
} else {
forwardMatch.byeState = true
forwardMatch._toggleMatchDisableState(state, forward: true)
}
// if next.disabled == false {
// forwardMatch.byeState = state
// }
//
// if next.disabled == state {
// if next.byeState != byeState {
// //forwardMatch.byeState = state
// forwardMatch._toggleMatchDisableState(state)
// } else {
// forwardMatch._toggleByeState(state)
// }
// } else {
// }
// forwardMatch._toggleByeState(state)
}
func _toggleMatchDisableState(_ state: Bool, forward: Bool = false) {
if disabled == state { return }
disabled = state
_toggleLoserMatchDisableState(state)
topPreviousRoundMatch()?._toggleMatchDisableState(state)
bottomPreviousRoundMatch()?._toggleMatchDisableState(state)
//byeState = false
try? DataStore.shared.matches.addOrUpdate(instance: self)
_toggleLoserMatchDisableState(state)
if forward {
_toggleForwardMatchDisableState(state)
} else {
topPreviousRoundMatch()?._toggleMatchDisableState(state)
bottomPreviousRoundMatch()?._toggleMatchDisableState(state)
}
}
func next() -> Match? {
@ -212,14 +276,14 @@ class Match: ModelObject, Storable {
func topPreviousRoundMatch() -> Match? {
guard let roundObject else { return nil }
return Store.main.filter { match in
match.index == topPreviousRoundMatchIndex() && match.round == roundObject.previousRound()?.id
match.index == topPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id
}.sorted(by: \.index).first
}
func bottomPreviousRoundMatch() -> Match? {
guard let roundObject else { return nil }
return Store.main.filter { match in
match.index == bottomPreviousRoundMatchIndex() && match.round == roundObject.previousRound()?.id
match.index == bottomPreviousRoundMatchIndex() && match.round != nil && match.round == roundObject.previousRound()?.id
}.sorted(by: \.index).first
}

@ -209,7 +209,7 @@ class Round: ModelObject, Storable {
}
func loserRounds(forRoundIndex roundIndex: Int) -> [Round] {
return loserRoundsAndChildren().filter({ $0.index == roundIndex }).sorted(by: \.cumulativeMatchCount)
return loserRoundsAndChildren().filter({ $0.index == roundIndex }).sorted(by: \.theoryCumulativeMatchCount)
}
func isDisabled() -> Bool {
@ -268,39 +268,6 @@ class Round: ModelObject, Storable {
try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches)
}
func handleLoserRoundState() {
let _matches = _matches()
_matches.forEach { match in
let previousRound = self.previousRound()
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: match.index)
var parentMatches = [Match]()
if isLoserBracket(), previousRound == nil, let parentRound = parentRound {
let upperBracketTopMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2)
let upperBracketBottomMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1)
parentMatches = [upperBracketTopMatch, upperBracketBottomMatch].compactMap({ $0 })
} else if let previousRound {
let previousRoundTopMatch : Match? = Store.main.filter {
$0.round == previousRound.id && $0.index == match.topPreviousRoundMatchIndex()
}.first
let previousRoundBottomMatch : Match? = Store.main.filter {
$0.round == previousRound.id && $0.index == match.bottomPreviousRoundMatchIndex()
}.first
parentMatches = [previousRoundTopMatch, previousRoundBottomMatch].compactMap({ $0 })
}
if parentMatches.anySatisfy({ $0.disabled }) {
match.disabled = true
} else if parentMatches.allSatisfy({ $0.disabled == false }) {
match.disabled = false
}
}
try? DataStore.shared.matches.addOrUpdate(contentOfs: _matches)
loserRounds().forEach { round in
round.handleLoserRoundState()
}
}
var cumulativeMatchCount: Int {
var totalMatches = playedMatches().count
if let parent = parentRound {
@ -317,12 +284,45 @@ class Round: ModelObject, Storable {
}
}
func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
func disabledMatches() -> [Match] {
_matches().filter({ $0.disabled })
}
var theoryCumulativeMatchCount: Int {
var totalMatches = RoundRule.numberOfMatches(forRoundIndex: index)
if let parent = parentRound {
totalMatches += parent.theoryCumulativeMatchCount
}
return totalMatches
}
func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
if let parentRound, let initialRound = parentRound.initialRound() {
let parentMatchCount = parentRound.cumulativeMatchCount - initialRound.playedMatches().count
// print("initialRound", initialRound.roundTitle())
if let initialRoundNextRound = initialRound.nextRound()?.playedMatches() {
return SeedInterval(first: parentMatchCount + initialRoundNextRound.count * 2 + 1, last: parentMatchCount + initialRoundNextRound.count * 2 + (previousRound() ?? parentRound).playedMatches().count).localizedLabel(displayStyle)
if let initialRoundNextRound = initialRound.nextRound() {
let total = initialRoundNextRound.playedMatches().count * 2 + initialRoundNextRound.disabledMatches().count
let previousRoundMatchCount = (previousRound() ?? parentRound).playedMatches().count
let seedInterval = SeedInterval(first: parentMatchCount + total + 1, last: parentMatchCount + total + previousRoundMatchCount).localizedLabel(displayStyle)
// print("seedInterval", seedInterval, parentMatchCount, total, previousRoundMatchCount)
return seedInterval
}
}
return RoundRule.roundName(fromRoundIndex: index)
}
func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
if let parentRound, let initialRound = parentRound.initialRound() {
let parentMatchCount = parentRound.theoryCumulativeMatchCount - RoundRule.numberOfMatches(forRoundIndex: initialRound.index)
// print("initialRound", initialRound.roundTitle())
if let initialRoundNextRound = initialRound.nextRound() {
let total = initialRoundNextRound.playedMatches().count * 2 + initialRoundNextRound.disabledMatches().count
let previousRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: (previousRound() ?? parentRound).index)
let seedInterval = SeedInterval(first: parentMatchCount + total + 1, last: parentMatchCount + total + previousRoundMatchCount).localizedLabel(displayStyle)
// print("seedInterval", seedInterval, parentMatchCount, total, previousRoundMatchCount)
return seedInterval
}
}
return RoundRule.roundName(fromRoundIndex: index)

@ -259,9 +259,9 @@ class Tournament : ModelObject, Storable {
let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex)
let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex)
if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.dimension == availableSeeds.count {
if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.count == availableSeeds.count {
return availableSeedGroup
} else if (availableSeeds.count == availableSeedOpponentSpot.count && availableSeeds.count == self.availableSeeds().count) && availableSeedGroup.dimension == availableSeedOpponentSpot.count {
} else if (availableSeeds.count == availableSeedOpponentSpot.count && availableSeeds.count == self.availableSeeds().count) && availableSeedGroup.count == availableSeedOpponentSpot.count {
return availableSeedGroup
} else if let chunks = availableSeedGroup.chunks() {
if let chunk = chunks.first(where: { seedInterval in
@ -303,8 +303,12 @@ class Tournament : ModelObject, Storable {
for (index, seed) in availableSeeds.enumerated() {
seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: true)
}
} else if let chunk = seedGroup.chunk() {
setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk)
} else if let chunks = seedGroup.chunks() {
if let chunk = chunks.first(where: { seedInterval in
seedInterval.first >= self.seededTeams().count
}) {
setSeeds(inRoundIndex: roundIndex, inSeedGroup: chunk)
}
}
}
}
@ -343,7 +347,12 @@ class Tournament : ModelObject, Storable {
let matches: [Match] = unsortedGroupStages.flatMap { $0._matches() } + allRounds().flatMap { $0._matches() }
return matches.filter({ $0.disabled == false })
}
func _allMatchesIncludingDisabled() -> [Match] {
let unsortedGroupStages : [GroupStage] = Store.main.filter { $0.tournament == self.id }
return unsortedGroupStages.flatMap { $0._matches() } + allRounds().flatMap { $0._matches() }
}
func allRounds() -> [Round] {
Store.main.filter { $0.tournament == self.id }
}

@ -15,7 +15,11 @@ struct SeedInterval: Hashable, Comparable {
return lhs.first < rhs.first
}
var dimension: Int {
var count: Int {
dimension + 1
}
private var dimension: Int {
(last - first)
}
@ -29,21 +33,9 @@ struct SeedInterval: Hashable, Comparable {
return nil
}
}
func chunk() -> SeedInterval? {
if dimension / 2 > 0 {
let halfDimension = last - dimension / 2
if halfDimension > first {
return SeedInterval(first: first, last: halfDimension - 1)
}
}
return nil
}
}
extension SeedInterval {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if last - first < 2 {
if dimension < 2 {
return "#\(first) / #\(last)"
} else {
return "#\(first) à #\(last)"

@ -43,9 +43,6 @@ struct PlayerBlockView: View {
}
private func _defaultLabel() -> String {
if match.upperBracketMatch(teamPosition)?.disabled == true {
return "Bye"
}
return teamPosition.localizedLabel()
}

@ -17,6 +17,39 @@ struct MatchRowView: View {
if isEditingTournamentSeed.wrappedValue == true && match.isGroupStage() == false && match.isLoserBracket == false {
MatchSetupView(match: match)
} else {
// MatchSummaryView(match: match, matchViewStyle: matchViewStyle)
// .overlay {
// if match.disabled {
// Image(systemName: "xmark")
// .resizable()
// .scaledToFit()
// .opacity(0.8)
// }
// }
// .contextMenu(menuItems: {
// Text("index: \(match.index)")
// Text("bye state : \(match.byeState)")
// Text("disable state : \(match.disabled)")
// Button("enable") {
// match._toggleMatchDisableState(false)
// }
// Button("disable") {
// match._toggleMatchDisableState(true)
// }
// Button("bye") {
// match.byeState = true
// }
// Button("not bye") {
// match.byeState = false
// }
// Button("solo toggle") {
// match.disabled.toggle()
// }
// Button("toggle fwrd match") {
// match._toggleForwardMatchDisableState(true)
// }
// })
//
NavigationLink {
MatchDetailView(match: match, matchViewStyle: matchViewStyle)
} label: {

@ -1,53 +0,0 @@
//
// LoserBracketView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 04/04/2024.
//
import SwiftUI
struct LoserBracketView: View {
@EnvironmentObject var dataStore: DataStore
let loserRounds: [Round]
@ViewBuilder
var body: some View {
if let first = loserRounds.first {
List {
ForEach(loserRounds) { loserRound in
_loserRoundView(loserRound)
let childLoserRounds = loserRound.loserRounds()
if childLoserRounds.isEmpty == false {
let uniqueChildRound = childLoserRounds.first
if childLoserRounds.count == 1, let uniqueChildRound {
_loserRoundView(uniqueChildRound)
} else if let uniqueChildRound {
NavigationLink {
LoserBracketView(loserRounds: childLoserRounds)
} label: {
Text(uniqueChildRound.roundTitle())
}
}
}
}
}
.navigationTitle(first.roundTitle())
}
}
private func _loserRoundView(_ loserRound: Round) -> some View {
Section {
ForEach(loserRound.playedMatches()) { match in
MatchRowView(match: match, matchViewStyle: .standardStyle)
}
} header: {
Text(loserRound.roundTitle())
}
}
}
#Preview {
LoserBracketView(loserRounds: [Round.mock()])
.environmentObject(DataStore.shared)
}

@ -0,0 +1,72 @@
//
// LoserRoundView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 04/04/2024.
//
import SwiftUI
struct LoserRoundView: View {
@EnvironmentObject var dataStore: DataStore
let loserRounds: [Round]
@State private var isEditingTournamentSeed: Bool = false
private func _roundDisabled() -> Bool {
loserRounds.allSatisfy({ $0.isDisabled() })
}
var body: some View {
List {
if isEditingTournamentSeed == true {
_editingView()
}
ForEach(loserRounds) { loserRound in
if isEditingTournamentSeed || loserRound.isDisabled() == false {
Section {
let matches = isEditingTournamentSeed ? loserRound.playedMatches() : loserRound.playedMatches().filter({ $0.disabled == false })
ForEach(matches) { match in
MatchRowView(match: match, matchViewStyle: .standardStyle)
.overlay {
if match.disabled && isEditingTournamentSeed {
Image(systemName: "xmark")
.resizable()
.scaledToFit()
.opacity(0.8)
}
}
.disabled(match.disabled)
}
} header: {
Text(loserRound.roundTitle(.wide))
}
}
}
}
.headerProminence(.increased)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(isEditingTournamentSeed == true ? "Valider" : "Modifier") {
isEditingTournamentSeed.toggle()
}
}
}
}
private func _editingView() -> some View {
if _roundDisabled() {
RowButtonView("Jouer ce tour", role: .destructive) {
loserRounds.forEach { round in
round.enableRound()
}
}
} else {
RowButtonView("Ne pas jouer ce tour", role: .destructive) {
loserRounds.forEach { round in
round.disableRound()
}
}
}
}
}

@ -7,20 +7,79 @@
import SwiftUI
enum LoserRound: Identifiable, Selectable {
case round(turnIndex: Int, rounds: [Round])
var id: Int {
switch self {
case .round(let turnIndex, _):
return turnIndex
}
}
}
extension LoserRound {
func selectionLabel() -> String {
switch self {
case .round(let turnIndex, _):
return "Tour #\(turnIndex + 1)"
}
}
func badgeValue() -> Int? {
switch self {
case .round(_, let rounds):
return rounds.flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count
}
}
func badgeImage() -> Badge? {
switch self {
case .round(_, let rounds):
return rounds.allSatisfy({ $0.hasEnded() }) ? .checkmark : nil
}
}
}
struct LoserRoundsView: View {
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
var upperBracketRound: Round
@State private var selectedRound: Round?
@State private var selectedRound: LoserRound?
let loserRounds: [Round]
init(upperBracketRound: Round) {
self.upperBracketRound = upperBracketRound
self.loserRounds = upperBracketRound.loserRounds()
_selectedRound = State(wrappedValue: upperBracketRound.getActiveLoserRound())
// _selectedRound = State(wrappedValue: upperBracketRound.getActiveLoserRound())
}
func allDestinations() -> [LoserRound] {
// return loserRounds
if isEditingTournamentSeed.wrappedValue == false {
let loserRounds = loserRounds.filter { loserRound in
upperBracketRound.loserRounds(forRoundIndex: loserRound.index).anySatisfy({ $0.isDisabled() == false })
}
var rounds = [LoserRound]()
for (index, round) in loserRounds.enumerated() {
rounds.append(LoserRound.round(turnIndex: index, rounds: upperBracketRound.loserRounds(forRoundIndex: round.index)))
}
return rounds
} else {
var rounds = [LoserRound]()
for (index, round) in loserRounds.enumerated() {
rounds.append(LoserRound.round(turnIndex: index, rounds: upperBracketRound.loserRounds(forRoundIndex: round.index)))
}
return rounds
}
}
var body: some View {
VStack(spacing: 0) {
GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: loserRounds, nilDestinationIsValid: true)
GenericDestinationPickerView(selectedDestination: $selectedRound, destinations: allDestinations(), nilDestinationIsValid: true)
switch selectedRound {
case .none:
List {
@ -29,72 +88,13 @@ struct LoserRoundsView: View {
}
}
case .some(let selectedRound):
LoserRoundView(loserRounds: upperBracketRound.loserRounds(forRoundIndex: selectedRound.index))
switch selectedRound {
case .round(_, let rounds):
LoserRoundView(loserRounds: rounds)
}
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
}
struct LoserRoundView: View {
@EnvironmentObject var dataStore: DataStore
let loserRounds: [Round]
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
private func _roundDisabled() -> Bool {
loserRounds.allSatisfy({ $0.isDisabled() })
}
var body: some View {
List {
if isEditingTournamentSeed.wrappedValue == true {
_editingView()
}
ForEach(loserRounds) { loserRound in
Section {
ForEach(loserRound.playedMatches()) { match in
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
.overlay {
if match.disabled {
Image(systemName: "xmark")
.resizable()
.scaledToFit()
.opacity(0.8)
}
}
.disabled(match.disabled)
}
} header: {
Text(loserRound.roundTitle(.wide))
}
}
}
.headerProminence(.increased)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") {
isEditingTournamentSeed.wrappedValue.toggle()
}
}
}
}
private func _editingView() -> some View {
if _roundDisabled() {
RowButtonView("Jouer ce tour", role: .destructive) {
loserRounds.forEach { round in
round.enableRound()
round.handleLoserRoundState()
}
}
} else {
RowButtonView("Ne pas jouer ce tour", role: .destructive) {
loserRounds.forEach { round in
round.disableRound()
}
}
}
}
}

@ -14,6 +14,16 @@ struct RoundSettingsView: View {
var body: some View {
List {
Section {
RowButtonView("Enabled", role: .destructive) {
let allMatches = tournament._allMatchesIncludingDisabled()
allMatches.forEach({
$0.disabled = false
$0.byeState = false
})
try? dataStore.matches.addOrUpdate(contentOfs: allMatches)
}
}
Section {
RowButtonView("Retirer toutes les têtes de séries", role: .destructive) {
tournament.unsortedTeams().forEach({ $0.bracketPosition = nil })

@ -19,14 +19,16 @@ struct RoundView: View {
if isEditingTournamentSeed.wrappedValue == false {
let loserRounds = round.loserRounds()
if loserRounds.isEmpty == false, let first = loserRounds.first(where: { $0.isDisabled() == false }) {
//(where: { $0.isDisabled() == false || isEditingTournamentSeed.wrappedValue })
if loserRounds.isEmpty == false, let first = loserRounds.first {
let correspondingLoserRoundTitle = first.correspondingLoserRoundTitle()
Section {
NavigationLink {
LoserRoundsView(upperBracketRound: round)
.environment(tournament)
.navigationTitle(first.roundTitle())
.navigationTitle(correspondingLoserRoundTitle)
} label: {
Text(first.roundTitle())
Text(correspondingLoserRoundTitle)
}
}
}

Loading…
Cancel
Save