You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
844 lines
30 KiB
844 lines
30 KiB
//
|
|
// SearchViewModel.swift
|
|
// Padel Tournament
|
|
//
|
|
// Created by Razmig Sarkissian on 07/02/2024.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
class DebouncableViewModel: ObservableObject {
|
|
@Published var debouncableText: String = ""
|
|
var debounceTrigger: Double = 0.15
|
|
}
|
|
|
|
class SearchViewModel: ObservableObject, Identifiable {
|
|
let id: UUID = UUID()
|
|
var allowSelection: Int = 0
|
|
var codeClub: String? = nil
|
|
var clubName: String? = nil
|
|
var ligueName: String? = nil
|
|
var showFemaleInMaleAssimilation: Bool = false
|
|
var hidePlayers: [String]?
|
|
|
|
@Published var debouncableText: String = ""
|
|
@Published var searchText: String = ""
|
|
@Published var task: DispatchWorkItem?
|
|
@Published var computedSearchText: String = ""
|
|
@Published var tokens = [SearchToken]()
|
|
@Published var suggestedTokens = [SearchToken]()
|
|
@Published var dataSet: DataSet = .national
|
|
@Published var filterOption = PlayerFilterOption.all
|
|
@Published var hideAssimilation: Bool = false
|
|
@Published var ascending: Bool = true
|
|
@Published var sortOption: SortOption = .rank
|
|
@Published var selectedPlayers: Set<ImportedPlayer> = Set()
|
|
@Published var filterSelectionEnabled: Bool = false
|
|
@Published var isPresented: Bool = false
|
|
@Published var selectedAgeCategory: FederalTournamentAge = .unlisted
|
|
@Published var mostRecentDate: Date? = nil
|
|
|
|
var selectionIsOver: Bool {
|
|
if allowSingleSelection && selectedPlayers.count == 1 {
|
|
return true
|
|
} else if allowMultipleSelection && selectedPlayers.count == allowSelection {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
var allowMultipleSelection: Bool {
|
|
allowSelection > 1 || allowSelection == -1
|
|
}
|
|
|
|
var allowSingleSelection: Bool {
|
|
allowSelection == 1
|
|
}
|
|
|
|
var debounceTrigger: Double {
|
|
(dataSet == .national || dataSet == .ligue) ? 0.4 : 0.1
|
|
}
|
|
|
|
var throttleTrigger: Double {
|
|
(dataSet == .national || dataSet == .ligue) ? 0.15 : 0.1
|
|
}
|
|
|
|
var contentUnavailableMessage: String {
|
|
var message = ["Vérifiez l'ortographe ou lancez une nouvelle recherche."]
|
|
if tokens.isEmpty {
|
|
message.append(
|
|
"Il est possible que cette personne n'est joué aucun tournoi depuis les 12 derniers mois, dans ce cas, Padel Club ne pourra pas le trouver."
|
|
)
|
|
}
|
|
return message.joined(separator: "\n")
|
|
}
|
|
|
|
func codeClubs() -> [String] {
|
|
let clubs: [Club] = DataStore.shared.user.clubsObjects()
|
|
return clubs.compactMap { $0.code }
|
|
}
|
|
|
|
func getCodeClub() -> String? {
|
|
if let codeClub { return codeClub }
|
|
if let userCodeClub = DataStore.shared.user.currentPlayerData()?.clubCode {
|
|
return userCodeClub
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getLigueName() -> String? {
|
|
if let ligueName { return ligueName }
|
|
if let userLigueName = DataStore.shared.user.currentPlayerData()?.ligueName {
|
|
return userLigueName
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func shouldIncludeSearchTextPredicate() -> Bool {
|
|
if allowMultipleSelection {
|
|
return true
|
|
}
|
|
|
|
if allowSingleSelection {
|
|
return true
|
|
}
|
|
|
|
if tokens.isEmpty == false || hideAssimilation || selectedAgeCategory != .unlisted {
|
|
return true
|
|
}
|
|
|
|
return dataSet == .national && searchText.isEmpty == false
|
|
&& (tokens.isEmpty == true && hideAssimilation == false
|
|
&& selectedAgeCategory == .unlisted)
|
|
}
|
|
|
|
func showIndex() -> Bool {
|
|
if dataSet == .national {
|
|
if searchText.isEmpty == false
|
|
&& (tokens.isEmpty == true && hideAssimilation == false
|
|
&& selectedAgeCategory == .unlisted)
|
|
{
|
|
return false
|
|
} else {
|
|
return isFiltering()
|
|
}
|
|
}
|
|
if dataSet == .ligue { return isFiltering() }
|
|
if filterOption == .all { return isFiltering() }
|
|
return true
|
|
}
|
|
|
|
func isFiltering() -> Bool {
|
|
searchText.isEmpty == false || tokens.isEmpty == false || hideAssimilation
|
|
|| selectedAgeCategory != .unlisted
|
|
}
|
|
|
|
func prompt(forDataSet: DataSet) -> String {
|
|
switch forDataSet {
|
|
case .national:
|
|
if let mostRecentDate {
|
|
return "base fédérale \(mostRecentDate.monthYearFormatted)"
|
|
} else {
|
|
return "rechercher"
|
|
}
|
|
case .ligue:
|
|
return "dans cette ligue"
|
|
case .club:
|
|
return "dans ce club"
|
|
case .favoriteClubs, .favoritePlayers:
|
|
return "dans mes favoris"
|
|
}
|
|
}
|
|
|
|
func label(forDataSet: DataSet) -> String {
|
|
switch forDataSet {
|
|
case .national:
|
|
return "National"
|
|
case .ligue:
|
|
return (ligueName)?.capitalized ?? "Ma ligue"
|
|
case .club:
|
|
return (clubName)?.capitalized ?? "Mon club"
|
|
case .favoriteClubs:
|
|
return "Clubs favoris"
|
|
case .favoritePlayers:
|
|
return "Joueurs favoris"
|
|
}
|
|
}
|
|
|
|
func words() -> [String] {
|
|
let cleanedText = searchText.cleanSearchText().canonicalVersionWithPunctuation.trimmed
|
|
return cleanedText.components(
|
|
separatedBy: .whitespaces)
|
|
}
|
|
|
|
func wordsPredicates() -> NSPredicate? {
|
|
let words = words().filter({ $0.isEmpty == false })
|
|
|
|
// Handle special case of hyphenated words
|
|
let hyphenatedWords = searchText.components(separatedBy: .whitespaces)
|
|
.filter { $0.contains("-") }
|
|
|
|
var predicates: [NSPredicate] = []
|
|
|
|
// Add predicates for hyphenated words
|
|
for word in hyphenatedWords {
|
|
predicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", word))
|
|
let parts = word.components(separatedBy: "-")
|
|
for part in parts where part.count > 1 {
|
|
predicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", part))
|
|
}
|
|
}
|
|
|
|
// Regular words processing
|
|
switch words.count {
|
|
case 2:
|
|
predicates.append(contentsOf: [
|
|
NSPredicate(
|
|
format:
|
|
"canonicalLastName CONTAINS[cd] %@ AND canonicalFirstName CONTAINS[cd] %@",
|
|
words[0], words[1]),
|
|
NSPredicate(
|
|
format:
|
|
"canonicalLastName CONTAINS[cd] %@ AND canonicalFirstName CONTAINS[cd] %@",
|
|
words[1], words[0]),
|
|
// For multi-word first names, try the two words as a first name
|
|
NSPredicate(
|
|
format: "canonicalFirstName CONTAINS[cd] %@", words.joined(separator: " ")),
|
|
])
|
|
case 3:
|
|
// Handle potential cases like "Jean Christophe CROS"
|
|
predicates.append(contentsOf: [
|
|
// First two words as first name, last as last name
|
|
NSPredicate(
|
|
format:
|
|
"canonicalFirstName CONTAINS[cd] %@ AND canonicalLastName CONTAINS[cd] %@",
|
|
words[0] + " " + words[1], words[2]),
|
|
// First as first name, last two as last name
|
|
NSPredicate(
|
|
format:
|
|
"canonicalFirstName CONTAINS[cd] %@ AND canonicalLastName CONTAINS[cd] %@",
|
|
words[0], words[1] + " " + words[2]),
|
|
// Last as first name, first two as last name
|
|
NSPredicate(
|
|
format:
|
|
"canonicalFirstName CONTAINS[cd] %@ AND canonicalLastName CONTAINS[cd] %@",
|
|
words[2], words[0] + " " + words[1]),
|
|
])
|
|
default:
|
|
if words.count > 0 {
|
|
// For single word or many words, try matching against full name
|
|
predicates.append(
|
|
NSPredicate(
|
|
format: "canonicalFullName CONTAINS[cd] %@",
|
|
words.joined(separator: " ")))
|
|
}
|
|
}
|
|
|
|
return predicates.isEmpty
|
|
? nil : NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
|
|
}
|
|
|
|
func searchTextPredicate() -> NSPredicate? {
|
|
var predicates: [NSPredicate] = []
|
|
let allowedCharacterSet = CharacterSet.alphanumerics.union(.whitespaces).union(
|
|
CharacterSet(charactersIn: "-"))
|
|
let canonicalVersionWithoutPunctuation = searchText.canonicalVersion
|
|
.components(separatedBy: allowedCharacterSet.inverted)
|
|
.joined()
|
|
.trimmed
|
|
|
|
if canonicalVersionWithoutPunctuation.isEmpty == false {
|
|
let wordsPredicates = wordsPredicates()
|
|
if let wordsPredicates {
|
|
predicates.append(wordsPredicates)
|
|
} else {
|
|
predicates.append(
|
|
NSPredicate(
|
|
format: "license contains[cd] %@", canonicalVersionWithoutPunctuation))
|
|
}
|
|
|
|
// Add match for full name
|
|
predicates.append(
|
|
NSPredicate(
|
|
format: "canonicalFullName contains[cd] %@", canonicalVersionWithoutPunctuation)
|
|
)
|
|
|
|
// Add pattern match for more flexible matching
|
|
let components = canonicalVersionWithoutPunctuation.split(separator: " ")
|
|
let pattern = components.joined(separator: ".*")
|
|
predicates.append(NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern))
|
|
|
|
// Look for exact matches on first or last name
|
|
let words = canonicalVersionWithoutPunctuation.components(separatedBy: .whitespaces)
|
|
for word in words where word.count > 2 {
|
|
predicates.append(
|
|
NSPredicate(
|
|
format: "firstName CONTAINS[cd] %@ OR lastName CONTAINS[cd] %@", word, word)
|
|
)
|
|
}
|
|
}
|
|
|
|
if predicates.isEmpty {
|
|
return nil
|
|
}
|
|
return NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
|
|
}
|
|
|
|
func orPredicate() -> NSPredicate? {
|
|
var predicates: [NSPredicate] = []
|
|
let allowedCharacterSet = CharacterSet.alphanumerics.union(.whitespaces).union(
|
|
CharacterSet(charactersIn: "-"))
|
|
let canonicalVersionWithoutPunctuation = searchText.canonicalVersion
|
|
.components(separatedBy: allowedCharacterSet.inverted)
|
|
.joined()
|
|
.trimmed
|
|
let canonicalVersionWithPunctuation = searchText.canonicalVersionWithPunctuation.trimmed
|
|
|
|
if tokens.isEmpty {
|
|
if shouldIncludeSearchTextPredicate(),
|
|
canonicalVersionWithoutPunctuation.isEmpty == false
|
|
{
|
|
if let searchTextPredicate = searchTextPredicate() {
|
|
predicates.append(searchTextPredicate)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process tokens
|
|
for token in tokens {
|
|
switch token {
|
|
case .ligue:
|
|
if canonicalVersionWithoutPunctuation.isEmpty {
|
|
predicates.append(NSPredicate(format: "ligueName == nil"))
|
|
} else {
|
|
predicates.append(
|
|
NSPredicate(
|
|
format: "ligueName contains[cd] %@", canonicalVersionWithoutPunctuation)
|
|
)
|
|
}
|
|
case .club:
|
|
if canonicalVersionWithoutPunctuation.isEmpty {
|
|
predicates.append(NSPredicate(format: "clubName == nil"))
|
|
} else {
|
|
predicates.append(
|
|
NSPredicate(
|
|
format: "clubName contains[cd] %@", canonicalVersionWithoutPunctuation))
|
|
}
|
|
case .rankMoreThan:
|
|
if canonicalVersionWithoutPunctuation.isEmpty
|
|
|| Int(canonicalVersionWithoutPunctuation) == 0
|
|
{
|
|
predicates.append(NSPredicate(format: "rank == 0"))
|
|
} else {
|
|
predicates.append(
|
|
NSPredicate(format: "rank >= %@", canonicalVersionWithoutPunctuation))
|
|
}
|
|
case .rankLessThan:
|
|
if canonicalVersionWithoutPunctuation.isEmpty
|
|
|| Int(canonicalVersionWithoutPunctuation) == 0
|
|
{
|
|
predicates.append(NSPredicate(format: "rank == 0"))
|
|
} else {
|
|
predicates.append(
|
|
NSPredicate(format: "rank <= %@", canonicalVersionWithoutPunctuation))
|
|
}
|
|
case .rankBetween:
|
|
let values = canonicalVersionWithPunctuation.components(separatedBy: ",")
|
|
if canonicalVersionWithPunctuation.isEmpty || values.count != 2 {
|
|
predicates.append(NSPredicate(format: "rank == 0"))
|
|
} else {
|
|
predicates.append(
|
|
NSPredicate(format: "rank BETWEEN {%@,%@}", values.first!, values.last!))
|
|
}
|
|
case .age:
|
|
if canonicalVersionWithoutPunctuation.isEmpty
|
|
|| Int(canonicalVersionWithoutPunctuation) == 0
|
|
{
|
|
predicates.append(NSPredicate(format: "birthYear == 0"))
|
|
} else if let birthYear = Int(canonicalVersionWithoutPunctuation) {
|
|
predicates.append(
|
|
NSPredicate(format: "birthYear == %@", birthYear.formattedAsRawString()))
|
|
}
|
|
}
|
|
}
|
|
|
|
if predicates.isEmpty {
|
|
return nil
|
|
}
|
|
return NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
|
|
}
|
|
|
|
func predicate() -> NSPredicate? {
|
|
var predicates: [NSPredicate?] = [
|
|
orPredicate(),
|
|
filterOption == .male ? NSPredicate(format: "male == YES") : nil,
|
|
filterOption == .female ? NSPredicate(format: "male == NO") : nil,
|
|
]
|
|
|
|
if let mostRecentDate {
|
|
predicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg))
|
|
}
|
|
|
|
if hideAssimilation {
|
|
predicates.append(NSPredicate(format: "assimilation == %@", "Non"))
|
|
}
|
|
|
|
if selectedAgeCategory != .unlisted {
|
|
let computedBirthYear = selectedAgeCategory.computedBirthYear()
|
|
if let left = computedBirthYear.0 {
|
|
predicates.append(
|
|
NSPredicate(format: "birthYear >= %@", left.formattedAsRawString()))
|
|
}
|
|
if let right = computedBirthYear.1 {
|
|
predicates.append(
|
|
NSPredicate(format: "birthYear <= %@", right.formattedAsRawString()))
|
|
}
|
|
|
|
}
|
|
|
|
switch dataSet {
|
|
case .national:
|
|
break
|
|
case .ligue:
|
|
if let ligueName = getLigueName() {
|
|
predicates.append(NSPredicate(format: "ligueName == %@", ligueName))
|
|
} else {
|
|
predicates.append(NSPredicate(format: "ligueName == nil"))
|
|
}
|
|
case .club:
|
|
if let codeClub = getCodeClub() {
|
|
predicates.append(NSPredicate(format: "clubCode == %@", codeClub))
|
|
} else {
|
|
predicates.append(NSPredicate(format: "clubCode == nil"))
|
|
}
|
|
case .favoriteClubs:
|
|
predicates.append(NSPredicate(format: "clubCode IN %@", codeClubs()))
|
|
case .favoritePlayers:
|
|
//todo
|
|
predicates.append(NSPredicate(format: "license == nil"))
|
|
}
|
|
|
|
if hidePlayers?.isEmpty == false {
|
|
predicates.append(NSPredicate(format: "NOT (license IN %@)", hidePlayers!))
|
|
}
|
|
|
|
return NSCompoundPredicate(andPredicateWithSubpredicates: predicates.compactMap({ $0 }))
|
|
}
|
|
|
|
func sortDescriptors() -> [SortDescriptor<ImportedPlayer>] {
|
|
sortOption.sortDescriptors(ascending, dataSet: dataSet)
|
|
}
|
|
|
|
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 cleanedInput = inputString.cleanSearchText()
|
|
|
|
let pattern = /(\b[A-Za-z]+)\s*\/\s*([A-Za-z]+\b)/
|
|
|
|
// Find matches in the input string
|
|
guard let match = cleanedInput.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? {
|
|
var andPredicates = [NSPredicate]()
|
|
var orPredicates = [NSPredicate]()
|
|
|
|
// Check for license numbers
|
|
let matches = pasteField.licencesFound()
|
|
let licensesPredicates = matches.map { NSPredicate(format: "license contains[cd] %@", $0) }
|
|
orPredicates = licensesPredicates
|
|
|
|
if matches.count == 2 {
|
|
return NSCompoundPredicate(orPredicateWithSubpredicates: orPredicates)
|
|
}
|
|
|
|
// Add gender filter if specified
|
|
if filterOption == .female {
|
|
andPredicates.append(NSPredicate(format: "male == NO"))
|
|
} else if filterOption == .male {
|
|
andPredicates.append(NSPredicate(format: "male == YES"))
|
|
}
|
|
|
|
// Add date filter if specified
|
|
if let mostRecentDate {
|
|
andPredicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg))
|
|
}
|
|
|
|
// Check for slashes (representing alternatives)
|
|
if let slashPredicate = getSpecialSlashPredicate(inputString: pasteField) {
|
|
orPredicates.append(slashPredicate)
|
|
}
|
|
|
|
// Prepare text for processing - preserve hyphens but remove digits
|
|
var text =
|
|
pasteField
|
|
.replacingOccurrences(of: "[\\(\\)\\[\\]\\{\\}]", with: "", options: .regularExpression)
|
|
.replacingOccurrences(of: "/", with: " ") // Replace slashes with spaces
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
|
// Remove digits
|
|
let digitPattern = /\b\w*\d\w*\b/
|
|
text = text.replacing(digitPattern, with: "").trimmingCharacters(
|
|
in: .whitespacesAndNewlines)
|
|
|
|
// Split text by whitespace to get potential name components
|
|
let textComponents = text.components(separatedBy: .whitespacesAndNewlines)
|
|
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
|
.filter {
|
|
!$0.isEmpty && $0.count > 1 && !["de", "la", "le", "du"].contains($0.lowercased())
|
|
}
|
|
|
|
if textComponents.count < 50 {
|
|
// Handle exact fullname match
|
|
let fullName = textComponents.joined(separator: " ")
|
|
if !fullName.isEmpty {
|
|
orPredicates.append(
|
|
NSPredicate(format: "canonicalFullName CONTAINS[cd] %@", fullName))
|
|
}
|
|
|
|
// Handle hyphenated last names
|
|
let hyphenatedComponents = textComponents.filter { $0.contains("-") }
|
|
for component in hyphenatedComponents {
|
|
orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", component))
|
|
|
|
// Also search for each part of the hyphenated name
|
|
let parts = component.components(separatedBy: "-")
|
|
for part in parts {
|
|
if part.count > 1 {
|
|
orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", part))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try different combinations for first/last name
|
|
if textComponents.count > 1 {
|
|
// Try each pair of components as first+last and last+first
|
|
for i in 0..<textComponents.count {
|
|
for j in 0..<textComponents.count where i != j {
|
|
orPredicates.append(
|
|
NSPredicate(
|
|
format:
|
|
"(firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@) OR (firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@)",
|
|
textComponents[i], textComponents[j], textComponents[j],
|
|
textComponents[i]
|
|
))
|
|
|
|
// Also try beginswith for more precise matches
|
|
orPredicates.append(
|
|
NSPredicate(
|
|
format:
|
|
"(firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@) OR (firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@)",
|
|
textComponents[i], textComponents[j], textComponents[j],
|
|
textComponents[i]
|
|
))
|
|
}
|
|
}
|
|
} else if textComponents.count == 1 {
|
|
// If only one component, search in both first and last name
|
|
orPredicates.append(
|
|
NSPredicate(
|
|
format: "firstName CONTAINS[cd] %@ OR lastName CONTAINS[cd] %@",
|
|
textComponents[0], textComponents[0]))
|
|
}
|
|
|
|
// Add pattern match for canonical full name
|
|
let pattern = textComponents.joined(separator: ".*")
|
|
orPredicates.append(NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern))
|
|
}
|
|
|
|
// Construct final predicate
|
|
var predicate = NSCompoundPredicate(andPredicateWithSubpredicates: andPredicates)
|
|
|
|
if !orPredicates.isEmpty {
|
|
let orCompoundPredicate = NSCompoundPredicate(
|
|
orPredicateWithSubpredicates: orPredicates)
|
|
predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
|
|
predicate, orCompoundPredicate,
|
|
])
|
|
}
|
|
|
|
return predicate
|
|
}
|
|
|
|
}
|
|
|
|
enum SearchToken: String, CaseIterable, Identifiable {
|
|
case club = "club"
|
|
case ligue = "ligue"
|
|
case rankMoreThan = "rang >"
|
|
case rankLessThan = "rang <"
|
|
case rankBetween = "rang <>"
|
|
case age = "âge sportif"
|
|
|
|
var id: String {
|
|
rawValue
|
|
}
|
|
|
|
var message: String {
|
|
switch self {
|
|
case .club:
|
|
return
|
|
"Taper le nom d'un club pour y voir tous les joueurs ayant déjà joué un tournoi dans les 12 derniers mois."
|
|
case .ligue:
|
|
return
|
|
"Taper le nom d'une ligue pour y voir tous les joueurs ayant déjà joué un tournoi dans les 12 derniers mois."
|
|
case .rankMoreThan:
|
|
return
|
|
"Taper un nombre pour chercher les joueurs ayant un classement supérieur ou égale."
|
|
case .rankLessThan:
|
|
return
|
|
"Taper un nombre pour chercher les joueurs ayant un classement inférieur ou égale."
|
|
case .rankBetween:
|
|
return
|
|
"Taper deux nombres séparés par une virgule pour chercher les joueurs dans cette intervalle de classement"
|
|
case .age:
|
|
return "Taper une année de naissance"
|
|
}
|
|
}
|
|
|
|
var titleLabel: String {
|
|
switch self {
|
|
case .club:
|
|
return "Chercher un club"
|
|
case .ligue:
|
|
return "Chercher une ligue"
|
|
case .rankMoreThan, .rankLessThan:
|
|
return "Chercher un rang"
|
|
case .rankBetween:
|
|
return "Chercher une intervalle de classement"
|
|
case .age:
|
|
return "Chercher une année de naissance"
|
|
}
|
|
}
|
|
|
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
switch self {
|
|
case .club:
|
|
return "Club"
|
|
case .ligue:
|
|
return "Ligue"
|
|
case .rankMoreThan:
|
|
return "Rang supérieur ou égale à"
|
|
case .rankLessThan:
|
|
return "Rang inférieur ou égale à"
|
|
case .rankBetween:
|
|
return "Rang entre deux valeurs"
|
|
case .age:
|
|
return "Année de naissance"
|
|
}
|
|
}
|
|
|
|
var shortLocalizedLabel: String {
|
|
switch self {
|
|
case .club:
|
|
return "Club"
|
|
case .ligue:
|
|
return "Ligue"
|
|
case .rankMoreThan:
|
|
return "Rang ≥"
|
|
case .rankLessThan:
|
|
return "Rang ≤"
|
|
case .rankBetween:
|
|
return "Rang ≥,≤"
|
|
case .age:
|
|
return "Né(e) en"
|
|
}
|
|
}
|
|
|
|
func icon() -> String {
|
|
switch self {
|
|
case .club:
|
|
return "house.and.flag.fill"
|
|
case .ligue:
|
|
return "house.and.flag.fill"
|
|
case .rankMoreThan:
|
|
return "figure.racquetball"
|
|
case .rankLessThan:
|
|
return "figure.racquetball"
|
|
case .rankBetween:
|
|
return "figure.racquetball"
|
|
case .age:
|
|
return "figure.racquetball"
|
|
}
|
|
}
|
|
|
|
var systemImage: String {
|
|
switch self {
|
|
case .club:
|
|
return "house.and.flag.fill"
|
|
case .ligue:
|
|
return "house.and.flag.fill"
|
|
case .rankMoreThan:
|
|
return "figure.racquetball"
|
|
case .rankLessThan:
|
|
return "figure.racquetball"
|
|
case .rankBetween:
|
|
return "figure.racquetball"
|
|
case .age:
|
|
return "figure.racquetball"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum DataSet: Int, Identifiable {
|
|
case national
|
|
case ligue
|
|
case club
|
|
case favoriteClubs
|
|
case favoritePlayers
|
|
|
|
static let allCases: [DataSet] = [.national, .ligue, .club, .favoriteClubs]
|
|
|
|
var id: Int { rawValue }
|
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
switch self {
|
|
case .national:
|
|
return "National"
|
|
case .ligue:
|
|
return "Ligue"
|
|
case .club:
|
|
return "Club"
|
|
case .favoritePlayers, .favoriteClubs:
|
|
return "Favori"
|
|
}
|
|
}
|
|
|
|
var tokens: [SearchToken] {
|
|
var _tokens: [SearchToken] = []
|
|
switch self {
|
|
case .national:
|
|
_tokens = [.club, .ligue, .rankMoreThan, .rankLessThan, .rankBetween]
|
|
case .ligue:
|
|
_tokens = [.club, .rankMoreThan, .rankLessThan, .rankBetween]
|
|
case .club:
|
|
_tokens = [.rankMoreThan, .rankLessThan, .rankBetween]
|
|
case .favoritePlayers, .favoriteClubs:
|
|
_tokens = [.rankMoreThan, .rankLessThan, .rankBetween]
|
|
}
|
|
|
|
_tokens.append(.age)
|
|
return _tokens
|
|
}
|
|
}
|
|
|
|
enum SortOption: Int, CaseIterable, Identifiable {
|
|
case name
|
|
case rank
|
|
case tournamentCount
|
|
case points
|
|
case progression
|
|
|
|
var id: Int { self.rawValue }
|
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
switch self {
|
|
case .name:
|
|
return "Nom"
|
|
case .rank:
|
|
return "Rang"
|
|
case .tournamentCount:
|
|
return "Tournoi"
|
|
case .points:
|
|
return "Points"
|
|
case .progression:
|
|
return "Progression"
|
|
}
|
|
}
|
|
|
|
func sortDescriptors(_ ascending: Bool, dataSet: DataSet) -> [SortDescriptor<ImportedPlayer>] {
|
|
switch self {
|
|
case .name:
|
|
return [
|
|
SortDescriptor(\ImportedPlayer.lastName, order: ascending ? .forward : .reverse),
|
|
SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
|
|
]
|
|
case .rank:
|
|
if dataSet == .national || dataSet == .ligue {
|
|
return [
|
|
SortDescriptor(\ImportedPlayer.rank, order: ascending ? .forward : .reverse)
|
|
]
|
|
} else {
|
|
return [
|
|
SortDescriptor(\ImportedPlayer.rank, order: ascending ? .forward : .reverse),
|
|
SortDescriptor(\ImportedPlayer.assimilation),
|
|
SortDescriptor(\ImportedPlayer.lastName),
|
|
]
|
|
}
|
|
case .tournamentCount:
|
|
return [
|
|
SortDescriptor(
|
|
\ImportedPlayer.tournamentCount, order: ascending ? .forward : .reverse),
|
|
SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
|
|
SortDescriptor(\ImportedPlayer.lastName),
|
|
]
|
|
case .points:
|
|
return [
|
|
SortDescriptor(\ImportedPlayer.points, order: ascending ? .forward : .reverse),
|
|
SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
|
|
SortDescriptor(\ImportedPlayer.lastName),
|
|
]
|
|
case .progression:
|
|
return [
|
|
SortDescriptor(\ImportedPlayer.progression, order: ascending ? .forward : .reverse),
|
|
SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
|
|
SortDescriptor(\ImportedPlayer.lastName),
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
enum PlayerFilterOption: Int, Hashable, CaseIterable, Identifiable {
|
|
case all = -1
|
|
case male = 1
|
|
case female = 0
|
|
|
|
var id: Int { rawValue }
|
|
|
|
func icon() -> String {
|
|
switch self {
|
|
case .all:
|
|
return "Tous"
|
|
case .male:
|
|
return "Homme"
|
|
case .female:
|
|
return "Femme"
|
|
}
|
|
}
|
|
|
|
var localizedPlayerLabel: String {
|
|
switch self {
|
|
case .female:
|
|
return "joueuse"
|
|
default:
|
|
return "joueur"
|
|
}
|
|
}
|
|
|
|
}
|
|
|