From 6e68226ac7ce89d541378030df13f926b84d57c0 Mon Sep 17 00:00:00 2001 From: Raz Date: Sun, 29 Sep 2024 13:22:31 +0200 Subject: [PATCH] fix some stuff on team selection and add a special animation mode for selecting players from club --- PadelClub.xcodeproj/project.pbxproj | 4 +- .../Data/Federal/FederalTournament.swift | 10 ++++- .../Federal/FederalTournamentHolder.swift | 4 +- PadelClub/Data/Tournament.swift | 41 ++++++++++++++++- PadelClub/Extensions/String+Extensions.swift | 1 + PadelClub/Utils/DisplayContext.swift | 38 ++++++++++++++++ PadelClub/Utils/PadelRule.swift | 8 +++- .../GroupStageTeamReplacementView.swift | 2 +- .../Navigation/Toolbox/ToolboxView.swift | 2 +- .../Views/Shared/ImportedPlayerView.swift | 1 + .../Shared/SelectablePlayerListView.swift | 12 +++-- .../Views/Tournament/Screen/AddTeamView.swift | 44 +++++++++++++++---- .../Shared/TournamentCellView.swift | 15 ++++--- 13 files changed, 156 insertions(+), 26 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 831567e..2bc0a9a 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3134,7 +3134,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3179,7 +3179,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Federal/FederalTournament.swift b/PadelClub/Data/Federal/FederalTournament.swift index 63595b5..e61f36c 100644 --- a/PadelClub/Data/Federal/FederalTournament.swift +++ b/PadelClub/Data/Federal/FederalTournament.swift @@ -210,9 +210,17 @@ extension FederalTournament: FederalTournamentHolder { nomClub ?? villeEngagement ?? installation?.nom ?? "" } - func subtitleLabel() -> String { + func subtitleLabel(forBuild build: any TournamentBuildHolder) -> String { "" } + + func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String { + build.level.localizedLevelLabel(displayStyle) + } + + func displayAgeAndCategory(forBuild build: any TournamentBuildHolder) -> Bool { + true + } } // MARK: - CategorieAge diff --git a/PadelClub/Data/Federal/FederalTournamentHolder.swift b/PadelClub/Data/Federal/FederalTournamentHolder.swift index b2e3890..4ee6bd6 100644 --- a/PadelClub/Data/Federal/FederalTournamentHolder.swift +++ b/PadelClub/Data/Federal/FederalTournamentHolder.swift @@ -14,9 +14,11 @@ protocol FederalTournamentHolder { var codeClub: String? { get } var tournaments: [any TournamentBuildHolder] { get } func clubLabel() -> String - func subtitleLabel() -> String + func subtitleLabel(forBuild build: any TournamentBuildHolder) -> String var dayDuration: Int { get } var dayPeriod: DayPeriod { get } + func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String + func displayAgeAndCategory(forBuild build: any TournamentBuildHolder) -> Bool } extension FederalTournamentHolder { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 89008b0..9edcef5 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2251,6 +2251,20 @@ extension Tournament: Hashable { } extension Tournament: FederalTournamentHolder { + + func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String { + if isAnimation() { + if let name { + return name.trunc(length: DeviceHelper.charLength()) + } else if build.age == .unlisted, build.category == .unlisted { + return build.level.localizedLevelLabel(.title) + } else { + return build.level.localizedLevelLabel(displayStyle) + } + } + return build.level.localizedLevelLabel(displayStyle) + } + var codeClub: String? { club()?.code } @@ -2261,8 +2275,18 @@ extension Tournament: FederalTournamentHolder { locationLabel() } - func subtitleLabel() -> String { - subtitle() + func subtitleLabel(forBuild build: any TournamentBuildHolder) -> String { + if isAnimation() { + if displayAgeAndCategory(forBuild: build) == false { + return [build.category.localizedLabel(), build.age.localizedLabel()].filter({ $0.isEmpty == false }).joined(separator: " ") + } else if name != nil { + return build.level.localizedLevelLabel(.title) + } else { + return "" + } + } else { + return subtitle() + } } var tournaments: [any TournamentBuildHolder] { @@ -2280,6 +2304,19 @@ extension Tournament: FederalTournamentHolder { return .weekend } } + + func displayAgeAndCategory(forBuild build: any TournamentBuildHolder) -> Bool { + if isAnimation() { + if let name, name.count < DeviceHelper.maxCharacter() { + return true + } else if build.age == .unlisted, build.category == .unlisted { + return true + } else { + return DeviceHelper.isBigScreen() + } + } + return true + } } extension Tournament: TournamentBuildHolder { diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift index ee29135..cce72bc 100644 --- a/PadelClub/Extensions/String+Extensions.swift +++ b/PadelClub/Extensions/String+Extensions.swift @@ -10,6 +10,7 @@ import Foundation // MARK: - Trimming and stuff extension String { func trunc(length: Int, trailing: String = "…") -> String { + if length <= 0 { return self } return (self.count > length) ? self.prefix(length) + trailing : self } diff --git a/PadelClub/Utils/DisplayContext.swift b/PadelClub/Utils/DisplayContext.swift index 1e99890..a5aaebe 100644 --- a/PadelClub/Utils/DisplayContext.swift +++ b/PadelClub/Utils/DisplayContext.swift @@ -6,6 +6,7 @@ // import Foundation +import UIKit enum DisplayContext { case addition @@ -27,3 +28,40 @@ enum MatchViewStyle { case plainStyle // vue detail case tournamentResultStyle //vue resultat tournoi } + +struct DeviceHelper { + static func isBigScreen() -> Bool { + switch UIDevice.current.userInterfaceIdiom { + case .pad: // iPads + return true + case .phone: // iPhones (you can add more cases here for large vs small phones) + if UIScreen.main.bounds.size.width > 375 { // iPhone X, 11, 12, 13 Pro Max etc. + return true // large phones + } else { + return false // smaller phones + } + default: + return false // Other devices (Apple Watch, TV, etc.) + } + + } + + static func maxCharacter() -> Int { + switch UIDevice.current.userInterfaceIdiom { + case .pad: // iPads + return 30 + case .phone: // iPhones (you can add more cases here for large vs small phones) + if UIScreen.main.bounds.size.width > 375 { // iPhone X, 11, 12, 13 Pro Max etc. + return 15 // large phones + } else { + return 9 // smaller phones + } + default: + return 9 // Other devices (Apple Watch, TV, etc.) + } + } + + static func charLength() -> Int { + isBigScreen() ? 0 : 15 + } +} diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift index 801276f..e809629 100644 --- a/PadelClub/Utils/PadelRule.swift +++ b/PadelClub/Utils/PadelRule.swift @@ -488,7 +488,13 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable { } func localizedLevelLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if self == .unlisted { return displayStyle == .title ? "Animation" : "Anim." } + if self == .unlisted { + if DeviceHelper.isBigScreen() { + return "Animation" + } else { + return displayStyle == .title ? "Animation" : "Anim." + } + } return String(describing: self).capitalized } diff --git a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift index 0ab01e9..cf613a4 100644 --- a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift +++ b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift @@ -123,7 +123,7 @@ struct GroupStageTeamReplacementView: View { private func _searchLinkView(_ teamRange: TeamRegistration.TeamRange) -> some View { NavigationStack { let tournament = team.tournamentObject() - SelectablePlayerListView(searchField: _searchableRange(teamRange), dataSet: .favoriteClubs, filterOption: tournament?.tournamentCategory.playerFilterOption ?? .all, sortOption: .rank, showFemaleInMaleAssimilation: true, tokens: [_searchToken(teamRange)], hidePlayers: tournament?.selectedPlayers().compactMap { $0.licenceId }) + SelectablePlayerListView(isPresented: false, searchField: _searchableRange(teamRange), dataSet: .favoriteClubs, filterOption: tournament?.tournamentCategory.playerFilterOption ?? .all, sortOption: .rank, showFemaleInMaleAssimilation: true, tokens: [_searchToken(teamRange)], hidePlayers: tournament?.selectedPlayers().compactMap { $0.licenceId }) } } diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 0a19da3..8c9dd36 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -129,7 +129,7 @@ struct ToolboxView: View { Section { NavigationLink { - SelectablePlayerListView() + SelectablePlayerListView(isPresented: false) } label: { Label("Rechercher un joueur", systemImage: "person.fill.viewfinder") } diff --git a/PadelClub/Views/Shared/ImportedPlayerView.swift b/PadelClub/Views/Shared/ImportedPlayerView.swift index 6de85dd..f81ae56 100644 --- a/PadelClub/Views/Shared/ImportedPlayerView.swift +++ b/PadelClub/Views/Shared/ImportedPlayerView.swift @@ -82,6 +82,7 @@ struct ImportedPlayerView: View { } } .lineLimit(1) + .truncationMode(.tail) if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { HStack(alignment: .top, spacing: 2) { diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index c555a1a..6c3b1d9 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -34,7 +34,7 @@ struct SelectablePlayerListView: View { return URL.importDateFormatter.date(from: lastDataSource) } - init(allowSelection: Int = 0, searchField: String? = nil, dataSet: DataSet = .national, filterOption: PlayerFilterOption = .all, hideAssimilation: Bool = false, ascending: Bool = true, sortOption: SortOption = .rank, fromPlayer: FederalPlayer? = nil, codeClub: String? = nil, ligue: String? = nil, showFemaleInMaleAssimilation: Bool = false, tokens: [SearchToken] = [], hidePlayers: [String]? = nil, playerSelectionAction: PlayerSelectionAction? = nil, contentUnavailableAction: ContentUnavailableAction? = nil) { + init(allowSelection: Int = 0, isPresented: Bool = true, searchField: String? = nil, dataSet: DataSet = .national, filterOption: PlayerFilterOption = .all, hideAssimilation: Bool = false, ascending: Bool = true, sortOption: SortOption = .rank, fromPlayer: FederalPlayer? = nil, codeClub: String? = nil, ligue: String? = nil, showFemaleInMaleAssimilation: Bool = false, tokens: [SearchToken] = [], hidePlayers: [String]? = nil, playerSelectionAction: PlayerSelectionAction? = nil, contentUnavailableAction: ContentUnavailableAction? = nil) { self.allowSelection = allowSelection self.playerSelectionAction = playerSelectionAction self.contentUnavailableAction = contentUnavailableAction @@ -45,7 +45,7 @@ struct SelectablePlayerListView: View { searchViewModel.debouncableText = searchField ?? "" searchViewModel.showFemaleInMaleAssimilation = showFemaleInMaleAssimilation searchViewModel.searchText = searchField ?? "" - searchViewModel.isPresented = allowSelection != 0 + searchViewModel.isPresented = isPresented searchViewModel.allowSelection = allowSelection searchViewModel.codeClub = fromPlayer?.clubCode ?? codeClub searchViewModel.clubName = nil @@ -221,7 +221,7 @@ struct SelectablePlayerListView: View { if searchViewModel.selectedPlayers.isEmpty && searchViewModel.filterSelectionEnabled { searchViewModel.filterSelectionEnabled = false - } else { + } else if searchViewModel.allowSelection >= searchViewModel.selectedPlayers.count { searchViewModel.filterSelectionEnabled = true } } @@ -430,6 +430,7 @@ struct MySearchView: View { } } .lineLimit(1) + .truncationMode(.tail) if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { HStack(alignment: .top, spacing: 2) { @@ -540,6 +541,7 @@ struct MySearchView: View { } } .lineLimit(1) + .truncationMode(.tail) if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { HStack(alignment: .top, spacing: 2) { @@ -654,6 +656,7 @@ struct MySearchView: View { } } .lineLimit(1) + .truncationMode(.tail) if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { HStack(alignment: .top, spacing: 2) { @@ -763,6 +766,7 @@ struct MySearchView: View { } } .lineLimit(1) + .truncationMode(.tail) if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { HStack(alignment: .top, spacing: 2) { @@ -874,6 +878,7 @@ struct MySearchView: View { } } .lineLimit(1) + .truncationMode(.tail) if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { HStack(alignment: .top, spacing: 2) { @@ -972,6 +977,7 @@ struct MySearchView: View { } } .lineLimit(1) + .truncationMode(.tail) if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { HStack(alignment: .top, spacing: 2) { diff --git a/PadelClub/Views/Tournament/Screen/AddTeamView.swift b/PadelClub/Views/Tournament/Screen/AddTeamView.swift index 518c8a7..431e7a1 100644 --- a/PadelClub/Views/Tournament/Screen/AddTeamView.swift +++ b/PadelClub/Views/Tournament/Screen/AddTeamView.swift @@ -45,9 +45,8 @@ struct AddTeamView: View { @State private var searchForHit: Int = 0 @State private var displayWarningNotEnoughCharacter: Bool = false @State private var testMessageIndex: Int = 0 - - let filterLimit : Int = 1000 - + @State private var presentLocalMultiplayerSearch: Bool = false + var tournamentStore: TournamentStore { return self.tournament.tournamentStore } @@ -97,7 +96,7 @@ struct AddTeamView: View { _buildingTeamView() } .onReceive(fetchPlayers.publisher.count()) { receivedCount in // <-- here - if receivedCount < filterLimit, let pasteString, pasteString.isEmpty == false, count == 2, autoSelect == true { + if let pasteString, pasteString.isEmpty == false, count == 2, autoSelect == true { fetchPlayers.filter { hitForSearch($0, pasteString) >= hitTarget }.sorted(by: { hitForSearch($0, pasteString) > hitForSearch($1, pasteString) }).forEach { player in createdPlayerIds.insert(player.license!) } @@ -142,6 +141,27 @@ struct AddTeamView: View { } message: { Text("Cette équipe existe déjà dans votre liste d'inscription.") } + .sheet(isPresented: $presentLocalMultiplayerSearch) { + NavigationStack { + SelectablePlayerListView(allowSelection: -1, isPresented: false, searchField: searchField, dataSet: .club, filterOption: _filterOption(), showFemaleInMaleAssimilation: tournament.tournamentCategory.showFemaleInMaleAssimilation) { players in + players.forEach { player in + let newPlayer = PlayerRegistration(importedPlayer: player) + newPlayer.setComputedRank(in: tournament) + createdPlayers = Set() + createdPlayerIds = Set() + createdPlayers.insert(newPlayer) + createdPlayerIds.insert(newPlayer.id) + _createTeam(checkDuplicates: false, checkHomonym: false) + } + + } contentUnavailableAction: { searchViewModel in + presentLocalMultiplayerSearch = false + selectionSearchField = searchViewModel.searchText + } + } + .tint(.master) + + } .sheet(isPresented: $presentPlayerSearch) { NavigationStack { SelectablePlayerListView(allowSelection: -1, searchField: searchField, filterOption: _filterOption(), showFemaleInMaleAssimilation: tournament.tournamentCategory.showFemaleInMaleAssimilation) { players in @@ -243,6 +263,16 @@ struct AddTeamView: View { } } + if tournament.isAnimation(), createdPlayers.isEmpty == true { + Section { + RowButtonView("Ajouter plusieurs joueurs du club") { + presentLocalMultiplayerSearch = true + } + } footer: { + Text("Crée une équipe par joueur sélectionné") + } + } + Section { RowButtonView("Créer un non classé / non licencié") { if let pasteString, pasteString.isEmpty == false { @@ -633,11 +663,7 @@ struct AddTeamView: View { } private func _sortedPlayers(searchFilteredPlayers: [ImportedPlayer], pasteString: String) -> [ImportedPlayer] { - if searchFilteredPlayers.count < filterLimit { - return searchFilteredPlayers.sorted(by: { hitForSearch($0, pasteString) > hitForSearch($1, pasteString) }) - } else { - return searchFilteredPlayers - } + return searchFilteredPlayers.sorted(by: { hitForSearch($0, pasteString) > hitForSearch($1, pasteString) }) } } diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index ed82548..06c6ce2 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -94,9 +94,9 @@ struct TournamentCellView: View { .font(.caption) } HStack(alignment: .bottom) { - Text(build.level.localizedLevelLabel()) + Text(tournament.tournamentTitle(displayStyle, forBuild: build)) .fontWeight(.semibold) - if displayStyle == .wide { + if displayStyle == .wide, tournament.displayAgeAndCategory(forBuild: build) { VStack(alignment: .leading, spacing: 0) { Text(build.category.localizedLabel()) Text(build.age.localizedLabel()) @@ -128,8 +128,14 @@ struct TournamentCellView: View { .font(displayStyle == .wide ? .title : .title3) if displayStyle == .wide { - HStack { - Text(tournament.durationLabel()) + HStack(alignment: .top) { + VStack(alignment: .leading) { + let sub = tournament.subtitleLabel(forBuild: build) + if sub.isEmpty == false { + Text(sub).lineLimit(1) + } + Text(tournament.durationLabel()) + } Spacer() if let tournament = tournament as? Tournament, tournament.isCanceled == false, let teamCount { let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() @@ -137,7 +143,6 @@ struct TournamentCellView: View { Text(word + teamCount.pluralSuffix) } } - Text(tournament.subtitleLabel()).lineLimit(1) } else { Text(build.category.localizedLabel()) Text(build.age.localizedLabel())