From badd580de71ac268f35066d7f48979b035412135 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 12 Sep 2024 11:46:12 +0200 Subject: [PATCH 1/3] update de la vue publication --- PadelClub/Data/Tournament.swift | 2 +- .../Tournament/Screen/BroadcastView.swift | 60 ++++++++++--------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 35c94eb..e042f11 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -439,7 +439,7 @@ final class Tournament : ModelObject, Storable { } func isTournamentPublished() -> Bool { - return (Date() >= publishedTournamentDate() && canBePublished()) || publishTournament + return (Date() >= publishedTournamentDate()) || publishTournament } func areTeamsPublished() -> Bool { diff --git a/PadelClub/Views/Tournament/Screen/BroadcastView.swift b/PadelClub/Views/Tournament/Screen/BroadcastView.swift index 23fc7fe..661f69e 100644 --- a/PadelClub/Views/Tournament/Screen/BroadcastView.swift +++ b/PadelClub/Views/Tournament/Screen/BroadcastView.swift @@ -118,30 +118,6 @@ struct BroadcastView: View { if tournament.isPrivate == false { - Section { - let links : [PageLink] = [.teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast] - Picker(selection: $pageLink) { - ForEach(links) { pageLink in - Text(pageLink.localizedLabel()).tag(pageLink) - } - } label: { - Text("Choisir la page à partager") - } - .pickerStyle(.menu) - actionForURL(title: "Partager la page '" + pageLink.localizedLabel() + "'", url: tournament.shareURL(pageLink)) - } header: { - Text("Lien du tournoi à partager") - } - - Section { - let club = tournament.club() - actionForURL(title: (club == nil) ? "Aucun club indiqué pour ce tournoi" : club!.clubTitle(), description: "Page du club", url: club?.shareURL()) - actionForURL(title: "Padel Club", url: URLs.main.url) - } header: { - Text("Autres liens") - } - - Section { LabeledContent { if tournament.isTournamentPublished() { @@ -156,14 +132,10 @@ struct BroadcastView: View { Text("Publication prévue") } } - - if tournament.canBePublished() == false { - Text("Pour être visible automatiquement, le tournoi doit avoir été créé il y a 24h, avoir une structure et au moins 4 inscriptions.") - } } header: { Text("Information sur le tournoi") } footer: { - if Date() < tournament.publishedTournamentDate() || tournament.canBePublished() == false { + if Date() < tournament.publishedTournamentDate() || tournament.isTournamentPublished() == false { HStack { Spacer() FooterButtonView(tournament.publishTournament ? "masquer sur le site" : "publier maintenant") { @@ -300,6 +272,36 @@ struct BroadcastView: View { //todo waitinglist & info } } + .toolbar(content: { + ToolbarItem(placement: .topBarTrailing) { + Menu { + Section { + let links : [PageLink] = [.teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast] + Picker(selection: $pageLink) { + ForEach(links) { pageLink in + Text(pageLink.localizedLabel()).tag(pageLink) + } + } label: { + Text("Choisir la page à partager") + } + .pickerStyle(.menu) + actionForURL(title: "Partager la page '" + pageLink.localizedLabel() + "'", url: tournament.shareURL(pageLink)) + } header: { + Text("Lien du tournoi à partager") + } + + Section { + let club = tournament.club() + actionForURL(title: (club == nil) ? "Aucun club indiqué pour ce tournoi" : club!.clubTitle(), description: "Page du club", url: club?.shareURL()) + actionForURL(title: "Padel Club", url: URLs.main.url) + } header: { + Text("Autres liens") + } + } label: { + Label("Partager les liens", systemImage: "square.and.arrow.up") + } + } + }) .headerProminence(.increased) .navigationTitle("Publication") .navigationBarTitleDisplayMode(.inline) From 90aee596f14b599ecf5c2a03e24340f913d3ad86 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 12 Sep 2024 13:29:06 +0200 Subject: [PATCH 2/3] fix player search --- PadelClub/Extensions/String+Extensions.swift | 2 +- PadelClub/ViewModel/SearchViewModel.swift | 88 +++++++++++++++++++ .../Views/Tournament/Screen/AddTeamView.swift | 62 ++----------- 3 files changed, 97 insertions(+), 55 deletions(-) diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift index b333e27..98567e7 100644 --- a/PadelClub/Extensions/String+Extensions.swift +++ b/PadelClub/Extensions/String+Extensions.swift @@ -14,7 +14,7 @@ extension String { } var trimmed: String { - replaceCharactersFromSet(characterSet: .newlines).trimmingCharacters(in: .whitespacesAndNewlines) + replaceCharactersFromSet(characterSet: .newlines, replacementString: " ").trimmingCharacters(in: .whitespacesAndNewlines) } var trimmedMultiline: String { diff --git a/PadelClub/ViewModel/SearchViewModel.swift b/PadelClub/ViewModel/SearchViewModel.swift index 56eaea8..67dd69f 100644 --- a/PadelClub/ViewModel/SearchViewModel.swift +++ b/PadelClub/ViewModel/SearchViewModel.swift @@ -286,6 +286,94 @@ class SearchViewModel: ObservableObject, Identifiable { func nsSortDescriptors() -> [NSSortDescriptor] { sortDescriptors().map { NSSortDescriptor($0) } } + + static func getSpecialSlashPredicate(inputString: String) -> NSPredicate? { + // Define a regular expression to find slashes between alphabetic characters (not digits) + print(inputString) + + let pattern = /(\b[A-Za-z]+)\s*\/\s*([A-Za-z]+\b)/ + + // Find matches in the input string + guard let match = inputString.firstMatch(of: pattern) else { + print("No valid name pairs found") + return nil + } + + let lastName1 = match.output.1.trimmingCharacters(in: .whitespacesAndNewlines) + let lastName2 = match.output.2.trimmingCharacters(in: .whitespacesAndNewlines) + + // Ensure both names are not empty + guard !lastName1.isEmpty && !lastName2.isEmpty else { + print("One or both names are empty") + return nil + } + + // Create the NSPredicate for searching in the `lastName` field + let predicate = NSPredicate(format: "lastName CONTAINS[cd] %@ OR lastName CONTAINS[cd] %@", lastName1, lastName2) + + // Output the result + //print("Generated Predicate: \(predicate)") + return predicate + } + + + static func pastePredicate(pasteField: String, mostRecentDate: Date?, filterOption: PlayerFilterOption) -> NSPredicate? { + let allowedCharacterSet = CharacterSet.alphanumerics.union(.whitespaces) + + // Remove all characters that are not in the allowedCharacterSet + var text = pasteField.canonicalVersion.components(separatedBy: allowedCharacterSet.inverted).joined().trimmedMultiline + // Define the regex pattern to match digits + let digitPattern = /\d+/ + + // Replace all occurrences of the pattern (digits) with an empty string + text = text.replacing(digitPattern, with: "") + + let textStrings: [String] = text.components(separatedBy: .whitespacesAndNewlines) + let nonEmptyStrings: [String] = textStrings.compactMap { $0.isEmpty ? nil : $0 } + let nameComponents = nonEmptyStrings.filter({ $0 != "de" && $0 != "la" && $0 != "le" && $0.count > 1 }) + var andPredicates = [NSPredicate]() + var orPredicates = [NSPredicate]() + //self.wordsCount = nameComponents.count + + if let slashPredicate = getSpecialSlashPredicate(inputString: pasteField) { + orPredicates.append(slashPredicate) + } + + if filterOption == .female { + andPredicates.append(NSPredicate(format: "male == NO")) + } + + if let mostRecentDate { + //andPredicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg)) + } + + if nameComponents.count > 1 { + orPredicates.append(contentsOf: nameComponents.pairs().map { + return NSPredicate(format: "(firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@) OR (firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@)", $0, $1, $1, $0) }) + } else { + orPredicates.append(contentsOf: nameComponents.map { NSPredicate(format: "firstName contains[cd] %@ OR lastName contains[cd] %@", $0,$0) }) + } + + let components = text.split(separator: " ").sorted() + let pattern = components.joined(separator: ".*") + print(text, pattern) + let canonicalFullNamePredicate = NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern) + orPredicates.append(canonicalFullNamePredicate) + + let matches = text.licencesFound() + let licensesPredicates = matches.map { NSPredicate(format: "license contains[cd] %@", $0) } + orPredicates = orPredicates + licensesPredicates + + var predicate = NSCompoundPredicate(andPredicateWithSubpredicates: andPredicates) + + if orPredicates.isEmpty == false { + predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, NSCompoundPredicate(orPredicateWithSubpredicates: orPredicates)]) + } + + + return predicate + } + } enum SearchToken: String, CaseIterable, Identifiable { diff --git a/PadelClub/Views/Tournament/Screen/AddTeamView.swift b/PadelClub/Views/Tournament/Screen/AddTeamView.swift index 555a5ce..f97e3a7 100644 --- a/PadelClub/Views/Tournament/Screen/AddTeamView.swift +++ b/PadelClub/Views/Tournament/Screen/AddTeamView.swift @@ -59,7 +59,7 @@ struct AddTeamView: View { if let pasteString { _pasteString = .init(wrappedValue: pasteString) - _fetchPlayers = FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)], predicate: Self._pastePredicate(pasteField: pasteString, mostRecentDate: tournament.rankSourceDate, filterOption: tournament.tournamentCategory.playerFilterOption)) + _fetchPlayers = FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)], predicate: SearchViewModel.pastePredicate(pasteField: pasteString, mostRecentDate: tournament.rankSourceDate, filterOption: tournament.tournamentCategory.playerFilterOption)) _autoSelect = .init(wrappedValue: true) cancelShouldDismiss = true } @@ -135,7 +135,7 @@ struct AddTeamView: View { guard let first = strings.first else { return } Task { await MainActor.run { - fetchPlayers.nsPredicate = Self._pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption()) + fetchPlayers.nsPredicate = SearchViewModel.pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption()) fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)] pasteString = first autoSelect = true @@ -202,56 +202,6 @@ struct AddTeamView: View { selectionSearchField ?? pasteString } - static private func _pastePredicate(pasteField: String, mostRecentDate: Date?, filterOption: PlayerFilterOption) -> NSPredicate? { - let allowedCharacterSet = CharacterSet.alphanumerics.union(.whitespaces) - - // Remove all characters that are not in the allowedCharacterSet - let text = pasteField.canonicalVersion.components(separatedBy: allowedCharacterSet.inverted).joined().trimmed - - let textStrings: [String] = text.components(separatedBy: .whitespacesAndNewlines) - let nonEmptyStrings: [String] = textStrings.compactMap { $0.isEmpty ? nil : $0 } - let nameComponents = nonEmptyStrings.filter({ $0 != "de" && $0 != "la" && $0 != "le" && $0.count > 1 }) - var andPredicates = [NSPredicate]() - var orPredicates = [NSPredicate]() - //self.wordsCount = nameComponents.count - - - if filterOption == .male { - andPredicates.append(NSPredicate(format: "male == YES")) - } else if filterOption == .female { - andPredicates.append(NSPredicate(format: "male == NO")) - } - - if let mostRecentDate { - //andPredicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg)) - } - - if nameComponents.count > 1 { - orPredicates = nameComponents.pairs().map { - return NSPredicate(format: "(firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@) OR (firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@)", $0, $1, $1, $0) } - } else { - orPredicates = nameComponents.map { NSPredicate(format: "firstName contains[cd] %@ OR lastName contains[cd] %@", $0,$0) } - } - - let components = text.split(separator: " ").sorted() - let pattern = components.joined(separator: ".*") - print(text, pattern) - let canonicalFullNamePredicate = NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern) - orPredicates.append(canonicalFullNamePredicate) - - let matches = text.licencesFound() - let licensesPredicates = matches.map { NSPredicate(format: "license contains[cd] %@", $0) } - orPredicates = orPredicates + licensesPredicates - - var predicate = NSCompoundPredicate(andPredicateWithSubpredicates: andPredicates) - - if orPredicates.isEmpty == false { - predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, NSCompoundPredicate(orPredicateWithSubpredicates: orPredicates)]) - } - - return predicate - } - private func _currentSelection() -> Set { var currentSelection = Set() createdPlayerIds.compactMap { id in @@ -325,7 +275,9 @@ struct AddTeamView: View { createdPlayers.removeAll() createdPlayerIds.removeAll() pasteString = nil - dismiss() + if team.players().count > 1 { + dismiss() + } } private func _updateTeam(checkDuplicates: Bool) { @@ -351,7 +303,9 @@ struct AddTeamView: View { createdPlayerIds.removeAll() pasteString = nil self.editedTeam = nil - dismiss() + if editedTeam.players().count > 1 { + dismiss() + } } private func _buildingTeamView() -> some View { From 3eea8d29763d2ab923bbb3c31510196c56edd13e Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 12 Sep 2024 13:36:50 +0200 Subject: [PATCH 3/3] fix search stuff --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 6607922..dcb75c1 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -2546,7 +2546,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -2588,7 +2588,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6;