sync2
Laurent 1 year ago
commit cc427dbdea
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 2
      PadelClub/Data/Tournament.swift
  3. 2
      PadelClub/Extensions/String+Extensions.swift
  4. 88
      PadelClub/ViewModel/SearchViewModel.swift
  5. 62
      PadelClub/Views/Tournament/Screen/AddTeamView.swift
  6. 60
      PadelClub/Views/Tournament/Screen/BroadcastView.swift

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

@ -439,7 +439,7 @@ final class Tournament : ModelObject, Storable {
}
func isTournamentPublished() -> Bool {
return (Date() >= publishedTournamentDate() && canBePublished()) || publishTournament
return (Date() >= publishedTournamentDate()) || publishTournament
}
func areTeamsPublished() -> Bool {

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

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

@ -59,7 +59,7 @@ struct AddTeamView: View {
if let pasteString {
_pasteString = .init(wrappedValue: pasteString)
_fetchPlayers = FetchRequest<ImportedPlayer>(sortDescriptors: [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)], predicate: Self._pastePredicate(pasteField: pasteString, mostRecentDate: tournament.rankSourceDate, filterOption: tournament.tournamentCategory.playerFilterOption))
_fetchPlayers = FetchRequest<ImportedPlayer>(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<PlayerRegistration> {
var currentSelection = Set<PlayerRegistration>()
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 {

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

Loading…
Cancel
Save