Laurent 2 years ago
commit 70c943330f
  1. 16
      PadelClub.xcodeproj/project.pbxproj
  2. 8
      PadelClub/Data/Match.swift
  3. 3
      PadelClub/Data/PlayerRegistration.swift
  4. 18
      PadelClub/Data/Round.swift
  5. 4
      PadelClub/Data/Tournament.swift
  6. 6
      PadelClub/ViewModel/SeedInterval.swift
  7. 7
      PadelClub/Views/Calling/CallSettingsView.swift
  8. 2
      PadelClub/Views/Calling/CallView.swift
  9. 80
      PadelClub/Views/Calling/Components/MenuWarningView.swift
  10. 13
      PadelClub/Views/Calling/GroupStageCallingView.swift
  11. 13
      PadelClub/Views/Calling/SeedsCallingView.swift
  12. 167
      PadelClub/Views/Calling/SendToAllView.swift
  13. 81
      PadelClub/Views/Match/MatchDetailView.swift
  14. 8
      PadelClub/Views/Planning/Components/DateUpdateManagerView.swift
  15. 12
      PadelClub/Views/Round/LoserRoundView.swift
  16. 19
      PadelClub/Views/Round/RoundView.swift

@ -122,6 +122,7 @@
FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; };
FF2BE4872B85E27400592328 /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C425D4542B6D24E2002A7B48 /* LeStorage.framework */; };
FF2BE4882B85E27400592328 /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C425D4542B6D24E2002A7B48 /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; };
FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; };
FF3795662B9399AA004EA093 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; };
FF3B60A32BC49BBC008C2E66 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; };
@ -242,6 +243,7 @@
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 */; };
FFEF7F4E2BDE69130033D0F0 /* MenuWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */; };
FFF03C942BD91D0C00B516FC /* ButtonValidateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */; };
FFF116E12BD2A9B600A33B06 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; };
FFF116E32BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; };
@ -422,6 +424,7 @@
FF1DC5582BAB767000FD8220 /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = "<group>"; };
FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayContext.swift; sourceTree = "<group>"; };
FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonView.swift; sourceTree = "<group>"; };
FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToAllView.swift; sourceTree = "<group>"; };
FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduler.swift; sourceTree = "<group>"; };
@ -543,6 +546,7 @@
FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySortDescriptor.swift; sourceTree = "<group>"; };
FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeferredViewModifier.swift; sourceTree = "<group>"; };
FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuWarningView.swift; sourceTree = "<group>"; };
FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonValidateView.swift; sourceTree = "<group>"; };
FFF116E02BD2A9B600A33B06 /* DateInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateInterval.swift; sourceTree = "<group>"; };
FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourtAvailabilitySettingsView.swift; sourceTree = "<group>"; };
@ -1084,6 +1088,8 @@
FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */,
FF9268082BCEDC2C0080F940 /* CallView.swift */,
FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */,
FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */,
FFEF7F4C2BDE68F80033D0F0 /* Components */,
);
path = Calling;
sourceTree = "<group>";
@ -1169,6 +1175,14 @@
path = ViewModifiers;
sourceTree = "<group>";
};
FFEF7F4C2BDE68F80033D0F0 /* Components */ = {
isa = PBXGroup;
children = (
FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */,
);
path = Components;
sourceTree = "<group>";
};
FFF8ACD02B9238A2008466FA /* Manager */ = {
isa = PBXGroup;
children = (
@ -1467,6 +1481,7 @@
C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */,
FFC83D512BB8087E00750834 /* RoundView.swift in Sources */,
FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */,
FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */,
FF8F263B2BAD528600650388 /* EventCreationView.swift in Sources */,
FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */,
FFF964552BC266CF00EEF017 /* SchedulerView.swift in Sources */,
@ -1560,6 +1575,7 @@
FF11628A2BD05247000C4809 /* DateUpdateManagerView.swift in Sources */,
FFCFC01A2BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift in Sources */,
FF025AE92BD1307F00A86CF8 /* MonthData.swift in Sources */,
FFEF7F4E2BDE69130033D0F0 /* MenuWarningView.swift in Sources */,
FF967D0B2BAF3D4C00A9A3BD /* TeamPickerView.swift in Sources */,
FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */,
FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */,

@ -55,6 +55,14 @@ class Match: ModelObject, Storable {
return RoundRule.matchIndexWithinRound(fromMatchIndex: index)
}
func matchWarningSubject() -> String {
[roundTitle(), matchTitle()].compacted().joined(separator: " ")
}
func matchWarningMessage() -> String {
[roundTitle(), matchTitle(), startDate?.localizedDate(), courtName()].compacted().joined(separator: "\n")
}
func matchTitle(_ displayStyle: DisplayStyle = .wide) -> String {
if let groupStageObject {
return groupStageObject.localizedMatchUpLabel(for: index)

@ -305,6 +305,7 @@ class PlayerRegistration: ModelObject, Storable {
case bankTransfer = 5
case clubHouse = 6
case creditCard = 7
case forfeit = 8
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
@ -322,6 +323,8 @@ class PlayerRegistration: ModelObject, Storable {
return "Clubhouse"
case .creditCard:
return "CB"
case .forfeit:
return "Forfait"
case .gift:
return "Offert"
}

@ -122,6 +122,10 @@ class Round: ModelObject, Storable {
_matches().compactMap { $0.losingTeamId }.compactMap { Store.main.findById($0) }
}
func teams() -> [TeamRegistration] {
playedMatches().flatMap({ $0.teams() })
}
func roundProjectedTeam(_ team: TeamPosition, inMatch match: Match) -> TeamRegistration? {
if isLoserBracket() == false, let seed = seed(team, inMatchIndex: match.index) {
return seed
@ -199,6 +203,16 @@ class Round: ModelObject, Storable {
Store.main.filter { $0.round == self.id && $0.disabled == false }
}
func displayableMatches() -> [Match] {
if index == 0 && isUpperBracket() {
var matches : [Match?] = [playedMatches().first]
matches.append(loserRounds().first?.playedMatches().first)
return matches.compactMap({ $0 })
} else {
return playedMatches()
}
}
func playedMatches() -> [Match] {
if parent == nil {
enabledMatches()
@ -370,6 +384,10 @@ class Round: ModelObject, Storable {
return loserRounds + loserRounds.flatMap({ $0.loserRoundsAndChildren() })
}
func isUpperBracket() -> Bool {
parent == nil
}
func isLoserBracket() -> Bool {
parent != nil
}

@ -562,13 +562,13 @@ class Tournament : ModelObject, Storable {
}
func isStartDateIsDifferentThanCallDate(_ team: TeamRegistration) -> Bool {
guard let callDate = team.callDate else { return false }
guard let callDate = team.callDate else { return true }
if let groupStageStartDate = team.groupStageObject()?.startDate {
return Calendar.current.compare(callDate, to: groupStageStartDate, toGranularity: .minute) != ComparisonResult.orderedSame
} else if let roundMatchStartDate = team.initialMatch()?.startDate {
return Calendar.current.compare(callDate, to: roundMatchStartDate, toGranularity: .minute) != ComparisonResult.orderedSame
}
return false
return true
}
func availableToStart(_ allMatches: [Match]) -> [Match] {

@ -12,6 +12,12 @@ struct SeedInterval: Hashable, Comparable {
let last: Int
var reduce: Int = 0
func pointsRange(tournamentLevel: TournamentLevel, teamsCount: Int) -> String {
let range = [tournamentLevel.points(for: last - 1 - reduce, count: teamsCount),
tournamentLevel.points(for: first - 1 - reduce, count: teamsCount)]
return range.map { $0.formatted(.number.sign(strategy: .always())) }.joined(separator: " / ") + " pts"
}
static func <(lhs: SeedInterval, rhs: SeedInterval) -> Bool {
return lhs.first < rhs.first
}

@ -10,6 +10,7 @@ import SwiftUI
struct CallSettingsView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
@State private var showSendToAllView: Bool = false
var body: some View {
List {
@ -24,7 +25,7 @@ struct CallSettingsView: View {
Section {
RowButtonView("Envoyer un message à tout le monde") {
showSendToAllView = true
}
}
@ -48,6 +49,10 @@ struct CallSettingsView: View {
}
}
}
.sheet(isPresented: $showSendToAllView) {
SendToAllView()
.tint(.master)
}
}
}

@ -20,7 +20,7 @@ struct CallView: View {
if let startDate {
Text(startDate.formattedAsHourMinute())
} else {
Text("Aucun horaire")
Text("Aucun horaire").font(.body)
}
Spacer()
Text(count.formatted() + "/" + total.formatted())

@ -0,0 +1,80 @@
//
// MenuWarningView.swift
// PadelClub
//
// Created by razmig on 28/04/2024.
//
import SwiftUI
struct MenuWarningView: View {
let teams: [TeamRegistration]
var date: Date?
var message: String?
var umpireMail: String?
var subject: String?
@Binding var contactType: ContactType?
private func _getUmpireMail() -> [String]? {
if let umpireMail {
return [umpireMail]
}
return nil
}
@ViewBuilder
private func _actionView(players: [PlayerRegistration], privateMode: Bool = false) -> some View {
Button("Message") {
contactType = .message(date: date, recipients: players.compactMap({ $0.phoneNumber }), body: message, tournamentBuild: nil)
}
Button("Mail") {
contactType = .mail(date: date, recipients: privateMode ? _getUmpireMail() : players.compactMap({ $0.email }), bccRecipients: privateMode ? players.compactMap({ $0.email }) : nil, body: message, subject: subject, tournamentBuild: nil)
}
}
var body: some View {
Menu {
Menu("Tout le monde") {
let players = teams.flatMap({ $0.players() })
_actionView(players: players, privateMode: true)
}
Divider()
ForEach(teams) { team in
_teamView(team)
}
} label: {
Text("Prévenir")
.underline()
}
}
func _playerView(_ player: PlayerRegistration) -> some View {
Menu {
let players = [player]
_actionView(players: players)
} label: {
Text(player.playerLabel(.short))
}
}
func _teamView(_ team: TeamRegistration) -> some View {
Menu {
Menu("Toute l'équipe") {
let players = team.players()
_actionView(players: players)
}
Divider()
ForEach(team.players()) { player in
_playerView(player)
}
} label: {
Text(team.teamLabel(.short))
}
}
}
#Preview {
MenuWarningView(teams: [], contactType: .constant(nil))
}

@ -83,6 +83,19 @@ struct GroupStageCallingView: View {
}
}
.overlay {
if groupStage.startDate == nil {
ContentUnavailableView {
Label("Aucun horaire défini", systemImage: "clock.badge.questionmark")
} description: {
Text("Vous n'avez pas encore défini d'horaire pour les différentes phases du tournoi")
} actions: {
// RowButtonView("Horaire intelligent") {
// selectedScheduleDestination = nil
// }
}
}
}
.headerProminence(.increased)
.navigationTitle(groupStage.groupStageTitle())
.navigationBarTitleDisplayMode(.inline)

@ -85,6 +85,19 @@ struct SeedsCallingView: View {
}
}
}
.overlay {
if (keys.isEmpty && displayByMatch == false) || (displayByMatch && roundMatches.isEmpty) {
ContentUnavailableView {
Label("Aucun horaire défini", systemImage: "clock.badge.questionmark")
} description: {
Text("Vous n'avez pas encore défini d'horaire pour les différentes phases du tournoi")
} actions: {
// RowButtonView("Horaire intelligent") {
// selectedScheduleDestination = nil
// }
}
}
}
.headerProminence(.increased)
.navigationTitle(round.roundTitle())
.navigationBarTitleDisplayMode(.inline)

@ -0,0 +1,167 @@
//
// SendToAllView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 28/04/2024.
//
import SwiftUI
import LeStorage
struct SendToAllView: View {
@Environment(Tournament.self) var tournament: Tournament
@EnvironmentObject var networkMonitor: NetworkMonitor
@State private var contactType: ContactType? = nil
@State private var contactMethod: Int = 1
@State private var contactRecipients: Set<String> = Set()
@State private var sentError: ContactManagerError? = nil
var messageSentFailed: Binding<Bool> {
Binding {
sentError != nil
} set: { newValue in
if newValue == false {
sentError = nil
}
}
}
var body: some View {
NavigationStack {
List(selection: $contactRecipients) {
Section {
Picker(selection: $contactMethod) {
Text("Contacter par sms").tag(0)
Text("Contacter par mail").tag(1)
} label: {
Text("méthode")
}
.labelsHidden()
.pickerStyle(.inline)
}
Section {
ForEach(tournament.groupStages()) { groupStage in
let teams = groupStage.teams()
if teams.isEmpty == false {
LabeledContent {
Text(teams.count.formatted() + " équipe" + teams.count.pluralSuffix)
} label: {
Text(groupStage.groupStageTitle())
}
.tag(groupStage.id)
}
}
ForEach(tournament.rounds()) { round in
let teams = round.teams()
if teams.isEmpty == false {
LabeledContent {
Text(teams.count.formatted() + " équipe" + teams.count.pluralSuffix)
} label: {
Text(round.roundTitle())
}
.tag(round.id)
}
}
}
Section {
RowButtonView("Contacter \(_totalString())") {
if contactMethod == 0 {
contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: tournament.tournamentTitle(), tournamentBuild: nil)
} else {
contactType = .mail(date: nil, recipients: nil, bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: nil, subject: tournament.tournamentTitle(), tournamentBuild: nil)
}
}
}
}
.environment(\.editMode, Binding.constant(EditMode.active))
.headerProminence(.increased)
.navigationTitle("Réglages")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.alert("Un problème est survenu", isPresented: messageSentFailed) {
Button("OK") {
}
} message: {
let message = [networkMonitor.connected == false ? "L'appareil n'est pas connecté à internet." as String? : nil, sentError == .mailNotSent ? "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." as String? : nil, (sentError == .messageFailed || sentError == .messageNotSent) ? "Le SMS n'a pas été envoyé" as String? : nil, sentError == .mailFailed ? "Le mail n'a pas été envoyé" as String? : nil].compacted().joined(separator: "\n")
Text(message)
}
.sheet(item: $contactType) { contactType in
Group {
switch contactType {
case .message(_, let recipients, let body, _):
if Guard.main.paymentForNewTournament() != nil {
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
}
}
} else {
SubscriptionView(showLackOfPlanMessage: true)
}
case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
if Guard.main.paymentForNewTournament() != nil {
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
}
}
} else {
SubscriptionView(showLackOfPlanMessage: true)
}
}
}
.tint(.master)
}
}
}
func _teams() -> [TeamRegistration] {
_roundTeams() + _groupStagesTeams()
}
func _roundTeams() -> [TeamRegistration] {
let rounds : [Round] = contactRecipients.compactMap { Store.main.findById($0) }
return rounds.flatMap({ $0.teams() })
}
func _groupStagesTeams() -> [TeamRegistration] {
let groupStages : [GroupStage] = contactRecipients.compactMap { Store.main.findById($0) }
return groupStages.flatMap({ $0.teams() })
}
func _totalString() -> String {
if contactRecipients.isEmpty {
return "toutes les équipes"
} else {
let teams = _teams()
return teams.count.formatted() + " équipe" + teams.count.pluralSuffix
}
}
}
#Preview {
SendToAllView()
}

@ -9,6 +9,7 @@ import SwiftUI
struct MatchDetailView: View {
@EnvironmentObject var dataStore: DataStore
@EnvironmentObject var networkMonitor: NetworkMonitor
@Environment(\.dismiss) var dismiss
let matchViewStyle: MatchViewStyle
@ -23,6 +24,19 @@ struct MatchDetailView: View {
@State private var endDate: Date = Date()
@State private var isEditing: Bool = false
@State private var showDetails: Bool = false
@State private var contactType: ContactType? = nil
@State private var sentError: ContactManagerError? = nil
var messageSentFailed: Binding<Bool> {
Binding {
sentError != nil
} set: { newValue in
if newValue == false {
sentError = nil
}
}
}
var match: Match
init(match: Match, matchViewStyle: MatchViewStyle = .standardStyle) {
@ -110,18 +124,12 @@ struct MatchDetailView: View {
} footer: {
if match.isEmpty() == false {
HStack {
Button {
FooterButtonView("Détails des joueurs") {
showDetails = true
} label: {
Text("Détails des joueurs")
}
Spacer()
Menu {
//MenuWarnView(warningSender: match)
} label: {
Text("Prévenir")
}
.buttonStyle(.borderless)
MenuWarningView(teams: match.teams(), message: match.matchWarningMessage(), umpireMail: dataStore.user?.email, subject: match.matchWarningSubject(), contactType: $contactType)
.buttonStyle(.borderless)
}
}
}
@ -197,6 +205,60 @@ struct MatchDetailView: View {
// }
}
.alert("Un problème est survenu", isPresented: messageSentFailed) {
Button("OK") {
}
} message: {
let message = [networkMonitor.connected == false ? "L'appareil n'est pas connecté à internet." as String? : nil, sentError == .mailNotSent ? "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." as String? : nil, (sentError == .messageFailed || sentError == .messageNotSent) ? "Le SMS n'a pas été envoyé" as String? : nil, sentError == .mailFailed ? "Le mail n'a pas été envoyé" as String? : nil].compacted().joined(separator: "\n")
Text(message)
}
.sheet(item: $contactType) { contactType in
Group {
switch contactType {
case .message(_, let recipients, let body, _):
if Guard.main.paymentForNewTournament() != nil {
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
}
}
} else {
SubscriptionView(showLackOfPlanMessage: true)
}
case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
if Guard.main.paymentForNewTournament() != nil {
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
}
}
} else {
SubscriptionView(showLackOfPlanMessage: true)
}
}
}
.tint(.master)
}
// .refreshable {
// if match.isBroadcasted() {
@ -264,6 +326,7 @@ struct MatchDetailView: View {
.navigationTitle(match.matchTitle())
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
enum ScoreType: Int, Identifiable, Hashable {

@ -41,6 +41,14 @@ struct DatePickingView: View {
} else {
HStack {
Menu {
Button("de 30 minutes") {
startDate = startDate.addingTimeInterval(1800)
}
Button("d'une heure") {
startDate = startDate.addingTimeInterval(3600)
}
Button("à 9h") {
startDate = startDate.atNine()
}

@ -9,6 +9,8 @@ import SwiftUI
struct LoserRoundView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
let loserRounds: [Round]
@State private var isEditingTournamentSeed: Bool = false
@ -39,7 +41,15 @@ struct LoserRoundView: View {
.disabled(match.disabled)
}
} header: {
Text(loserRound.roundTitle(.wide))
HStack {
Text(loserRound.roundTitle(.wide))
let tournamentTeamCount = tournament.teamCount
if let seedIntervalPointRange = loserRound.seedInterval()?.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournamentTeamCount) {
Spacer()
Text(seedIntervalPointRange)
.font(.caption)
}
}
}
}
}

@ -17,8 +17,9 @@ struct RoundView: View {
var body: some View {
List {
let loserRounds = round.loserRounds()
if isEditingTournamentSeed.wrappedValue == false {
let loserRounds = round.loserRounds()
//(where: { $0.isDisabled() == false || isEditingTournamentSeed.wrappedValue })
if loserRounds.isEmpty == false, let first = loserRounds.first {
let correspondingLoserRoundTitle = round.correspondingLoserRoundTitle()
@ -61,11 +62,23 @@ struct RoundView: View {
}
}
ForEach(round.playedMatches()) { match in
ForEach(round.displayableMatches()) { match in
Section {
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
} header: {
Text(round.roundTitle(.wide) + " " + match.matchTitle(.short))
HStack {
Text(round.roundTitle(.wide))
if round.index > 0 {
Text(match.matchTitle(.short))
} else {
let tournamentTeamCount = tournament.teamCount
if let seedIntervalPointRange = loserRounds.first?.seedInterval()?.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournamentTeamCount) {
Spacer()
Text(seedIntervalPointRange)
.font(.caption)
}
}
}
}
}
}

Loading…
Cancel
Save