diff --git a/PadelClub/ViewModel/SearchViewModel.swift b/PadelClub/ViewModel/SearchViewModel.swift index aeb3039..5a03c11 100644 --- a/PadelClub/ViewModel/SearchViewModel.swift +++ b/PadelClub/ViewModel/SearchViewModel.swift @@ -27,7 +27,8 @@ class SearchViewModel: ObservableObject, Identifiable { @Published var sortOption: SortOption = .rank @Published var selectedPlayers: Set = Set() @Published var filterSelectionEnabled: Bool = false - var forcedSearch = false + @Published var isPresented: Bool = false + var mostRecentDate: Date? = nil var selectionIsOver: Bool { diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index fa5e9fe..3cae2de 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -48,11 +48,11 @@ struct MainView: View { await self._checkSourceFileAvailability() await self._downloadPreviousDate() } - .refreshable { - Task { - await self._checkSourceFileAvailability() - } - } +// .refreshable { +// Task { +// await self._checkSourceFileAvailability() +// } +// } .overlay(alignment: .bottom) { if importingFiles { _activityStatusBoxView() diff --git a/PadelClub/Views/Player/Components/PlayerPopoverView.swift b/PadelClub/Views/Player/Components/PlayerPopoverView.swift index b1b7a12..2e33a63 100644 --- a/PadelClub/Views/Player/Components/PlayerPopoverView.swift +++ b/PadelClub/Views/Player/Components/PlayerPopoverView.swift @@ -29,10 +29,10 @@ struct PlayerPopoverView: View { @FocusState private var licenseIsFocused: Bool @FocusState private var amountIsFocused: Bool - static var source: String? - - init(sex: Int, requiredField: [PlayerCreationField] = [.firstName, .lastName], creationCompletionHandler: @escaping (PlayerRegistration) -> Void) { - let source = PlayerPopoverView.source + @State private var source: String? + + init(source: String?, sex: Int, requiredField: [PlayerCreationField] = [.firstName, .lastName], creationCompletionHandler: @escaping (PlayerRegistration) -> Void) { + let source = source if let source { let words = source.components(separatedBy: .whitespaces) if words.isEmpty == false { @@ -51,29 +51,28 @@ struct PlayerPopoverView: View { self.requiredField = requiredField self.creationCompletionHandler = creationCompletionHandler - self.pasteBoard = UIPasteboard.general.string + _source = State(wrappedValue: source) } - @State private var pasteBoard: String? - var body: some View { NavigationStack { List { - if let pasteBoard { + if let source { Section { - Text(pasteBoard).foregroundColor(.clear).padding(8) + Text(source).foregroundColor(.clear).padding(8) .frame(maxWidth: .infinity) .overlay( - TextEditor(text: .constant(pasteBoard)) + TextEditor(text: .constant(source)) ) .frame(minHeight: 20.0) } header: { HStack { Spacer() - Button { - self.pasteBoard = "" + Button(role: .cancel) { + self.source = nil } label: { Text("effacer le contenu du presse-papier") + .font(.caption) } .buttonStyle(.borderless) } @@ -95,6 +94,8 @@ struct PlayerPopoverView: View { Spacer() TextField("Prénom", text: $firstName) .submitLabel(.next) + .keyboardType(.alphabet) + .textInputAutocapitalization(.words) .focused($firstNameIsFocused) .onSubmit { firstName = firstName.trimmed @@ -106,8 +107,10 @@ struct PlayerPopoverView: View { Text("Nom").foregroundStyle(.secondary) Spacer() TextField("Nom", text: $lastName) - .focused($lastNameIsFocused) .submitLabel(.next) + .textInputAutocapitalization(.words) + .keyboardType(.alphabet) + .focused($lastNameIsFocused) .onSubmit { lastName = lastName.trimmed licenseIsFocused = true @@ -119,7 +122,8 @@ struct PlayerPopoverView: View { Spacer() TextField("Licence", text: $license) .focused($licenseIsFocused) - .keyboardType(.namePhonePad) + .textInputAutocapitalization(.never) + .keyboardType(.numberPad) .submitLabel(.next) .onSubmit { license = license.trimmed @@ -138,21 +142,23 @@ struct PlayerPopoverView: View { HStack { Text("Rang").foregroundStyle(.secondary) Spacer() - TextField("Non classé", value: $rank, format: .number) + TextField("Non Classé" + (sex == 1 ? "" : "e"), value: $rank, format: .number) .focused($amountIsFocused) - .keyboardType(.asciiCapable) + .textInputAutocapitalization(.never) + .keyboardType(.numberPad) .submitLabel(.done) .fixedSize() } } header: { HStack { Spacer() - Button { + Button(role: .cancel) { let last = firstName firstName = lastName lastName = last } label: { Text("inverser nom & prénom") + .font(.caption) }.buttonStyle(.borderless) } .textCase(nil) @@ -192,6 +198,27 @@ struct PlayerPopoverView: View { dismiss() } } + + if amountIsFocused || licenseIsFocused { + ToolbarItem(placement: .keyboard) { + Button("Confirmer") { + if licenseIsFocused { + license = license.trimmed + if requiredField.contains(.license) { + if license.isLicenseNumber { + amountIsFocused = true + } else { + displayWrongLicenceError = true + } + } else { + amountIsFocused = true + } + } else { + amountIsFocused = false + } + } + } + } } .alert("Attention", isPresented: $displayWrongLicenceError) { Button("OK") { @@ -223,7 +250,7 @@ struct PlayerPopoverView: View { } #Preview { - PlayerPopoverView(sex: 1) { player in + PlayerPopoverView(source: "Razmig Sarkissian", sex: 1) { player in } } diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index c32a32d..05378b2 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -34,6 +34,7 @@ struct SelectablePlayerListView: View { self.playerSelectionAction = playerSelectionAction self.contentUnavailableAction = contentUnavailableAction let searchViewModel = SearchViewModel() + searchViewModel.isPresented = allowSelection != 0 searchViewModel.user = user searchViewModel.allowSelection = allowSelection searchViewModel.codeClub = fromPlayer?.clubCode ?? codeClub @@ -65,9 +66,11 @@ struct SelectablePlayerListView: View { } MySearchView(searchViewModel: searchViewModel, contentUnavailableAction: contentUnavailableAction) .environment(\.editMode, searchViewModel.allowMultipleSelection ? .constant(.active) : .constant(.inactive)) - .searchable(text: $searchViewModel.debouncableText, tokens: $searchViewModel.tokens, suggestedTokens: $searchViewModel.suggestedTokens, placement: .navigationBarDrawer(displayMode: .always), prompt: searchViewModel.prompt(forDataSet: searchViewModel.dataSet), token: { token in + .searchable(text: $searchViewModel.debouncableText, tokens: $searchViewModel.tokens, suggestedTokens: $searchViewModel.suggestedTokens, isPresented: $searchViewModel.isPresented, placement: .navigationBarDrawer(displayMode: .always), prompt: searchViewModel.prompt(forDataSet: searchViewModel.dataSet), token: { token in Text(token.shortLocalizedLabel) }) + .keyboardType(.alphabet) + .autocorrectionDisabled() // .searchSuggestions({ // ForEach(searchViewModel.suggestedTokens) { token in // Button { @@ -261,24 +264,6 @@ struct MySearchView: View { .listStyle(.grouped) .headerProminence(.increased) .scrollDismissesKeyboard(.immediately) - .onChange(of: searchViewModel.searchText) { - search() - } - .onChange(of: searchViewModel.filterOption) { - search() - } - .onChange(of: searchViewModel.dataSet) { - search() - } - .onChange(of: searchViewModel.sortOption) { - sort() - } - .onChange(of: searchViewModel.ascending) { - sort() - } - .onChange(of: searchViewModel.hideAssimilation) { - search() - } } var specificBugFixUUID: String { @@ -311,6 +296,7 @@ struct MySearchView: View { } header: { Text(searchViewModel.selectedPlayers.count.formatted() + " " + searchViewModel.filterOption.localizedPlayerLabel + searchViewModel.selectedPlayers.count.pluralSuffix) } + } else if (searchViewModel.isPresented == true && searchViewModel.dataSet == .national && searchViewModel.searchText.isEmpty == true) { } else { Section { ForEach(players, id: \.self) { player in @@ -455,12 +441,4 @@ struct MySearchView: View { } } } - - private func search() { - //players.nsPredicate = searchViewModel.predicate() - } - - private func sort() { - //players.nsSortDescriptors = searchViewModel.nsSortDescriptors() - } } diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index f786e2a..98e7903 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -23,13 +23,14 @@ struct InscriptionManagerView: View { @State private var presentPlayerCreation: Bool = false @State private var presentImportView: Bool = false @State private var createdPlayers: Set = Set() - @State private var testCreatedPlayers: Set = Set() + @State private var createdPlayerIds: Set = Set() @State private var editedTeam: TeamRegistration? @State private var pasteString: String? @State private var currentRankSourceDate: Date? @State private var confirmUpdateRank = false @State private var updatingRank = false - + @State private var selectionSearchField: String? + let slideToDeleteTip = SlideToDeleteTip() let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip() let fileTip = InscriptionManagerFileInputTip() @@ -54,6 +55,10 @@ struct InscriptionManagerView: View { } } + private func _searchSource() -> String? { + selectionSearchField ?? pasteString + } + private func _pastePredicate(pasteField: String, mostRecentDate: Date?) -> NSPredicate? { let text = pasteField.canonicalVersion @@ -95,7 +100,7 @@ struct InscriptionManagerView: View { private func _currentSelection() -> Set { var currentSelection = Set() - testCreatedPlayers.compactMap { id in + createdPlayerIds.compactMap { id in fetchPlayers.first(where: { id == $0.license }) }.forEach { player in let player = PlayerRegistration(importedPlayer: player) @@ -103,7 +108,7 @@ struct InscriptionManagerView: View { currentSelection.insert(player) } - testCreatedPlayers.compactMap { id in + createdPlayerIds.compactMap { id in createdPlayers.first(where: { id == $0.id }) }.forEach { currentSelection.insert($0) @@ -114,22 +119,22 @@ struct InscriptionManagerView: View { private func _createTeam() { tournament.addTeam(_currentSelection()) createdPlayers.removeAll() - testCreatedPlayers.removeAll() + createdPlayerIds.removeAll() pasteString = nil } private func _updateTeam() { editedTeam?.updatePlayers(_currentSelection()) createdPlayers.removeAll() - testCreatedPlayers.removeAll() + createdPlayerIds.removeAll() pasteString = nil editedTeam = nil } private func _buildingTeamView() -> some View { - List(selection: $testCreatedPlayers) { + List(selection: $createdPlayerIds) { Section { - ForEach(testCreatedPlayers.sorted(), id: \.self) { id in + ForEach(createdPlayerIds.sorted(), id: \.self) { id in if let p = createdPlayers.first(where: { $0.id == id }) { PlayerView(player: p).tag(p.id) } @@ -143,7 +148,7 @@ struct InscriptionManagerView: View { } if editedTeam == nil { - if testCreatedPlayers.isEmpty { + if createdPlayerIds.isEmpty { RowButtonView(title: "Bloquer une place") { _createTeam() } @@ -169,7 +174,7 @@ struct InscriptionManagerView: View { Button("effacer", role: .destructive) { self.pasteString = nil self.createdPlayers.removeAll() - self.testCreatedPlayers.removeAll() + self.createdPlayerIds.removeAll() } .buttonStyle(.borderless) } @@ -204,7 +209,7 @@ struct InscriptionManagerView: View { .onReceive(fetchPlayers.publisher.count()) { _ in // <-- here if let pasteString, count == 2 { fetchPlayers.filter { $0.hitForSearch(pasteString) >= hitTarget }.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) }).forEach { player in - testCreatedPlayers.insert(player.license!) + createdPlayerIds.insert(player.license!) } } } @@ -307,7 +312,7 @@ struct InscriptionManagerView: View { editedTeam = team team.unsortedPlayers().forEach { player in createdPlayers.insert(player) - testCreatedPlayers.insert(player.id) + createdPlayerIds.insert(player.id) } } Divider() @@ -325,12 +330,14 @@ struct InscriptionManagerView: View { } } .searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites")) + .keyboardType(.alphabet) + .autocorrectionDisabled() } var body: some View { VStack(spacing: 0) { _managementView() - if testCreatedPlayers.isEmpty == false || pasteString != nil || editedTeam != nil { + if createdPlayerIds.isEmpty == false || pasteString != nil || editedTeam != nil { _buildingTeamView() } else if tournament.unsortedTeams().isEmpty { _inscriptionTipsView() @@ -338,22 +345,29 @@ struct InscriptionManagerView: View { _teamRegisteredView() } } - .sheet(isPresented: $presentPlayerSearch) { + .sheet(isPresented: $presentPlayerSearch, onDismiss: { + selectionSearchField = nil + }) { NavigationStack { SelectablePlayerListView(allowSelection: -1, filterOption: _filterOption()) { players in + selectionSearchField = nil players.forEach { player in let newPlayer = PlayerRegistration(importedPlayer: player) newPlayer.setWeight(in: tournament) createdPlayers.insert(newPlayer) - testCreatedPlayers.insert(newPlayer.id) + createdPlayerIds.insert(newPlayer.id) } + } contentUnavailableAction: { searchViewModel in + selectionSearchField = searchViewModel.searchText + presentPlayerSearch = false + presentPlayerCreation = true } } } .sheet(isPresented: $presentPlayerCreation) { - PlayerPopoverView(sex: _addPlayerSex()) { p in + PlayerPopoverView(source: _searchSource(), sex: _addPlayerSex()) { p in createdPlayers.insert(p) - testCreatedPlayers.insert(p.id) + createdPlayerIds.insert(p.id) } } .sheet(isPresented: $presentImportView) { @@ -374,11 +388,11 @@ struct InscriptionManagerView: View { } .toolbar { - if testCreatedPlayers.isEmpty == false { + if createdPlayerIds.isEmpty == false { ToolbarItem(placement: .cancellationAction) { Button("Annuler", role: .cancel) { createdPlayers.removeAll() - testCreatedPlayers.removeAll() + createdPlayerIds.removeAll() } } } @@ -443,7 +457,7 @@ struct InscriptionManagerView: View { } } } - .navigationBarBackButtonHidden(testCreatedPlayers.isEmpty == false) + .navigationBarBackButtonHidden(createdPlayerIds.isEmpty == false) .toolbarBackground(.visible, for: .navigationBar) .navigationTitle("Inscriptions") .navigationBarTitleDisplayMode(.inline)