fix some stuff on team selection and add a special animation mode for selecting players from club

sync2
Raz 1 year ago
parent eca125ef73
commit 6e68226ac7
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 10
      PadelClub/Data/Federal/FederalTournament.swift
  3. 4
      PadelClub/Data/Federal/FederalTournamentHolder.swift
  4. 41
      PadelClub/Data/Tournament.swift
  5. 1
      PadelClub/Extensions/String+Extensions.swift
  6. 38
      PadelClub/Utils/DisplayContext.swift
  7. 8
      PadelClub/Utils/PadelRule.swift
  8. 2
      PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift
  9. 2
      PadelClub/Views/Navigation/Toolbox/ToolboxView.swift
  10. 1
      PadelClub/Views/Shared/ImportedPlayerView.swift
  11. 12
      PadelClub/Views/Shared/SelectablePlayerListView.swift
  12. 44
      PadelClub/Views/Tournament/Screen/AddTeamView.swift
  13. 15
      PadelClub/Views/Tournament/Shared/TournamentCellView.swift

@ -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;

@ -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

@ -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 {

@ -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 {

@ -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
}

@ -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
}
}

@ -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
}

@ -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 })
}
}

@ -129,7 +129,7 @@ struct ToolboxView: View {
Section {
NavigationLink {
SelectablePlayerListView()
SelectablePlayerListView(isPresented: false)
} label: {
Label("Rechercher un joueur", systemImage: "person.fill.viewfinder")
}

@ -82,6 +82,7 @@ struct ImportedPlayerView: View {
}
}
.lineLimit(1)
.truncationMode(.tail)
if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() {
HStack(alignment: .top, spacing: 2) {

@ -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) {

@ -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<PlayerRegistration>()
createdPlayerIds = Set<String>()
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) })
}
}

@ -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())

Loading…
Cancel
Save