From ac60d64accb78c80e3bf2fc122d5bf963408f979 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 11 Oct 2024 17:18:31 +0200 Subject: [PATCH] add match followup picker --- PadelClub.xcodeproj/project.pbxproj | 8 ++ PadelClub/Data/GroupStage.swift | 4 +- PadelClub/Data/Match.swift | 23 ++- PadelClub/Data/TeamRegistration.swift | 4 + PadelClub/Data/Tournament.swift | 4 +- PadelClub/Extensions/Date+Extensions.swift | 7 + PadelClub/Utils/DisplayContext.swift | 18 +++ .../Views/GroupStage/GroupStageView.swift | 8 +- .../Match/Components/MatchDateView.swift | 22 ++- .../Match/Components/PlayerBlockView.swift | 16 ++- PadelClub/Views/Match/MatchDetailView.swift | 28 ++-- PadelClub/Views/Match/MatchRowView.swift | 5 +- PadelClub/Views/Match/MatchSummaryView.swift | 10 +- PadelClub/Views/Score/FollowUpMatchView.swift | 133 ++++++++++++++++++ 14 files changed, 261 insertions(+), 29 deletions(-) create mode 100644 PadelClub/Views/Score/FollowUpMatchView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 9f7a7bb..dad8d2c 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -84,6 +84,9 @@ FF17CA492CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; FF17CA4A2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; FF17CA4B2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; + FF17CA4D2CB9243E003C7323 /* 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 */; }; FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.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 */; }; @@ -981,6 +984,7 @@ FF1162892BD05247000C4809 /* DateUpdateManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateUpdateManagerView.swift; sourceTree = ""; }; FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundStepScheduleEditorView.swift; sourceTree = ""; }; FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiCourtPickerView.swift; sourceTree = ""; }; + FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowUpMatchView.swift; sourceTree = ""; }; FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournament.swift; sourceTree = ""; }; FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Extensions.swift"; sourceTree = ""; }; FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournamentSearchScope.swift; sourceTree = ""; }; @@ -1860,6 +1864,7 @@ isa = PBXGroup; children = ( FFCFC0012BBC39A600B82851 /* EditScoreView.swift */, + FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */, FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */, FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */, FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */, @@ -2263,6 +2268,7 @@ FFE103082C353B7600684FC9 /* EventClubSettingsView.swift in Sources */, C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */, FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */, + FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */, FFCD16B32C3E5E590092707B /* TeamsCallingView.swift in Sources */, FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */, FF967CF22BAECC0B00A9A3BD /* TeamScore.swift in Sources */, @@ -2533,6 +2539,7 @@ FF4CBF672C996C0600151637 /* EventClubSettingsView.swift in Sources */, FF4CBF682C996C0600151637 /* AccountView.swift in Sources */, FF4CBF692C996C0600151637 /* PlayersWithoutContactView.swift in Sources */, + FF17CA4D2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */, FF4CBF6A2C996C0600151637 /* TeamsCallingView.swift in Sources */, FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */, FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */, @@ -2782,6 +2789,7 @@ FF70FAE62C90584900129CC2 /* EventClubSettingsView.swift in Sources */, FF70FAE72C90584900129CC2 /* AccountView.swift in Sources */, FF70FAE82C90584900129CC2 /* PlayersWithoutContactView.swift in Sources */, + FF17CA4E2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */, FF70FAE92C90584900129CC2 /* TeamsCallingView.swift in Sources */, FF70FAEA2C90584900129CC2 /* Calendar+Extensions.swift in Sources */, FF70FAEB2C90584900129CC2 /* TeamScore.swift in Sources */, diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index f51fe5e..8f81297 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -238,7 +238,7 @@ final class GroupStage: ModelObject, Storable { return _matches().first(where: { matchIndexes.contains($0.index) }) } - func availableToStart(playedMatches: [Match], in runningMatches: [Match]) -> [Match] { + func availableToStart(playedMatches: [Match], in runningMatches: [Match], checkCanPlay: Bool = true) -> [Match] { #if _DEBUG_TIME //DEBUGING TIME let start = Date() defer { @@ -246,7 +246,7 @@ final class GroupStage: ModelObject, Storable { print("func group stage availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) } #endif - return playedMatches.filter({ $0.canBeStarted(inMatches: runningMatches) && $0.isRunning() == false }) + return playedMatches.filter({ $0.isRunning() == false && $0.canBeStarted(inMatches: runningMatches, checkCanPlay: checkCanPlay) }) } func runningMatches(playedMatches: [Match]) -> [Match] { diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 548055c..acce813 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -9,7 +9,11 @@ import Foundation import LeStorage @Observable -final class Match: ModelObject, Storable { +final class Match: ModelObject, Storable, Equatable { + static func == (lhs: Match, rhs: Match) -> Bool { + lhs.id == rhs.id && lhs.startDate == rhs.startDate + } + static func resourceName() -> String { "matches" } static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func filterByStoreIdentifier() -> Bool { return true } @@ -684,12 +688,15 @@ defer { self.courtIndex = courtIndex } - func canBeStarted(inMatches matches: [Match]) -> Bool { + func canBeStarted(inMatches matches: [Match], checkCanPlay: Bool) -> Bool { let teams = teamScores - guard teams.count == 2 else { return false } - guard hasEnded() == false else { return false } - guard hasStarted() == false else { return false } - return teams.compactMap({ $0.team }).allSatisfy({ $0.canPlay() && isTeamPlaying($0, inMatches: matches) == false }) + guard teams.count == 2 else { + print("teams.count != 2") + return false + } + return teams.compactMap({ $0.team }).allSatisfy({ + ((checkCanPlay && $0.canPlay()) || checkCanPlay == false) && isTeamPlaying($0, inMatches: matches) == false + }) } func isTeamPlaying(_ team: TeamRegistration, inMatches matches: [Match]) -> Bool { @@ -891,6 +898,10 @@ defer { } } + var restingTimeForSorting: TimeInterval { + (teams().compactMap({ $0.restingTime() }).max() ?? .distantFuture).timeIntervalSinceNow + } + enum CodingKeys: String, CodingKey { case _id = "id" case _round = "round" diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index e3cf09b..b85fceb 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -532,6 +532,10 @@ final class TeamRegistration: ModelObject, Storable { } } + func restingTime() -> Date? { + matches().sorted(by: \.computedEndDateForSorting).last?.endDate + } + enum CodingKeys: String, CodingKey { case _id = "id" case _tournament = "tournament" diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index a493e39..0105ee0 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1109,7 +1109,7 @@ defer { // return Store.main.filter(isIncluded: { $0.groupStage != nil && groupStageIds.contains($0.groupStage!) }) } - func availableToStart(_ allMatches: [Match], in runningMatches: [Match]) -> [Match] { + func availableToStart(_ allMatches: [Match], in runningMatches: [Match], checkCanPlay: Bool = true) -> [Match] { #if _DEBUG_TIME //DEBUGING TIME let start = Date() defer { @@ -1117,7 +1117,7 @@ defer { print("func tournament availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) } #endif - return allMatches.filter({ $0.canBeStarted(inMatches: runningMatches) && $0.isRunning() == false }).sorted(by: \.computedStartDateForSorting) + return allMatches.filter({ $0.isRunning() == false && $0.canBeStarted(inMatches: runningMatches, checkCanPlay: checkCanPlay) }).sorted(by: \.computedStartDateForSorting) } func runningMatches(_ allMatches: [Match]) -> [Match] { diff --git a/PadelClub/Extensions/Date+Extensions.swift b/PadelClub/Extensions/Date+Extensions.swift index 2758f48..6e99c01 100644 --- a/PadelClub/Extensions/Date+Extensions.swift +++ b/PadelClub/Extensions/Date+Extensions.swift @@ -231,4 +231,11 @@ extension Date { func localizedWeekDay() -> String { self.formatted(.dateTime.weekday(.wide)) } + + static var hourMinuteFormatter: DateComponentsFormatter = { + let formatter = DateComponentsFormatter() + formatter.allowedUnits = [.hour, .minute] // Customize units + formatter.unitsStyle = .abbreviated // You can choose .abbreviated or .short + return formatter + }() } diff --git a/PadelClub/Utils/DisplayContext.swift b/PadelClub/Utils/DisplayContext.swift index a5aaebe..71786af 100644 --- a/PadelClub/Utils/DisplayContext.swift +++ b/PadelClub/Utils/DisplayContext.swift @@ -27,6 +27,24 @@ enum MatchViewStyle { case feedStyle // vue programmation case plainStyle // vue detail case tournamentResultStyle //vue resultat tournoi + case followUpStyle // vue normal + + func displayRestingTime() -> Bool { + switch self { + case .standardStyle: + return false + case .sectionedStandardStyle: + return false + case .feedStyle: + return false + case .plainStyle: + return false + case .tournamentResultStyle: + return false + case .followUpStyle: + return true + } + } } struct DeviceHelper { diff --git a/PadelClub/Views/GroupStage/GroupStageView.swift b/PadelClub/Views/GroupStage/GroupStageView.swift index 8a2e134..292347b 100644 --- a/PadelClub/Views/GroupStage/GroupStageView.swift +++ b/PadelClub/Views/GroupStage/GroupStageView.swift @@ -28,6 +28,8 @@ struct GroupStageView: View { var body: some View { List { + let playedMatches = groupStage.playedMatches() + Section { GroupStageScoreView(groupStage: groupStage, sortByScore: sortingMode == .score) } header: { @@ -49,8 +51,12 @@ struct GroupStageView: View { } } .headerProminence(.increased) + .onChange(of: playedMatches) { + if groupStage.hasEnded() { + sortingMode = .score + } + } - let playedMatches = groupStage.playedMatches() let runningMatches = groupStage.runningMatches(playedMatches: playedMatches) MatchListView(section: "en cours", matches: groupStage.runningMatches(playedMatches: playedMatches), hideWhenEmpty: true) let availableToStart = groupStage.availableToStart(playedMatches: playedMatches, in: runningMatches) diff --git a/PadelClub/Views/Match/Components/MatchDateView.swift b/PadelClub/Views/Match/Components/MatchDateView.swift index 8034631..7c37d38 100644 --- a/PadelClub/Views/Match/Components/MatchDateView.swift +++ b/PadelClub/Views/Match/Components/MatchDateView.swift @@ -17,13 +17,15 @@ struct MatchDateView: View { private var isReady: Bool private var hasWalkoutTeam: Bool private var hasEnded: Bool + private let updatedField: Int? - init(match: Match, showPrefix: Bool) { + init(match: Match, showPrefix: Bool, updatedField: Int? = nil) { self.match = match self.showPrefix = showPrefix self.isReady = match.isReady() self.hasWalkoutTeam = match.hasWalkoutTeam() self.hasEnded = match.hasEnded() + self.updatedField = updatedField } var body: some View { @@ -34,21 +36,33 @@ struct MatchDateView: View { let estimatedDuration = match.getDuration() if match.startDate == nil && isReady { Button("Démarrer") { + if let updatedField { + match.setCourt(updatedField) + } match.startDate = Date() match.confirmed = true _save() } Button("Démarrer dans 5 minutes") { + if let updatedField { + match.setCourt(updatedField) + } match.startDate = Calendar.current.date(byAdding: .minute, value: 5, to: Date()) match.confirmed = true _save() } Button("Démarrer dans 15 minutes") { + if let updatedField { + match.setCourt(updatedField) + } match.startDate = Calendar.current.date(byAdding: .minute, value: 15, to: Date()) match.confirmed = true _save() } Button("Démarrer dans \(estimatedDuration.formatted()) minutes") { + if let updatedField { + match.setCourt(updatedField) + } match.startDate = Calendar.current.date(byAdding: .minute, value: estimatedDuration, to: Date()) match.confirmed = true _save() @@ -56,6 +70,9 @@ struct MatchDateView: View { } else { if isReady { Button("Démarrer maintenant") { + if let updatedField { + match.setCourt(updatedField) + } match.startDate = Date() match.endDate = nil match.confirmed = true @@ -63,6 +80,9 @@ struct MatchDateView: View { } } else { Button("Décaler de \(estimatedDuration) minutes") { + if let updatedField { + match.setCourt(updatedField) + } match.cleanScheduleAndSave(match.startDate?.addingTimeInterval(Double(estimatedDuration) * 60.0)) } } diff --git a/PadelClub/Views/Match/Components/PlayerBlockView.swift b/PadelClub/Views/Match/Components/PlayerBlockView.swift index 5f5b278..864a734 100644 --- a/PadelClub/Views/Match/Components/PlayerBlockView.swift +++ b/PadelClub/Views/Match/Components/PlayerBlockView.swift @@ -15,8 +15,9 @@ struct PlayerBlockView: View { let width: CGFloat let teamScore: TeamScore? let isWalkOut: Bool + let displayRestingTime: Bool - init(match: Match, teamPosition: TeamPosition, color: Color, width: CGFloat) { + init(match: Match, teamPosition: TeamPosition, color: Color, width: CGFloat, displayRestingTime: Bool) { self.match = match self.teamPosition = teamPosition let theTeam = match.team(teamPosition) @@ -26,6 +27,7 @@ struct PlayerBlockView: View { let theTeamScore = match.teamScore(ofTeam: theTeam) self.teamScore = theTeamScore self.isWalkOut = theTeamScore?.isWalkOut() == true + self.displayRestingTime = displayRestingTime } var names: [String]? { @@ -77,6 +79,18 @@ struct PlayerBlockView: View { Text(_defaultLabel()).foregroundStyle(.secondary).lineLimit(1) } } + + if displayRestingTime, let restingTime = team?.restingTime()?.timeIntervalSinceNow, let value = Date.hourMinuteFormatter.string(from: restingTime * -1) { + if restingTime > -300 { + Text("vient de finir") + .font(.footnote) + .foregroundStyle(.secondary) + } else { + Text("en repos depuis " + value) + .font(.footnote) + .foregroundStyle(.secondary) + } + } } .bold(hasWon) Spacer() diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 300a5fa..a4a63bb 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -33,6 +33,8 @@ struct MatchDetailView: View { @State var showSubscriptionView: Bool = false @State var showUserCreationView: Bool = false + @State private var presentFollowUpMatch: Bool = false + @State private var dismissWhenPresentFollowUpMatchIsDismissed: Bool = false var tournamentStore: TournamentStore { return match.tournamentStore @@ -50,7 +52,7 @@ struct MatchDetailView: View { var match: Match - init(match: Match, matchViewStyle: MatchViewStyle = .standardStyle) { + init(match: Match, matchViewStyle: MatchViewStyle = .standardStyle, updatedField: Int? = nil) { self.match = match self.matchViewStyle = matchViewStyle @@ -69,7 +71,7 @@ struct MatchDetailView: View { _endDate = State(wrappedValue: endDate) } - if let courtIndex = match.courtIndex { + if let courtIndex = updatedField ?? match.courtIndex { _fieldSetup = State(wrappedValue: .field(courtIndex)) } } @@ -153,9 +155,17 @@ struct MatchDetailView: View { } } }) + .sheet(isPresented: $presentFollowUpMatch, onDismiss: { + if dismissWhenPresentFollowUpMatchIsDismissed { + dismiss() + } + }) { + FollowUpMatchView(match: match, dismissWhenPresentFollowUpMatchIsDismissed: $dismissWhenPresentFollowUpMatchIsDismissed) + .tint(.master) + } .sheet(item: $scoreType, onDismiss: { if match.hasEnded() { - dismiss() + presentFollowUpMatch = true } }) { scoreType in let matchDescriptor = MatchDescriptor(match: match) @@ -402,13 +412,11 @@ struct MatchDetailView: View { Text("Partage sur les réseaux sociaux") } -// if let followUpMatch = match.followUpMatch { -// Section { -// MatchRowView(match: followUpMatch) -// } header: { -// Text("à suivre terrain \(match.fieldIndex)") -// } -// } + Section { + RowButtonView("Match à suivre") { + presentFollowUpMatch = true + } + } } var editionView: some View { diff --git a/PadelClub/Views/Match/MatchRowView.swift b/PadelClub/Views/Match/MatchRowView.swift index 954da7f..1cde456 100644 --- a/PadelClub/Views/Match/MatchRowView.swift +++ b/PadelClub/Views/Match/MatchRowView.swift @@ -13,6 +13,7 @@ struct MatchRowView: View { @State var match: Match let matchViewStyle: MatchViewStyle var title: String? = nil + var updatedField: Int? = nil @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @@ -58,9 +59,9 @@ struct MatchRowView: View { // }) NavigationLink { - MatchDetailView(match: match, matchViewStyle: matchViewStyle) + MatchDetailView(match: match, matchViewStyle: matchViewStyle, updatedField: updatedField) } label: { - MatchSummaryView(match: match, matchViewStyle: matchViewStyle, title: title) + MatchSummaryView(match: match, matchViewStyle: matchViewStyle, title: title, updatedField: updatedField) .contextMenu { NavigationLink { EditSharingView(match: match) diff --git a/PadelClub/Views/Match/MatchSummaryView.swift b/PadelClub/Views/Match/MatchSummaryView.swift index 5b890f0..fc03111 100644 --- a/PadelClub/Views/Match/MatchSummaryView.swift +++ b/PadelClub/Views/Match/MatchSummaryView.swift @@ -18,14 +18,16 @@ struct MatchSummaryView: View { let padding: CGFloat let color: Color let width: CGFloat + let updatedField: Int? - init(match: Match, matchViewStyle: MatchViewStyle, title: String? = nil) { + init(match: Match, matchViewStyle: MatchViewStyle, title: String? = nil, updatedField: Int? = nil) { self.match = match self.matchViewStyle = matchViewStyle self.padding = matchViewStyle == .plainStyle ? 0 : 8 self.spacing = matchViewStyle == .plainStyle ? 8 : 0 self.width = matchViewStyle == .plainStyle ? 1 : 2 self.color = Color(white: 0.9) + self.updatedField = updatedField if let groupStage = match.groupStageObject { self.roundTitle = groupStage.groupStageTitle(.title) @@ -69,14 +71,14 @@ struct MatchSummaryView: View { HStack(spacing: 0) { VStack(alignment: .leading, spacing: spacing) { - PlayerBlockView(match: match, teamPosition: .one, color: color, width: width) + PlayerBlockView(match: match, teamPosition: .one, color: color, width: width, displayRestingTime: matchViewStyle.displayRestingTime()) .padding(padding) if width == 1 { Divider() } else { Divider().frame(height: width).overlay(color) } - PlayerBlockView(match: match, teamPosition: .two, color: color, width: width) + PlayerBlockView(match: match, teamPosition: .two, color: color, width: width, displayRestingTime: matchViewStyle.displayRestingTime()) .padding(padding) } } @@ -90,7 +92,7 @@ struct MatchSummaryView: View { if matchViewStyle != .plainStyle { HStack { Spacer() - MatchDateView(match: match, showPrefix: matchViewStyle == .tournamentResultStyle) + MatchDateView(match: match, showPrefix: matchViewStyle == .tournamentResultStyle, updatedField: updatedField) } } } diff --git a/PadelClub/Views/Score/FollowUpMatchView.swift b/PadelClub/Views/Score/FollowUpMatchView.swift new file mode 100644 index 0000000..b65c800 --- /dev/null +++ b/PadelClub/Views/Score/FollowUpMatchView.swift @@ -0,0 +1,133 @@ +// +// FollowUpMatchView.swift +// PadelClub +// +// Created by razmig on 11/10/2024. +// + +import SwiftUI + +struct FollowUpMatchView: View { + @EnvironmentObject var dataStore: DataStore + @Environment(\.dismiss) private var dismiss + let match: Match + let readyMatches: [Match] + let isFree: Bool + + @State private var sortingMode: SortingMode = .index + @State private var selectedCourt: Int? + @State private var checkCanPlay: Bool = false + @Binding var dismissWhenPresentFollowUpMatchIsDismissed: Bool + + enum SortingMode: Int, Identifiable, CaseIterable { + var id: Int { self.rawValue } + case index + case restingTime + case court + + func localizedSortingModeLabel() -> String { + switch self { + case .index: + return "Ordre" + case .court: + return "Terrain" + case .restingTime: + return "Temps de repos" + } + } + } + + init(match: Match, dismissWhenPresentFollowUpMatchIsDismissed: Binding) { + _dismissWhenPresentFollowUpMatchIsDismissed = dismissWhenPresentFollowUpMatchIsDismissed + self.match = match + _selectedCourt = .init(wrappedValue: match.courtIndex) + let currentTournament = match.currentTournament() + let allMatches = currentTournament?.allMatches() ?? [] + let runningMatches = currentTournament?.runningMatches(allMatches) ?? [] + let readyMatches = currentTournament?.readyMatches(allMatches) ?? [] + self.readyMatches = currentTournament?.availableToStart(readyMatches, in: runningMatches, checkCanPlay: false) ?? [] + self.isFree = currentTournament?.isFree() ?? true + } + + var sortedMatches: [Match] { + switch sortingMode { + case .index: + return readyMatches + case .restingTime: + return readyMatches.sorted(by: \.restingTimeForSorting) + case .court: + return readyMatches.sorted(using: [.keyPath(\.courtIndexForSorting), .keyPath(\.restingTimeForSorting)], order: .ascending) + } + } + + var body: some View { + NavigationStack { + List { + Section { + Picker(selection: $selectedCourt) { + Text("Aucun").tag(nil as Int?) + if let tournament = match.currentTournament() { + ForEach(0..