diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index d0feadc..fbdf266 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1847,7 +1847,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 9; + CURRENT_PROJECT_VERSION = 10; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1885,7 +1885,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 9; + CURRENT_PROJECT_VERSION = 10; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/MockData.swift b/PadelClub/Data/MockData.swift index 9e838fd..01266d7 100644 --- a/PadelClub/Data/MockData.swift +++ b/PadelClub/Data/MockData.swift @@ -25,7 +25,7 @@ extension Club { } static func newEmptyInstance() -> Club { - Club(name: "", acronym: "") + Club(creator: DataStore.shared.user.id, name: "", acronym: "") } } diff --git a/PadelClub/Utils/DisplayContext.swift b/PadelClub/Utils/DisplayContext.swift index 29187ed..704454f 100644 --- a/PadelClub/Utils/DisplayContext.swift +++ b/PadelClub/Utils/DisplayContext.swift @@ -11,6 +11,7 @@ enum DisplayContext { case addition case edition case lockedForEditing + case selection } enum DisplayStyle { diff --git a/PadelClub/Utils/LocationManager.swift b/PadelClub/Utils/LocationManager.swift index 50afc59..3961bf6 100644 --- a/PadelClub/Utils/LocationManager.swift +++ b/PadelClub/Utils/LocationManager.swift @@ -53,7 +53,7 @@ class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { } func geocodeCity(cityOrZipcode: String, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) { - CLGeocoder().geocodeAddressString(cityOrZipcode, in: nil, completionHandler: completion) + CLGeocoder().geocodeAddressString(cityOrZipcode, completionHandler: completion) } } diff --git a/PadelClub/Utils/Tips.swift b/PadelClub/Utils/Tips.swift index 78e24fb..8dd7576 100644 --- a/PadelClub/Utils/Tips.swift +++ b/PadelClub/Utils/Tips.swift @@ -304,20 +304,12 @@ struct MultiTournamentsEventTip: Tip { } var message: Text? { - Text("Padel Club permet de gérer plusieurs tournois ayant lieu en même temps. Un P100 homme et dame par le même week-end par exemple.") + Text("Padel Club permet de gérer plusieurs tournois ayant lieu en même temps. Un P100 homme et dame le même week-end par exemple.") } var image: Image? { Image(systemName: "trophy.circle") } - - var actions: [Action] { - Action(id: ActionKey.addEvent.rawValue, title: "Ajoutez une épreuve") - } - - enum ActionKey: String { - case addEvent = "add-event" - } } struct NotFoundAreWalkOutTip: Tip { diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index be2b761..d20ecf9 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -9,17 +9,20 @@ import SwiftUI import LeStorage struct ClubDetailView: View { - @Bindable var club: Club - var displayContext: DisplayContext @EnvironmentObject var dataStore: DataStore + @Environment(\.dismiss) var dismiss @FocusState var focusedField: Club.CodingKeys? @State private var acronymMode: Club.AcronymMode = .automatic @State private var city: String @State private var zipCode: String + @Bindable var club: Club + var displayContext: DisplayContext + var selection: ((Club) -> ())? = nil - init(club: Club, displayContext: DisplayContext) { + init(club: Club, displayContext: DisplayContext, selection: ((Club) -> ())? = nil) { _club = Bindable(club) self.displayContext = displayContext + self.selection = selection _acronymMode = State(wrappedValue: club.shortNameMode()) _city = State(wrappedValue: club.city ?? "") _zipCode = State(wrappedValue: club.zipCode ?? "") @@ -27,41 +30,22 @@ struct ClubDetailView: View { var body: some View { Form { - Section { - NavigationLink { - ClubSearchView(displayContext: .edition, club: club) - } label: { - Label("Chercher dans la base fédérale", systemImage: "magnifyingglass") - } - } footer: { - Text("Vous pouvez chercher un club dans la base fédérale et importer les informations directement.") - } - - Section { - LabeledContent { - TextField("Nom du club", text: $club.name) - .autocorrectionDisabled() - .keyboardType(.alphabet) - .multilineTextAlignment(.trailing) - .frame(maxWidth: .infinity) - .focused($focusedField, equals: ._name) - .submitLabel( displayContext == .addition ? .next : .done) - .onSubmit { - if club.acronym.isEmpty { - club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines) - focusedField = ._city - } - if displayContext == .addition { - focusedField = ._acronym - } + TextField("Nom du club", text: $club.name) + .autocorrectionDisabled() + .keyboardType(.alphabet) + .frame(maxWidth: .infinity) + .focused($focusedField, equals: ._name) + .submitLabel( displayContext == .addition ? .next : .done) + .onSubmit { + if club.acronym.isEmpty { + club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines) + focusedField = ._city } - } label: { - Text("Nom du club") - } - .onTapGesture { - focusedField = ._name - } + if displayContext == .addition { + focusedField = ._acronym + } + } LabeledContent { if acronymMode == .automatic { Text(club.acronym) @@ -106,8 +90,16 @@ struct ClubDetailView: View { club.acronym = "" } } - - if club.code == nil { + } 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.") + } + } + + if club.code == nil { + Section { LabeledContent { TextField("Ville", text: $city) .autocorrectionDisabled() @@ -146,16 +138,13 @@ struct ClubDetailView: View { .onTapGesture { focusedField = ._zipCode } + } footer: { + if displayContext == .lockedForEditing { + Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) + } } - - } 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.") - } + .disabled(displayContext == .lockedForEditing) } - .disabled(displayContext == .lockedForEditing) if let federalLink = club.federalLink() { @@ -171,7 +160,32 @@ struct ClubDetailView: View { } } } - + + if displayContext == .addition { + Section { + } header: { + HStack { + VStack { + Divider() + } + Text("ou") + VStack { + Divider() + } + } + } + + Section { + NavigationLink { + ClubSearchView(displayContext: .edition, club: club) + } label: { + Label("Chercher dans la base fédérale", systemImage: "magnifyingglass") + } + } footer: { + Text("Vous pouvez chercher un club dans la base fédérale et importer les informations directement.") + } + } + if displayContext == .edition { Section { RowButtonView("Supprimer ce club", role: .destructive) { diff --git a/PadelClub/Views/Club/ClubImportView.swift b/PadelClub/Views/Club/ClubImportView.swift index 293983d..8a80052 100644 --- a/PadelClub/Views/Club/ClubImportView.swift +++ b/PadelClub/Views/Club/ClubImportView.swift @@ -9,10 +9,11 @@ import SwiftUI struct ClubImportView: View { @Environment(\.dismiss) var dismiss + var selection: ((Club) -> ())? = nil var body: some View { NavigationStack { - ClubSearchView(displayContext: .addition) + ClubSearchView(displayContext: .addition, selection: selection) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Annuler", role: .cancel) { diff --git a/PadelClub/Views/Club/ClubRowView.swift b/PadelClub/Views/Club/ClubRowView.swift index f9d5a89..05e89a2 100644 --- a/PadelClub/Views/Club/ClubRowView.swift +++ b/PadelClub/Views/Club/ClubRowView.swift @@ -9,11 +9,14 @@ import SwiftUI struct ClubRowView: View { let club: Club + var displayContext: DisplayContext = .edition var body: some View { LabeledContent { - Image(systemName: club.isFavorite() ? "star.fill" : "star") - .foregroundStyle(club.isFavorite() ? .green : .logoRed) + if displayContext == .edition { + 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 4d6c14e..f94cab0 100644 --- a/PadelClub/Views/Club/ClubSearchView.swift +++ b/PadelClub/Views/Club/ClubSearchView.swift @@ -26,11 +26,21 @@ 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 + @State private var newClub: Club? + + var presentClubCreationView: Binding { Binding( + get: { newClub != nil }, + set: { isPresented in + if isPresented == false { + newClub = nil + } + } + )} var displayContext: DisplayContext = .edition var club: Club? - + var selection: ((Club) -> ())? = nil + fileprivate class DebouncableViewModel: ObservableObject { @Published var debouncableText: String = "" var debounceTrigger: Double = 0.15 @@ -162,7 +172,7 @@ struct ClubSearchView: View { if searchAttempted { RowButtonView("Créer un club manuellement") { - presentClubCreationView = true + newClub = club ?? Club.newEmptyInstance() } } } @@ -171,9 +181,14 @@ struct ClubSearchView: View { ContentUnavailableView("recherche en cours", systemImage: "mappin.and.ellipse", description: Text("recherche des clubs autour de vous")) } } - .sheet(isPresented: $presentClubCreationView) { - CreateClubView() + .sheet(isPresented: presentClubCreationView) { + if let newClub { + CreateClubView(club: newClub) { club in + selection?(club) + dismiss() + } .tint(.master) + } } .alert(isPresented: $showingSettingsAlert) { Alert( @@ -295,8 +310,8 @@ struct ClubSearchView: View { private func _importClub(clubToEdit: Club, clubMarker: ClubMarker) { if clubToEdit.creator == dataStore.user.id { if clubToEdit.name.isEmpty { - clubToEdit.name = clubMarker.nom - clubToEdit.acronym = clubToEdit.automaticShortName() + clubToEdit.name = clubMarker.nom.capitalized + clubToEdit.acronym = clubToEdit.automaticShortName().capitalized } clubToEdit.code = clubMarker.clubID clubToEdit.latitude = clubMarker.lat @@ -316,6 +331,7 @@ struct ClubSearchView: View { } } dismiss() + selection?(clubToEdit) } private func _filteredClubs() -> [ClubMarker] { diff --git a/PadelClub/Views/Club/ClubsView.swift b/PadelClub/Views/Club/ClubsView.swift index 11d3f2f..6722175 100644 --- a/PadelClub/Views/Club/ClubsView.swift +++ b/PadelClub/Views/Club/ClubsView.swift @@ -12,20 +12,21 @@ import LeStorage struct ClubsView: View { @EnvironmentObject var dataStore: DataStore @Environment(\.dismiss) private var dismiss - @State private var presentClubCreationView: Bool = false @State private var presentClubSearchView: Bool = false - let tip = SlideToDeleteTip() + @State private var newClub: Club? var selection: ((Club) -> ())? = nil + var presentClubCreationView: Binding { Binding( + get: { newClub != nil }, + set: { isPresented in + if isPresented == false { + newClub = nil + } + } + )} + var body: some View { List { -// -// 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 { @@ -33,7 +34,7 @@ struct ClubsView: View { selection(club) dismiss() } label: { - ClubRowView(club: club) + ClubRowView(club: club, displayContext: .selection) .frame(maxWidth: .infinity) } .contentShape(Rectangle()) @@ -62,7 +63,7 @@ struct ClubsView: View { Text("Texte décrivant l'utilité d'un club et les features que cela apporte") } actions: { RowButtonView("Créer un nouveau club", systemImage: "plus.circle.fill") { - presentClubCreationView = true + newClub = Club.newEmptyInstance() } RowButtonView("Chercher un club", systemImage: "magnifyingglass.circle.fill") { presentClubSearchView = true @@ -71,13 +72,27 @@ struct ClubsView: View { } } .navigationTitle(selection == nil ? "Mes clubs" : "Choisir un club") - .sheet(isPresented: $presentClubCreationView) { - CreateClubView() + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + .sheet(isPresented: presentClubCreationView) { + if let newClub { + CreateClubView(club: newClub) { club in + if let selection { + selection(club) + dismiss() + } + } .tint(.master) + } } .sheet(isPresented: $presentClubSearchView) { - ClubImportView() - .tint(.master) + ClubImportView() { club in + if let selection { + selection(club) + dismiss() + } + } + .tint(.master) } .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { @@ -91,7 +106,7 @@ struct ClubsView: View { } Button { - presentClubCreationView = true + newClub = Club.newEmptyInstance() } label: { Image(systemName: "plus.circle.fill") .resizable() diff --git a/PadelClub/Views/Club/CreateClubView.swift b/PadelClub/Views/Club/CreateClubView.swift index 5f8f326..fbd1cda 100644 --- a/PadelClub/Views/Club/CreateClubView.swift +++ b/PadelClub/Views/Club/CreateClubView.swift @@ -9,18 +9,14 @@ import SwiftUI import LeStorage struct CreateClubView: View { - @Bindable var club: Club @EnvironmentObject var dataStore: DataStore @Environment(\.dismiss) var dismiss - - init() { - self.club = Club.newEmptyInstance() - } - + var club: Club + var selection: ((Club) -> ())? = nil var body: some View { NavigationStack { - ClubDetailView(club: club, displayContext: .addition) + ClubDetailView(club: club, displayContext: .addition, selection: selection) .task { do { try await dataStore.clubs.loadDataFromServerIfAllowed() @@ -55,6 +51,7 @@ struct CreateClubView: View { self.dataStore.saveUser() } dismiss() + selection?(existingOrCreatedClub) } .disabled(club.isValid == false) } @@ -64,6 +61,6 @@ struct CreateClubView: View { } #Preview { - CreateClubView() + CreateClubView(club: Club.mock()) .environmentObject(DataStore.shared) } diff --git a/PadelClub/Views/Components/BarButtonView.swift b/PadelClub/Views/Components/BarButtonView.swift index 7d52007..b92c01e 100644 --- a/PadelClub/Views/Components/BarButtonView.swift +++ b/PadelClub/Views/Components/BarButtonView.swift @@ -22,15 +22,23 @@ struct BarButtonView: View { Button(action: { action() }) { - Label { - Text(accessibilityLabel) - } icon: { - Image(systemName: icon) - .resizable() - .scaledToFit() - .frame(minHeight: 36) - } - .labelStyle(.iconOnly) + Image(systemName: icon) + .resizable() + .scaledToFit() + .frame(minHeight: 28) + + /* + Label { + Text(accessibilityLabel) + } icon: { + Image(systemName: icon) + .resizable() + .scaledToFit() + .frame(minHeight: 36) + } + .labelStyle(.iconOnly) + //todo: resizing not working when label used + */ } } } diff --git a/PadelClub/Views/Event/EventCreationView.swift b/PadelClub/Views/Event/EventCreationView.swift index e958b8f..802751d 100644 --- a/PadelClub/Views/Event/EventCreationView.swift +++ b/PadelClub/Views/Event/EventCreationView.swift @@ -57,7 +57,7 @@ struct EventCreationView: View { } } label: { if let selectedClub { - ClubRowView(club: selectedClub) + ClubRowView(club: selectedClub, displayContext: .selection) } else { Text("Choisir un club") } @@ -66,24 +66,17 @@ struct EventCreationView: View { TextField("Nom de l'événement", text: $eventName) .autocorrectionDisabled() .keyboardType(.alphabet) - + LabeledContent { Text(tournaments.count.formatted()) } label: { - Text("Nombre d'épreuves") + Text("Nombre d'épreuve") } + } header: { + Text("") } - Section { - TipView(multiTournamentsEventTip) { action in - let tournament = Tournament.newEmptyInstance() - self.tournaments.append(tournament) - } - .tipStyle(tint: .orange) - } - - switch eventType { case .approvedTournament: approvedTournamentEditorView @@ -134,10 +127,23 @@ struct EventCreationView: View { dismiss() } } + + ToolbarItem(placement: .topBarTrailing) { + BarButtonView("Ajouter une épreuve", icon: "plus.circle.fill") { + let tournament = Tournament.newEmptyInstance() + self.tournaments.append(tournament) + } + .popoverTip(multiTournamentsEventTip) + } } .navigationTitle("Nouvel événement") - .navigationBarTitleDisplayMode(.large) + .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) + .onAppear { + if selectedClub == nil { + selectedClub = dataStore.user.clubsObjects().first + } + } } } diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 73c09f6..3986974 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct TournamentCellView: View { @EnvironmentObject var dataStore: DataStore @@ -123,7 +124,11 @@ struct TournamentCellView: View { newTournament.dayDuration = federalTournament.dayDuration newTournament.startDate = federalTournament.startDate.atBeginningOfDay(hourInt: 9) newTournament.setupFederalSettings() - try? dataStore.tournaments.addOrUpdate(instance: newTournament) + do { + try dataStore.tournaments.addOrUpdate(instance: newTournament) + } catch { + Logger.error(error) + } } } } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 8987448..97b8216 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -154,38 +154,40 @@ struct TournamentView: View { TournamentSelectionTip.tournamentCount = tournament.eventObject()?.tournaments.count } } - - ToolbarItem(placement: .topBarTrailing) { - Menu { - if presentationContext == .agenda { - Button { - navigation.openTournamentInOrganizer(tournament) - } label: { - Label("Voir dans le gestionnaire", systemImage: "line.diagonal.arrow") - } - } - - Divider() - if tournament.state() == .build { - NavigationLink(value: Screen.event) { - Text("Gestion de l'événement") - } - - NavigationLink(value: Screen.settings) { - LabelSettings() - } - NavigationLink(value: Screen.structure) { - LabelStructure() - } - NavigationLink(value: Screen.rankings) { - Text("Classement") + + if presentationContext == .agenda || tournament.state() == .build { + ToolbarItem(placement: .topBarTrailing) { + Menu { + if presentationContext == .agenda { + Button { + navigation.openTournamentInOrganizer(tournament) + } label: { + Label("Voir dans le gestionnaire", systemImage: "line.diagonal.arrow") + } } - NavigationLink(value: Screen.broadcast) { - Text("Publication") + + Divider() + if tournament.state() == .build { + NavigationLink(value: Screen.event) { + Text("Gestion de l'événement") + } + + NavigationLink(value: Screen.settings) { + LabelSettings() + } + NavigationLink(value: Screen.structure) { + LabelStructure() + } + NavigationLink(value: Screen.rankings) { + Text("Classement") + } + NavigationLink(value: Screen.broadcast) { + Text("Publication") + } } + } label: { + LabelOptions() } - } label: { - LabelOptions() } } }