From 645beb7171dcc901d78c903843cd2978e4d74471 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sat, 25 May 2024 11:03:43 +0200 Subject: [PATCH] fix fortune wheel stuff --- PadelClub.xcodeproj/project.pbxproj | 4 +- PadelClub/Data/Match.swift | 8 +- PadelClub/Data/TeamRegistration.swift | 14 ++- .../Views/Components/FortuneWheelView.swift | 97 ++++++++++------ .../Components/GroupStageTeamView.swift | 32 +++--- .../Views/GroupStage/GroupStagesView.swift | 13 ++- PadelClub/Views/Round/RoundView.swift | 104 ++++++++++++------ PadelClub/Views/Round/RoundsView.swift | 5 +- PadelClub/Views/Subscription/Guard.swift | 8 +- .../Tournament/TournamentBuildView.swift | 2 + .../Tournament/TournamentRunningView.swift | 4 +- 11 files changed, 189 insertions(+), 102 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 7f616f5..285d8e7 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1923,7 +1923,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 23; + CURRENT_PROJECT_VERSION = 24; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1961,7 +1961,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 23; + CURRENT_PROJECT_VERSION = 24; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index e31d4b8..00fb5ee 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -588,15 +588,17 @@ class Match: ModelObject, Storable { } func hasSpaceLeft() -> Bool { - teams().count == 1 + teamScores.count < 2 } func isReady() -> Bool { - teams().count == 2 + teamScores.count == 2 +// teams().count == 2 } func isEmpty() -> Bool { - teams().isEmpty + teamScores.isEmpty +// teams().isEmpty } func hasEnded() -> Bool { diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index cf860e2..edf0ebd 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -117,7 +117,19 @@ class TeamRegistration: ModelObject, Storable { func teamScores() -> [TeamScore] { Store.main.filter(isIncluded: { $0.teamRegistration == id }) } + + func wins() -> [Match] { + Store.main.filter(isIncluded: { $0.winningTeamId == id }) + } + + func loses() -> [Match] { + Store.main.filter(isIncluded: { $0.losingTeamId == id }) + } + func matches() -> [Match] { + Store.main.filter(isIncluded: { $0.losingTeamId == id || $0.winningTeamId == id }) + } + var tournamentCategory: TournamentCategory { tournamentObject()?.tournamentCategory ?? .men } @@ -171,7 +183,7 @@ class TeamRegistration: ModelObject, Storable { } func canPlay() -> Bool { - teamScores().isEmpty == false || players().allSatisfy({ $0.hasPaid() || $0.hasArrived }) + matches().isEmpty == false || players().allSatisfy({ $0.hasPaid() || $0.hasArrived }) } func availableForSeedPick() -> Bool { diff --git a/PadelClub/Views/Components/FortuneWheelView.swift b/PadelClub/Views/Components/FortuneWheelView.swift index f0b8d60..abd7ece 100644 --- a/PadelClub/Views/Components/FortuneWheelView.swift +++ b/PadelClub/Views/Components/FortuneWheelView.swift @@ -61,16 +61,14 @@ struct SpinDrawView: View { let drawees: [any SpinDrawable] @State var segments: [any SpinDrawable] - let completion: ([DrawResult]) -> Void // Completion closure + var autoMode: Bool = false + let completion: ([DrawResult]) async -> Void // Completion closure @State private var drawCount: Int = 0 @State private var draws: [DrawResult] = [DrawResult]() @State private var drawOptions: [DrawOption] = [DrawOption]() @State private var selectedIndex: Int? - - var autoMode: Bool { - drawees.count > 1 - } + @State private var disabled: Bool = false var body: some View { List { @@ -79,7 +77,7 @@ struct SpinDrawView: View { _validationLabelView(drawee: drawCount, result: segments[draws.last!.drawIndex]) if autoMode == false || drawCount == drawees.count { RowButtonView("Valider le tirage") { - completion(draws) + await completion(draws) dismiss() } } else { @@ -92,27 +90,49 @@ struct SpinDrawView: View { } Section { - FortuneWheelContainerView(segments: drawOptions, autoMode: autoMode) { index in - self.selectedIndex = index - self.draws.append(DrawResult(drawee: drawCount, drawIndex: drawOptions[index].initialIndex)) - self.drawOptions.remove(at: index) - - if autoMode && drawCount < drawees.count { - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - self.drawCount += 1 - if drawOptions.count == 1 { - self.draws.append(DrawResult(drawee: self.drawCount, drawIndex: self.drawOptions[0].initialIndex)) - self.drawOptions.remove(at: 0) + ZStack { + FortuneWheelContainerView(segments: drawOptions, autoMode: autoMode) { index in + self.selectedIndex = index + self.draws.append(DrawResult(drawee: drawCount, drawIndex: drawOptions[index].initialIndex)) + self.drawOptions.remove(at: index) + + if autoMode && drawCount < drawees.count { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.drawCount += 1 - self.selectedIndex = nil - } else { - self.selectedIndex = nil + if drawOptions.count == 1 { + self.draws.append(DrawResult(drawee: self.drawCount, drawIndex: self.drawOptions[0].initialIndex)) + self.drawOptions.remove(at: 0) + self.drawCount += 1 + self.selectedIndex = nil + } else { + self.selectedIndex = nil + } } } } + .simultaneousGesture( + DragGesture().onChanged({ (value) in + disabled = true + }).onEnded({ (value) in + }) + ) + Rectangle() + .fill(.white.opacity(0.01)) + .clipShape(Circle()) + .allowsHitTesting(disabled) } .listRowBackground(Color.clear) .listRowSeparator(.hidden) + } footer: { + HStack { + Spacer() + if autoMode { + Text("Mode automatique") + } else { + Text("Lancer la roue en glissant avec le doigt").multilineTextAlignment(.center) + } + Spacer() + } } } else { Section { @@ -123,7 +143,7 @@ struct SpinDrawView: View { } RowButtonView("Valider les tirages") { - completion(draws) + await completion(draws) dismiss() } } @@ -142,6 +162,7 @@ struct SpinDrawView: View { Button("Annuler", role: .cancel) { dismiss() } + .disabled(disabled || autoMode) } } .navigationBarBackButtonHidden() @@ -151,6 +172,7 @@ struct SpinDrawView: View { .toolbar(.hidden, for: .tabBar) .listStyle(.insetGrouped) .scrollDisabled(true) + .interactiveDismissDisabled() .onAppear { for (index, segment) in segments.enumerated() { drawOptions.append(DrawOption(initialIndex: index, option: segment)) @@ -162,19 +184,24 @@ struct SpinDrawView: View { private func _segmentLabelView(segment: [String], horizontalAlignment: HorizontalAlignment = .leading) -> some View { VStack(alignment: horizontalAlignment, spacing: 0.0) { ForEach(segment, id: \.self) { string in - Text(string) + Text(string).font(.title3) .frame(maxWidth: .infinity) + .lineLimit(1) } } } @ViewBuilder private func _validationLabelView(drawee: Int, result: SpinDrawable) -> some View { - HStack(spacing: 0.0) { + VStack(spacing: 0.0) { let draw = drawees[drawee] - _segmentLabelView(segment: draw.segmentLabel(.wide), horizontalAlignment: .leading) - Image(systemName: "arrowshape.forward.fill") - _segmentLabelView(segment: result.segmentLabel(.wide), horizontalAlignment: .trailing) + _segmentLabelView(segment: draw.segmentLabel(.wide), horizontalAlignment: .center) + if result as? TeamRegistration != nil { + Image(systemName: "flag.2.crossed.fill").font(.largeTitle).foregroundColor(.logoRed) + } else { + Image(systemName: "arrowshape.down.fill").font(.largeTitle).foregroundColor(.logoRed) + } + _segmentLabelView(segment: result.segmentLabel(.wide), horizontalAlignment: .center) } } } @@ -200,26 +227,24 @@ struct FortuneWheelContainerView: View { .onAppear { if autoMode { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + rotation = 0 rollWheel() } } } .gesture( - DragGesture() - .onChanged { value in - // Calculate rotation based on the velocity of the drag - let initialVelocity = value.predictedEndTranslation.width / 10 // Adjust sensitivity - rotation += Double(initialVelocity) - } - .onEnded { value in - // Roll the wheel when drag ends - rollWheel() + DragGesture().onChanged({ (value) in + if value.translation.width < 0 { + rotation = Double(-value.translation.width) } + }).onEnded({ (value) in + rollWheel() + }) ) } func rollWheel() { - rotation = 0 + //rotation = 0 // Generate a random angle for the wheel to rotate let randomAngle = Double.random(in: 1440...2880) // Adjust range for more or less rotations diff --git a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift index 0f47caa..2b59ba6 100644 --- a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift +++ b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift @@ -23,22 +23,22 @@ struct GroupStageTeamView: View { } if groupStage.tournamentObject()?.hasEnded() == false { - if team.qualified && team.bracketPosition == nil, let tournament = team.tournamentObject() { - Section { - NavigationLink { - SpinDrawView(drawees: [team], segments: tournament.matchesWithSpace()) { results in - - } - } label: { - Text("Tirage au sort visuel") - } - } - - Section { - RowButtonView("Tirage au sort automatique", role: .destructive) { - } - } - } +// if team.qualified && team.bracketPosition == nil, let tournament = team.tournamentObject() { +// Section { +// NavigationLink { +// SpinDrawView(drawees: [team], segments: tournament.matchesWithSpace()) { results in +// +// } +// } label: { +// Text("Tirage au sort visuel") +// } +// } +// +// Section { +// RowButtonView("Tirage au sort automatique", role: .destructive) { +// } +// } +// } if team.qualified == false { Section { diff --git a/PadelClub/Views/GroupStage/GroupStagesView.swift b/PadelClub/Views/GroupStage/GroupStagesView.swift index b09c5dc..fc1c9f6 100644 --- a/PadelClub/Views/GroupStage/GroupStagesView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesView.swift @@ -51,8 +51,12 @@ struct GroupStagesView: View { } } + let allMatches: [Match] + init(tournament: Tournament) { self.tournament = tournament + self.allMatches = tournament.groupStages().flatMap({ $0.playedMatches() }) + if tournament.shouldVerifyGroupStage { _selectedDestination = State(wrappedValue: nil) } else if tournament.unsortedTeams().filter({ $0.groupStagePosition != nil }).isEmpty { @@ -77,11 +81,10 @@ struct GroupStagesView: View { GenericDestinationPickerView(selectedDestination: $selectedDestination, destinations: allDestinations(), nilDestinationIsValid: true) switch selectedDestination { case .all: - let allGroupStages = tournament.groupStages() - let availableToStart = allGroupStages.flatMap({ $0.availableToStart() }) - let runningMatches = allGroupStages.flatMap({ $0.runningMatches() }) - let readyMatches = allGroupStages.flatMap({ $0.readyMatches() }) - let finishedMatches = allGroupStages.flatMap({ $0.finishedMatches() }) + let availableToStart = tournament.availableToStart(allMatches) + let runningMatches = tournament.runningMatches(allMatches) + let readyMatches = tournament.readyMatches(allMatches) + let finishedMatches = tournament.finishedMatches(allMatches) List { MatchListView(section: "disponible", matches: availableToStart, matchViewStyle: .standardStyle, isExpanded: false) diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index a2f99d7..9d9f344 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -31,6 +31,7 @@ struct RoundView: View { let availableQualifiedTeams = tournament.availableQualifiedTeams() let displayableMatches = round.displayableMatches().sorted(by: \.index) let spaceLeft = displayableMatches.filter({ $0.hasSpaceLeft() }) + let seedSpaceLeft = displayableMatches.filter({ $0.isEmpty() }) if isEditingTournamentSeed.wrappedValue == false { //(where: { $0.isDisabled() == false || isEditingTournamentSeed.wrappedValue }) if loserRounds.isEmpty == false { @@ -45,41 +46,82 @@ struct RoundView: View { } } } - } else if availableQualifiedTeams.isEmpty == false && spaceLeft.isEmpty == false { - NavigationLink("Tirer au sort la position d'un qualifié") { - SpinDrawView(drawees: availableQualifiedTeams, segments: spaceLeft) { results in - results.forEach { drawResult in - print(availableQualifiedTeams[drawResult.drawee].teamLabel()) - print(spaceLeft[drawResult.drawIndex].matchTitle()) - availableQualifiedTeams[drawResult.drawee].setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true) + } else { + if let availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: round.index) { + Section { + RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) { + tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup) + _save() + if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { + self.isEditingTournamentSeed.wrappedValue = false + } } - _save() - if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { - self.isEditingTournamentSeed.wrappedValue = false + } 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 { + RowButtonView("Tirage au sort \(availableSeedGroup.localizedLabel()) 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.") } } } - } else if let availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: round.index) { - Section { - RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) { - tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup) - _save() - if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { - self.isEditingTournamentSeed.wrappedValue = false + + if availableQualifiedTeams.isEmpty == false && spaceLeft.isEmpty == false { + Section { + ForEach(availableQualifiedTeams) { team in + NavigationLink { + SpinDrawView(drawees: [team], segments: spaceLeft) { results in + Task { + results.forEach { drawResult in + team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true) + } + _save() + if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { + self.isEditingTournamentSeed.wrappedValue = false + } + } + } + } label: { + TeamRowView(team: team, displayCallDate: false) + } } + } header: { + Text("Tirage au sort visuel d'un qualifié").font(.subheadline) } } - if (availableSeedGroup.isFixed() == false) { + if tournament.availableSeeds().isEmpty == false && seedSpaceLeft.isEmpty == false { Section { - RowButtonView("Tirage au sort \(availableSeedGroup.localizedLabel()) visuel") { - self.selectedSeedGroup = availableSeedGroup + ForEach(tournament.availableSeeds()) { team in + NavigationLink { + SpinDrawView(drawees: [team], segments: seedSpaceLeft) { results in + Task { + results.forEach { drawResult in + team.setSeedPosition(inSpot: seedSpaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false) + } + _save() + if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { + self.isEditingTournamentSeed.wrappedValue = false + } + } + } + } label: { + TeamRowView(team: team, displayCallDate: false) + } } + } header: { + Text("Tirage au sort visuel d'une tête de série").font(.subheadline) } } } - ForEach(displayableMatches) { match in Section { MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle) @@ -100,20 +142,20 @@ struct RoundView: View { } } } - .sheet(isPresented: showVisualDrawView) { + .fullScreenCover(isPresented: showVisualDrawView) { if let availableSeedGroup = selectedSeedGroup { let seeds = tournament.seeds(inSeedGroup: availableSeedGroup) let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: round.index) NavigationStack { - SpinDrawView(drawees: seeds, segments: availableSeedSpot) { draws in - draws.forEach { drawResult in - print(seeds[drawResult.drawee].teamLabel()) - print(availableSeedSpot[drawResult.drawIndex].matchTitle()) - seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: false) - } - _save() - if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { - self.isEditingTournamentSeed.wrappedValue = false + SpinDrawView(drawees: seeds, segments: availableSeedSpot, autoMode: true) { draws in + Task { + draws.forEach { drawResult in + seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: false) + } + _save() + if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { + self.isEditingTournamentSeed.wrappedValue = false + } } } } diff --git a/PadelClub/Views/Round/RoundsView.swift b/PadelClub/Views/Round/RoundsView.swift index 08a6c72..eac0d07 100644 --- a/PadelClub/Views/Round/RoundsView.swift +++ b/PadelClub/Views/Round/RoundsView.swift @@ -14,12 +14,13 @@ struct RoundsView: View { init(tournament: Tournament) { self.tournament = tournament - if tournament.shouldVerifyBracket { + let availableSeeds = tournament.availableSeeds() + if tournament.shouldVerifyBracket && availableSeeds.isEmpty { _selectedRound = State(wrappedValue: nil) } else { _selectedRound = State(wrappedValue: tournament.getActiveRound()) } - if tournament.availableSeeds().isEmpty == false || tournament.availableQualifiedTeams().isEmpty == false { + if availableSeeds.isEmpty == false || tournament.availableQualifiedTeams().isEmpty == false { _isEditingTournamentSeed = State(wrappedValue: true) } } diff --git a/PadelClub/Views/Subscription/Guard.swift b/PadelClub/Views/Subscription/Guard.swift index 92d8fd1..ccc1f04 100644 --- a/PadelClub/Views/Subscription/Guard.swift +++ b/PadelClub/Views/Subscription/Guard.swift @@ -145,14 +145,14 @@ import LeStorage } var currentPlan: StoreItem? { -// #if DEBUG -// return .monthlyUnlimited -// #else + #if DEBUG + return .monthlyUnlimited + #else if let currentBestPlan = self.currentBestPlan, let plan = StoreItem(rawValue: currentBestPlan.productID) { return plan } return nil -// #endif + #endif } func userFilteredPurchases() -> [StoreKit.Transaction] { diff --git a/PadelClub/Views/Tournament/TournamentBuildView.swift b/PadelClub/Views/Tournament/TournamentBuildView.swift index 8749b6a..2c88f24 100644 --- a/PadelClub/Views/Tournament/TournamentBuildView.swift +++ b/PadelClub/Views/Tournament/TournamentBuildView.swift @@ -25,6 +25,7 @@ struct TournamentBuildView: View { NavigationLink(value: Screen.groupStage) { LabeledContent { Text(tournament.groupStageStatus()) + .multilineTextAlignment(.trailing) } label: { Text("Poules") if tournament.shouldVerifyGroupStage { @@ -38,6 +39,7 @@ struct TournamentBuildView: View { NavigationLink(value: Screen.round) { LabeledContent { Text(tournament.bracketStatus()) + .multilineTextAlignment(.trailing) } label: { Text("Tableau") if tournament.shouldVerifyBracket { diff --git a/PadelClub/Views/Tournament/TournamentRunningView.swift b/PadelClub/Views/Tournament/TournamentRunningView.swift index f0e4ec1..b6c6cdf 100644 --- a/PadelClub/Views/Tournament/TournamentRunningView.swift +++ b/PadelClub/Views/Tournament/TournamentRunningView.swift @@ -19,8 +19,8 @@ struct TournamentRunningView: View { @ViewBuilder var body: some View { MatchListView(section: "en cours", matches: tournament.runningMatches(allMatches)) - MatchListView(section: "à lancer", matches: tournament.readyMatches(allMatches), isExpanded: false) - MatchListView(section: "disponible", matches: tournament.availableToStart(allMatches), isExpanded: false) +// MatchListView(section: "à lancer", matches: tournament.readyMatches(allMatches), isExpanded: false) +// MatchListView(section: "disponible", matches: tournament.availableToStart(allMatches), isExpanded: false) MatchListView(section: "terminés", matches: tournament.finishedMatches(allMatches), isExpanded: false) } }