parent
82140ae210
commit
4b5c05dfa3
@ -0,0 +1,538 @@ |
|||||||
|
// |
||||||
|
// AddTeamView.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Razmig Sarkissian on 15/07/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import SwiftUI |
||||||
|
import LeStorage |
||||||
|
|
||||||
|
struct AddTeamView: View { |
||||||
|
|
||||||
|
@EnvironmentObject var dataStore: DataStore |
||||||
|
@Environment(\.dismiss) var dismiss |
||||||
|
|
||||||
|
@FetchRequest( |
||||||
|
sortDescriptors: [], |
||||||
|
animation: .default) |
||||||
|
private var fetchPlayers: FetchedResults<ImportedPlayer> |
||||||
|
|
||||||
|
var tournament: Tournament |
||||||
|
var cancelShouldDismiss: Bool = false |
||||||
|
|
||||||
|
@State private var searchField: String = "" |
||||||
|
@State private var presentSearch: Bool = false |
||||||
|
@State private var presentPlayerSearch: Bool = false |
||||||
|
@State private var presentPlayerCreation: Bool = false |
||||||
|
@State private var presentImportView: Bool = false |
||||||
|
@State private var createdPlayers: Set<PlayerRegistration> = Set() |
||||||
|
@State private var createdPlayerIds: Set<String> = Set() |
||||||
|
@State private var editedTeam: TeamRegistration? |
||||||
|
@State private var pasteString: String? |
||||||
|
@State private var selectionSearchField: String? |
||||||
|
@State private var autoSelect: Bool = false |
||||||
|
@State private var teamsHash: Int? |
||||||
|
@State private var presentationCount: Int = 0 |
||||||
|
@State private var confirmDuplicate: Bool = false |
||||||
|
|
||||||
|
var tournamentStore: TournamentStore { |
||||||
|
return self.tournament.tournamentStore |
||||||
|
} |
||||||
|
|
||||||
|
init(tournament: Tournament, pasteString: String? = nil, editedTeam: TeamRegistration?) { |
||||||
|
self.tournament = tournament |
||||||
|
_editedTeam = .init(wrappedValue: editedTeam) |
||||||
|
if let team = editedTeam { |
||||||
|
var createdPlayers: Set<PlayerRegistration> = Set() |
||||||
|
var createdPlayerIds: Set<String> = Set() |
||||||
|
|
||||||
|
team.unsortedPlayers().forEach { player in |
||||||
|
createdPlayers.insert(player) |
||||||
|
createdPlayerIds.insert(player.id) |
||||||
|
} |
||||||
|
|
||||||
|
_createdPlayers = .init(wrappedValue: createdPlayers) |
||||||
|
_createdPlayerIds = .init(wrappedValue: createdPlayerIds) |
||||||
|
} |
||||||
|
|
||||||
|
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)) |
||||||
|
_autoSelect = .init(wrappedValue: true) |
||||||
|
cancelShouldDismiss = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Function to create a simple hash from a list of IDs |
||||||
|
private func _simpleHash(ids: [String]) -> Int { |
||||||
|
// Combine the hash values of each string |
||||||
|
return ids.reduce(0) { $0 ^ $1.hashValue } |
||||||
|
} |
||||||
|
|
||||||
|
// Function to check if two lists of IDs produce different hashes |
||||||
|
private func _areDifferent(ids1: [String], ids2: [String]) -> Bool { |
||||||
|
return _simpleHash(ids: ids1) != _simpleHash(ids: ids2) |
||||||
|
} |
||||||
|
|
||||||
|
private func _setHash() async { |
||||||
|
#if DEBUG_TIME //DEBUGING TIME |
||||||
|
let start = Date() |
||||||
|
defer { |
||||||
|
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) |
||||||
|
print("func _setHash", duration.formatted(.units(allowed: [.seconds, .milliseconds]))) |
||||||
|
} |
||||||
|
#endif |
||||||
|
let selectedSortedTeams = tournament.selectedSortedTeams() |
||||||
|
if self.teamsHash == nil, selectedSortedTeams.isEmpty == false { |
||||||
|
self.teamsHash = _simpleHash(ids: selectedSortedTeams.map { $0.id }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private func _handleHashDiff() async { |
||||||
|
let selectedSortedTeams = tournament.selectedSortedTeams() |
||||||
|
let newHash = _simpleHash(ids: selectedSortedTeams.map { $0.id }) |
||||||
|
if (self.teamsHash != nil && newHash != teamsHash!) || (self.teamsHash == nil && selectedSortedTeams.isEmpty == false) { |
||||||
|
self.teamsHash = newHash |
||||||
|
if self.tournament.shouldVerifyBracket == false || self.tournament.shouldVerifyGroupStage == false { |
||||||
|
self.tournament.shouldVerifyBracket = true |
||||||
|
self.tournament.shouldVerifyGroupStage = true |
||||||
|
|
||||||
|
let waitingList = self.tournament.waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true) |
||||||
|
waitingList.forEach { team in |
||||||
|
if team.bracketPosition != nil || team.groupStagePosition != nil { |
||||||
|
team.resetPositions() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
do { |
||||||
|
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: waitingList) |
||||||
|
try dataStore.tournaments.addOrUpdate(instance: tournament) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var body: some View { |
||||||
|
_buildingTeamView() |
||||||
|
.navigationBarBackButtonHidden(true) |
||||||
|
.alert("Cette équipe existe déjà", isPresented: $confirmDuplicate) { |
||||||
|
Button("Créer l'équipe quand même") { |
||||||
|
_createTeam(checkDuplicates: false) |
||||||
|
} |
||||||
|
|
||||||
|
Button("Annuler", role: .cancel) { |
||||||
|
confirmDuplicate = false |
||||||
|
} |
||||||
|
|
||||||
|
} message: { |
||||||
|
Text("Cette équipe existe déjà dans votre liste d'inscription.") |
||||||
|
} |
||||||
|
.sheet(isPresented: $presentPlayerSearch, onDismiss: { |
||||||
|
selectionSearchField = nil |
||||||
|
}) { |
||||||
|
NavigationStack { |
||||||
|
SelectablePlayerListView(allowSelection: -1, filterOption: _filterOption(), showFemaleInMaleAssimilation: tournament.tournamentCategory.showFemaleInMaleAssimilation) { players in |
||||||
|
selectionSearchField = nil |
||||||
|
players.forEach { player in |
||||||
|
let newPlayer = PlayerRegistration(importedPlayer: player) |
||||||
|
newPlayer.setComputedRank(in: tournament) |
||||||
|
createdPlayers.insert(newPlayer) |
||||||
|
createdPlayerIds.insert(newPlayer.id) |
||||||
|
} |
||||||
|
} contentUnavailableAction: { searchViewModel in |
||||||
|
selectionSearchField = searchViewModel.searchText |
||||||
|
presentPlayerSearch = false |
||||||
|
presentPlayerCreation = true |
||||||
|
} |
||||||
|
} |
||||||
|
.tint(.master) |
||||||
|
} |
||||||
|
.sheet(isPresented: $presentPlayerCreation) { |
||||||
|
PlayerPopoverView(source: _searchSource(), sex: _addPlayerSex()) { p in |
||||||
|
p.setComputedRank(in: tournament) |
||||||
|
createdPlayers.insert(p) |
||||||
|
createdPlayerIds.insert(p.id) |
||||||
|
} |
||||||
|
.tint(.master) |
||||||
|
} |
||||||
|
.navigationTitle(editedTeam == nil ? "Ajouter une équipe" : "Modifier l'équipe") |
||||||
|
.toolbar { |
||||||
|
ToolbarItem(placement: .cancellationAction) { |
||||||
|
Button("Annuler", role: .cancel) { |
||||||
|
dismiss() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if pasteString == nil { |
||||||
|
ToolbarItem(placement: .topBarTrailing) { |
||||||
|
PasteButton(payloadType: String.self) { strings in |
||||||
|
guard let first = strings.first else { return } |
||||||
|
Task { |
||||||
|
await MainActor.run { |
||||||
|
fetchPlayers.nsPredicate = Self._pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption()) |
||||||
|
fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)] |
||||||
|
pasteString = first |
||||||
|
autoSelect = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
.foregroundStyle(.master) |
||||||
|
.labelStyle(.iconOnly) |
||||||
|
.buttonBorderShape(.capsule) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
.navigationBarBackButtonHidden(_isEditingTeam()) |
||||||
|
.toolbarBackground(.visible, for: .navigationBar) |
||||||
|
.navigationBarTitleDisplayMode(.inline) |
||||||
|
} |
||||||
|
|
||||||
|
private func _isEditingTeam() -> Bool { |
||||||
|
createdPlayerIds.isEmpty == false || editedTeam != nil || pasteString != nil |
||||||
|
} |
||||||
|
|
||||||
|
var unsortedPlayers: [PlayerRegistration] { |
||||||
|
tournament.unsortedPlayers() |
||||||
|
} |
||||||
|
|
||||||
|
private func _getTeams() { |
||||||
|
Task { |
||||||
|
await _setHash() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@ViewBuilder |
||||||
|
private func _managementView() -> some View { |
||||||
|
Section { |
||||||
|
RowButtonView("Rechercher dans la base fédérale") { |
||||||
|
presentPlayerSearch = true |
||||||
|
} |
||||||
|
} footer: { |
||||||
|
if let rankSourceDate = tournament.rankSourceDate { |
||||||
|
Text("Cherchez dans la base fédérale de \(rankSourceDate.monthYearFormatted), vous y trouverez tous les joueurs ayant participé à au moins un tournoi dans les 12 derniers mois.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Section { |
||||||
|
RowButtonView("Créer un non classé / non licencié") { |
||||||
|
presentPlayerCreation = true |
||||||
|
} |
||||||
|
} footer: { |
||||||
|
Text("Si le joueur n'a pas encore de licence ou n'a pas encore participé à une compétition, vous pouvez le créer vous-même.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private func _addPlayerSex() -> Int { |
||||||
|
switch tournament.tournamentCategory { |
||||||
|
case .men, .unlisted: |
||||||
|
return 1 |
||||||
|
case .women: |
||||||
|
return 0 |
||||||
|
case .mix: |
||||||
|
return 1 |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private func _filterOption() -> PlayerFilterOption { |
||||||
|
return tournament.tournamentCategory.playerFilterOption |
||||||
|
} |
||||||
|
|
||||||
|
private func _searchSource() -> String? { |
||||||
|
selectionSearchField ?? pasteString |
||||||
|
} |
||||||
|
|
||||||
|
static private func _pastePredicate(pasteField: String, mostRecentDate: Date?, filterOption: PlayerFilterOption) -> NSPredicate? { |
||||||
|
let text = pasteField.canonicalVersion |
||||||
|
|
||||||
|
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 contains[cd] %@ AND lastName contains[cd] %@) OR (firstName contains[cd] %@ AND lastName contains[cd] %@)", $0, $1, $1, $0) } |
||||||
|
} else { |
||||||
|
orPredicates = nameComponents.map { NSPredicate(format: "firstName contains[cd] %@ OR lastName contains[cd] %@", $0,$0) } |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
fetchPlayers.first(where: { id == $0.license }) |
||||||
|
}.forEach { player in |
||||||
|
let player = PlayerRegistration(importedPlayer: player) |
||||||
|
player.setComputedRank(in: tournament) |
||||||
|
currentSelection.insert(player) |
||||||
|
} |
||||||
|
|
||||||
|
createdPlayerIds.compactMap { id in |
||||||
|
createdPlayers.first(where: { id == $0.id }) |
||||||
|
}.forEach { |
||||||
|
currentSelection.insert($0) |
||||||
|
} |
||||||
|
return currentSelection |
||||||
|
} |
||||||
|
|
||||||
|
private func _currentSelectionIds() -> [String?] { |
||||||
|
var currentSelection = [String?]() |
||||||
|
createdPlayerIds.compactMap { id in |
||||||
|
fetchPlayers.first(where: { id == $0.license }) |
||||||
|
}.forEach { player in |
||||||
|
currentSelection.append(player.license) |
||||||
|
} |
||||||
|
|
||||||
|
createdPlayerIds.compactMap { id in |
||||||
|
createdPlayers.first(where: { id == $0.id }) |
||||||
|
}.forEach { |
||||||
|
currentSelection.append($0.licenceId) |
||||||
|
} |
||||||
|
return currentSelection |
||||||
|
} |
||||||
|
|
||||||
|
private func _isDuplicate() -> Bool { |
||||||
|
let ids : [String?] = _currentSelectionIds() |
||||||
|
if tournament.selectedSortedTeams().anySatisfy({ $0.containsExactlyPlayerLicenses(ids) }) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
private func _createTeam(checkDuplicates: Bool) { |
||||||
|
if checkDuplicates && _isDuplicate() { |
||||||
|
confirmDuplicate = true |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
let players = _currentSelection() |
||||||
|
let team = tournament.addTeam(players) |
||||||
|
do { |
||||||
|
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
do { |
||||||
|
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
|
||||||
|
createdPlayers.removeAll() |
||||||
|
createdPlayerIds.removeAll() |
||||||
|
pasteString = nil |
||||||
|
dismiss() |
||||||
|
} |
||||||
|
|
||||||
|
private func _updateTeam(checkDuplicates: Bool) { |
||||||
|
guard let editedTeam else { return } |
||||||
|
if checkDuplicates && _isDuplicate() { |
||||||
|
confirmDuplicate = true |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
let players = _currentSelection() |
||||||
|
editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory) |
||||||
|
do { |
||||||
|
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: editedTeam) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
do { |
||||||
|
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
createdPlayers.removeAll() |
||||||
|
createdPlayerIds.removeAll() |
||||||
|
pasteString = nil |
||||||
|
self.editedTeam = nil |
||||||
|
dismiss() |
||||||
|
} |
||||||
|
|
||||||
|
private func _buildingTeamView() -> some View { |
||||||
|
List(selection: $createdPlayerIds) { |
||||||
|
if let pasteString { |
||||||
|
|
||||||
|
Section { |
||||||
|
Text(pasteString) |
||||||
|
} footer: { |
||||||
|
HStack { |
||||||
|
Text("contenu du presse-papier") |
||||||
|
Spacer() |
||||||
|
Button("effacer", role: .destructive) { |
||||||
|
self.pasteString = nil |
||||||
|
self.createdPlayers.removeAll() |
||||||
|
self.createdPlayerIds.removeAll() |
||||||
|
} |
||||||
|
.buttonStyle(.borderless) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Section { |
||||||
|
ForEach(createdPlayerIds.sorted(), id: \.self) { id in |
||||||
|
if let p = createdPlayers.first(where: { $0.id == id }) { |
||||||
|
VStack(alignment: .leading, spacing: 0) { |
||||||
|
if let player = unsortedPlayers.first(where: { $0.licenceId == p.licenceId }), editedTeam?.includes(player: player) == false { |
||||||
|
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() |
||||||
|
} |
||||||
|
PlayerView(player: p).tag(p.id) |
||||||
|
} |
||||||
|
} |
||||||
|
if let p = fetchPlayers.first(where: { $0.license == id }) { |
||||||
|
VStack(alignment: .leading, spacing: 0) { |
||||||
|
if unsortedPlayers.first(where: { $0.licenceId == p.license }) != nil { |
||||||
|
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold() |
||||||
|
} |
||||||
|
ImportedPlayerView(player: p).tag(p.license!) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} header: { |
||||||
|
let _currentSelection = _currentSelection() |
||||||
|
let selectedSortedTeams = tournament.selectedSortedTeams() |
||||||
|
let rank = _currentSelection.map { |
||||||
|
$0.computedRank |
||||||
|
}.reduce(0, +) |
||||||
|
let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count |
||||||
|
if _currentSelection.isEmpty == false, tournament.hideWeight() == false, rank > 0 { |
||||||
|
HStack(spacing: 16.0) { |
||||||
|
VStack(alignment: .leading, spacing: 0) { |
||||||
|
Text("Rang").font(.caption) |
||||||
|
Text("#" + (teamIndex + 1).formatted()) |
||||||
|
} |
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 0) { |
||||||
|
Text("Poids").font(.caption) |
||||||
|
Text(rank.formatted()) |
||||||
|
} |
||||||
|
Spacer() |
||||||
|
VStack(alignment: .trailing, spacing: 0) { |
||||||
|
Text("").font(.caption) |
||||||
|
Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Section { |
||||||
|
if editedTeam == nil { |
||||||
|
if createdPlayerIds.isEmpty { |
||||||
|
RowButtonView("Bloquer une place") { |
||||||
|
_createTeam(checkDuplicates: false) |
||||||
|
} |
||||||
|
} else { |
||||||
|
RowButtonView("Ajouter l'équipe") { |
||||||
|
_createTeam(checkDuplicates: true) |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
RowButtonView("Modifier l'équipe") { |
||||||
|
_updateTeam(checkDuplicates: false) |
||||||
|
editedTeam = nil |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if let pasteString { |
||||||
|
if fetchPlayers.isEmpty { |
||||||
|
ContentUnavailableView { |
||||||
|
Label("Aucun résultat", systemImage: "person.2.slash") |
||||||
|
} description: { |
||||||
|
Text("Aucun joueur classé n'a été trouvé dans ce message.") |
||||||
|
} actions: { |
||||||
|
RowButtonView("Créer un joueur non classé") { |
||||||
|
presentPlayerCreation = true |
||||||
|
} |
||||||
|
|
||||||
|
RowButtonView("Effacer cette recherche") { |
||||||
|
self.pasteString = nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
Section { |
||||||
|
ForEach(fetchPlayers.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) })) { player in |
||||||
|
ImportedPlayerView(player: player).tag(player.license!) |
||||||
|
} |
||||||
|
} header: { |
||||||
|
Text(fetchPlayers.count.formatted() + " résultat" + fetchPlayers.count.pluralSuffix) |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
_managementView() |
||||||
|
} |
||||||
|
} |
||||||
|
.onAppear { |
||||||
|
_getTeams() |
||||||
|
} |
||||||
|
.onDisappear { |
||||||
|
Task { |
||||||
|
await _handleHashDiff() |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
.headerProminence(.increased) |
||||||
|
.onReceive(fetchPlayers.publisher.count()) { _ in // <-- here |
||||||
|
if let pasteString, count == 2, autoSelect == true { |
||||||
|
fetchPlayers.filter { $0.hitForSearch(pasteString) >= hitTarget }.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) }).forEach { player in |
||||||
|
createdPlayerIds.insert(player.license!) |
||||||
|
} |
||||||
|
autoSelect = false |
||||||
|
} |
||||||
|
} |
||||||
|
.environment(\.editMode, Binding.constant(EditMode.active)) |
||||||
|
} |
||||||
|
|
||||||
|
private var count: Int { |
||||||
|
return fetchPlayers.filter { $0.hitForSearch(pasteString ?? "") >= hitTarget }.count |
||||||
|
} |
||||||
|
|
||||||
|
private var hitTarget: Int { |
||||||
|
if (pasteString?.matches(of: /[1-9][0-9]{5,7}/).count ?? 0) > 1 { |
||||||
|
if fetchPlayers.filter({ $0.hitForSearch(pasteString ?? "") == 100 }).count == 2 { return 100 } |
||||||
|
} else { |
||||||
|
return 2 |
||||||
|
} |
||||||
|
return 1 |
||||||
|
} |
||||||
|
|
||||||
|
private func _save() { |
||||||
|
do { |
||||||
|
try dataStore.tournaments.addOrUpdate(instance: tournament) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue