From 3e1c8ae63688b5e0f0f281ede402b44993b46a29 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 3 May 2024 21:14:27 +0200 Subject: [PATCH 1/5] fix stuff --- PadelClub.xcodeproj/project.pbxproj | 12 +----- PadelClub/Data/DataStore.swift | 2 +- PadelClub/Data/User.swift | 4 +- PadelClub/Utils/LocationManager.swift | 7 +--- .../PresentationContext.swift | 0 .../Screen => ViewModel}/Screen.swift | 0 PadelClub/Views/Club/ClubDetailView.swift | 15 +++++++ PadelClub/Views/Club/ClubSearchView.swift | 14 ++++++- .../Navigation/Agenda/ActivityView.swift | 21 +++++++--- .../Navigation/Agenda/EmptyActivityView.swift | 40 ------------------- .../Views/Navigation/Agenda/WelcomeView.swift | 24 ----------- PadelClub/Views/Navigation/MainView.swift | 2 +- .../Views/Navigation/PadelClubView.swift | 30 +++++++------- .../Views/Navigation/Umpire/UmpireView.swift | 33 +++++++-------- 14 files changed, 85 insertions(+), 119 deletions(-) rename PadelClub/{Views/Tournament/Screen => ViewModel}/PresentationContext.swift (100%) rename PadelClub/{Views/Tournament/Screen => ViewModel}/Screen.swift (100%) delete mode 100644 PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift delete mode 100644 PadelClub/Views/Navigation/Agenda/WelcomeView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 7d15d0c..55692ae 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -248,8 +248,6 @@ FFCFC01A2BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0192BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift */; }; FFCFC01C2BBC5AAA00B82851 /* SetDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */; }; FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; }; - FFD784022B91C1B4000F62A6 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD784012B91C1B4000F62A6 /* WelcomeView.swift */; }; - FFD784042B91C280000F62A6 /* EmptyActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD784032B91C280000F62A6 /* EmptyActivityView.swift */; }; FFDB1C6D2BB2A02000F1E467 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; }; FFDB1C732BB2CFE900F1E467 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */; }; FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; }; @@ -546,8 +544,6 @@ FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDescriptor.swift; sourceTree = ""; }; FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubView.swift; sourceTree = ""; }; FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; - FFD784012B91C1B4000F62A6 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; - FFD784032B91C280000F62A6 /* EmptyActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyActivityView.swift; sourceTree = ""; }; FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySortDescriptor.swift; sourceTree = ""; }; FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeferredViewModifier.swift; sourceTree = ""; }; @@ -936,8 +932,6 @@ FF3F74F92B91A018004CFE0E /* Screen */ = { isa = PBXGroup; children = ( - FF6EC8FD2B94792300EA7F5A /* Screen.swift */, - FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */, FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */, FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */, FF8F26532BAE1E4400650388 /* TableStructureView.swift */, @@ -984,6 +978,8 @@ FF3F74FD2B91A087004CFE0E /* ViewModel */ = { isa = PBXGroup; children = ( + FF6EC8FD2B94792300EA7F5A /* Screen.swift */, + FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */, FF7091652B90F0B000AB08DA /* TabDestination.swift */, FF025AEC2BD1513700A86CF8 /* AppScreen.swift */, FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */, @@ -1176,8 +1172,6 @@ isa = PBXGroup; children = ( FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */, - FFD784032B91C280000F62A6 /* EmptyActivityView.swift */, - FFD784012B91C1B4000F62A6 /* WelcomeView.swift */, FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */, FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */, ); @@ -1596,7 +1590,6 @@ FF9AC3952BE3627B00C2E883 /* GroupStageTeamReplacementView.swift in Sources */, FF4C7F022BBBD7150031B6A3 /* TabItemModifier.swift in Sources */, FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */, - FFD784042B91C280000F62A6 /* EmptyActivityView.swift in Sources */, FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */, FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */, FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */, @@ -1618,7 +1611,6 @@ FFF964532BC262B000EEF017 /* PlanningSettingsView.swift in Sources */, FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */, FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */, - FFD784022B91C1B4000F62A6 /* WelcomeView.swift in Sources */, FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */, FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */, FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */, diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 4109db9..4a8f346 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -105,7 +105,7 @@ class DataStore: ObservableObject { if let _ = Guard.main.currentPlan { return .creation } - if let user = self.user, user.club != nil { + if let user = self.user, user.clubs != nil { if user.umpireCode != nil { return .creation } else { diff --git a/PadelClub/Data/User.swift b/PadelClub/Data/User.swift index 34f6d95..b3b99dc 100644 --- a/PadelClub/Data/User.swift +++ b/PadelClub/Data/User.swift @@ -24,7 +24,7 @@ class User: UserBase, Storable { public var id: String = Store.randomId() public var username: String public var email: String - var club: String? + var clubs: [String]? var umpireCode: String? var licenceId: String? var firstName: String @@ -61,7 +61,7 @@ class User: UserBase, Storable { case _id = "id" case _username = "username" case _email = "email" - case _club = "club" + case _clubs = "clubs" case _umpireCode = "umpireCode" case _licenceId = "licenceId" case _firstName = "firstName" diff --git a/PadelClub/Utils/LocationManager.swift b/PadelClub/Utils/LocationManager.swift index 660f095..50afc59 100644 --- a/PadelClub/Utils/LocationManager.swift +++ b/PadelClub/Utils/LocationManager.swift @@ -17,7 +17,6 @@ class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { @Published var requestStarted: Bool = false @Published var userReadableCityOrZipcode: String = "" @Published var lastError: Error? = nil - var shouldRequestLocation: Bool = false override init() { super.init() @@ -41,10 +40,8 @@ class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { if manager.authorizationStatus == .authorizedWhenInUse || manager.authorizationStatus == .authorizedAlways { - if requestStarted == false && shouldRequestLocation { - DispatchQueue.main.async { - self.requestLocation() - } + DispatchQueue.main.async { + self.requestLocation() } } } diff --git a/PadelClub/Views/Tournament/Screen/PresentationContext.swift b/PadelClub/ViewModel/PresentationContext.swift similarity index 100% rename from PadelClub/Views/Tournament/Screen/PresentationContext.swift rename to PadelClub/ViewModel/PresentationContext.swift diff --git a/PadelClub/Views/Tournament/Screen/Screen.swift b/PadelClub/ViewModel/Screen.swift similarity index 100% rename from PadelClub/Views/Tournament/Screen/Screen.swift rename to PadelClub/ViewModel/Screen.swift diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index ed77f5b..9a0b310 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct ClubDetailView: View { @Bindable var club: Club @@ -138,6 +139,20 @@ struct ClubDetailView: View { .navigationBarTitleDisplayMode(.inline) .toolbar(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar) + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Button { + do { + dataStore.user?.clubs?.removeAll(where: { $0.id == club.id }) + try dataStore.userStorage.update() + } catch { + Logger.error(error) + } + } label: { + LabelDelete() + } + } + } .onDisappear { if displayContext == .edition { try? dataStore.clubs.addOrUpdate(instance: club) diff --git a/PadelClub/Views/Club/ClubSearchView.swift b/PadelClub/Views/Club/ClubSearchView.swift index f753c68..b80e524 100644 --- a/PadelClub/Views/Club/ClubSearchView.swift +++ b/PadelClub/Views/Club/ClubSearchView.swift @@ -25,6 +25,8 @@ struct ClubSearchView: View { @State private var getForwardCityList: [CLPlacemark] = [] @State private var searchPresented: Bool = false @State private var showingSettingsAlert = false + @State private var presentClubCreationView: Bool = false + var displayContext: DisplayContext = .edition var club: Club? @@ -141,7 +143,7 @@ struct ClubSearchView: View { if searchAttempted { Label("Aucun club trouvé", systemImage: "mappin.slash") } else { - Text("Recherche de club") + Label("Recherche de club", systemImage: "location.circle") } } description: { Text("Padel Club peut rechercher un club autour de vous, d'une ville ou d'un code postal, facilitant ainsi la saisie d'information.") @@ -160,12 +162,22 @@ struct ClubSearchView: View { RowButtonView("Chercher une ville ou un code postal") { searchPresented = true } + + if searchAttempted { + RowButtonView("Créer un club manuellement") { + presentClubCreationView = true + } + } } } } else { ContentUnavailableView("recherche en cours", systemImage: "mappin.and.ellipse", description: Text("recherche des clubs autour de vous")) } } + .sheet(isPresented: $presentClubCreationView) { + CreateClubView() + .tint(.master) + } .alert(isPresented: $showingSettingsAlert) { Alert( title: Text("Réglages"), diff --git a/PadelClub/Views/Navigation/Agenda/ActivityView.swift b/PadelClub/Views/Navigation/Agenda/ActivityView.swift index 90c2cee..55ef80d 100644 --- a/PadelClub/Views/Navigation/Agenda/ActivityView.swift +++ b/PadelClub/Views/Navigation/Agenda/ActivityView.swift @@ -20,7 +20,8 @@ struct ActivityView: View { @State private var isGatheringFederalTournaments: Bool = false @State private var error: Error? @State private var uuid: UUID = UUID() - + @State private var presentClubSearchView: Bool = false + var runningTournaments: [FederalTournamentHolder] { dataStore.tournaments.filter({ $0.endDate == nil }) .filter({ federalDataViewModel.isTournamentValidForFilters($0) }) @@ -199,6 +200,10 @@ struct ActivityView: View { .environment(navigation) .tint(.master) } + .sheet(isPresented: $presentClubSearchView) { + ClubImportView() + .tint(.master) + } } } } @@ -245,8 +250,14 @@ struct ActivityView: View { RowButtonView("Créer un nouvel événement") { newTournament = Tournament.newEmptyInstance() } - RowButtonView("Importer via Tenup") { - navigation.agendaDestination = .tenup + if dataStore.clubs.isEmpty { + RowButtonView("Chercher l'un de vos clubs") { + presentClubSearchView = true + } + } else { + RowButtonView("Importer via Tenup") { + navigation.agendaDestination = .tenup + } } } } @@ -264,9 +275,9 @@ struct ActivityView: View { ContentUnavailableView { Label("Aucun tournoi", systemImage: "shield.slash") } description: { - Text("Pour voir vos tournois tenup ici, indiquez vos clubs préférés.") + Text("Pour voir vos tournois tenup ici, choisissez vos clubs.") } actions: { - RowButtonView("Choisir mes clubs préférés") { + RowButtonView("Choisir mes clubs") { navigation.selectedTab = .umpire } } diff --git a/PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift b/PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift deleted file mode 100644 index bd0af4f..0000000 --- a/PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// EmptyActivityView.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/03/2024. -// - -import SwiftUI - -struct EmptyActivityView: View { - @State private var newTournament: Tournament? - - var body: some View { - NavigationStack { - List { - WelcomeView() - - Section { - RowButtonView("Créer votre premier événement", action: { - newTournament = Tournament.newEmptyInstance() - }) - } - - Section { - RowButtonView("Importer vos tournois Tenup", action: { - - }) - } - } - .sheet(item: $newTournament) { tournament in - EventCreationView(tournaments: [tournament]) - .tint(.master) - } - } - } -} - -#Preview { - EmptyActivityView() -} diff --git a/PadelClub/Views/Navigation/Agenda/WelcomeView.swift b/PadelClub/Views/Navigation/Agenda/WelcomeView.swift deleted file mode 100644 index f7763b7..0000000 --- a/PadelClub/Views/Navigation/Agenda/WelcomeView.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// WelcomeView.swift -// PadelClub -// -// Created by Razmig Sarkissian on 01/03/2024. -// - -import SwiftUI - -struct WelcomeView: View { - var body: some View { - Image(.padelClubLogoFondfonce) - .resizable() - .scaledToFit() - .buttonStyle(.borderedProminent) - .tint(.logoBackground) - .listRowBackground(Color.clear) - .listRowInsets(EdgeInsets(.zero)) - } -} - -#Preview { - WelcomeView() -} diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 94768e7..6a4920a 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -101,7 +101,7 @@ struct MainView: View { checkingFiles = false if let mostRecentDateAvailable = SourceFileManager.shared.mostRecentDateAvailable, mostRecentDateAvailable > SourceFileManager.shared.lastDataSourceDate() ?? .distantPast { - _startImporting() + //_startImporting() } } diff --git a/PadelClub/Views/Navigation/PadelClubView.swift b/PadelClub/Views/Navigation/PadelClubView.swift index 7c74e05..5f81c40 100644 --- a/PadelClub/Views/Navigation/PadelClubView.swift +++ b/PadelClub/Views/Navigation/PadelClubView.swift @@ -11,8 +11,8 @@ import SwiftData struct PadelClubView: View { @State private var checkingFilesAttempt: Int = 0 @State private var checkingFiles: Bool = false - @State private var importingFiles: Bool = false - + @AppStorage("importingFiles") var importingFiles: Bool = false + @EnvironmentObject var dataStore: DataStore var lastDataSource: String? { @@ -72,18 +72,20 @@ struct PadelClubView: View { Text(monthData.monthKey) } } -// -// if players.isEmpty { -// ContentUnavailableView { -// Label("Aucun joueur importé", systemImage: "person.slash") -// } description: { -// Text("Padel peut importer toutes les données publique de la FFT concernant tous les compétiteurs et compétitrices.") -// } actions: { -// RowButtonView("Démarrer l'importation") { -// _startImporting() -// } -// } -// } + + if importingFiles { + ContentUnavailableView("Importation en cours", systemImage: "server.rack", description: Text("Une importation des données fédérales publiques est en cours, veuillez patienter.")) + } else if (players.isEmpty || lastDataSource == nil) { + ContentUnavailableView { + Label("Aucun joueur importé", systemImage: "person.slash") + } description: { + Text("Padel Club peut importer toutes les données publiques de la FFT concernant tous les compétiteurs et compétitrices.") + } actions: { + RowButtonView("Démarrer l'importation") { + _startImporting() + } + } + } } .task { await self._checkSourceFileAvailability() diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index ad416cf..37bddc4 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -59,7 +59,11 @@ struct UmpireView: View { user.licenceId = player.license let club = dataStore.clubs.first(where: { $0.code == player.clubCode }) ?? Club(name: player.clubName!, code: player.clubCode!) try? dataStore.clubs.addOrUpdate(instance: club) - user.club = club.id + if user.clubs == nil { + user.clubs = [club.id] + } else { + user.clubs!.insert(club.id, at: 0) + } dataStore.setUser(user) } }) @@ -82,22 +86,19 @@ struct UmpireView: View { } } - if let clubId = user.club { - if let club = dataStore.clubs.findById(clubId) { - Section { - NavigationLink { - ClubDetailView(club: club, displayContext: .edition) - } label: { - ClubRowView(club: club) - } - } header: { - Text("Mon club") - } footer: { - Button("supprimer", role: .destructive) { - user.club = nil - dataStore.setUser(user) + if let clubs = user.clubs { + Section { + ForEach(clubs) { clubId in + if let club = dataStore.clubs.findById(clubId) { + NavigationLink { + ClubDetailView(club: club, displayContext: .edition) + } label: { + ClubRowView(club: club) + } } } + } header: { + Text("Mes clubs") } } } @@ -109,7 +110,7 @@ struct UmpireView: View { LabeledContent { Text(dataStore.clubs.count.formatted()) } label: { - Label("Mes clubs favoris", systemImage: "house.and.flag.circle.fill") + Label("Mes clubs", systemImage: "house.and.flag.circle.fill") } } } From 963d110f2321ec34f5614a8a29bf5aac09fd2b1f Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Fri, 3 May 2024 22:01:55 +0200 Subject: [PATCH 2/5] fix uncomment --- PadelClub/Views/Navigation/MainView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 6a4920a..94768e7 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -101,7 +101,7 @@ struct MainView: View { checkingFiles = false if let mostRecentDateAvailable = SourceFileManager.shared.mostRecentDateAvailable, mostRecentDateAvailable > SourceFileManager.shared.lastDataSourceDate() ?? .distantPast { - //_startImporting() + _startImporting() } } From a61e16123cbcfe5da7fc9d7152bd415f2f16b554 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sat, 4 May 2024 11:35:12 +0200 Subject: [PATCH 3/5] setup final clubs management --- PadelClub.xcodeproj/project.pbxproj | 2 +- PadelClub/Data/Club.swift | 38 ++++++ PadelClub/Data/Event.swift | 2 +- .../Data/Federal/FederalTournament.swift | 17 ++- PadelClub/Data/Tournament.swift | 6 +- PadelClub/Data/User.swift | 9 ++ PadelClub/Utils/DisplayContext.swift | 1 + PadelClub/ViewModel/MatchScheduler.swift | 6 +- PadelClub/ViewModel/NavigationViewModel.swift | 3 + PadelClub/ViewModel/TabDestination.swift | 10 -- .../CallMessageCustomizationView.swift | 44 +++++-- PadelClub/Views/Club/ClubDetailView.swift | 124 ++++++++++++------ PadelClub/Views/Club/ClubRowView.swift | 3 +- PadelClub/Views/Club/ClubSearchView.swift | 31 +++-- PadelClub/Views/Club/ClubsView.swift | 37 +++--- PadelClub/Views/Club/CreateClubView.swift | 24 +++- PadelClub/Views/Event/EventCreationView.swift | 25 ++-- .../Navigation/Agenda/ActivityView.swift | 47 +++---- .../Navigation/Agenda/CalendarView.swift | 6 +- .../Navigation/Agenda/EventListView.swift | 5 +- PadelClub/Views/Navigation/MainView.swift | 32 +++-- .../Navigation/Ongoing/OngoingView.swift | 4 +- .../{ => Toolbox}/PadelClubView.swift | 2 +- .../Navigation/Toolbox/ToolboxView.swift | 7 +- .../Views/Navigation/Umpire/UmpireView.swift | 48 ++++--- .../Views/Planning/PlanningSettingsView.swift | 2 +- .../Views/Shared/TournamentFilterView.swift | 33 ++--- .../TournamentClubSettingsView.swift | 16 +-- .../Screen/InscriptionManagerView.swift | 24 ++-- 29 files changed, 394 insertions(+), 214 deletions(-) rename PadelClub/Views/Navigation/{ => Toolbox}/PadelClubView.swift (98%) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 55692ae..ff3dbf5 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -896,7 +896,6 @@ isa = PBXGroup; children = ( FF59FFB62B90EFBF0061EFF9 /* MainView.swift */, - FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */, FFD783FB2B91B919000F62A6 /* Agenda */, FF3F74FA2B91A04B004CFE0E /* Organizer */, FF3F74FB2B91A060004CFE0E /* Toolbox */, @@ -963,6 +962,7 @@ FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */, FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */, FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */, + FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */, ); path = Toolbox; sourceTree = ""; diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index 98fb664..f02328d 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -111,4 +111,42 @@ extension Club { guard let code else { return nil } return URL(string: "https://tenup.fft.fr/club/\(code)") } + + func update(fromClub club: Club) { + self.acronym = club.acronym + self.name = club.name + self.phone = club.phone + self.code = club.code + self.address = club.address + self.city = club.city + self.zipCode = club.zipCode + self.latitude = club.latitude + self.longitude = club.longitude + } + + func hasBeenCreated(by creatorId: String?) -> Bool { + guard let creatorId else { return false } + guard let creator else { return false } + return creatorId == creator + } + + func isFavorite() -> Bool { + DataStore.shared.user?.clubs?.contains(where: { $0 == id }) == true + } + + static func findOrCreate(name: String, code: String?, city: String? = nil, zipCode: String? = nil) -> Club { + + /* + + identify a club : code, name, ?? + + */ + let clubs: [Club] = Store.main.filter(isIncluded: { (code == nil && $0.name == name && $0.city == city && $0.zipCode == zipCode) || $0.code == code }) + + if clubs.isEmpty == false { + return clubs.first! + } else { + return Club(creator: DataStore.shared.user?.id, name: name, code: code) + } + } } diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index a33e05f..3ecf737 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -37,7 +37,7 @@ class Event: ModelObject, Storable { // self.loserRoundFormat = loserRoundFormat } - var clubObject: Club? { + func clubObject() -> Club? { guard let club else { return nil } return Store.main.findById(club) } diff --git a/PadelClub/Data/Federal/FederalTournament.swift b/PadelClub/Data/Federal/FederalTournament.swift index c9391ec..338ed0f 100644 --- a/PadelClub/Data/Federal/FederalTournament.swift +++ b/PadelClub/Data/Federal/FederalTournament.swift @@ -5,6 +5,7 @@ import Foundation import CoreLocation +import LeStorage enum DayPeriod { case all @@ -18,14 +19,22 @@ struct FederalTournament: Identifiable, Codable { func getEvent() -> Event { var club = DataStore.shared.clubs.first(where: { $0.code == codeClub }) if club == nil { - club = Club(name: clubLabel(), code: codeClub) - try? DataStore.shared.clubs.addOrUpdate(instance: club!) + club = Club.findOrCreate(name: clubLabel(), code: codeClub) + do { + try DataStore.shared.clubs.addOrUpdate(instance: club!) + } catch { + Logger.error(error) + } } var event = DataStore.shared.events.first(where: { $0.tenupId == id.string }) if event == nil { - event = Event(club: club?.id, name: libelle, tenupId: id.string) - try? DataStore.shared.events.addOrUpdate(instance: event!) + event = Event(creator: DataStore.shared.user?.id, club: club?.id, name: libelle, tenupId: id.string) + do { + try DataStore.shared.events.addOrUpdate(instance: event!) + } catch { + Logger.error(error) + } } return event! } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 9e70175..0ce195b 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -290,7 +290,7 @@ class Tournament : ModelObject, Storable { startDate <= Date() } - var eventObject: Event? { + func eventObject() -> Event? { guard let event else { return nil } return Store.main.findById(event) } @@ -301,7 +301,7 @@ class Tournament : ModelObject, Storable { } func club() -> Club? { - eventObject?.clubObject + eventObject()?.clubObject() } func locationLabel(_ displayStyle: DisplayStyle = .wide) -> String { @@ -656,7 +656,7 @@ class Tournament : ModelObject, Storable { //todo var clubName: String? { - eventObject?.clubObject?.name + eventObject()?.clubObject()?.name } //todo diff --git a/PadelClub/Data/User.swift b/PadelClub/Data/User.swift index b3b99dc..b2604ef 100644 --- a/PadelClub/Data/User.swift +++ b/PadelClub/Data/User.swift @@ -57,6 +57,15 @@ class User: UserBase, Storable { return try? federalContext.fetch(fetchRequest).first } + func hasClubs() -> Bool { + clubs?.isEmpty == false + } + + func clubsObjects(includeCreated: Bool = false) -> [Club] { + guard let clubs else { return [] } + return Store.main.filter(isIncluded: { (includeCreated && $0.creator == id) || clubs.contains($0.id) }) + } + enum CodingKeys: String, CodingKey { case _id = "id" case _username = "username" diff --git a/PadelClub/Utils/DisplayContext.swift b/PadelClub/Utils/DisplayContext.swift index 7ca52be..29187ed 100644 --- a/PadelClub/Utils/DisplayContext.swift +++ b/PadelClub/Utils/DisplayContext.swift @@ -10,6 +10,7 @@ import Foundation enum DisplayContext { case addition case edition + case lockedForEditing } enum DisplayStyle { diff --git a/PadelClub/ViewModel/MatchScheduler.swift b/PadelClub/ViewModel/MatchScheduler.swift index 6c4708d..85760fc 100644 --- a/PadelClub/ViewModel/MatchScheduler.swift +++ b/PadelClub/ViewModel/MatchScheduler.swift @@ -99,7 +99,7 @@ class MatchScheduler { let groupStageCourtCount = tournament.groupStageCourtCount ?? 1 let groupStages = tournament.groupStages() let numberOfCourtsAvailablePerRotation: Int = tournament.courtCount - courtsUnavailability = tournament.eventObject?.courtsUnavailability + courtsUnavailability = tournament.eventObject()?.courtsUnavailability let matches = groupStages.flatMap({ $0._matches() }) matches.forEach({ @@ -502,7 +502,7 @@ class MatchScheduler { } func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) { - courtsUnavailability = tournament.eventObject?.courtsUnavailability + courtsUnavailability = tournament.eventObject()?.courtsUnavailability let upperRounds = tournament.rounds() let allMatches = tournament.allMatches() @@ -607,7 +607,7 @@ class MatchScheduler { } func updateSchedule(tournament: Tournament) { - courtsUnavailability = tournament.eventObject?.courtsUnavailability + courtsUnavailability = tournament.eventObject()?.courtsUnavailability let lastDate = updateGroupStageSchedule(tournament: tournament) updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate) } diff --git a/PadelClub/ViewModel/NavigationViewModel.swift b/PadelClub/ViewModel/NavigationViewModel.swift index 1a8467e..dfe589c 100644 --- a/PadelClub/ViewModel/NavigationViewModel.swift +++ b/PadelClub/ViewModel/NavigationViewModel.swift @@ -10,6 +10,9 @@ import SwiftUI @Observable class NavigationViewModel { var path = NavigationPath() + var toolboxPath = NavigationPath() + var umpirePath = NavigationPath() + var ongoingPath = NavigationPath() var selectedTab: TabDestination? var agendaDestination: AgendaDestination? = .activity var tournament: Tournament? diff --git a/PadelClub/ViewModel/TabDestination.swift b/PadelClub/ViewModel/TabDestination.swift index b773564..429b2fd 100644 --- a/PadelClub/ViewModel/TabDestination.swift +++ b/PadelClub/ViewModel/TabDestination.swift @@ -13,19 +13,15 @@ enum TabDestination: CaseIterable, Identifiable { } case activity - case eventList case toolbox case tournamentOrganizer case umpire - case padelClub case ongoing var title: String { switch self { case .activity: return "Activité" - case .eventList: - return "Journal" case .ongoing: return "En cours" case .toolbox: @@ -34,8 +30,6 @@ enum TabDestination: CaseIterable, Identifiable { return "Gestionnaire" case .umpire: return "Juge-Arbitre" - case .padelClub: - return "Padel Club" } } @@ -43,8 +37,6 @@ enum TabDestination: CaseIterable, Identifiable { switch self { case .activity: return "calendar.day.timeline.left" - case .eventList: - return "book.closed" case .ongoing: return "figure.tennis" case .toolbox: @@ -53,8 +45,6 @@ enum TabDestination: CaseIterable, Identifiable { return "squares.below.rectangle" case .umpire: return "person.bust" - case .padelClub: - return "shield" } } } diff --git a/PadelClub/Views/Calling/CallMessageCustomizationView.swift b/PadelClub/Views/Calling/CallMessageCustomizationView.swift index 3187722..d6626af 100644 --- a/PadelClub/Views/Calling/CallMessageCustomizationView.swift +++ b/PadelClub/Views/Calling/CallMessageCustomizationView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct CallMessageCustomizationView: View { @EnvironmentObject var dataStore: DataStore @@ -69,20 +70,9 @@ struct CallMessageCustomizationView: View { } header: { Text("Signature du message") } - - Section { - TextField("Nom du club", text: $customClubName) - .autocorrectionDisabled() - .onSubmit { - if let eventClub = tournament.eventObject?.clubObject { - eventClub.name = customClubName - try? dataStore.clubs.addOrUpdate(instance: eventClub) - } - } - } header: { - Text("Nom du club") - } - + + _clubNameView() + Section { if appSettings.callUseFullCustomMessage { Text(self.computedFullCustomMessage()) @@ -174,6 +164,32 @@ struct CallMessageCustomizationView: View { private func _save() { dataStore.updateSettings() } + + @ViewBuilder + private func _clubNameView() -> some View { + if let eventClub = tournament.eventObject()?.clubObject() { + let hasBeenCreated: Bool = eventClub.hasBeenCreated(by: dataStore.user?.id) + Section { + TextField("Nom du club", text: $customClubName) + .autocorrectionDisabled() + .onSubmit { + eventClub.name = customClubName + do { + try dataStore.clubs.addOrUpdate(instance: eventClub) + } catch { + Logger.error(error) + } + } + .disabled(hasBeenCreated == false) + } header: { + Text("Nom du club") + } footer: { + if hasBeenCreated == false { + Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) + } + } + } + } func computedFullCustomMessage() -> String { var text = customCallMessageBody.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle()) diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index 9a0b310..acaa1aa 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -14,12 +14,15 @@ struct ClubDetailView: View { @EnvironmentObject var dataStore: DataStore @FocusState var focusedField: Club.CodingKeys? @State private var acronymMode: Club.AcronymMode = .automatic - @State private var updateClubData: Bool = false - - init(club: Club, displayContext: DisplayContext = .edition) { + @State private var city: String + @State private var zipCode: String + + init(club: Club, displayContext: DisplayContext) { _club = Bindable(club) self.displayContext = displayContext _acronymMode = State(wrappedValue: club.shortNameMode()) + _city = State(wrappedValue: club.city ?? "") + _zipCode = State(wrappedValue: club.zipCode ?? "") } var body: some View { @@ -87,31 +90,51 @@ struct ClubDetailView: View { club.acronym = "" } } - } footer: { - Text("Vous pouvez personaliser le nom court ou laisser celui généré par défaut. Le nom court est utile au niveau des liens de diffusions.") - } - - - if club.code == nil || updateClubData { - Section { - NavigationLink { - ClubSearchView(displayContext: .edition, club: club) - } label: { - Label("Chercher dans la base fédérale", systemImage: "magnifyingglass") + + if club.code == nil { + VStack(alignment: .leading, spacing: 0) { + Text("Ville").foregroundStyle(.secondary).font(.caption) + TextField("Ville", text: $city) + .fixedSize() + .focused($focusedField, equals: ._city) + .submitLabel( displayContext == .addition ? .next : .done) + .onSubmit { + if displayContext == .addition { + focusedField = ._zipCode + } + club.city = city + } } - } footer: { - if club.code != nil { - HStack { - Spacer() - Button("annuler", role: .cancel) { - updateClubData = false + .onTapGesture { + focusedField = ._city + } + + VStack(alignment: .leading, spacing: 0) { + Text("Code Postal").foregroundStyle(.secondary).font(.caption) + TextField("Code Postal", text: $zipCode) + .fixedSize() + .focused($focusedField, equals: ._zipCode) + .submitLabel( displayContext == .addition ? .next : .done) + .onSubmit { + club.zipCode = zipCode } - } - } else { - Text("Vous pouvez chercher un club dans la base fédérale et importer les informations directement.") } + .onTapGesture { + focusedField = ._zipCode + } + } + + } footer: { + if displayContext == .lockedForEditing { + Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) + } else { + Text("Vous pouvez personaliser le nom court ou laisser celui généré par défaut.") } - } else if let federalLink = club.federalLink() { + } + .disabled(displayContext == .lockedForEditing) + + + if let federalLink = club.federalLink() { Section { LabeledContent("Code Club") { Text(club.code ?? "") @@ -119,14 +142,24 @@ struct ClubDetailView: View { LabeledContent("Ville") { Text(club.city ?? "") } + LabeledContent("Code Postal") { + Text(club.zipCode ?? "") + } Link(destination: federalLink) { Text("Fiche du club sur tenup") } - } footer: { - HStack { - Spacer() - Button("modifier", role: .destructive) { - updateClubData = true + } + } + + if displayContext == .edition { + Section { + RowButtonView("Supprimer ce club", role: .destructive) { + do { + try dataStore.clubs.deleteById(club.id) + dataStore.user?.clubs?.removeAll(where: { $0 == club.id }) + try dataStore.userStorage.update() + } catch { + Logger.error(error) } } } @@ -135,27 +168,36 @@ struct ClubDetailView: View { .keyboardType(.alphabet) .autocorrectionDisabled() .defaultFocus($focusedField, ._name, priority: .automatic) - .navigationTitle(displayContext == .edition ? club.name : "Nouveau club") + .navigationTitle(displayContext == .addition ? "Nouveau club" : club.name) .navigationBarTitleDisplayMode(.inline) .toolbar(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar) .toolbar { - ToolbarItem(placement: .topBarTrailing) { - Button { - do { - dataStore.user?.clubs?.removeAll(where: { $0.id == club.id }) - try dataStore.userStorage.update() - } catch { - Logger.error(error) + if displayContext == .edition || displayContext == .lockedForEditing { + let isFavorite = club.isFavorite() + ToolbarItem(placement: .topBarTrailing) { + BarButtonView("Favori", icon: isFavorite ? "start" : "star.fill") { + do { + if isFavorite { + dataStore.user?.clubs?.removeAll(where: { $0 == club.id }) + } else { + dataStore.user?.clubs?.append(club.id) + } + try dataStore.userStorage.update() + } catch { + Logger.error(error) + } } - } label: { - LabelDelete() } } } .onDisappear { if displayContext == .edition { - try? dataStore.clubs.addOrUpdate(instance: club) + do { + try dataStore.clubs.addOrUpdate(instance: club) + } catch { + Logger.error(error) + } } } .onAppear { @@ -169,5 +211,5 @@ struct ClubDetailView: View { } #Preview { - ClubDetailView(club: Club.mock()) + ClubDetailView(club: Club.mock(), displayContext: .edition) } diff --git a/PadelClub/Views/Club/ClubRowView.swift b/PadelClub/Views/Club/ClubRowView.swift index a87c51b..f9d5a89 100644 --- a/PadelClub/Views/Club/ClubRowView.swift +++ b/PadelClub/Views/Club/ClubRowView.swift @@ -12,7 +12,8 @@ struct ClubRowView: View { var body: some View { LabeledContent { - + Image(systemName: club.isFavorite() ? "star.fill" : "star") + .foregroundStyle(club.isFavorite() ? .green : .logoRed) } label: { Text(club.name) Text(club.acronym) diff --git a/PadelClub/Views/Club/ClubSearchView.swift b/PadelClub/Views/Club/ClubSearchView.swift index b80e524..4fca530 100644 --- a/PadelClub/Views/Club/ClubSearchView.swift +++ b/PadelClub/Views/Club/ClubSearchView.swift @@ -9,6 +9,7 @@ import SwiftUI import CoreLocation import CoreLocationUI import TipKit +import LeStorage struct ClubSearchView: View { @Environment(\.dismiss) private var dismiss @@ -81,17 +82,29 @@ struct ClubSearchView: View { Section { ForEach(_filteredClubs()) { clubMark in Button { - let clubToEdit = club ?? Club(name: clubMark.nom) - if clubToEdit.name.isEmpty { - clubToEdit.name = clubMark.nom - clubToEdit.acronym = clubToEdit.automaticShortName() + let clubToEdit = club ?? Club.findOrCreate(name: clubMark.nom, code: clubMark.clubID) + + if clubToEdit.creator == dataStore.user?.id && dataStore.user?.id != nil { + if clubToEdit.name.isEmpty { + clubToEdit.name = clubMark.nom + clubToEdit.acronym = clubToEdit.automaticShortName() + } + clubToEdit.code = clubMark.clubID + clubToEdit.latitude = clubMark.lat + clubToEdit.longitude = clubMark.lng + clubToEdit.city = clubMark.ville } - clubToEdit.code = clubMark.clubID - clubToEdit.latitude = clubMark.lat - clubToEdit.longitude = clubMark.lng - clubToEdit.city = clubMark.ville + if displayContext == .addition { - try? dataStore.clubs.addOrUpdate(instance: clubToEdit) + do { + try dataStore.clubs.addOrUpdate(instance: clubToEdit) + if dataStore.user?.clubs?.contains(where: { $0 == clubToEdit.id }) == false { + dataStore.user?.clubs?.append(clubToEdit.id) + try dataStore.userStorage.update() + } + } catch { + Logger.error(error) + } } dismiss() } label: { diff --git a/PadelClub/Views/Club/ClubsView.swift b/PadelClub/Views/Club/ClubsView.swift index dce11a8..c5786f5 100644 --- a/PadelClub/Views/Club/ClubsView.swift +++ b/PadelClub/Views/Club/ClubsView.swift @@ -7,6 +7,7 @@ import SwiftUI import TipKit +import LeStorage struct ClubsView: View { @EnvironmentObject var dataStore: DataStore @@ -18,15 +19,15 @@ struct ClubsView: View { var body: some View { List { - - if dataStore.clubs.isEmpty == false && selection == nil { - Section { - TipView(tip) - .tipStyle(tint: nil) - } - } - - ForEach(dataStore.clubs) { club in +// +// if dataStore.clubs.isEmpty == false && selection == nil { +// Section { +// TipView(tip) +// .tipStyle(tint: nil) +// } +// } + let clubs : [Club] = (dataStore.user?.clubsObjects(includeCreated: true)) ?? [] + ForEach(clubs) { club in if let selection { Button { selection(club) @@ -39,22 +40,22 @@ struct ClubsView: View { .buttonStyle(.plain) } else { NavigationLink { - ClubDetailView(club: club) + ClubDetailView(club: club, displayContext: club.hasBeenCreated(by: dataStore.user?.id) ? .edition : .lockedForEditing) } label: { ClubRowView(club: club) } - .swipeActions(edge: .trailing, allowsFullSwipe: true) { - Button(role: .destructive) { - try? dataStore.clubs.delete(instance: club) - } label: { - LabelDelete() - } - } +// .swipeActions(edge: .trailing, allowsFullSwipe: true) { +// Button(role: .destructive) { +// try? dataStore.clubs.delete(instance: club) +// } label: { +// LabelDelete() +// } +// } } } } .overlay { - if dataStore.clubs.isEmpty { + if dataStore.user == nil || dataStore.user?.hasClubs() == true { ContentUnavailableView { Label("Aucun club", systemImage: "house.and.flag.fill") } description: { diff --git a/PadelClub/Views/Club/CreateClubView.swift b/PadelClub/Views/Club/CreateClubView.swift index 8655200..f455d3e 100644 --- a/PadelClub/Views/Club/CreateClubView.swift +++ b/PadelClub/Views/Club/CreateClubView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct CreateClubView: View { @Bindable var club: Club @@ -28,7 +29,28 @@ struct CreateClubView: View { } ToolbarItem(placement: .confirmationAction) { ButtonValidateView { - try? dataStore.clubs.addOrUpdate(instance: club) + + let existingOrCreatedClub = Club.findOrCreate(name: club.name, code: club.code, city: club.city, zipCode: club.zipCode) + + //update existing club if rights ok / freshly created club with data input from user + if existingOrCreatedClub.hasBeenCreated(by: dataStore.user?.id) { + existingOrCreatedClub.update(fromClub: club) + do { + try dataStore.clubs.addOrUpdate(instance: existingOrCreatedClub) + } catch { + Logger.error(error) + } + } + + //save into user + do { + if dataStore.user?.clubs?.contains(where: { $0 == existingOrCreatedClub.id }) == false { + dataStore.user?.clubs?.append(existingOrCreatedClub.id) + try dataStore.userStorage.update() + } + } catch { + Logger.error(error) + } dismiss() } .disabled(club.isValid == false) diff --git a/PadelClub/Views/Event/EventCreationView.swift b/PadelClub/Views/Event/EventCreationView.swift index 9bb2938..6689ede 100644 --- a/PadelClub/Views/Event/EventCreationView.swift +++ b/PadelClub/Views/Event/EventCreationView.swift @@ -7,6 +7,7 @@ import SwiftUI import TipKit +import LeStorage struct EventCreationView: View { @Environment(\.dismiss) private var dismiss @@ -89,15 +90,18 @@ struct EventCreationView: View { Section { RowButtonView("Valider") { - if tournaments.count > 1 || eventName.trimmed.isEmpty == false || selectedClub != nil { - let event = Event(name: eventName) - event.club = selectedClub?.id - tournaments.forEach { tournament in - tournament.event = event.id - } - try? dataStore.events.addOrUpdate(instance: event) + let event = Event(creator: dataStore.user?.id, name: eventName) + event.club = selectedClub?.id + tournaments.forEach { tournament in + tournament.event = event.id } + do { + try dataStore.events.addOrUpdate(instance: event) + } catch { + Logger.error(error) + } + tournaments.forEach { tournament in tournament.courtCount = selectedClub?.courts.count ?? 2 tournament.startDate = startingDate @@ -105,7 +109,12 @@ struct EventCreationView: View { tournament.setupFederalSettings() } - try? dataStore.tournaments.addOrUpdate(contentOfs: tournaments) + do { + try dataStore.tournaments.addOrUpdate(contentOfs: tournaments) + } catch { + Logger.error(error) + } + dismiss() navigation.path.append(tournaments.first!) } diff --git a/PadelClub/Views/Navigation/Agenda/ActivityView.swift b/PadelClub/Views/Navigation/Agenda/ActivityView.swift index 55ef80d..26cee95 100644 --- a/PadelClub/Views/Navigation/Agenda/ActivityView.swift +++ b/PadelClub/Views/Navigation/Agenda/ActivityView.swift @@ -113,43 +113,45 @@ struct ActivityView: View { .environment(navigation) .tint(.master) } - .refreshable { - if navigation.agendaDestination == .tenup { - federalDataViewModel.federalTournaments.removeAll() - NetworkFederalService.shared.formId = "" - _gatherFederalTournaments() - } - } +// .refreshable { +// if navigation.agendaDestination == .tenup { +// federalDataViewModel.federalTournaments.removeAll() +// NetworkFederalService.shared.formId = "" +// _gatherFederalTournaments() +// } +// } .task { if navigation.agendaDestination == .tenup - && dataStore.clubs.isEmpty == false + && dataStore.user?.hasClubs() == false && federalDataViewModel.federalTournaments.isEmpty { _gatherFederalTournaments() } } .onChange(of: navigation.agendaDestination) { if navigation.agendaDestination == .tenup - && dataStore.clubs.isEmpty == false + && dataStore.user?.hasClubs() == false && federalDataViewModel.federalTournaments.isEmpty { _gatherFederalTournaments() } } .toolbar { if presentToolbar { - ToolbarItem(placement: .status) { - VStack(spacing: -2) { - if federalDataViewModel.areFiltersEnabled() { - Text(federalDataViewModel.filterStatus()) - } - if let _activityStatus = _activityStatus() { - Text(_activityStatus) - .foregroundStyle(.secondary) + let _activityStatus = _activityStatus() + if federalDataViewModel.areFiltersEnabled() || _activityStatus != nil { + ToolbarItem(placement: .status) { + VStack(spacing: -2) { + if federalDataViewModel.areFiltersEnabled() { + Text(federalDataViewModel.filterStatus()) + } + if let _activityStatus { + Text(_activityStatus) + .foregroundStyle(.secondary) + } } + .font(.footnote) } - .font(.footnote) } - ToolbarItemGroup(placement: .topBarLeading) { Button { switch viewStyle { @@ -212,7 +214,8 @@ struct ActivityView: View { isGatheringFederalTournaments = true Task { do { - try await federalDataViewModel.gatherTournaments(clubs: dataStore.clubs.filter { $0.code != nil }, startDate: .now.startOfMonth) + let clubs : [Club] = (dataStore.user?.clubsObjects()) ?? [] + try await federalDataViewModel.gatherTournaments(clubs: clubs.filter { $0.code != nil }, startDate: .now.startOfMonth) } catch { self.error = error } @@ -250,7 +253,7 @@ struct ActivityView: View { RowButtonView("Créer un nouvel événement") { newTournament = Tournament.newEmptyInstance() } - if dataStore.clubs.isEmpty { + if dataStore.user == nil || dataStore.user?.hasClubs() == true { RowButtonView("Chercher l'un de vos clubs") { presentClubSearchView = true } @@ -271,7 +274,7 @@ struct ActivityView: View { } private func _tenupEmptyView() -> some View { - if dataStore.clubs.isEmpty { + if dataStore.user == nil || dataStore.user?.hasClubs() == true { ContentUnavailableView { Label("Aucun tournoi", systemImage: "shield.slash") } description: { diff --git a/PadelClub/Views/Navigation/Agenda/CalendarView.swift b/PadelClub/Views/Navigation/Agenda/CalendarView.swift index 5a9bfda..e700bf9 100644 --- a/PadelClub/Views/Navigation/Agenda/CalendarView.swift +++ b/PadelClub/Views/Navigation/Agenda/CalendarView.swift @@ -109,14 +109,14 @@ struct CalendarView: View { } label: { Text(day.formatted(.dateTime.day())) .fontWeight(.bold) - .foregroundStyle(.white) + .foregroundStyle((counts[day.dayInt] != nil ? Color.white : Color.black)) .frame(maxWidth: .infinity, minHeight: 40) .background( Circle() .foregroundStyle( Date.now.startOfDay == day.startOfDay - ? .green.opacity(counts[day.dayInt] != nil ? 0.8 : 0.3) - : color.opacity(counts[day.dayInt] != nil ? 0.8 : 0.3) + ? (counts[day.dayInt] != nil ? Color.logoRed : Color.green) + : (counts[day.dayInt] != nil ? Color.master : Color.beige) ) ) .overlay(alignment: .bottomTrailing) { diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 944f01d..bc0f872 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -51,7 +51,7 @@ struct EventListView: View { .headerProminence(.increased) .task { if navigation.agendaDestination == .tenup - && dataStore.clubs.isEmpty == false + && dataStore.user?.hasClubs() == false && _tournaments.isEmpty { _gatherFederalTournaments(startDate: section) } @@ -64,7 +64,8 @@ struct EventListView: View { // isGatheringFederalTournaments = true Task { do { - try await federalDataViewModel.gatherTournaments(clubs: dataStore.clubs.filter { $0.code != nil }, startDate: startDate, endDate: startDate.endOfMonth) + let clubs : [Club] = (dataStore.user?.clubsObjects()) ?? [] + try await federalDataViewModel.gatherTournaments(clubs: clubs.filter { $0.code != nil }, startDate: startDate, endDate: startDate.endOfMonth) } catch { Logger.error(error) // self.error = error diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 94768e7..4e0af40 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -20,16 +20,32 @@ struct MainView: View { dataStore.appSettings.lastDataSource } - @Environment(\.managedObjectContext) private var viewContext - - @FetchRequest( - sortDescriptors: [], - animation: .default) - private var players: FetchedResults + var selectedTabHandler: Binding { Binding( + get: { navigation.selectedTab }, + set: { + if $0 == navigation.selectedTab { +// switch navigation.selectedTab { +// case .activity: +// navigation.path.removeLast() +// case .toolbox: +// navigation.toolboxPath = NavigationPath() +// case .umpire: +// navigation.umpirePath = NavigationPath() +// case .ongoing: +// navigation.ongoingPath = NavigationPath() +// case .tournamentOrganizer: +// break +// case .none: +// break +// } + } else { + navigation.selectedTab = $0 + } + } + )} var body: some View { - @Bindable var navigation = navigation - TabView(selection: $navigation.selectedTab) { + TabView(selection: selectedTabHandler) { ActivityView() .tabItem(for: .activity) TournamentOrganizerView() diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift index cfc9646..aec2a16 100644 --- a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift +++ b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift @@ -8,6 +8,7 @@ import SwiftUI struct OngoingView: View { + @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel @EnvironmentObject var dataStore: DataStore var matches: [Match] { @@ -15,7 +16,8 @@ struct OngoingView: View { } var body: some View { - NavigationStack { + @Bindable var navigation = navigation + NavigationStack(path: $navigation.ongoingPath) { List { ForEach(matches) { match in MatchRowView(match: match, matchViewStyle: .feedStyle) diff --git a/PadelClub/Views/Navigation/PadelClubView.swift b/PadelClub/Views/Navigation/Toolbox/PadelClubView.swift similarity index 98% rename from PadelClub/Views/Navigation/PadelClubView.swift rename to PadelClub/Views/Navigation/Toolbox/PadelClubView.swift index 5f81c40..40d0b8f 100644 --- a/PadelClub/Views/Navigation/PadelClubView.swift +++ b/PadelClub/Views/Navigation/Toolbox/PadelClubView.swift @@ -96,7 +96,7 @@ struct PadelClubView: View { } } .headerProminence(.increased) - .navigationTitle(TabDestination.padelClub.title) + .navigationTitle("Source des données fédérales") } @ViewBuilder diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index a875a2c..3e110e0 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -8,10 +8,13 @@ import SwiftUI struct ToolboxView: View { + @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel + var body: some View { - NavigationStack { + @Bindable var navigation = navigation + NavigationStack(path: $navigation.toolboxPath) { List { - Section { + Section { NavigationLink { SelectablePlayerListView() } label: { diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index 37bddc4..5fe8e34 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -7,8 +7,10 @@ import SwiftUI import CoreLocation +import LeStorage struct UmpireView: View { + @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel @EnvironmentObject var dataStore: DataStore var lastDataSource: String? { dataStore.appSettings.lastDataSource @@ -24,7 +26,8 @@ struct UmpireView: View { } var body: some View { - NavigationStack { + @Bindable var navigation = navigation + NavigationStack(path: $navigation.umpirePath) { List { PurchaseListView() @@ -57,14 +60,19 @@ struct UmpireView: View { SelectablePlayerListView(allowSelection: 1, playerSelectionAction: { players in if let player = players.first { user.licenceId = player.license - let club = dataStore.clubs.first(where: { $0.code == player.clubCode }) ?? Club(name: player.clubName!, code: player.clubCode!) - try? dataStore.clubs.addOrUpdate(instance: club) - if user.clubs == nil { - user.clubs = [club.id] - } else { - user.clubs!.insert(club.id, at: 0) + let userClub = Club.findOrCreate(name: player.clubName!, code: player.clubCode) + do { + try dataStore.clubs.addOrUpdate(instance: userClub) + + if user.clubs == nil { + user.clubs = [userClub.id] + } else { + user.clubs!.insert(userClub.id, at: 0) + } + try dataStore.userStorage.update() + } catch { + Logger.error(error) } - dataStore.setUser(user) } }) } label: { @@ -81,24 +89,12 @@ struct UmpireView: View { } else { Button("supprimer", role: .destructive) { user.licenceId = nil - dataStore.setUser(user) - } - } - } - - if let clubs = user.clubs { - Section { - ForEach(clubs) { clubId in - if let club = dataStore.clubs.findById(clubId) { - NavigationLink { - ClubDetailView(club: club, displayContext: .edition) - } label: { - ClubRowView(club: club) - } + do { + try dataStore.userStorage.update() + } catch { + Logger.error(error) } } - } header: { - Text("Mes clubs") } } } @@ -108,11 +104,13 @@ struct UmpireView: View { ClubsView() } label: { LabeledContent { - Text(dataStore.clubs.count.formatted()) + Text((dataStore.user?.clubs ?? []).count.formatted()) } label: { Label("Mes clubs", systemImage: "house.and.flag.circle.fill") } } + } footer: { + Text("Il s'agit des clubs qui sont utilisés pour récupérer les tournois tenup.") } Section { diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index 1b47663..42d5218 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -64,7 +64,7 @@ struct PlanningSettingsView: View { TournamentFieldsManagerView(localizedStringKey: "Terrains par poule", count: $groupStageCourtCount, max: tournament.maximumCourtsPerGroupSage()) } - if let event = tournament.eventObject { + if let event = tournament.eventObject() { NavigationLink { CourtAvailabilitySettingsView(event: event) .environment(tournament) diff --git a/PadelClub/Views/Shared/TournamentFilterView.swift b/PadelClub/Views/Shared/TournamentFilterView.swift index 7ea8c1d..3a9a0ff 100644 --- a/PadelClub/Views/Shared/TournamentFilterView.swift +++ b/PadelClub/Views/Shared/TournamentFilterView.swift @@ -27,26 +27,29 @@ struct TournamentFilterView: View { var body: some View { NavigationView { Form { - Section { - ForEach(dataStore.clubs.filter({ $0.code != nil })) { club in - LabeledContent { - Button { - if selectedClubs.contains(club.code!) { - selectedClubs.remove(club.code!) - } else { - selectedClubs.insert(club.code!) + let clubs : [Club] = (dataStore.user?.clubsObjects()) ?? [] + if clubs.filter({ $0.code != nil }).isEmpty == false { + Section { + ForEach(clubs.filter({ $0.code != nil })) { club in + LabeledContent { + Button { + if selectedClubs.contains(club.code!) { + selectedClubs.remove(club.code!) + } else { + selectedClubs.insert(club.code!) + } + } label: { + if selectedClubs.contains(club.code!) { + Image(systemName: "checkmark.circle.fill") + } } } label: { - if selectedClubs.contains(club.code!) { - Image(systemName: "checkmark.circle.fill") - } + Text(club.clubTitle()) } - } label: { - Text(club.clubTitle()) } + } header: { + Text("Clubs") } - } header: { - Text("Clubs") } Section { ForEach(TournamentLevel.allCases) { level in diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift index 7d8b320..8169b8a 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift @@ -16,12 +16,12 @@ struct TournamentClubSettingsView: View { var body: some View { @Bindable var tournament = tournament List { - let event = tournament.eventObject - let selectedClub = event?.clubObject + let event = tournament.eventObject() + let selectedClub = event?.clubObject() Section { if let selectedClub { NavigationLink { - ClubDetailView(club: selectedClub, displayContext: .edition) + ClubDetailView(club: selectedClub, displayContext: selectedClub.hasBeenCreated(by: dataStore.user?.id) ? .edition : .lockedForEditing) } label: { ClubRowView(club: selectedClub) } @@ -30,11 +30,11 @@ struct TournamentClubSettingsView: View { ClubsView() { club in if let event { event.club = club.id - try? dataStore.events.addOrUpdate(instance: event) - } else { - let event = Event(club: club.id) - tournament.event = event.id - try? dataStore.events.addOrUpdate(instance: event) + do { + try dataStore.events.addOrUpdate(instance: event) + } catch { + Logger.error(error) + } } } } label: { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index d0d202e..e3a828a 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -689,13 +689,13 @@ struct InscriptionManagerView: View { Divider() NavigationLink { ClubsView() { club in - if let event = tournament.eventObject { + if let event = tournament.eventObject() { event.club = club.id - try? dataStore.events.addOrUpdate(instance: event) - } else { - let event = Event(club: club.id) - tournament.event = event.id - try? dataStore.events.addOrUpdate(instance: event) + do { + try dataStore.events.addOrUpdate(instance: event) + } catch { + Logger.error(error) + } } _save() } @@ -710,13 +710,13 @@ struct InscriptionManagerView: View { } else { NavigationLink { ClubsView() { club in - if let event = tournament.eventObject { + if let event = tournament.eventObject() { event.club = club.id - try? dataStore.events.addOrUpdate(instance: event) - } else { - let event = Event(club: club.id) - tournament.event = event.id - try? dataStore.events.addOrUpdate(instance: event) + do { + try dataStore.events.addOrUpdate(instance: event) + } catch { + Logger.error(error) + } } _save() } From 4a002de680d6a116b14f5dad9be86200e341184d Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sat, 4 May 2024 11:50:34 +0200 Subject: [PATCH 4/5] rename player weight into computedRank --- PadelClub/Data/PlayerRegistration.swift | 16 ++++++++-------- PadelClub/Data/TeamRegistration.swift | 2 +- PadelClub/Data/Tournament.swift | 8 ++++---- PadelClub/Utils/FileImportManager.swift | 12 ++++++------ PadelClub/Views/Cashier/CashierView.swift | 4 ++-- .../Shared/GroupStageTeamReplacementView.swift | 2 +- .../Player/Components/PlayerSexPickerView.swift | 2 +- PadelClub/Views/Player/PlayerDetailView.swift | 6 +++--- PadelClub/Views/Tournament/FileImportView.swift | 2 +- .../Components/UpdateSourceRankDateView.swift | 2 +- .../Screen/InscriptionManagerView.swift | 4 ++-- 11 files changed, 30 insertions(+), 30 deletions(-) diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 4654725..90c5265 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -31,7 +31,7 @@ class PlayerRegistration: ModelObject, Storable { var email: String? var birthdate: String? - var weight: Int = 0 + var computedRank: Int = 0 var source: PlayerDataSource? var hasArrived: Bool = false @@ -169,8 +169,8 @@ class PlayerRegistration: ModelObject, Storable { func rankLabel(_ displayStyle: DisplayStyle = .wide) -> String { if let rank, rank > 0 { - if rank != weight { - return weight.formatted() + " (" + rank.formatted() + ")" + if rank != computedRank { + return computedRank.formatted() + " (" + rank.formatted() + ")" } else { return rank.formatted() } @@ -180,7 +180,7 @@ class PlayerRegistration: ModelObject, Storable { } func getRank() -> Int { - weight + computedRank } @MainActor @@ -238,13 +238,13 @@ class PlayerRegistration: ModelObject, Storable { } } - func setWeight(in tournament: Tournament) { + func setComputedRank(in tournament: Tournament) { let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 100_000 switch tournament.tournamentCategory { case .men: - weight = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0) + computedRank = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0) default: - weight = currentRank + computedRank = currentRank } } @@ -279,7 +279,7 @@ class PlayerRegistration: ModelObject, Storable { case _birthdate = "birthdate" case _phoneNumber = "phoneNumber" case _email = "email" - case _weight = "weight" + case _computedRank = "computedRank" case _source = "source" case _hasArrived = "hasArrived" diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 1f5f52d..4c53a38 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -268,7 +268,7 @@ class TeamRegistration: ModelObject, Storable { func setWeight(from players: [PlayerRegistration], inTournamentCategory tournamentCategory: TournamentCategory) { let significantPlayerCount = significantPlayerCount() - weight = (players.prefix(significantPlayerCount).map { $0.weight } + missingPlayerType(inTournamentCategory: tournamentCategory).map { unrankValue(for: $0 == 1 ? true : false ) }).prefix(significantPlayerCount).reduce(0,+) + weight = (players.prefix(significantPlayerCount).map { $0.computedRank } + missingPlayerType(inTournamentCategory: tournamentCategory).map { unrankValue(for: $0 == 1 ? true : false ) }).prefix(significantPlayerCount).reduce(0,+) } func significantPlayerCount() -> Int { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 0ce195b..18e8e1f 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -632,11 +632,11 @@ class Tournament : ModelObject, Storable { } func selectedPlayers() -> [PlayerRegistration] { - selectedSortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.weight) + selectedSortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.computedRank) } func players() -> [PlayerRegistration] { - unsortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.weight) + unsortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.computedRank) } func femalePlayers() -> [PlayerRegistration] { @@ -670,7 +670,7 @@ class Tournament : ModelObject, Storable { } return players.filter { player in if player.rank == nil { return false } - if player.weight <= tournamentLevel.minimumPlayerRank(category: tournamentCategory, ageCategory: federalTournamentAge) { + if player.computedRank <= tournamentLevel.minimumPlayerRank(category: tournamentCategory, ageCategory: federalTournamentAge) { return true } else { return false @@ -846,7 +846,7 @@ class Tournament : ModelObject, Storable { let teams = self.unsortedTeams() teams.forEach { team in let players = team.unsortedPlayers() - players.forEach { $0.setWeight(in: self) } + players.forEach { $0.setComputedRank(in: self) } team.setWeight(from: players, inTournamentCategory: tournamentCategory) try? DataStore.shared.playerRegistrations.addOrUpdate(contentOfs: players) } diff --git a/PadelClub/Utils/FileImportManager.swift b/PadelClub/Utils/FileImportManager.swift index b2d6ca5..d9adf4c 100644 --- a/PadelClub/Utils/FileImportManager.swift +++ b/PadelClub/Utils/FileImportManager.swift @@ -71,7 +71,7 @@ class FileImportManager { self.players = Set(players) self.tournamentCategory = tournamentCategory self.previousTeam = previousTeam - self.weight = players.map { $0.weight }.reduce(0,+) + self.weight = players.map { $0.computedRank }.reduce(0,+) self.registrationDate = registrationDate } @@ -210,9 +210,9 @@ class FileImportManager { } let playerOne = PlayerRegistration(federalData: Array(resultOne[0...7]), sex: sexPlayerOne, sexUnknown: sexUnknown) - playerOne.setWeight(in: tournament) + playerOne.setComputedRank(in: tournament) let playerTwo = PlayerRegistration(federalData: Array(resultTwo[0...7]), sex: sexPlayerTwo, sexUnknown: sexUnknown) - playerTwo.setWeight(in: tournament) + playerTwo.setComputedRank(in: tournament) let team = TeamHolder(players: [playerOne, playerTwo], tournamentCategory: tournamentCategory, previousTeam: tournament.findTeam([playerOne, playerTwo])) results.append(team) } @@ -256,9 +256,9 @@ class FileImportManager { } let playerOne = PlayerRegistration(federalData: Array(result[0...7]), sex: sexPlayerOne, sexUnknown: sexUnknown) - playerOne.setWeight(in: tournament) + playerOne.setComputedRank(in: tournament) let playerTwo = PlayerRegistration(federalData: Array(result[8...]), sex: sexPlayerTwo, sexUnknown: sexUnknown) - playerTwo.setWeight(in: tournament) + playerTwo.setComputedRank(in: tournament) let team = TeamHolder(players: [playerOne, playerTwo], tournamentCategory: tournamentCategory, previousTeam: tournament.findTeam([playerOne, playerTwo])) results.append(team) @@ -281,7 +281,7 @@ class FileImportManager { let found = try? federalContext.fetch(fetchRequest) let registeredPlayers = found?.map({ importedPlayer in let player = PlayerRegistration(importedPlayer: importedPlayer) - player.setWeight(in: tournament) + player.setComputedRank(in: tournament) return player }) if let registeredPlayers, registeredPlayers.isEmpty == false { diff --git a/PadelClub/Views/Cashier/CashierView.swift b/PadelClub/Views/Cashier/CashierView.swift index 9236a46..5b5700d 100644 --- a/PadelClub/Views/Cashier/CashierView.swift +++ b/PadelClub/Views/Cashier/CashierView.swift @@ -188,7 +188,7 @@ struct CashierView: View { Text(teamCallDate.localizedDate()) } Spacer() - Text(player.weight.formatted()) + Text(player.computedRank.formatted()) } } footer: { if tournaments.count > 1, let tournamentTitle = player.tournament()?.tournamentTitle() { @@ -200,7 +200,7 @@ struct CashierView: View { @ViewBuilder private func _byPlayerRank() -> some View { - let players = teams.flatMap({ $0.players() }).sorted(using: .keyPath(\.weight)).filter({ _shouldDisplayPlayer($0) }) + let players = teams.flatMap({ $0.players() }).sorted(using: .keyPath(\.computedRank)).filter({ _shouldDisplayPlayer($0) }) _byPlayer(players) } diff --git a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift index 678e308..e37e36b 100644 --- a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift +++ b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift @@ -21,7 +21,7 @@ struct GroupStageTeamReplacementView: View { private func _getWeight() -> Int { guard let selectedPlayer else { return 0 } - return team.weight - selectedPlayer.weight + return team.weight - selectedPlayer.computedRank } private func _searchableRange(_ teamRange: TeamRegistration.TeamRange) -> String { diff --git a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift index b81e27e..a218fe9 100644 --- a/PadelClub/Views/Player/Components/PlayerSexPickerView.swift +++ b/PadelClub/Views/Player/Components/PlayerSexPickerView.swift @@ -32,7 +32,7 @@ struct PlayerSexPickerView: View { private func _save() { do { - player.setWeight(in: tournament) + player.setComputedRank(in: tournament) try dataStore.playerRegistrations.addOrUpdate(instance: player) if let team = player.team() { team.updateWeight(inTournamentCategory: tournament.tournamentCategory) diff --git a/PadelClub/Views/Player/PlayerDetailView.swift b/PadelClub/Views/Player/PlayerDetailView.swift index e6289a0..4d96fad 100644 --- a/PadelClub/Views/Player/PlayerDetailView.swift +++ b/PadelClub/Views/Player/PlayerDetailView.swift @@ -60,7 +60,7 @@ struct PlayerDetailView: View { Text("Valeur à rajouter") } LabeledContent { - TextField("Rang", value: $player.weight, format: .number) + TextField("Rang", value: $player.computedRank, format: .number) .keyboardType(.decimalPad) .multilineTextAlignment(.trailing) .frame(maxWidth: .infinity) @@ -79,12 +79,12 @@ struct PlayerDetailView: View { .onChange(of: player.sex) { _save() } - .onChange(of: player.weight) { + .onChange(of: player.computedRank) { player.team()?.updateWeight(inTournamentCategory: tournament.tournamentCategory) _save() } .onChange(of: player.rank) { - player.setWeight(in: tournament) + player.setComputedRank(in: tournament) player.team()?.updateWeight(inTournamentCategory: tournament.tournamentCategory) _save() } diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index 23e79a1..309cd98 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -272,7 +272,7 @@ struct FileImportView: View { Section { HStack { VStack(alignment: .leading) { - ForEach(team.players.sorted(by: \.weight)) { + ForEach(team.players.sorted(by: \.computedRank)) { Text($0.playerLabel()) } } diff --git a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift index 6024b6e..e6ed2c1 100644 --- a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift @@ -40,7 +40,7 @@ struct UpdateSourceRankDateView: View { try await tournament.updateRank(to: currentRankSourceDate) try await MainActor.run { tournament.unsortedPlayers().forEach { player in - player.setWeight(in: tournament) + player.setComputedRank(in: tournament) } try dataStore.playerRegistrations.addOrUpdate(contentOfs: tournament.unsortedPlayers()) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index e3a828a..db50340 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -83,7 +83,7 @@ struct InscriptionManagerView: View { selectionSearchField = nil players.forEach { player in let newPlayer = PlayerRegistration(importedPlayer: player) - newPlayer.setWeight(in: tournament) + newPlayer.setComputedRank(in: tournament) createdPlayers.insert(newPlayer) createdPlayerIds.insert(newPlayer.id) } @@ -529,7 +529,7 @@ struct InscriptionManagerView: View { fetchPlayers.first(where: { id == $0.license }) }.forEach { player in let player = PlayerRegistration(importedPlayer: player) - player.setWeight(in: tournament) + player.setComputedRank(in: tournament) currentSelection.insert(player) } From 41823ec8968f8eed3028d779dda7a6f072f4f9e3 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sun, 5 May 2024 11:25:31 +0200 Subject: [PATCH 5/5] fix stuff for syncing --- PadelClub/Data/DataStore.swift | 6 ++-- PadelClub/Data/User.swift | 12 +++++++ PadelClub/Extensions/String+Extensions.swift | 6 ++-- PadelClub/Utils/Network/NetworkManager.swift | 2 +- PadelClub/Utils/SourceFileManager.swift | 35 +++++++++++++++---- PadelClub/Views/Club/ClubDetailView.swift | 8 ++--- PadelClub/Views/Club/ClubsView.swift | 2 +- .../Navigation/Agenda/ActivityView.swift | 14 +++++--- .../Navigation/Agenda/EventListView.swift | 2 +- .../Views/Navigation/Umpire/UmpireView.swift | 22 ++++++------ 10 files changed, 73 insertions(+), 36 deletions(-) diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 4a8f346..42d62fb 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -77,7 +77,7 @@ class DataStore: ObservableObject { // store.addMigration(Migration(version: 3)) let indexed : Bool = true - let synchronized : Bool = false + let synchronized : Bool = true self.clubs = store.registerCollection(synchronized: synchronized, indexed: indexed) self.courts = store.registerCollection(synchronized: synchronized, indexed: indexed) self.tournaments = store.registerCollection(synchronized: synchronized, indexed: indexed) @@ -88,10 +88,10 @@ class DataStore: ObservableObject { self.playerRegistrations = store.registerCollection(synchronized: synchronized, indexed: indexed) self.rounds = store.registerCollection(synchronized: synchronized, indexed: indexed) self.matches = store.registerCollection(synchronized: synchronized, indexed: indexed) - self.monthData = store.registerCollection(synchronized: synchronized, indexed: indexed) + self.monthData = store.registerCollection(synchronized: false, indexed: indexed) self.dateIntervals = store.registerCollection(synchronized: synchronized, indexed: indexed) - self.userStorage = store.registerObject(synchronized: synchronized) + self.userStorage = store.registerObject(synchronized: false) NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidLoad, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidChange, object: nil) diff --git a/PadelClub/Data/User.swift b/PadelClub/Data/User.swift index b2604ef..51571b4 100644 --- a/PadelClub/Data/User.swift +++ b/PadelClub/Data/User.swift @@ -61,6 +61,18 @@ class User: UserBase, Storable { clubs?.isEmpty == false } + func hasFavoriteClubsAndCreatedClubs() -> Bool { + clubsObjects(includeCreated: true).isEmpty == false + } + + func setUserClub(_ userClub: Club) { + if clubs == nil { + clubs = [userClub.id] + } else { + clubs!.insert(userClub.id, at: 0) + } + } + func clubsObjects(includeCreated: Bool = false) -> [Club] { guard let clubs else { return [] } return Store.main.filter(isIncluded: { (includeCreated && $0.creator == id) || clubs.contains($0.id) }) diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift index f0afc3a..2a54554 100644 --- a/PadelClub/Extensions/String+Extensions.swift +++ b/PadelClub/Extensions/String+Extensions.swift @@ -52,8 +52,10 @@ extension String { // Extract the first character of each sentence let firstLetters = sentences.compactMap { sentence -> Character? in let trimmedSentence = sentence.trimmingCharacters(in: .whitespacesAndNewlines) - if let firstCharacter = trimmedSentence.first { - return firstCharacter + if trimmedSentence.count > 2 { + if let firstCharacter = trimmedSentence.first { + return firstCharacter + } } return nil } diff --git a/PadelClub/Utils/Network/NetworkManager.swift b/PadelClub/Utils/Network/NetworkManager.swift index dc2ef7b..6da1982 100644 --- a/PadelClub/Utils/Network/NetworkManager.swift +++ b/PadelClub/Utils/Network/NetworkManager.swift @@ -22,7 +22,7 @@ class NetworkManager { let dateString = ["CLASSEMENT-PADEL", fileName, lastDateString].joined(separator: "-") + ".csv" - let documentsUrl:URL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)! + let documentsUrl: URL = SourceFileManager.shared.rankingSourceDirectory let destinationFileUrl = documentsUrl.appendingPathComponent("\(dateString)") let fileURL = URL(string: "https://padelclub.app/static/\(dateString)") diff --git a/PadelClub/Utils/SourceFileManager.swift b/PadelClub/Utils/SourceFileManager.swift index fab3def..0c1b68a 100644 --- a/PadelClub/Utils/SourceFileManager.swift +++ b/PadelClub/Utils/SourceFileManager.swift @@ -9,7 +9,32 @@ import Foundation class SourceFileManager { static let shared = SourceFileManager() + + init() { + createDirectoryIfNeeded() + } + + let rankingSourceDirectory : URL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appending(path: "rankings") + func createDirectoryIfNeeded() { + let fileManager = FileManager.default + do { + let directoryURL = rankingSourceDirectory + + // Check if the directory exists + if !fileManager.fileExists(atPath: directoryURL.path) { + // Directory does not exist, create it + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + print("Directory created at: \(directoryURL)") + } else { + print("Directory already exists at: \(directoryURL)") + } + } catch { + print("Error: \(error)") + } + } + + var lastDataSource: String? { DataStore.shared.appSettings.lastDataSource } @@ -117,16 +142,14 @@ class SourceFileManager { } func removeAllFilesFromServer() { - let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) - let allFiles = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil) + let allFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil) allFiles.filter { $0.pathExtension == "csv" }.forEach { url in try? FileManager.default.removeItem(at: url) } } var allFiles: [URL] { - let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) - let allFiles = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil).filter({ url in + let allFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil).filter({ url in url.pathExtension == "csv" }) @@ -149,8 +172,8 @@ enum SourceFile: String, CaseIterable { case messieurs = "MESSIEURS" var filesFromServer: [URL] { - let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) - let allFiles = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil) + let rankingSourceDirectory = SourceFileManager.shared.rankingSourceDirectory + let allFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil) return allFiles.filter{$0.pathExtension == "csv" && $0.path().contains(rawValue)} } diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index acaa1aa..73b3a30 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -142,9 +142,6 @@ struct ClubDetailView: View { LabeledContent("Ville") { Text(club.city ?? "") } - LabeledContent("Code Postal") { - Text(club.zipCode ?? "") - } Link(destination: federalLink) { Text("Fiche du club sur tenup") } @@ -168,7 +165,7 @@ struct ClubDetailView: View { .keyboardType(.alphabet) .autocorrectionDisabled() .defaultFocus($focusedField, ._name, priority: .automatic) - .navigationTitle(displayContext == .addition ? "Nouveau club" : club.name) + .navigationTitle(displayContext == .addition ? "Nouveau club" : "Détail du club") .navigationBarTitleDisplayMode(.inline) .toolbar(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar) @@ -176,7 +173,7 @@ struct ClubDetailView: View { if displayContext == .edition || displayContext == .lockedForEditing { let isFavorite = club.isFavorite() ToolbarItem(placement: .topBarTrailing) { - BarButtonView("Favori", icon: isFavorite ? "start" : "star.fill") { + BarButtonView("Favori", icon: isFavorite ? "star.fill" : "star") { do { if isFavorite { dataStore.user?.clubs?.removeAll(where: { $0 == club.id }) @@ -188,6 +185,7 @@ struct ClubDetailView: View { Logger.error(error) } } + .tint(isFavorite ? .green : .logoRed) } } } diff --git a/PadelClub/Views/Club/ClubsView.swift b/PadelClub/Views/Club/ClubsView.swift index c5786f5..ec2830f 100644 --- a/PadelClub/Views/Club/ClubsView.swift +++ b/PadelClub/Views/Club/ClubsView.swift @@ -55,7 +55,7 @@ struct ClubsView: View { } } .overlay { - if dataStore.user == nil || dataStore.user?.hasClubs() == true { + if dataStore.user?.hasFavoriteClubsAndCreatedClubs() == false { ContentUnavailableView { Label("Aucun club", systemImage: "house.and.flag.fill") } description: { diff --git a/PadelClub/Views/Navigation/Agenda/ActivityView.swift b/PadelClub/Views/Navigation/Agenda/ActivityView.swift index 26cee95..b4fe287 100644 --- a/PadelClub/Views/Navigation/Agenda/ActivityView.swift +++ b/PadelClub/Views/Navigation/Agenda/ActivityView.swift @@ -122,14 +122,14 @@ struct ActivityView: View { // } .task { if navigation.agendaDestination == .tenup - && dataStore.user?.hasClubs() == false + && dataStore.user?.hasClubs() == true && federalDataViewModel.federalTournaments.isEmpty { _gatherFederalTournaments() } } .onChange(of: navigation.agendaDestination) { if navigation.agendaDestination == .tenup - && dataStore.user?.hasClubs() == false + && dataStore.user?.hasClubs() == true && federalDataViewModel.federalTournaments.isEmpty { _gatherFederalTournaments() } @@ -202,7 +202,11 @@ struct ActivityView: View { .environment(navigation) .tint(.master) } - .sheet(isPresented: $presentClubSearchView) { + .sheet(isPresented: $presentClubSearchView, onDismiss: { + if dataStore.user?.hasClubs() == true { + navigation.agendaDestination = .tenup + } + }) { ClubImportView() .tint(.master) } @@ -253,7 +257,7 @@ struct ActivityView: View { RowButtonView("Créer un nouvel événement") { newTournament = Tournament.newEmptyInstance() } - if dataStore.user == nil || dataStore.user?.hasClubs() == true { + if dataStore.user?.hasClubs() == false { RowButtonView("Chercher l'un de vos clubs") { presentClubSearchView = true } @@ -274,7 +278,7 @@ struct ActivityView: View { } private func _tenupEmptyView() -> some View { - if dataStore.user == nil || dataStore.user?.hasClubs() == true { + if dataStore.user?.hasClubs() == false { ContentUnavailableView { Label("Aucun tournoi", systemImage: "shield.slash") } description: { diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index bc0f872..dab5805 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -51,7 +51,7 @@ struct EventListView: View { .headerProminence(.increased) .task { if navigation.agendaDestination == .tenup - && dataStore.user?.hasClubs() == false + && dataStore.user?.hasClubs() == true && _tournaments.isEmpty { _gatherFederalTournaments(startDate: section) } diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index 5fe8e34..67952e7 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -57,24 +57,22 @@ struct UmpireView: View { } } else { NavigationLink { - SelectablePlayerListView(allowSelection: 1, playerSelectionAction: { players in + SelectablePlayerListView(allowSelection: 1, searchField: user.firstName + " " + user.lastName, playerSelectionAction: { players in if let player = players.first { user.licenceId = player.license - let userClub = Club.findOrCreate(name: player.clubName!, code: player.clubCode) - do { - try dataStore.clubs.addOrUpdate(instance: userClub) - - if user.clubs == nil { - user.clubs = [userClub.id] - } else { - user.clubs!.insert(userClub.id, at: 0) + if user.clubsObjects().contains(where: { $0.code == player.clubCode }) == false { + let userClub = Club.findOrCreate(name: player.clubName!, code: player.clubCode) + do { + try dataStore.clubs.addOrUpdate(instance: userClub) + user.setUserClub(userClub) + try dataStore.userStorage.update() + } catch { + Logger.error(error) } - try dataStore.userStorage.update() - } catch { - Logger.error(error) } } }) + .id(UUID()) } label: { Label("Ma fiche joueur", systemImage: "person.bust.circle.fill") }