sync
Laurent 8 months ago
commit 8bf560e566
  1. 16
      PadelClub.xcodeproj/project.pbxproj
  2. 4
      PadelClub/Data/Match.swift
  3. 57
      PadelClub/Data/Tournament.swift
  4. 2
      PadelClub/PadelClubApp.swift
  5. 7
      PadelClub/Utils/URLs.swift
  6. 29
      PadelClub/ViewModel/MatchSpot.swift
  7. 27
      PadelClub/Views/Match/MatchSetupView.swift
  8. 81
      PadelClub/Views/Navigation/Agenda/EventListView.swift
  9. 7
      PadelClub/Views/Navigation/Toolbox/ToolboxView.swift
  10. 96
      PadelClub/Views/Round/RoundView.swift
  11. 2
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift
  12. 10
      PadelClub/Views/Tournament/TournamentBuildView.swift

@ -912,6 +912,9 @@
FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; };
FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; };
FFB378362D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; };
FFB39B342D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; };
FFB39B352D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; };
FFB39B362D8E8B05008E0C89 /* MatchSpot.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */; };
FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; };
FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; };
FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; };
@ -1354,6 +1357,7 @@
FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = "<group>"; };
FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatGuideView.swift; sourceTree = "<group>"; };
FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchSpot.swift; sourceTree = "<group>"; };
FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = "<group>"; };
FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = "<group>"; };
FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchViewStyle.swift; sourceTree = "<group>"; };
@ -1950,6 +1954,7 @@
isa = PBXGroup;
children = (
FF6EC8FD2B94792300EA7F5A /* Screen.swift */,
FFB39B332D8E8B05008E0C89 /* MatchSpot.swift */,
FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */,
FF7091652B90F0B000AB08DA /* TabDestination.swift */,
FF025AEC2BD1513700A86CF8 /* AppScreen.swift */,
@ -2777,6 +2782,7 @@
FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */,
FFBF41842BF75ED7001B24CB /* EventTournamentsView.swift in Sources */,
FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */,
FFB39B352D8E8B05008E0C89 /* MatchSpot.swift in Sources */,
FF17CA4A2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */,
FF9268072BCE94D90080F940 /* TournamentCallView.swift in Sources */,
FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */,
@ -3092,6 +3098,7 @@
FF4CBFE92C996C0600151637 /* CloudConvert.swift in Sources */,
FF4CBFEA2C996C0600151637 /* EventTournamentsView.swift in Sources */,
FF4CBFEB2C996C0600151637 /* DisplayContext.swift in Sources */,
FFB39B342D8E8B05008E0C89 /* MatchSpot.swift in Sources */,
FF17CA4B2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */,
FF4CBFEC2C996C0600151637 /* TournamentCallView.swift in Sources */,
FF4CBFED2C996C0600151637 /* LoserRoundsView.swift in Sources */,
@ -3384,6 +3391,7 @@
FF70FB682C90584900129CC2 /* CloudConvert.swift in Sources */,
FF70FB692C90584900129CC2 /* EventTournamentsView.swift in Sources */,
FF70FB6A2C90584900129CC2 /* DisplayContext.swift in Sources */,
FFB39B362D8E8B05008E0C89 /* MatchSpot.swift in Sources */,
FF17CA492CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */,
FF70FB6B2C90584900129CC2 /* TournamentCallView.swift in Sources */,
FF70FB6C2C90584900129CC2 /* LoserRoundsView.swift in Sources */,
@ -3650,7 +3658,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.25;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3695,7 +3703,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.25;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3902,7 +3910,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.23;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3944,7 +3952,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.23;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

@ -1019,6 +1019,10 @@ defer {
previousMatches() + loserMatches()
}
func matchSpots() -> [MatchSpot] {
[MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)]
}
func insertOnServer() {
self.tournamentStore?.matches.writeChangeAndInsertOnServer(instance: self)
for teamScore in self.teamScores {

@ -393,18 +393,32 @@ defer {
func availableSeedOpponentSpot(inRoundIndex roundIndex: Int) -> [Match] {
return getRound(atRoundIndex: roundIndex)?.playedMatches().filter { $0.hasSpaceLeft() } ?? []
}
func availableSeedGroups() -> [SeedInterval] {
func availableSeedGroups(includeAll: Bool = false) -> [SeedInterval] {
let seeds = seeds()
var availableSeedGroup = Set<SeedInterval>()
for (index, seed) in seeds.enumerated() {
if seed.isSeedable(), let seedGroup = seedGroup(for: index) {
if includeAll {
if let chunks = seedGroup.chunks() {
chunksBy(in: chunks, availableSeedGroup: &availableSeedGroup)
}
} else {
availableSeedGroup.insert(seedGroup)
}
}
}
return availableSeedGroup.sorted(by: <)
}
func chunksBy(in chunks: [SeedInterval], availableSeedGroup: inout Set<SeedInterval>) {
chunks.forEach { chunk in
availableSeedGroup.insert(chunk)
if let moreChunk = chunk.chunks() {
self.chunksBy(in: moreChunk, availableSeedGroup: &availableSeedGroup)
}
}
}
func seedGroup(for alreadySetupSeeds: Int) -> SeedInterval? {
switch alreadySetupSeeds {
case 0...1:
@ -453,28 +467,32 @@ defer {
}
func seedGroupAvailable(atRoundIndex roundIndex: Int, availableSeedGroup: SeedInterval) -> SeedInterval? {
if availableSeeds().isEmpty == false && roundIndex >= lastSeedRound() {
let fullLeftSeeds = availableSeeds()
if fullLeftSeeds.isEmpty == false && roundIndex >= lastSeedRound() {
if availableSeedGroup == SeedInterval(first: 1, last: 2) { return availableSeedGroup }
let availableSeeds = seeds(inSeedGroup: availableSeedGroup)
let availableSeedSpot = availableSeedSpot(inRoundIndex: roundIndex)
let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex)
let targetSpots = availableSeedSpot.isEmpty ? availableSeedOpponentSpot.count : availableSeedSpot.count
if availableSeedGroup == SeedInterval(first: 3, last: 4), availableSeedSpot.count == 6 {
print("availableSeedGroup == SeedInterval(first: 3, last: 4)")
return availableSeedGroup
}
if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.count == availableSeeds.count {
return availableSeedGroup
} else if availableSeeds.count == availableSeedOpponentSpot.count && availableSeedGroup.count == availableSeedOpponentSpot.count {
return availableSeedGroup
} else if let chunks = availableSeedGroup.chunks() {
let seededTeamsCount = self.seededTeams().count
if let chunk = chunks.first(where: { seedInterval in
seedInterval.first >= self.seededTeams().count
return seedInterval.first == seededTeamsCount
}) {
return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: chunk)
} else if fullLeftSeeds.count > 1, targetSpots > 1, fullLeftSeeds.count >= targetSpots {
let seeds = seeds()
if let firstIndex = seeds.firstIndex(where: { $0.isSeedable() }) {
return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: SeedInterval(first: firstIndex + 1, last: firstIndex + targetSpots))
}
}
}
}
@ -507,6 +525,14 @@ defer {
for (index, seed) in availableSeeds.enumerated() {
seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false)
}
// } else if seedGroup == SeedInterval(first: 5, last: 6), availableSeedSpot.count == 4 {
// var spots = [Match]()
// spots.append(availableSeedSpot[1])
// spots.append(availableSeedSpot[2])
// spots = spots.shuffled()
// for (index, seed) in availableSeeds.enumerated() {
// seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false)
// }
} else {
if availableSeeds.count <= availableSeedSpot.count {
let spots = availableSeedSpot.shuffled()
@ -1105,7 +1131,17 @@ defer {
}
let groupStages = groupStages()
let baseRank = teamCount - groupStageSpots() + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified
var baseRank = teamCount - groupStageSpots() + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified
//TODO: RAZ ajouté une option pour choisir entre la règle officiel et la règle 'maison'
/*
Request by Philippe Morin 24/03/2025
*/
let defaultOption = false
if defaultOption {
baseRank += qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified - 1
}
let alreadyPlaceTeams = Array(teams.values.flatMap({ $0 }))
groupStages.forEach { groupStage in
let groupStageTeams = groupStage.teams(true)
@ -1115,6 +1151,7 @@ defer {
let groupStageWidth = max(((index == qualifiedPerGroupStage) ? groupStageCount - groupStageAdditionalQualified : groupStageCount) * (index - qualifiedPerGroupStage), 0)
let _index = baseRank + groupStageWidth + 1 - (index > qualifiedPerGroupStage ? groupStageAdditionalQualified : 0)
print("finalRanking", team.teamLabel() , _index, baseRank, groupStageWidth)
if let existingTeams = teams[_index] {
teams[_index] = existingTeams + [team.id]
} else {
@ -2564,7 +2601,7 @@ extension Tournament {
func deadline(for type: TournamentDeadlineType) -> Date? {
guard [.p500, .p1000, .p1500, .p2000].contains(tournamentLevel) else { return nil }
var daysOffset = type.daysOffset(level: tournamentLevel)
let daysOffset = type.daysOffset(level: tournamentLevel)
if let date = Calendar.current.date(byAdding: .day, value: daysOffset, to: startDate) {
let startOfDay = Calendar.current.startOfDay(for: date)
return Calendar.current.date(byAdding: type.timeOffset, to: startOfDay)

@ -106,7 +106,7 @@ struct PadelClubApp: App {
}
.task {
try? Tips.resetDatastore()
// try? Tips.resetDatastore()
try? Tips.configure([
.displayFrequency(.immediate),

@ -31,9 +31,10 @@ enum URLs: String, Identifiable {
case beachPadel = "https://beach-padel.app.fft.fr/beachja/index/"
//case padelClub = "https://padelclub.app"
case tenup = "https://tenup.fft.fr"
case padelCompetitionGeneralGuide = "https://fft-site.cdn.prismic.io/fft-site/Z2mH0ZbqstJ98yso_CHAPITREIRèglesgénérales.pdf"
case padelCompetitionSpecificGuide = "https://fft-site.cdn.prismic.io/fft-site/Z2mHz5bqstJ98ysm_CHAPITREIIICahierdeschargesdestournois.pdf"
case padelRules = "https://xlr.alwaysdata.net/static/rules/padel-rules-2024.pdf"
case padelCompetitionGeneralGuide = "https://padelclub.app/static/rules/padel-guide-general.pdf"
case padelCompetitionSpecificGuide = "https://padelclub.app/static/rules/padel-guide-cdc.pdf"
case padelCompetitionRankingGuide = "https://padelclub.app/static/rules/padel-guide-rankings.pdf"
case padelRules = "https://padelclub.app/static/rules/padel-rules.pdf"
case restingDischarge = "https://club.fft.fr/tennisfirmidecazeville/60120370_d/data_1/pdf/fo/formlairededechargederesponsabilitetournoidepadel.pdf"
case appReview = "https://apps.apple.com/app/padel-club/id6484163558?mt=8&action=write-review"
case appDescription = "https://padelclub.app/download/"

@ -0,0 +1,29 @@
//
// MatchSpot.swift
// PadelClub
//
// Created by razmig on 22/03/2025.
//
struct MatchSpot: SpinDrawable {
let match: Match
let teamPosition: TeamPosition
func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] {
[match.roundTitle(), matchTitle(displayStyle: displayStyle)].compactMap { $0 }
}
func matchTitle(displayStyle: DisplayStyle) -> String {
[match.matchTitle(displayStyle), teamPositionLabel()].joined(separator: " - ")
}
func teamPositionLabel() -> String {
switch teamPosition {
case .one:
return "haut"
case .two:
return "bas"
}
}
}

@ -146,6 +146,33 @@ struct MatchSetupView: View {
Label(seedGroup.localizedInterval(), systemImage: "dice")
}
}
Divider()
Menu {
ForEach(tournament.availableSeedGroups(includeAll: true), id: \.self) { seedGroup in
ConfirmButtonView(shouldConfirm: shouldConfirm, message: MatchSetupView.confirmationMessage) {
if let randomTeam = tournament.randomSeed(fromSeedGroup: seedGroup) {
randomTeam.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false)
do {
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
do {
try tournamentStore?.teamRegistrations.addOrUpdate(instance: randomTeam)
} catch {
Logger.error(error)
}
}
} label: {
Label(seedGroup.localizedInterval(), systemImage: "dice")
}
}
} label: {
Text("plus d'options")
}
} label: {
Text("Tirer au sort").tag(nil as SeedInterval?)
.underline()

@ -109,7 +109,7 @@ struct EventListView: View {
@ViewBuilder
private func _options(_ pcTournaments: [Tournament]) -> some View {
if let lastDataSource, pcTournaments.anySatisfy({ $0.rankSourceShouldBeRefreshed() != nil && $0.hasEnded() == false }) {
Section {
Menu {
Button {
Task {
do {
@ -136,14 +136,14 @@ struct EventListView: View {
}
}
} label: {
Text("Rafraîchir les classements")
Text("M-à-j des classements")
}
} header: {
Text("Source disponible : \(lastDataSource.monthYearFormatted)")
} label: {
Text("Classement \(lastDataSource.monthYearFormatted)")
}
Divider()
}
Section {
Menu {
if pcTournaments.anySatisfy({ $0.isPrivate == true }) {
Button {
pcTournaments.forEach { tournament in
@ -155,7 +155,7 @@ struct EventListView: View {
Logger.error(error)
}
} label: {
Text("Afficher ce\(pcTournaments.count.pluralSuffix) tournoi\(pcTournaments.count.pluralSuffix) sur Padel Club")
Text("Afficher sur Padel Club")
}
}
@ -170,15 +170,15 @@ struct EventListView: View {
Logger.error(error)
}
} label: {
Text("Masquer ce\(pcTournaments.count.pluralSuffix) tournoi\(pcTournaments.count.pluralSuffix) sur Padel Club")
Text("Masquer sur Padel Club")
}
}
} header: {
} label: {
Text("Visibilité sur Padel Club")
}
Divider()
if pcTournaments.anySatisfy({ $0.hasEnded() == false && $0.enableOnlineRegistration == false && $0.onlineRegistrationCanBeEnabled() }) || pcTournaments.anySatisfy({ $0.enableOnlineRegistration == true && $0.hasEnded() == false }) {
Section {
Menu {
if pcTournaments.anySatisfy({ $0.hasEnded() == false && $0.enableOnlineRegistration == false && $0.onlineRegistrationCanBeEnabled() }) {
Button {
pcTournaments.forEach { tournament in
@ -190,7 +190,7 @@ struct EventListView: View {
Logger.error(error)
}
} label: {
Text("Activer l'inscription en ligne")
Text("Activer")
}
}
@ -203,7 +203,7 @@ struct EventListView: View {
}
}
} label: {
Text("Rafraîchir la liste des équipes inscrites en ligne")
Text("M-à-j des inscriptions")
}
@ -217,13 +217,64 @@ struct EventListView: View {
Logger.error(error)
}
} label: {
Text("Désactiver l'inscription en ligne")
Text("Désactiver")
}
}
} header: {
} label: {
Text("Inscription en ligne")
}
}
Divider()
Menu {
Button {
pcTournaments.forEach { tournament in
tournament.information = nil
}
do {
try dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments)
} catch {
Logger.error(error)
}
} label: {
Text("Effacer les descriptions")
}
let info = Set(pcTournaments.compactMap { tournament in
tournament.information?.trimmedMultiline
}).joined(separator: "\n")
if info.isEmpty == false {
Button {
pcTournaments.forEach { tournament in
tournament.information = info
}
do {
try dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments)
} catch {
Logger.error(error)
}
} label: {
Text("Mettre '\(info.trunc(length: 12))'")
}
}
PasteButton(payloadType: String.self) { strings in
if let pasteboard = strings.first {
pcTournaments.forEach { tournament in
tournament.information = pasteboard
}
do {
try dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments)
} catch {
Logger.error(error)
}
}
}
} label: {
Text("Description des tournois")
}
}
private func _nextMonths() -> [Date] {
@ -263,9 +314,11 @@ struct EventListView: View {
TournamentCellView(tournament: tournament)
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name.CollectionDidLoad), perform: { notification in
if let store = notification.object as? StoredCollection<TeamRegistration>, store.id == tournament.id {
if let store = notification.object as? StoredCollection<TeamRegistration> {
if store.storeId == tournament.id {
tournament.lastTeamRefresh = Date()
}
}
})
.id(tournament.lastTeamRefresh)

@ -104,9 +104,10 @@ struct ToolboxView: View {
}
Section {
Link("Accéder au guide de la compétition", destination: URLs.padelCompetitionGeneralGuide.url)
Link("Accéder aux CDC des tournois", destination: URLs.padelCompetitionSpecificGuide.url)
Link("Accéder aux règles du jeu", destination: URLs.padelRules.url)
Link("Guide de la compétition", destination: URLs.padelCompetitionGeneralGuide.url)
Link("CDC des tournois", destination: URLs.padelCompetitionSpecificGuide.url)
Link("Barèmes et assimilations", destination: URLs.padelCompetitionRankingGuide.url)
Link("Règles du jeu", destination: URLs.padelRules.url)
Link("Décharge des temps de repos", destination: URLs.restingDischarge.url)
} header: {
Text("Documents fédéraux")

@ -131,34 +131,7 @@ struct RoundView: View {
let availableQualifiedTeams = tournament.availableQualifiedTeams()
if availableSeeds.isEmpty == false, let availableSeedGroup {
Section {
RowButtonView("Placer \(availableSeedGroup.localizedInterval())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) {
Task {
tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: availableSeedGroup)
_save(seeds: availableSeeds)
}
}
} footer: {
if availableSeedGroup.isFixed() == false {
Text("Le tirage au sort ne sera pas visuel. Toutes les équipes de ce chapeau seront tirées.")
}
}
if (availableSeedGroup.isFixed() == false) {
Section {
Toggle(isOn: $hideNames) {
Text("Masquer les noms")
if hideNames {
Text("Réalise un tirage des positions.")
}
}
RowButtonView("Tirage au sort \(availableSeedGroup.localizedInterval()) visuel") {
self.selectedSeedGroup = availableSeedGroup
}
} footer: {
Text("Le tirage au sort sera visuel et automatique, n'hésitez pas à enregistrer une vidéo de votre écran. Toutes les équipes de ce chapeau seront tirées les unes après les autres.")
}
}
_seedGroupSection(availableSeeds: availableSeeds, availableSeedGroup: availableSeedGroup)
}
if availableQualifiedTeams.isEmpty == false {
@ -312,6 +285,12 @@ struct RoundView: View {
seedSpaceLeft.isEmpty ? true : false
}
private func _warnGroupWillCloseRound(availableSeedGroup: SeedInterval) -> Bool {
opposingSeeding
&& seedSpaceLeft.count <= availableSeedGroup.count
&& tournament.rounds().count - 1 > upperRound.round.index
}
private func _availableSeedSpot(availableSeedGroup: SeedInterval) -> [Match] {
let spots = opposingSeeding ? spaceLeft : seedSpaceLeft
if availableSeedGroup == SeedInterval(first: 3, last: 4), spots.count == 6 {
@ -403,32 +382,51 @@ struct RoundView: View {
}
}
}
}
struct MatchSpot: SpinDrawable {
let match: Match
let teamPosition: TeamPosition
func segmentLabel(_ displayStyle: DisplayStyle, hideNames: Bool) -> [String] {
[match.roundTitle(), matchTitle(displayStyle: displayStyle)].compactMap { $0 }
private func _seedGroupSection(availableSeeds: [TeamRegistration], availableSeedGroup: SeedInterval) -> some View {
Group {
let warnGroupWillCloseRound = _warnGroupWillCloseRound(availableSeedGroup: availableSeedGroup)
Section {
RowButtonView("Placer \(availableSeedGroup.localizedInterval())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : ""), role: warnGroupWillCloseRound ? .destructive : nil, confirmationMessage: warnGroupWillCloseRound ? "Attention, tous vos matchs du tour précédent seront désactivés" : nil) {
Task {
tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: availableSeedGroup)
_save(seeds: availableSeeds)
}
func matchTitle(displayStyle: DisplayStyle) -> String {
[match.matchTitle(displayStyle), teamPositionLabel()].joined(separator: " - ")
}
func teamPositionLabel() -> String {
switch teamPosition {
case .one:
return "haut"
case .two:
return "bas"
// if upperRound.round.index == 3, availableSeedGroup.first == 5, availableSeedGroup.last == 8, availableSeeds.count > 1, let half = availableSeedGroup.chunks()?.first {
// FooterButtonView("ou placer \(half.localizedInterval())" + ((half.isFixed() == false) ? " au hasard" : "")) {
// Task {
// tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: half)
// _save(seeds: availableSeeds)
// }
// }
// }
} footer: {
if availableSeedGroup.isFixed() == false {
Text("Le tirage au sort ne sera pas visuel. Toutes les équipes de ce chapeau seront tirées.")
}
}
}
extension Match {
func matchSpots() -> [MatchSpot] {
[MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)]
if (availableSeedGroup.isFixed() == false) {
Section {
Toggle(isOn: $hideNames) {
Text("Masquer les noms")
if hideNames {
Text("Réalise un tirage des positions.")
}
}
RowButtonView("Tirage au sort \(availableSeedGroup.localizedInterval()) visuel", role: warnGroupWillCloseRound ? .destructive : nil, confirmationMessage: warnGroupWillCloseRound ? "Attention, tous vos matchs du tour précédent seront désactivés" : nil) {
self.selectedSeedGroup = availableSeedGroup
}
// if upperRound.round.index == 3, availableSeedGroup.first == 5, availableSeedGroup.last == 8, availableSeeds.count > 1, let half = availableSeedGroup.chunks()?.first {
// FooterButtonView("ou tirage au sort \(half.localizedInterval()) visuel") {
// self.selectedSeedGroup = half
// }
// }
} footer: {
Text("Le tirage au sort sera visuel et automatique, n'hésitez pas à enregistrer une vidéo de votre écran. Toutes les équipes de ce chapeau seront tirées les unes après les autres.")
}
}
}
}
}

@ -175,7 +175,7 @@ struct TournamentRankView: View {
@State var key: Int
var body: some View {
VStack(spacing: 0) {
VStack {
if editMode?.wrappedValue.isEditing == true {
if key > 1 {
FooterButtonView("monter") {

@ -130,10 +130,6 @@ struct TournamentBuildView: View {
}
}
}
} else {
NavigationLink(value: Screen.restingTime) {
Text("Temps de repos")
}
}
if state == .running || state == .finished {
TournamentInscriptionView(tournament: tournament)
@ -203,6 +199,12 @@ struct TournamentBuildView: View {
cashierStatus = await tournament.cashierStatus()
}
}
if state == .running {
NavigationLink(value: Screen.restingTime) {
Text("Temps de repos")
}
}
}
}

Loading…
Cancel
Save