diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 50b2b06..999a7c0 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1919,7 +1919,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16; + CURRENT_PROJECT_VERSION = 17; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1957,7 +1957,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16; + CURRENT_PROJECT_VERSION = 17; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 2f43caf..aaad8a5 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -762,6 +762,10 @@ class Tournament : ModelObject, Storable { Store.main.filter { $0.tournament == self.id && $0.walkOut == false } } + func walkoutTeams() -> [TeamRegistration] { + Store.main.filter { $0.tournament == self.id && $0.walkOut == true } + } + func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] { var duplicates = [PlayerRegistration]() Set(players.compactMap({ $0.licenceId })).forEach { licenceId in @@ -1029,7 +1033,7 @@ class Tournament : ModelObject, Storable { } func tournamentTitle(_ displayStyle: DisplayStyle = .wide) -> String { - [tournamentLevel.localizedLabel(displayStyle), tournamentCategory.localizedLabel(displayStyle), name].compactMap({ $0 }).joined(separator: " ") + [tournamentLevel.localizedLabel(.wide) + " " + tournamentCategory.localizedLabel(.wide), displayStyle == .wide ? name : nil].compactMap({ $0 }).joined(separator: " - ") } func subtitle(_ displayStyle: DisplayStyle = .wide) -> String { @@ -1723,7 +1727,7 @@ extension Tournament: FederalTournamentHolder { extension Tournament: TournamentBuildHolder { func buildHolderTitle() -> String { - tournamentTitle() + tournamentTitle(.short) } var category: TournamentCategory { diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift index 2a54554..4e7c075 100644 --- a/PadelClub/Extensions/String+Extensions.swift +++ b/PadelClub/Extensions/String+Extensions.swift @@ -174,3 +174,15 @@ extension StringProtocol { extension LosslessStringConvertible { var string: String { .init(self) } } + +extension String { + func createTxtFile(_ withName: String = "temp") -> URL { + let url = FileManager.default.temporaryDirectory + .appendingPathComponent(withName) + .appendingPathExtension("txt") + let string = self + try? FileManager.default.removeItem(at: url) + try? string.write(to: url, atomically: true, encoding: .utf8) + return url + } +} diff --git a/PadelClub/PadelClubApp.swift b/PadelClub/PadelClubApp.swift index c899697..8dd82ed 100644 --- a/PadelClub/PadelClubApp.swift +++ b/PadelClub/PadelClubApp.swift @@ -22,6 +22,7 @@ struct PadelClubApp: App { .environment(navigationViewModel) .accentColor(.master) .onAppear { + networkMonitor.checkConnection() self._onAppear() } .task { diff --git a/PadelClub/Utils/ContactManager.swift b/PadelClub/Utils/ContactManager.swift index 077b0b2..53b7e99 100644 --- a/PadelClub/Utils/ContactManager.swift +++ b/PadelClub/Utils/ContactManager.swift @@ -31,7 +31,7 @@ enum ContactType: Identifiable { extension ContactType { static let defaultCustomMessage = "Il est conseillé de vous présenter 10 minutes avant de jouer.\nMerci de me confirmer votre présence avec votre nom et de prévenir votre partenaire." - static let defaultAvailablePaymentMethods = "Règlement possible par chèque ou espèce." + static let defaultAvailablePaymentMethods = "Règlement possible par chèque ou espèces." static func callingCustomMessage(source: String? = nil, tournament: Tournament?, startDate: Date?, roundLabel: String) -> String { let tournamentCustomMessage = source ?? DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage @@ -41,7 +41,7 @@ extension ContactType { let date = startDate ?? tournament?.startDate ?? Date() if let tournament { - text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle()) + text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle(.short)) text = text.replacingOccurrences(of: "#prix", with: tournament.entryFeeMessage) } @@ -77,11 +77,11 @@ extension ContactType { } var computedMessage: String { - [entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n") + [entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n\n") } if let tournament { - return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" + return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" } else { return "Bonjour,\n\nVous êtes \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)" } diff --git a/PadelClub/Views/Calling/CallMessageCustomizationView.swift b/PadelClub/Views/Calling/CallMessageCustomizationView.swift index 88386db..08c3154 100644 --- a/PadelClub/Views/Calling/CallMessageCustomizationView.swift +++ b/PadelClub/Views/Calling/CallMessageCustomizationView.swift @@ -48,7 +48,7 @@ struct CallMessageCustomizationView: View { var finalMessage: String? { let localizedCalled = "convoqué" + (tournament.tournamentCategory == .women ? "e" : "") + "s" - return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(tournament.startDate.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(tournament.startDate.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)" + return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(tournament.startDate.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(tournament.startDate.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)" } var body: some View { @@ -103,9 +103,6 @@ struct CallMessageCustomizationView: View { Spacer() Button { focusedField = nil - user.summonsMessageBody = customCallMessageBody - user.summonsMessageSignature = customCallMessageSignature - user.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods _save() } label: { Text("Valider") @@ -129,6 +126,9 @@ struct CallMessageCustomizationView: View { } private func _save() { + self.dataStore.user.summonsMessageBody = customCallMessageBody + self.dataStore.user.summonsMessageSignature = customCallMessageSignature + self.dataStore.user.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods self.dataStore.saveUser() } diff --git a/PadelClub/Views/Calling/CallSettingsView.swift b/PadelClub/Views/Calling/CallSettingsView.swift index 425bcb7..0043eb3 100644 --- a/PadelClub/Views/Calling/CallSettingsView.swift +++ b/PadelClub/Views/Calling/CallSettingsView.swift @@ -34,7 +34,6 @@ struct CallSettingsView: View { Section { RowButtonView("Envoyer le lien du tournoi") { addLink = true - showSendToAllView = true } .disabled(tournament.isPrivate) } footer: { @@ -75,7 +74,11 @@ struct CallSettingsView: View { #endif } .sheet(isPresented: $showSendToAllView) { - SendToAllView(addLink: addLink) + SendToAllView(addLink: false) + .tint(.master) + } + .sheet(isPresented: $addLink) { + SendToAllView(addLink: true) .tint(.master) } } diff --git a/PadelClub/Views/Calling/SendToAllView.swift b/PadelClub/Views/Calling/SendToAllView.swift index 454701a..cf7d8a8 100644 --- a/PadelClub/Views/Calling/SendToAllView.swift +++ b/PadelClub/Views/Calling/SendToAllView.swift @@ -72,9 +72,9 @@ struct SendToAllView: View { Section { RowButtonView("Contacter \(_totalString())") { if contactMethod == 0 { - contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: tournament.shareURL()?.absoluteString , tournamentBuild: nil) + contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: addLink ? tournament.shareURL()?.absoluteString : nil, tournamentBuild: nil) } else { - contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: tournament.shareURL()?.absoluteString, subject: tournament.tournamentTitle(), tournamentBuild: nil) + contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: addLink ? tournament.shareURL()?.absoluteString : nil, subject: tournament.tournamentTitle(), tournamentBuild: nil) } } } diff --git a/PadelClub/Views/Cashier/CashierView.swift b/PadelClub/Views/Cashier/CashierView.swift index 8fab647..5ca8df8 100644 --- a/PadelClub/Views/Cashier/CashierView.swift +++ b/PadelClub/Views/Cashier/CashierView.swift @@ -152,7 +152,7 @@ struct CashierView: View { .searchable(text: $searchText, isPresented: $isSearching, prompt: Text("Chercher un joueur")) .toolbar { ToolbarItem(placement: .topBarTrailing) { - ShareLink(item: _sharedData()) + ShareLink(item: _sharedData().createTxtFile("bilan")) } } } diff --git a/PadelClub/Views/Navigation/Agenda/CalendarView.swift b/PadelClub/Views/Navigation/Agenda/CalendarView.swift index e700bf9..771f439 100644 --- a/PadelClub/Views/Navigation/Agenda/CalendarView.swift +++ b/PadelClub/Views/Navigation/Agenda/CalendarView.swift @@ -85,7 +85,7 @@ struct CalendarView: View { ForEach(tournamentsByDay, id: \.holderId) { tournamentHolder in if let tournament = tournamentHolder as? Tournament { Section { - Button(tournament.tournamentTitle()) { + Button(tournament.tournamentTitle(.short)) { navigation.path.append(tournament) } } header: { diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift index 6753f1c..0b4c24c 100644 --- a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift +++ b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift @@ -32,7 +32,7 @@ struct OngoingView: View { } header: { if let tournament = match.currentTournament() { HStack { - Text(tournament.tournamentTitle()) + Text(tournament.tournamentTitle(.short)) Spacer() if let club = tournament.club() { Text("@" + club.clubTitle(.short)) diff --git a/PadelClub/Views/Shared/LearnMoreSheetView.swift b/PadelClub/Views/Shared/LearnMoreSheetView.swift index fa64b4e..d055183 100644 --- a/PadelClub/Views/Shared/LearnMoreSheetView.swift +++ b/PadelClub/Views/Shared/LearnMoreSheetView.swift @@ -28,7 +28,7 @@ struct LearnMoreSheetView: View { .foregroundStyle(.secondary) - ShareLink(item: tournament.pasteDataForImporting()) { + ShareLink(item: tournament.pasteDataForImporting().createTxtFile(tournament.tournamentTitle(.short))) { HStack { Spacer() Text("Exporter les inscriptions") diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 2a3528d..3c99479 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -9,12 +9,19 @@ import SwiftUI import LeStorage struct TournamentGeneralSettingsView: View { - @Environment(Tournament.self) private var tournament: Tournament @EnvironmentObject var dataStore: DataStore + var tournament: Tournament @State private var tournamentName: String = "" - @FocusState private var textFieldIsFocus: Bool - + @State private var entryFee: Double? = nil + @FocusState private var focusedField: Tournament.CodingKeys? + + init(tournament: Tournament) { + self.tournament = tournament + _tournamentName = State(wrappedValue: tournament.name ?? "") + _entryFee = State(wrappedValue: tournament.entryFee) + } + var body: some View { @Bindable var tournament = tournament Form { @@ -29,44 +36,43 @@ struct TournamentGeneralSettingsView: View { Section { LabeledContent { - TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $tournament.entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR")) + TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR")) .keyboardType(.decimalPad) .multilineTextAlignment(.trailing) .frame(maxWidth: .infinity) - .focused($textFieldIsFocus) + .focused($focusedField, equals: ._entryFee) } label: { Text("Inscription") } } Section { - LabeledContent { - TextField("Nom", text: $tournamentName) - .multilineTextAlignment(.trailing) - .frame(maxWidth: .infinity) - .keyboardType(.alphabet) - .autocorrectionDisabled() - .onSubmit { - if tournamentName.trimmed.isEmpty { - tournament.name = nil - } else { - tournament.name = tournamentName - } - } - } label: { - Text("Nom du tournoi") - } + TextField("Nom du tournoi", text: $tournamentName, axis: .vertical) + .lineLimit(2) + .frame(maxWidth: .infinity) + .keyboardType(.alphabet) + .autocorrectionDisabled() + .focused($focusedField, equals: ._name) } } .scrollDismissesKeyboard(.immediately) .toolbarBackground(.visible, for: .navigationBar) .toolbar { - if textFieldIsFocus { + if focusedField != nil { ToolbarItem(placement: .keyboard) { HStack { Spacer() Button("Valider") { - textFieldIsFocus = false + if focusedField == ._name { + if tournamentName.trimmed.isEmpty { + tournament.name = nil + } else { + tournament.name = tournamentName + } + } else if focusedField == ._entryFee { + tournament.entryFee = entryFee + } + focusedField = nil } .buttonStyle(.bordered) } @@ -115,7 +121,3 @@ struct TournamentGeneralSettingsView: View { } } } - -#Preview { - TournamentGeneralSettingsView() -} diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index c0ec79a..e2b4f37 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -249,7 +249,7 @@ struct InscriptionManagerView: View { Label("Clôturer", systemImage: "lock") } Divider() - ShareLink(item: tournament.pasteDataForImporting()) { + ShareLink(item: tournament.pasteDataForImporting().createTxtFile(self.tournament.tournamentTitle(.short))) { Label("Exporter les paires", systemImage: "square.and.arrow.up") } Button { @@ -531,15 +531,19 @@ struct InscriptionManagerView: View { private func _informationView(count: Int) -> some View { Section { - let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO() + let unsortedTeams = tournament.unsortedTeams() + let walkoutTeams = tournament.walkoutTeams() + LabeledContent { - Text(unsortedTeamsWithoutWO.count.formatted() + "/" + tournament.teamCount.formatted()) + Text(unsortedTeams.count.formatted() + "/" + tournament.teamCount.formatted()).font(.largeTitle) } label: { - Text("Paires inscrites") + Text("Paire\(unsortedTeams.count.pluralSuffix) inscrite\(unsortedTeams.count.pluralSuffix)") + Text("dont \(walkoutTeams.count.pluralSuffix) forfaite\(walkoutTeams.count.pluralSuffix)") } - + + let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO() LabeledContent { - Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()) + Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()).font(.largeTitle) } label: { Text("Liste d'attente") } @@ -549,7 +553,7 @@ struct InscriptionManagerView: View { .environment(tournament) } label: { LabeledContent { - Text(tournament.registrationIssues().formatted()) + Text(tournament.registrationIssues().formatted()).font(.largeTitle) } label: { Text("Problèmes détéctés") if let closedRegistrationDate = tournament.closedRegistrationDate { diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index f81916d..d9f0121 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -67,7 +67,7 @@ struct TournamentSettingsView: View { case .matchFormats: TournamentMatchFormatsSettingsView() case .general: - TournamentGeneralSettingsView() + TournamentGeneralSettingsView(tournament: tournament) case .club: TournamentClubSettingsView() } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 9f22401..0a4ed9b 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -121,7 +121,7 @@ struct TournamentView: View { .toolbar { ToolbarItem(placement: .principal) { VStack(spacing: -4.0) { - Text(tournament.tournamentTitle()).font(.headline) + Text(tournament.tournamentTitle(.short)).font(.headline) Text(tournament.formattedDate()) .font(.subheadline).foregroundStyle(.secondary) }