add tournament subscribe

xcode16
Razmig Sarkissian 1 year ago
parent 8fc6f09908
commit 470aa619d6
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 4
      PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift
  3. 12
      PadelClub/Data/Federal/FederalTournament.swift
  4. 6
      PadelClub/Utils/PadelRule.swift
  5. 194
      PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift
  6. 4
      PadelClub/Views/Shared/TournamentFilterView.swift
  7. 10
      PadelClub/Views/Tournament/Shared/TournamentCellView.swift

@ -162,6 +162,7 @@
FF70916A2B90F95E00AB08DA /* DateBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091692B90F95E00AB08DA /* DateBoxView.swift */; };
FF70916C2B91005400AB08DA /* TournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916B2B91005400AB08DA /* TournamentView.swift */; };
FF70916E2B9108C600AB08DA /* InscriptionManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */; };
FF8044AC2C8F676D00A49A52 /* TournamentSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */; };
FF82CFC52B911F5B00B0CAF2 /* OrganizedTournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */; };
FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; };
FF8E1CE62C006E0200184680 /* Alphabet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8E1CE52C006E0200184680 /* Alphabet.swift */; };
@ -509,6 +510,7 @@
FF7091692B90F95E00AB08DA /* DateBoxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateBoxView.swift; sourceTree = "<group>"; };
FF70916B2B91005400AB08DA /* TournamentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentView.swift; sourceTree = "<group>"; };
FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InscriptionManagerView.swift; sourceTree = "<group>"; };
FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentSubscriptionView.swift; sourceTree = "<group>"; };
FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganizedTournamentView.swift; sourceTree = "<group>"; };
FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = "<group>"; };
FF8E1CE52C006E0200184680 /* Alphabet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alphabet.swift; sourceTree = "<group>"; };
@ -1294,6 +1296,7 @@
FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */,
FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */,
FFD655D72C8DE27400E5B35E /* TournamentLookUpView.swift */,
FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */,
);
path = Agenda;
sourceTree = "<group>";
@ -1594,6 +1597,7 @@
FFDB1C6D2BB2A02000F1E467 /* AppSettings.swift in Sources */,
FF0EC5202BB16F680056B6D1 /* SwiftParser.swift in Sources */,
C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */,
FF8044AC2C8F676D00A49A52 /* TournamentSubscriptionView.swift in Sources */,
FFC83D512BB8087E00750834 /* RoundView.swift in Sources */,
FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */,
FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */,

