|
|
|
|
@ -15,10 +15,6 @@ struct TournamentLookUpView: View { |
|
|
|
|
@Environment(\.dismiss) private var dismiss |
|
|
|
|
|
|
|
|
|
@State private var searchField: String = "" |
|
|
|
|
@State private var sectionedTournaments: [String: [FederalTournament]] = [:] |
|
|
|
|
@State private var dayPeriod: DayPeriod = .all |
|
|
|
|
@State private var duration: Int = 3 |
|
|
|
|
|
|
|
|
|
@State var page: Int = 0 |
|
|
|
|
@State var total: Int = 0 |
|
|
|
|
|
|
|
|
|
@ -32,56 +28,49 @@ struct TournamentLookUpView: View { |
|
|
|
|
@AppStorage("lastCity") private var city: String = "" |
|
|
|
|
@State private var ligue: String = "" |
|
|
|
|
@AppStorage("lastDistance") private var distance: Double = 30 |
|
|
|
|
@AppStorage("lastSortingOption") private var sortingOption: String = "_DIST_" |
|
|
|
|
@AppStorage("lastSortingOption") private var sortingOption: String = "dateDebut+asc" |
|
|
|
|
@State private var requestedToGetAllPages: Bool = false |
|
|
|
|
@AppStorage("lastNationalCup") private var nationalCup: Bool = false |
|
|
|
|
@State private var revealSearchParameters: Bool = true |
|
|
|
|
@State private var searchScope = FederalTournamentSearchScope.all |
|
|
|
|
@State private var presentAlert: Bool = false |
|
|
|
|
|
|
|
|
|
var tournaments: [FederalTournament] { |
|
|
|
|
federalDataViewModel.searchedFederalTournaments |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func canShowTournament(_ tournament: FederalTournament) -> Bool { |
|
|
|
|
guard tournament.dayDuration <= duration else { return false } |
|
|
|
|
guard (tournament.dayPeriod == dayPeriod && dayPeriod != .all) || dayPeriod == .all else { return false } |
|
|
|
|
if searchField.isEmpty { |
|
|
|
|
return true |
|
|
|
|
} else { |
|
|
|
|
return tournament.validForSearch(searchField, scope: searchScope) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var body: some View { |
|
|
|
|
List { |
|
|
|
|
searchParametersView |
|
|
|
|
|
|
|
|
|
if tournaments.isEmpty == false && tournaments.count < total && total >= 200 && requestedToGetAllPages == false { |
|
|
|
|
Section { |
|
|
|
|
Text("Il y a beacoup de tournois pour cette requête, êtes-vous sûr de vouloir tout récupérer ? Sinon essayez d'affiner votre recherche.") |
|
|
|
|
Button { |
|
|
|
|
requestedToGetAllPages = true |
|
|
|
|
page += 1 |
|
|
|
|
searching = true |
|
|
|
|
Task { |
|
|
|
|
await getNewPage() |
|
|
|
|
searching = false |
|
|
|
|
buildSectionedData() |
|
|
|
|
} |
|
|
|
|
} label: { |
|
|
|
|
Label("Tout voir", systemImage: "arrow.down.circle") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
.alert("Attention", isPresented: $presentAlert, actions: { |
|
|
|
|
Button { |
|
|
|
|
presentAlert = false |
|
|
|
|
requestedToGetAllPages = true |
|
|
|
|
page += 1 |
|
|
|
|
searching = true |
|
|
|
|
Task { |
|
|
|
|
await getNewPage() |
|
|
|
|
searching = false |
|
|
|
|
dismiss() |
|
|
|
|
} |
|
|
|
|
} label: { |
|
|
|
|
Label("Tout voir", systemImage: "arrow.down.circle") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Button("Annuler") { |
|
|
|
|
revealSearchParameters = true |
|
|
|
|
presentAlert = false |
|
|
|
|
} |
|
|
|
|
}, message: { |
|
|
|
|
Text("Il y a beacoup de tournois pour cette requête, êtes-vous sûr de vouloir tout récupérer ? Sinon essayez d'affiner votre recherche.") |
|
|
|
|
}) |
|
|
|
|
.toolbarBackground(.visible, for: .bottomBar, .navigationBar) |
|
|
|
|
.navigationTitle("Chercher un tournoi") |
|
|
|
|
.navigationBarTitleDisplayMode(.inline) |
|
|
|
|
.onChange(of: locationManager.city, perform: { newValue in |
|
|
|
|
if let newValue, city.isEmpty { |
|
|
|
|
.onChange(of: locationManager.city) { |
|
|
|
|
if let newValue = locationManager.city, city.isEmpty { |
|
|
|
|
city = newValue |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
.toolbarTitleDisplayMode(.large) |
|
|
|
|
.toolbar { |
|
|
|
|
ToolbarItem(placement: .bottomBar) { |
|
|
|
|
@ -90,14 +79,6 @@ struct TournamentLookUpView: View { |
|
|
|
|
runSearch() |
|
|
|
|
} |
|
|
|
|
.disabled(searching) |
|
|
|
|
} else if searchField.isEmpty == false && searchScope != .all { |
|
|
|
|
let count = _totalVisibleEpreuves().count |
|
|
|
|
VStack { |
|
|
|
|
Text(searchField) |
|
|
|
|
.foregroundStyle(.secondary) |
|
|
|
|
Text(count.formatted() + " tournoi" + count.pluralSuffix) |
|
|
|
|
} |
|
|
|
|
.font(.caption) |
|
|
|
|
} else if searching { |
|
|
|
|
HStack(spacing: 20) { |
|
|
|
|
Spacer() |
|
|
|
|
@ -109,25 +90,13 @@ struct TournamentLookUpView: View { |
|
|
|
|
} |
|
|
|
|
Spacer() |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
let count = _totalVisibleEpreuves().count |
|
|
|
|
Text(count.formatted() + " tournoi" + count.pluralSuffix) |
|
|
|
|
.font(.caption) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ToolbarItem(placement: .topBarTrailing) { |
|
|
|
|
Menu { |
|
|
|
|
#if DEBUG |
|
|
|
|
if tournaments.isEmpty == false { |
|
|
|
|
Section { |
|
|
|
|
let preview = SharePreview(Text("Ma recherche de tournois"), icon: Image("PadelClub_logo_fondclair_transparent")) |
|
|
|
|
ShareLink(item: renderedImage ?? Image(systemName: "photo"), preview: preview) { |
|
|
|
|
if renderedImage == nil { |
|
|
|
|
ProgressView() |
|
|
|
|
} else { |
|
|
|
|
Label("Par image (20max)", systemImage: "square.and.arrow.up") |
|
|
|
|
.labelStyle(.titleAndIcon) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ShareLink(item: pastedTournaments) { |
|
|
|
|
Label("Par texte", systemImage: "square.and.arrow.up") |
|
|
|
|
.labelStyle(.titleAndIcon) |
|
|
|
|
@ -141,7 +110,9 @@ struct TournamentLookUpView: View { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Divider() |
|
|
|
|
Button { |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
Button(role: .destructive) { |
|
|
|
|
tournamentLevels = Set() |
|
|
|
|
tournamentCategories = Set() |
|
|
|
|
city = "" |
|
|
|
|
@ -150,8 +121,10 @@ struct TournamentLookUpView: View { |
|
|
|
|
distance = 30 |
|
|
|
|
startDate = Date() |
|
|
|
|
endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())! |
|
|
|
|
sortingOption = "_DIST_" |
|
|
|
|
sortingOption = "dateDebut+asc" |
|
|
|
|
revealSearchParameters = true |
|
|
|
|
federalDataViewModel.searchedFederalTournaments = [] |
|
|
|
|
federalDataViewModel.searchAttemptCount = 0 |
|
|
|
|
} label: { |
|
|
|
|
Text("Ré-initialiser la recherche") |
|
|
|
|
} |
|
|
|
|
@ -169,75 +142,6 @@ struct TournamentLookUpView: View { |
|
|
|
|
Set(tournaments.map { $0.japMessage }).joined(separator: "\n") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func isTypeLookedAfter(_ type: any TournamentBuildHolder) -> Bool { |
|
|
|
|
if levels.contains(where: { level in |
|
|
|
|
type.level == level |
|
|
|
|
}) || levels.isEmpty { |
|
|
|
|
if categories.contains(where: { category in |
|
|
|
|
type.category == category |
|
|
|
|
}) || categories.isEmpty { |
|
|
|
|
|
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Environment(\.displayScale) var displayScale |
|
|
|
|
@State private var renderedImage: Image? |
|
|
|
|
|
|
|
|
|
@MainActor |
|
|
|
|
func render() { |
|
|
|
|
let renderer = ImageRenderer(content: tournamentsView) |
|
|
|
|
renderer.scale = displayScale |
|
|
|
|
renderer.isOpaque = true |
|
|
|
|
if let uiImage = renderer.uiImage { |
|
|
|
|
renderedImage = Image(uiImage: uiImage) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ViewBuilder |
|
|
|
|
private var tournamentsView: some View { |
|
|
|
|
let tournaments = tournaments.prefix(20) |
|
|
|
|
VStack { |
|
|
|
|
ForEach(tournaments.indices, id: \.self) { tournamentIndex in |
|
|
|
|
let tournament = tournaments[tournamentIndex] |
|
|
|
|
HStack(alignment: .center) { |
|
|
|
|
VStack(alignment: .leading) { |
|
|
|
|
Text(tournament.libelle ?? "unknown").font(.headline) |
|
|
|
|
if let club = tournament.nomClub { |
|
|
|
|
Text(club) |
|
|
|
|
.font(.footnote) |
|
|
|
|
.lineLimit(1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Spacer() |
|
|
|
|
VStack(alignment: .trailing) { |
|
|
|
|
if let startDate = tournament.dateDebut { |
|
|
|
|
Text(startDate.monthYearFormatted) |
|
|
|
|
HStack { |
|
|
|
|
Text(startDate.formatted(.dateTime.weekday())) |
|
|
|
|
Text(startDate.formatted(.dateTime.day())).font(.largeTitle) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if let distance = tournament.distanceEnMetres { |
|
|
|
|
let measurement = Measurement(value: distance / 1000, unit: UnitLength.kilometers) |
|
|
|
|
Text(measurement.formatted()).font(.caption) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
.padding() |
|
|
|
|
.foregroundColor(Color.black) |
|
|
|
|
.background { |
|
|
|
|
tournamentIndex%2 == 0 ? Color.mint : Color.cyan |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
.padding() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var clubsFound: [String] { |
|
|
|
|
Set(tournaments.compactMap { $0.nomClub }).sorted() |
|
|
|
|
} |
|
|
|
|
@ -253,18 +157,17 @@ struct TournamentLookUpView: View { |
|
|
|
|
federalDataViewModel.searchedFederalTournaments = [] |
|
|
|
|
searching = true |
|
|
|
|
requestedToGetAllPages = false |
|
|
|
|
renderedImage = nil |
|
|
|
|
federalDataViewModel.searchAttemptCount += 1 |
|
|
|
|
Task { |
|
|
|
|
await getNewPage() |
|
|
|
|
searching = false |
|
|
|
|
dismiss() |
|
|
|
|
if tournaments.isEmpty == false && tournaments.count < total && total >= 200 && requestedToGetAllPages == false { |
|
|
|
|
presentAlert = true |
|
|
|
|
} else { |
|
|
|
|
dismiss() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func buildSectionedData() { |
|
|
|
|
sectionedTournaments = FederalTournament.sectionedData(from: tournaments) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private var distanceLimit: Measurement<UnitLength> { |
|
|
|
|
distanceLimit(distance: distance) |
|
|
|
|
@ -308,15 +211,9 @@ struct TournamentLookUpView: View { |
|
|
|
|
print("count", count, total, tournaments.count, page) |
|
|
|
|
total = count |
|
|
|
|
|
|
|
|
|
if renderedImage == nil { |
|
|
|
|
render() |
|
|
|
|
} |
|
|
|
|
if tournaments.count < count && page < total / 30 { |
|
|
|
|
if total < 200 || requestedToGetAllPages { |
|
|
|
|
page += 1 |
|
|
|
|
await MainActor.run() { |
|
|
|
|
buildSectionedData() |
|
|
|
|
} |
|
|
|
|
await getNewPage() |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
@ -361,12 +258,10 @@ struct TournamentLookUpView: View { |
|
|
|
|
city = "" |
|
|
|
|
locationManager.location = nil |
|
|
|
|
locationManager.city = nil |
|
|
|
|
dayPeriod = .all |
|
|
|
|
duration = 3 |
|
|
|
|
distance = 30 |
|
|
|
|
startDate = Date() |
|
|
|
|
endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())! |
|
|
|
|
sortingOption = "_DIST_" |
|
|
|
|
sortingOption = "dateDebut+asc" |
|
|
|
|
revealSearchParameters = true |
|
|
|
|
} label: { |
|
|
|
|
Label("Ré-initialiser la recherche", systemImage: "xmark.circle") |
|
|
|
|
@ -376,27 +271,27 @@ struct TournamentLookUpView: View { |
|
|
|
|
|
|
|
|
|
@ViewBuilder |
|
|
|
|
var searchParametersView: some View { |
|
|
|
|
@Bindable var federalDataViewModel = federalDataViewModel |
|
|
|
|
Section { |
|
|
|
|
DatePicker("Début", selection: $startDate, displayedComponents: .date) |
|
|
|
|
DatePicker("Fin", selection: $endDate, displayedComponents: .date) |
|
|
|
|
|
|
|
|
|
Picker(selection: $duration) { |
|
|
|
|
Text("Aucune").tag(7) |
|
|
|
|
Text(1.formatted()).tag(1) |
|
|
|
|
Text(2.formatted()).tag(2) |
|
|
|
|
Text(3.formatted()).tag(3) |
|
|
|
|
Picker(selection: $federalDataViewModel.dayDuration) { |
|
|
|
|
Text("aucune").tag(nil as Int?) |
|
|
|
|
Text(1.formatted()).tag(1 as Int?) |
|
|
|
|
Text(2.formatted()).tag(2 as Int?) |
|
|
|
|
Text(3.formatted()).tag(3 as Int?) |
|
|
|
|
} label: { |
|
|
|
|
Text("Durée max (en jours)") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Picker(selection: $dayPeriod) { |
|
|
|
|
Text("N'importe").tag(DayPeriod.all) |
|
|
|
|
Text("le weekend").tag(DayPeriod.weekend) |
|
|
|
|
Text("la semaine").tag(DayPeriod.week) |
|
|
|
|
|
|
|
|
|
Picker(selection: $federalDataViewModel.dayPeriod) { |
|
|
|
|
ForEach(DayPeriod.allCases) { |
|
|
|
|
Text($0.localizedDayPeriodLabel()).tag($0) |
|
|
|
|
} |
|
|
|
|
} label: { |
|
|
|
|
Text("En semaine ou week-end") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HStack { |
|
|
|
|
TextField("Ville", text: $city) |
|
|
|
|
if let city = locationManager.city { |
|
|
|
|
@ -585,53 +480,4 @@ struct TournamentLookUpView: View { |
|
|
|
|
return "Distance" |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _totalVisibleTournaments(_ date: Date? = nil) -> [FederalTournament] { |
|
|
|
|
if let date { |
|
|
|
|
if let tournaments = sectionedTournaments[URL.importDateFormatter.string(from: date)] { |
|
|
|
|
let allTournaments = tournaments.filter({ canShowTournament($0) }).filter({ tournament in |
|
|
|
|
if tournament.tournaments.count > 1 { |
|
|
|
|
return tournament.tournaments.anySatisfy { isTypeLookedAfter($0) } |
|
|
|
|
} else { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
return allTournaments |
|
|
|
|
} else { |
|
|
|
|
return [] |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
let allTournaments = sectionedTournaments.values.flatMap({ $0 }).filter({ canShowTournament($0) }).filter({ tournament in |
|
|
|
|
if tournament.tournaments.count > 1 { |
|
|
|
|
return tournament.tournaments.anySatisfy { isTypeLookedAfter($0) } |
|
|
|
|
} else { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
return allTournaments |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func _totalVisibleEpreuves(_ date: Date? = nil) -> [any TournamentBuildHolder] { |
|
|
|
|
if let date { |
|
|
|
|
if let tournaments = sectionedTournaments[URL.importDateFormatter.string(from: date)] { |
|
|
|
|
let allTournaments = tournaments |
|
|
|
|
.filter({ canShowTournament($0) }) |
|
|
|
|
.compactMap({ $0.tournaments }) |
|
|
|
|
.flatMap({ $0 }) |
|
|
|
|
.filter({ isTypeLookedAfter($0) }) |
|
|
|
|
return allTournaments |
|
|
|
|
} else { |
|
|
|
|
return [] |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
let allTournaments = sectionedTournaments.values.flatMap({ $0 }) |
|
|
|
|
.filter({ canShowTournament($0) }) |
|
|
|
|
.compactMap({ $0.tournaments }) |
|
|
|
|
.flatMap({ $0 }) |
|
|
|
|
.filter({ isTypeLookedAfter($0) }) |
|
|
|
|
return allTournaments |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|