multistore
Laurent 1 year ago
commit 395b5a80c7
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 2
      PadelClub/Data/Club.swift
  3. 7
      PadelClub/Data/TeamRegistration.swift
  4. 2
      PadelClub/Data/Tournament.swift
  5. 25
      PadelClub/Views/Components/FooterButtonView.swift
  6. 29
      PadelClub/Views/Navigation/Agenda/ActivityView.swift
  7. 6
      PadelClub/Views/Navigation/MainView.swift
  8. 72
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  9. 4
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift

@ -1859,7 +1859,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 44; CURRENT_PROJECT_VERSION = 45;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1882,7 +1882,6 @@
); );
MARKETING_VERSION = 0.1; MARKETING_VERSION = 0.1;
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=5 -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=50";
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@ -1897,7 +1896,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
<<<<<<< HEAD
CURRENT_PROJECT_VERSION = 44; CURRENT_PROJECT_VERSION = 44;
=======
CURRENT_PROJECT_VERSION = 45;
>>>>>>> 599c942c5910dc651db20fcea6c660dff139e08c
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1920,7 +1923,6 @@
); );
MARKETING_VERSION = 0.1; MARKETING_VERSION = 0.1;
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=5 -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=50";
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;

@ -63,7 +63,7 @@ class Club : ModelObject, Storable, Hashable {
} }
func shareURL() -> URL? { func shareURL() -> URL? {
return URLs.main.url.appending(path: "?club=\(id)") return URL(string: URLs.main.url.appending(path: "?club=\(id)").absoluteString.removingPercentEncoding!)
} }
var customizedCourts: [Court] { var customizedCourts: [Court] {

@ -192,6 +192,13 @@ class TeamRegistration: ModelObject, Storable {
unsortedPlayers().anySatisfy({ $0.contains(searchField) }) || self.name?.localizedCaseInsensitiveContains(searchField) == true unsortedPlayers().anySatisfy({ $0.contains(searchField) }) || self.name?.localizedCaseInsensitiveContains(searchField) == true
} }
func containsExactlyPlayerLicenses(_ playerLicenses: [String?]) -> Bool {
let arrayOfIds : [String] = unsortedPlayers().compactMap({ $0.licenceId?.strippedLicense?.canonicalVersion })
let ids : Set<String> = Set<String>(arrayOfIds.sorted())
let searchedIds = Set<String>(playerLicenses.compactMap({ $0?.strippedLicense?.canonicalVersion }).sorted())
return ids.hashValue == searchedIds.hashValue
}
func includes(_ players: [PlayerRegistration]) -> Bool { func includes(_ players: [PlayerRegistration]) -> Bool {
players.allSatisfy { player in players.allSatisfy { player in
includes(player) includes(player)

@ -1075,7 +1075,7 @@ class Tournament : ModelObject, Storable {
} }
let groupStages = groupStages() let groupStages = groupStages()
let baseRank = teamCount - teamsPerGroupStage * groupStageCount + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified let baseRank = teamCount - groupStageSpots() + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified
groupStages.forEach { groupStage in groupStages.forEach { groupStage in
let groupStageTeams = groupStage.teams(true) let groupStageTeams = groupStage.teams(true)

@ -7,22 +7,43 @@
import SwiftUI import SwiftUI
fileprivate let defaultConfirmationMessage = "Êtes-vous sûr de vouloir faire cela ?"
struct FooterButtonView: View { struct FooterButtonView: View {
var role: ButtonRole? = nil
let title: String let title: String
let confirmationMessage: String
let action: () -> () let action: () -> ()
@State private var askConfirmation: Bool = false
init(_ title: String, action: @escaping () -> Void) { init(_ title: String, role: ButtonRole? = nil, confirmationMessage: String? = nil, action: @escaping () -> Void) {
self.title = title self.title = title
self.action = action self.action = action
self.role = role
self.confirmationMessage = confirmationMessage ?? defaultConfirmationMessage
} }
var body: some View { var body: some View {
Button { Button(role: role) {
if role == .destructive {
askConfirmation = true
} else {
action() action()
}
} label: { } label: {
Text(title) Text(title)
.underline() .underline()
} }
.buttonStyle(.borderless) .buttonStyle(.borderless)
.confirmationDialog("Confirmation",
isPresented: $askConfirmation,
titleVisibility: .visible) {
Button("OK") {
action()
}
Button("Annuler", role: .cancel) {}
} message: {
Text(confirmationMessage)
}
} }
} }

@ -27,6 +27,11 @@ struct ActivityView: View {
.filter({ federalDataViewModel.isTournamentValidForFilters($0) }) .filter({ federalDataViewModel.isTournamentValidForFilters($0) })
} }
func getRunningTournaments() -> [Tournament] {
dataStore.tournaments.filter({ $0.endDate == nil })
.filter({ federalDataViewModel.isTournamentValidForFilters($0) })
}
var endedTournaments: [Tournament] { var endedTournaments: [Tournament] {
dataStore.tournaments.filter({ $0.endDate != nil }) dataStore.tournaments.filter({ $0.endDate != nil })
.filter({ federalDataViewModel.isTournamentValidForFilters($0) }) .filter({ federalDataViewModel.isTournamentValidForFilters($0) })
@ -53,6 +58,29 @@ struct ActivityView: View {
} }
} }
@ViewBuilder
private func _pasteView() -> some View {
if UIPasteboard.general.hasStrings {
// Enable string-related control...
if let string = UIPasteboard.general.string {
// use the string here
Section {
Menu("Utiliser le contenu du presse-papier") {
Section {
ForEach(getRunningTournaments()) { tournament in
Button(tournament.tournamentTitle()) {
navigation.path.append(tournament)
tournament.navigationPath = [Screen.inscription]
}
}
} header: {
Text("coller dans")
}
}
}
}
}
}
var body: some View { var body: some View {
@Bindable var navigation = navigation @Bindable var navigation = navigation
NavigationStack(path: $navigation.path) { NavigationStack(path: $navigation.path) {
@ -62,6 +90,7 @@ struct ActivityView: View {
List { List {
switch navigation.agendaDestination! { switch navigation.agendaDestination! {
case .activity: case .activity:
_pasteView()
EventListView(tournaments: runningTournaments, viewStyle: viewStyle, sortAscending: true) EventListView(tournaments: runningTournaments, viewStyle: viewStyle, sortAscending: true)
case .history: case .history:
EventListView(tournaments: endedTournaments, viewStyle: viewStyle, sortAscending: false) EventListView(tournaments: endedTournaments, viewStyle: viewStyle, sortAscending: false)

@ -48,8 +48,12 @@ struct MainView: View {
dataStore.matches.filter({ $0.confirmed && $0.startDate != nil && $0.endDate == nil && $0.courtIndex != nil }) dataStore.matches.filter({ $0.confirmed && $0.startDate != nil && $0.endDate == nil && $0.courtIndex != nil })
} }
private func _isConnected() -> Bool {
Store.main.hasToken() && Store.main.userId != nil
}
var badgeText: Text? { var badgeText: Text? {
Store.main.userId == nil ? Text("!").font(.headline) : nil _isConnected() == false ? Text("!").font(.headline) : nil
} }
var body: some View { var body: some View {

@ -58,6 +58,7 @@ struct InscriptionManagerView: View {
@State private var unsortedTeamsWithoutWO: [TeamRegistration] = [] @State private var unsortedTeamsWithoutWO: [TeamRegistration] = []
@State private var unsortedPlayers: [PlayerRegistration] = [] @State private var unsortedPlayers: [PlayerRegistration] = []
@State private var teamPaste: URL? @State private var teamPaste: URL?
@State private var confirmDuplicate: Bool = false
var messageSentFailed: Binding<Bool> { var messageSentFailed: Binding<Bool> {
Binding { Binding {
@ -197,6 +198,21 @@ struct InscriptionManagerView: View {
} }
} }
.alert("Cette équipe existe déjà", isPresented: $confirmDuplicate) {
Button("Créer l'équipe quand même") {
_createTeam(checkDuplicates: false)
}
Button("Annuler", role: .cancel) {
pasteString = nil
editedTeam = nil
createdPlayers.removeAll()
createdPlayerIds.removeAll()
}
} message: {
Text("Cette équipe existe déjà dans votre liste d'inscription.")
}
.alert("Un problème est survenu", isPresented: messageSentFailed) { .alert("Un problème est survenu", isPresented: messageSentFailed) {
Button("OK") { Button("OK") {
} }
@ -362,7 +378,7 @@ struct InscriptionManagerView: View {
//_prioritizeClubMembersButton() //_prioritizeClubMembersButton()
Button("Bloquer une place") { Button("Bloquer une place") {
_createTeam() _createTeam(checkDuplicates: false)
} }
} }
Divider() Divider()
@ -848,7 +864,36 @@ struct InscriptionManagerView: View {
return currentSelection return currentSelection
} }
private func _createTeam() { private func _currentSelectionIds() -> [String?] {
var currentSelection = [String?]()
createdPlayerIds.compactMap { id in
fetchPlayers.first(where: { id == $0.license })
}.forEach { player in
currentSelection.append(player.license)
}
createdPlayerIds.compactMap { id in
createdPlayers.first(where: { id == $0.id })
}.forEach {
currentSelection.append($0.licenceId)
}
return currentSelection
}
private func _isDuplicate() -> Bool {
let ids : [String?] = _currentSelectionIds()
if unfilteredTeams.anySatisfy({ $0.containsExactlyPlayerLicenses(ids) }) {
return true
}
return false
}
private func _createTeam(checkDuplicates: Bool) {
if checkDuplicates && _isDuplicate() {
confirmDuplicate = true
return
}
let players = _currentSelection() let players = _currentSelection()
let team = tournament.addTeam(players) let team = tournament.addTeam(players)
do { do {
@ -870,8 +915,13 @@ struct InscriptionManagerView: View {
_getTeams() _getTeams()
} }
private func _updateTeam() { private func _updateTeam(checkDuplicates: Bool) {
guard let editedTeam else { return } guard let editedTeam else { return }
if checkDuplicates && _isDuplicate() {
confirmDuplicate = true
return
}
let players = _currentSelection() let players = _currentSelection()
editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory) editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory)
do { do {
@ -915,27 +965,37 @@ struct InscriptionManagerView: View {
Section { Section {
ForEach(createdPlayerIds.sorted(), id: \.self) { id in ForEach(createdPlayerIds.sorted(), id: \.self) { id in
if let p = createdPlayers.first(where: { $0.id == id }) { if let p = createdPlayers.first(where: { $0.id == id }) {
VStack(alignment: .leading, spacing: 0) {
if unsortedPlayers.first(where: { $0.licenceId == p.licenceId }) != nil {
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
}
PlayerView(player: p).tag(p.id) PlayerView(player: p).tag(p.id)
} }
}
if let p = fetchPlayers.first(where: { $0.license == id }) { if let p = fetchPlayers.first(where: { $0.license == id }) {
VStack(alignment: .leading, spacing: 0) {
if unsortedPlayers.first(where: { $0.licenceId == p.license }) != nil {
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
}
ImportedPlayerView(player: p).tag(p.license!) ImportedPlayerView(player: p).tag(p.license!)
} }
} }
} }
}
if editedTeam == nil { if editedTeam == nil {
if createdPlayerIds.isEmpty { if createdPlayerIds.isEmpty {
RowButtonView("Bloquer une place") { RowButtonView("Bloquer une place") {
_createTeam() _createTeam(checkDuplicates: false)
} }
} else { } else {
RowButtonView("Ajouter l'équipe") { RowButtonView("Ajouter l'équipe") {
_createTeam() _createTeam(checkDuplicates: true)
} }
} }
} else { } else {
RowButtonView("Modifier l'équipe") { RowButtonView("Modifier l'équipe") {
_updateTeam() _updateTeam(checkDuplicates: true)
} }
} }

@ -36,7 +36,7 @@ struct TournamentRankView: View {
Text("Classement publié") Text("Classement publié")
} }
RowButtonView("Publier le classement", role: .destructive) { RowButtonView(rankingPublished ? "Re-publier le classement" : "Publier le classement", role: .destructive) {
rankings.keys.sorted().forEach { rank in rankings.keys.sorted().forEach { rank in
if let rankedTeams = rankings[rank] { if let rankedTeams = rankings[rank] {
rankedTeams.forEach { team in rankedTeams.forEach { team in
@ -48,7 +48,7 @@ struct TournamentRankView: View {
_save() _save()
} }
} footer: { } footer: {
FooterButtonView("masquer le classement") { FooterButtonView("masquer le classement", role: .destructive) {
tournament.unsortedTeams().forEach { team in tournament.unsortedTeams().forEach { team in
team.finalRanking = nil team.finalRanking = nil
team.pointsEarned = nil team.pointsEarned = nil

Loading…
Cancel
Save