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.
563 lines
23 KiB
563 lines
23 KiB
//
|
|
// ActivityView.swift
|
|
// PadelClub
|
|
//
|
|
// Created by Razmig Sarkissian on 29/02/2024.
|
|
//
|
|
|
|
import SwiftUI
|
|
import PadelClubData
|
|
|
|
struct ActivityView: View {
|
|
@EnvironmentObject var dataStore: DataStore
|
|
@Environment(NavigationViewModel.self) private var navigation
|
|
@State private var federalDataViewModel: FederalDataViewModel = FederalDataViewModel.shared
|
|
@State private var searchText: String = ""
|
|
|
|
@State private var presentFilterView: Bool = false
|
|
@State private var presentToolbar: Bool = false
|
|
@State private var newTournament: Tournament?
|
|
@State private var viewStyle: ViewStyle = .list
|
|
@State private var isGatheringFederalTournaments: Bool = false
|
|
@State private var error: Error?
|
|
@State private var uuid: UUID = UUID()
|
|
@State private var presentClubSearchView: Bool = false
|
|
@State private var quickAccessScreen: QuickAccessScreen? = nil
|
|
@State private var displaySearchView: Bool = false
|
|
@State private var pasteString: String? = nil
|
|
|
|
enum QuickAccessScreen : Identifiable, Hashable {
|
|
case inscription
|
|
|
|
var id: String {
|
|
switch self {
|
|
case .inscription:
|
|
return "inscription"
|
|
}
|
|
}
|
|
}
|
|
|
|
var runningTournaments: [FederalTournamentHolder] {
|
|
return dataStore.tournaments.filter({ $0.endDate == nil && $0.sharing != .granted })
|
|
.filter({ federalDataViewModel.isTournamentValidForFilters($0) })
|
|
}
|
|
|
|
func getRunningTournaments() -> [Tournament] {
|
|
return dataStore.tournaments.filter({ $0.endDate == nil && $0.sharing != .granted })
|
|
.filter({ federalDataViewModel.isTournamentValidForFilters($0) })
|
|
}
|
|
|
|
var endedTournaments: [Tournament] {
|
|
return dataStore.tournaments.filter({ $0.endDate != nil && $0.sharing != .granted })
|
|
.filter({ federalDataViewModel.isTournamentValidForFilters($0) })
|
|
}
|
|
//
|
|
// func _activityStatus() -> String? {
|
|
// let tournaments = tournaments
|
|
// if tournaments.isEmpty && federalDataViewModel.areFiltersEnabled() == false {
|
|
// return nil
|
|
// } else {
|
|
// let count = tournaments.map { $0.tournaments.count }.reduce(0,+)
|
|
// return "\(count) tournoi" + count.pluralSuffix
|
|
// }
|
|
// }
|
|
|
|
var tournaments: [FederalTournamentHolder] {
|
|
switch navigation.agendaDestination! {
|
|
case .activity:
|
|
return runningTournaments
|
|
case .history:
|
|
return endedTournaments
|
|
case .tenup:
|
|
return federalDataViewModel.filteredFederalTournaments
|
|
case .around:
|
|
return federalDataViewModel.filteredSearchedFederalTournaments
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func _pasteView() -> some View {
|
|
Button {
|
|
quickAccessScreen = .inscription
|
|
} label: {
|
|
Image(systemName: "person.crop.circle.badge.plus")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(minHeight: 32)
|
|
}
|
|
.accessibilityLabel("Ajouter une équipe")
|
|
|
|
// if pasteButtonIsDisplayed == nil || pasteButtonIsDisplayed == true {
|
|
// PasteButton(payloadType: String.self) { strings in
|
|
// let first = strings.first ?? "aucun texte"
|
|
// quickAccessScreen = .inscription(pasteString: first)
|
|
// }
|
|
// .foregroundStyle(.master)
|
|
// .labelStyle(.iconOnly)
|
|
// .buttonBorderShape(.capsule)
|
|
// .onAppear {
|
|
// pasteButtonIsDisplayed = true
|
|
// }
|
|
// } else if let pasteButtonIsDisplayed, pasteButtonIsDisplayed == false {
|
|
// }
|
|
}
|
|
|
|
var body: some View {
|
|
@Bindable var navigation = navigation
|
|
NavigationStack(path: $navigation.path) {
|
|
VStack(spacing: 0) {
|
|
GenericDestinationPickerView(selectedDestination: $navigation.agendaDestination, destinations: AgendaDestination.allCases, nilDestinationIsValid: false)
|
|
|
|
ScrollViewReader { proxy in
|
|
List {
|
|
switch navigation.agendaDestination! {
|
|
case .activity:
|
|
EventListView(tournaments: runningTournaments, sortAscending: true)
|
|
case .history:
|
|
EventListView(tournaments: endedTournaments, sortAscending: false)
|
|
case .tenup:
|
|
EventListView(tournaments: federalDataViewModel.federalTournaments, sortAscending: true)
|
|
.id(uuid)
|
|
case .around:
|
|
EventListView(tournaments: federalDataViewModel.searchedFederalTournaments, sortAscending: true)
|
|
}
|
|
}
|
|
.onChange(of: navigation.agendaDestination) {
|
|
withAnimation {
|
|
proxy.scrollTo(0, anchor: .center)
|
|
}
|
|
}
|
|
}
|
|
.environment(\.viewStyle, viewStyle)
|
|
.environment(federalDataViewModel)
|
|
.overlay {
|
|
if let error, navigation.agendaDestination == .tenup {
|
|
ContentUnavailableView {
|
|
Label("Une erreur est survenue", systemImage: "exclamationmark.circle.fill")
|
|
} description: {
|
|
Text("Tenup est peut-être en maintenance. " + error.localizedDescription)
|
|
} actions: {
|
|
Link(destination: URLs.tenup.url) {
|
|
Text("Voir si tenup est en maintenance")
|
|
}
|
|
|
|
RowButtonView("D'accord.") {
|
|
self.error = nil
|
|
}
|
|
}
|
|
} else if isGatheringFederalTournaments {
|
|
ProgressView()
|
|
} else {
|
|
if tournaments.isEmpty && viewStyle == .list {
|
|
if searchText.isEmpty == false {
|
|
ContentUnavailableView.search(text: searchText)
|
|
} else if federalDataViewModel.areFiltersEnabled() {
|
|
ContentUnavailableView {
|
|
Text("Aucun tournoi")
|
|
} description: {
|
|
Text("Aucun tournoi ne correspond aux fitres que vous avez choisis : \(federalDataViewModel.filterStatus())")
|
|
} actions: {
|
|
FooterButtonView("supprimer vos filtres") {
|
|
federalDataViewModel.removeFilters()
|
|
}
|
|
FooterButtonView("modifier vos filtres") {
|
|
presentFilterView = true
|
|
}
|
|
}
|
|
} else {
|
|
_dataEmptyView()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//.searchable(text: $searchText)
|
|
// .onAppear { presentToolbar = true }
|
|
// .onDisappear { presentToolbar = false }
|
|
.refreshable {
|
|
if navigation.agendaDestination == .tenup {
|
|
federalDataViewModel.federalTournaments.removeAll()
|
|
NetworkFederalService.shared.formId = ""
|
|
_gatherFederalTournaments()
|
|
} else if navigation.agendaDestination == .activity {
|
|
runningTournaments.forEach { t in
|
|
if let tournament = t as? Tournament {
|
|
tournament.lastTeamRefresh = nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.task {
|
|
if navigation.agendaDestination == .tenup
|
|
&& dataStore.user.hasTenupClubs() == true
|
|
&& federalDataViewModel.federalTournaments.isEmpty {
|
|
_gatherFederalTournaments()
|
|
}
|
|
}
|
|
.onChange(of: navigation.agendaDestination) {
|
|
if tournaments.isEmpty, viewStyle == .calendar {
|
|
viewStyle = .list
|
|
}
|
|
|
|
if navigation.agendaDestination == .tenup
|
|
&& dataStore.user.hasTenupClubs() == true
|
|
&& federalDataViewModel.federalTournaments.isEmpty {
|
|
_gatherFederalTournaments()
|
|
}
|
|
}
|
|
.onChange(of: presentFilterView, { old, new in
|
|
if old == true, new == false { //closing filter view
|
|
if tournaments.isEmpty, viewStyle == .calendar {
|
|
viewStyle = .list
|
|
}
|
|
}
|
|
})
|
|
.toolbarTitleDisplayMode(.large)
|
|
.navigationTitle(TabDestination.activity.title)
|
|
.navigationDestination(for: Tournament.self) { tournament in
|
|
TournamentView(tournament: tournament)
|
|
}
|
|
// .onDisappear(perform: {
|
|
// pasteButtonIsDisplayed = nil
|
|
// print("disappearing", "pasteButtonIsDisplayed", pasteButtonIsDisplayed)
|
|
// })
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .topBarLeading) {
|
|
Button {
|
|
switch viewStyle {
|
|
case .list:
|
|
viewStyle = .calendar
|
|
case .calendar:
|
|
viewStyle = .list
|
|
}
|
|
} label: {
|
|
Image(systemName: "calendar.circle")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(minHeight: 32)
|
|
}
|
|
.symbolVariant(viewStyle == .calendar ? .fill : .none)
|
|
|
|
Button {
|
|
presentFilterView.toggle()
|
|
} label: {
|
|
Image(systemName: "line.3.horizontal.decrease.circle")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(minHeight: 32)
|
|
}
|
|
.symbolVariant(federalDataViewModel.areFiltersEnabled() ? .fill : .none)
|
|
|
|
_pasteView()
|
|
}
|
|
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button {
|
|
newTournament = Tournament.newEmptyInstance()
|
|
|
|
} label: {
|
|
Image(systemName: "plus.circle.fill")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(minHeight: 32)
|
|
}
|
|
}
|
|
|
|
if tournaments.isEmpty == false, federalDataViewModel.areFiltersEnabled() || navigation.agendaDestination == .around {
|
|
ToolbarItemGroup(placement: .bottomBar) {
|
|
VStack(spacing: 0) {
|
|
let searchStatus = _searchStatus()
|
|
if searchStatus.isEmpty == false {
|
|
Text(_searchStatus())
|
|
.font(.footnote)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
HStack {
|
|
if navigation.agendaDestination == .around {
|
|
FooterButtonView("modifier votre recherche") {
|
|
displaySearchView = true
|
|
}
|
|
|
|
if federalDataViewModel.areFiltersEnabled() {
|
|
Text("ou")
|
|
}
|
|
}
|
|
|
|
if federalDataViewModel.areFiltersEnabled() {
|
|
FooterButtonView(_filterButtonTitle()) {
|
|
presentFilterView = true
|
|
}
|
|
|
|
}
|
|
}
|
|
.padding(.bottom, 8)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.sheet(isPresented: $presentFilterView) {
|
|
TournamentFilterView(federalDataViewModel: federalDataViewModel)
|
|
.environment(navigation)
|
|
.tint(.master)
|
|
}
|
|
.sheet(isPresented: $presentClubSearchView, onDismiss: {
|
|
if dataStore.user.hasTenupClubs() == true {
|
|
federalDataViewModel.federalTournaments.removeAll()
|
|
navigation.agendaDestination = .tenup
|
|
}
|
|
}) {
|
|
ClubImportView()
|
|
.tint(.master)
|
|
}
|
|
.sheet(isPresented: $displaySearchView) {
|
|
NavigationStack {
|
|
TournamentLookUpView()
|
|
.environment(federalDataViewModel)
|
|
.environment(navigation)
|
|
}
|
|
}
|
|
.sheet(item: $newTournament) { tournament in
|
|
EventCreationView(tournaments: [tournament], selectedClub: federalDataViewModel.selectedClub())
|
|
.environment(navigation)
|
|
.tint(.master)
|
|
}
|
|
.sheet(item: $quickAccessScreen) { screen in
|
|
switch screen {
|
|
case .inscription:
|
|
NavigationStack {
|
|
List {
|
|
|
|
if let pasteString {
|
|
Section {
|
|
Text(pasteString)
|
|
.frame(maxWidth: .infinity)
|
|
.overlay {
|
|
if pasteString.isEmpty {
|
|
Text("Le presse-papier est vide")
|
|
.foregroundStyle(.secondary)
|
|
.italic()
|
|
}
|
|
}
|
|
} header: {
|
|
Text("Contenu du presse-papier")
|
|
}
|
|
}
|
|
|
|
Section {
|
|
ForEach(getRunningTournaments()) { tournament in
|
|
NavigationLink {
|
|
AddTeamView(tournament: tournament, pasteString: pasteString, editedTeam: nil)
|
|
} label: {
|
|
LabeledContent {
|
|
Text(tournament.unsortedTeamsWithoutWO().count.formatted())
|
|
} label: {
|
|
Text(tournament.tournamentTitle())
|
|
Text(tournament.formattedDate())
|
|
}
|
|
}
|
|
}
|
|
} header: {
|
|
Text("Ajouter à la liste d'inscription")
|
|
}
|
|
}
|
|
.toolbar {
|
|
ToolbarItem(placement: .topBarLeading) {
|
|
Button("Fermer") {
|
|
self.quickAccessScreen = nil
|
|
}
|
|
}
|
|
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button {
|
|
pasteString = UIPasteboard.general.string ?? ""
|
|
} label: {
|
|
Label("Coller", systemImage: "doc.on.clipboard").labelStyle(.iconOnly)
|
|
}
|
|
.foregroundStyle(.master)
|
|
.labelStyle(.iconOnly)
|
|
.buttonBorderShape(.capsule)
|
|
}
|
|
|
|
ToolbarItem(placement: .bottomBar) {
|
|
PasteButton(payloadType: String.self) { strings in
|
|
pasteString = strings.first ?? ""
|
|
}
|
|
.foregroundStyle(.master)
|
|
.labelStyle(.titleAndIcon)
|
|
.buttonBorderShape(.capsule)
|
|
}
|
|
}
|
|
.navigationTitle("Choix du tournoi")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbarBackground(.visible, for: .navigationBar)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func _searchStatus() -> String {
|
|
var searchStatus : [String] = []
|
|
if navigation.agendaDestination == .around, federalDataViewModel.searchedFederalTournaments.isEmpty == false {
|
|
let filteredSearchedFederalTournaments = federalDataViewModel.filteredSearchedFederalTournaments
|
|
|
|
let status : String = filteredSearchedFederalTournaments.count.formatted() + " tournoi" + filteredSearchedFederalTournaments.count.pluralSuffix
|
|
searchStatus.append(status)
|
|
}
|
|
|
|
if federalDataViewModel.areFiltersEnabled(), tournaments.isEmpty == false {
|
|
searchStatus.append(federalDataViewModel.filterStatus())
|
|
}
|
|
|
|
return searchStatus.joined(separator: " ")
|
|
}
|
|
|
|
private func _filterButtonTitle() -> String {
|
|
var prefix = "modifier "
|
|
if navigation.agendaDestination == .around, federalDataViewModel.searchedFederalTournaments.isEmpty == false {
|
|
prefix = ""
|
|
}
|
|
return prefix + "vos filtres"
|
|
}
|
|
|
|
private func _gatherFederalTournaments() {
|
|
isGatheringFederalTournaments = true
|
|
Task {
|
|
do {
|
|
let clubs : [Club] = dataStore.user.clubsObjects()
|
|
try await federalDataViewModel.gatherTournaments(
|
|
clubs: clubs.filter { $0.code != nil },
|
|
startDate: .now.startOfCurrentMonth,
|
|
endDate: .now.startOfCurrentMonth.addingMonths(4)
|
|
)
|
|
} catch {
|
|
self.error = error
|
|
}
|
|
isGatheringFederalTournaments = false
|
|
uuid = UUID()
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func _dataEmptyView() -> some View {
|
|
switch navigation.agendaDestination! {
|
|
case .activity:
|
|
_runningEmptyView()
|
|
case .history:
|
|
_endedEmptyView()
|
|
case .tenup:
|
|
_tenupEmptyView()
|
|
case .around:
|
|
_searchTenupEmptyView()
|
|
}
|
|
}
|
|
|
|
private func _runningEmptyView() -> some View {
|
|
ContentUnavailableView {
|
|
Label {
|
|
Text("Bienvenue sur Padel Club")
|
|
.font(.largeTitle)
|
|
} icon: {
|
|
Image(.padelClubLogoFondclairTransparent)
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 100)
|
|
}
|
|
} description: {
|
|
Text("Aucun événement en cours ou à venir dans votre agenda.")
|
|
} actions: {
|
|
RowButtonView("Créer un nouvel événement") {
|
|
newTournament = Tournament.newEmptyInstance()
|
|
}
|
|
RowButtonView("Importer via Tenup") {
|
|
navigation.agendaDestination = .tenup
|
|
}
|
|
SupportButtonView(contentIsUnavailable: true)
|
|
}
|
|
}
|
|
|
|
private func _endedEmptyView() -> some View {
|
|
ContentUnavailableView {
|
|
Label("Aucun tournoi terminé", systemImage: "shield.slash")
|
|
} description: {
|
|
Text("Vos tournois terminés seront listés ici.")
|
|
}
|
|
}
|
|
|
|
private func _tenupEmptyView() -> some View {
|
|
if dataStore.user.hasTenupClubs() == false {
|
|
ContentUnavailableView {
|
|
Label("Aucun clubs FFT", systemImage: "shield.slash")
|
|
} description: {
|
|
Text("Pour voir vos tournois tenup ici, mettez en favori des clubs FFT.")
|
|
} actions: {
|
|
RowButtonView("Choisir mes clubs") {
|
|
presentClubSearchView = true
|
|
}
|
|
.padding()
|
|
}
|
|
} else {
|
|
ContentUnavailableView {
|
|
Label("Aucun tournoi", systemImage: "shield.slash")
|
|
} description: {
|
|
Text("Aucun tournoi n'a pu être récupéré via tenup.")
|
|
} actions: {
|
|
RowButtonView("Rafraîchir") {
|
|
NetworkFederalService.shared.formId = ""
|
|
_gatherFederalTournaments()
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func _searchTenupEmptyView() -> some View {
|
|
if federalDataViewModel.searchAttemptCount == 0 {
|
|
ContentUnavailableView {
|
|
Label("Recherche de tournoi", systemImage: "magnifyingglass")
|
|
} description: {
|
|
Text("Chercher les tournois autour de vous pour mieux décider les tournois à proposer dans votre club. Padel Club vous facilite même l'inscription !")
|
|
} actions: {
|
|
RowButtonView("Lancer la recherche") {
|
|
displaySearchView = true
|
|
}
|
|
.padding()
|
|
}
|
|
} else {
|
|
if federalDataViewModel.lastError == nil {
|
|
ContentUnavailableView {
|
|
Label("Aucun tournoi", systemImage: "shield.slash")
|
|
} description: {
|
|
Text("Aucun tournoi ne correspond aux critères sélectionnés.")
|
|
} actions: {
|
|
FooterButtonView("modifier vos critères de recherche") {
|
|
displaySearchView = true
|
|
}
|
|
.padding()
|
|
}
|
|
} else {
|
|
ContentUnavailableView {
|
|
Label("Une erreur est survenue", systemImage: "exclamationmark.circle.fill")
|
|
} description: {
|
|
Text("Tenup est peut-être en maintenance, veuillez ré-essayer plus tard.")
|
|
} actions: {
|
|
Link(destination: URLs.tenup.url) {
|
|
Text("Voir si tenup est en maintenance")
|
|
}
|
|
|
|
FooterButtonView("modifier vos critères de recherche") {
|
|
displaySearchView = true
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//#Preview {
|
|
// ActivityView()
|
|
//}
|
|
|