@ -58,6 +58,10 @@ extension ImportedPlayer: PlayerHolder {
male
}
func pasteData() -> String {
return [firstName?.capitalized, lastName?.capitalized, license].compactMap({ $0 }).joined(separator: " ")
}
func isNotFromCurrentDate() -> Bool {
if let importDate, importDate != SourceFileManager.shared.lastDataSourceDate() {
return true

@ -155,6 +155,18 @@ struct FederalTournament: Identifiable, Codable {
[nomClub, jugeArbitre?.nom, jugeArbitre?.prenom, courrielEngagement, installation?.telephone].compactMap({$0}).joined(separator: ";")
}
func umpireLabel() -> String {
[jugeArbitre?.nom, jugeArbitre?.prenom].compactMap({$0}).joined(separator: " ")
}
func phoneLabel() -> String {
[installation?.telephone].compactMap({$0}).joined(separator: " ")
}
func mailLabel() -> String {
[courrielEngagement].compactMap({$0}).joined(separator: " ")
}
func validForSearch(_ searchText: String, scope: FederalTournamentSearchScope) -> Bool {
var trimmedSearchText = searchText.lowercased().trimmingCharacters(in: .whitespaces).folding(options: .diacriticInsensitive, locale: .current)
trimmedSearchText = trimmedSearchText.replaceCharactersFromSet(characterSet: .punctuationCharacters, replacementString: " ")

@ -6,6 +6,7 @@
//
import Foundation
import LeStorage
enum RankSource: Hashable {
case national
@ -33,7 +34,8 @@ protocol TournamentBuildHolder: Identifiable {
}
struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable {
var id: String { identifier }
var uniqueId: String = Store.randomId()
var id: String { uniqueId }
let category: TournamentCategory
let level: TournamentLevel
let age: FederalTournamentAge
@ -42,7 +44,7 @@ struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable {
// var japLastName: String? = nil
func buildHolderTitle() -> String {
localizedLabel()
computedLabel
}
var identifier: String {

@ -0,0 +1,194 @@
//
// TournamentSubscriptionView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 09/09/2024.
//
import SwiftUI
struct TournamentSubscriptionView: View {
@EnvironmentObject var networkMonitor: NetworkMonitor
let federalTournament: FederalTournament
let build: any TournamentBuildHolder
@State private var selectedPlayers: [ImportedPlayer]
@State private var contactType: ContactType? = nil
@State private var sentError: ContactManagerError? = nil
init(federalTournament: FederalTournament, build: any TournamentBuildHolder, user: User) {
self.federalTournament = federalTournament
self.build = build
_selectedPlayers = .init(wrappedValue: [user.currentPlayerData()].compactMap({ $0 }))
}
var body: some View {
List {
Section {
LabeledContent("Tournoi") {
Text(federalTournament.libelle ?? "Tournoi")
}
LabeledContent("Club") {
Text(federalTournament.clubLabel())
}
LabeledContent("Épreuve") {
Text(build.buildHolderTitle())
}
} header: {
Text("Informations")
}
Section {
ForEach(selectedPlayers) { teamPlayer in
NavigationLink {
SelectablePlayerListView(allowSelection: 1, playerSelectionAction: { players in
if let player = players.first {
selectedPlayers.remove(elements: [teamPlayer])
selectedPlayers.append(player)
}
})
} label: {
ImportedPlayerView(player: teamPlayer)
}
}
if selectedPlayers.count < 2 {
NavigationLink {
SelectablePlayerListView(allowSelection: 1, playerSelectionAction: { players in
if let player = players.first {
selectedPlayers.append(player)
}
})
} label: {
Text("Choisir un partenaire")
}
}
} header: {
HStack {
Text("Poids")
Spacer()
Text(selectedPlayers.map { $0.rank }.reduce(0, +).formatted())
}
}
Section {
LabeledContent("JAP") {
Text(federalTournament.umpireLabel())
}
LabeledContent("Mail") {
Text(federalTournament.mailLabel())
}
LabeledContent("Téléphone") {
Text(federalTournament.phoneLabel())
}
}
let teams = selectedPlayers.map { $0.pasteData() }.joined(separator: "\n")
let body = [[build.buildHolderTitle(), federalTournament.computedStartDate].compacted().joined(separator: " "), teams].compactMap { $0 }.joined(separator: "\n") + "\n"
let subject = [build.buildHolderTitle(), federalTournament.nomClub].compacted().joined(separator: " ")
if let courrielEngagement = federalTournament.courrielEngagement {
Section {
RowButtonView("Contacter par email") {
contactType = .mail(date: nil, recipients: [courrielEngagement], bccRecipients: nil, body: body, subject: subject, tournamentBuild: build as? TournamentBuild)
}
}
}
if let installation = federalTournament.installation, let telephone = installation.telephone {
if telephone.isMobileNumber() {
let body = [[build.buildHolderTitle(), federalTournament.nomClub].compacted().joined(separator: " "), federalTournament.computedStartDate, teams].compacted().joined(separator: "\n") + "\n"
Section {
RowButtonView("Contacter par message") {
contactType = .message(date: nil, recipients: [telephone], body: body, tournamentBuild: build as? TournamentBuild)
}
}
} else {
let number = telephone.replacingOccurrences(of: " ", with: "")
if let url = URL(string: "tel:\(number)") {
Link(destination: url) {
Label("Appeler", systemImage: "phone")
}
}
}
}
}
.alert("Un problème est survenu", isPresented: messageSentFailed) {
Button("OK") {
}
} message: {
Text(_networkErrorMessage)
}
.sheet(item: $contactType) { contactType in
Group {
switch contactType {
case .message(_, let recipients, let body, _):
MessageComposeView(recipients: recipients, body: body) { result in
switch result {
case .cancelled:
break
case .failed:
self.sentError = .messageFailed
case .sent:
if networkMonitor.connected == false {
self.sentError = .messageNotSent
}
@unknown default:
break
}
}
case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in
switch result {
case .cancelled, .saved:
self.contactType = nil
case .failed:
self.contactType = nil
self.sentError = .mailFailed
case .sent:
if networkMonitor.connected == false {
self.contactType = nil
self.sentError = .mailNotSent
}
@unknown default:
break
}
}
}
}
.tint(.master)
}
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Détail du tournoi")
}
var messageSentFailed: Binding<Bool> {
Binding {
sentError != nil
} set: { newValue in
if newValue == false {
sentError = nil
}
}
}
private var _networkErrorMessage: String {
var errors: [String] = []
if networkMonitor.connected == false {
errors.append("L'appareil n'est pas connecté à internet.")
}
if sentError == .mailNotSent {
errors.append("Le mail est dans la boîte d'envoi de l'app Mail. Vérifiez son état dans l'app Mail avant d'essayer de le renvoyer.")
}
if (sentError == .messageFailed || sentError == .messageNotSent) {
errors.append("Le SMS n'a pas été envoyé")
}
if sentError == .mailFailed {
errors.append("Le mail n'a pas été envoyé")
}
return errors.joined(separator: "\n")
}
}

@ -71,7 +71,7 @@ struct TournamentFilterView: View {
}
Section {
ForEach(TournamentCategory.allCases) { category in
ForEach([TournamentCategory.men, TournamentCategory.women, TournamentCategory.mix]) { category in
LabeledContent {
Button {
if categories.contains(category) {
@ -85,7 +85,7 @@ struct TournamentFilterView: View {
}
}
} label: {
Text(category.localizedLabel(.title))
Text(category.localizedLabel(.wide))
}
}
} header: {

@ -23,8 +23,16 @@ struct TournamentCellView: View {
var body: some View {
ForEach(tournament.tournaments, id: \.id) { build in
if navigation.agendaDestination == .around, let federalTournament = tournament as? FederalTournament {
NavigationLink {
TournamentSubscriptionView(federalTournament: federalTournament, build: build, user: dataStore.user)
} label: {
_buildView(build, existingTournament: event?.existingBuild(build))
}
} else {
_buildView(build, existingTournament: event?.existingBuild(build))
}
}
}
var teamCount: Int? {
@ -98,7 +106,7 @@ struct TournamentCellView: View {
} else if let teamCount {
Text(teamCount.formatted())
}
} else if let federalTournament = tournament as? FederalTournament {
} else if let federalTournament = tournament as? FederalTournament, navigation.agendaDestination != .around {
Button {
_createOrShow(federalTournament: federalTournament, existingTournament: existingTournament, build: build)
} label: {

Loading…
Cancel
Save