multistore
Razmig Sarkissian 2 years ago
parent 53756b3c58
commit f06a54cc67
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 20
      PadelClub/Data/AppSettings.swift
  3. 17
      PadelClub/Data/Tournament.swift
  4. 6
      PadelClub/Manager/FileImportManager.swift
  5. 39
      PadelClub/Manager/SourceFileManager.swift
  6. 2
      PadelClub/Manager/Tips.swift
  7. 26
      PadelClub/Views/Navigation/MainView.swift
  8. 54
      PadelClub/Views/Navigation/PadelClubView.swift
  9. 2
      PadelClub/Views/Shared/SelectablePlayerListView.swift
  10. 86
      PadelClub/Views/Tournament/Screen/Components/InscriptionTipsView.swift
  11. 136
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  12. 7
      PadelClub/Views/Tournament/TournamentView.swift

@ -34,7 +34,6 @@
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; }; C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; };
FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; }; FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; };
FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; }; FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; };
FF089EB82BB00ABF00F0AEC7 /* InscriptionTipsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB72BB00ABF00F0AEC7 /* InscriptionTipsView.swift */; };
FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; }; FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; };
FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */; }; FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */; };
FF089EBF2BB0B14600F0AEC7 /* FileImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */; }; FF089EBF2BB0B14600F0AEC7 /* FileImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */; };
@ -159,6 +158,7 @@
FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; }; FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; };
FFD784022B91C1B4000F62A6 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD784012B91C1B4000F62A6 /* WelcomeView.swift */; }; FFD784022B91C1B4000F62A6 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD784012B91C1B4000F62A6 /* WelcomeView.swift */; };
FFD784042B91C280000F62A6 /* EmptyActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD784032B91C280000F62A6 /* EmptyActivityView.swift */; }; FFD784042B91C280000F62A6 /* EmptyActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD784032B91C280000F62A6 /* EmptyActivityView.swift */; };
FFDB1C6D2BB2A02000F1E467 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; };
FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; }; FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; };
FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; }; FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; };
FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; }; FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; };
@ -244,7 +244,6 @@
C4A47DB22B86387500ADC637 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; }; C4A47DB22B86387500ADC637 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; };
FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSexPickerView.swift; sourceTree = "<group>"; }; FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSexPickerView.swift; sourceTree = "<group>"; };
FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRowView.swift; sourceTree = "<group>"; }; FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRowView.swift; sourceTree = "<group>"; };
FF089EB72BB00ABF00F0AEC7 /* InscriptionTipsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InscriptionTipsView.swift; sourceTree = "<group>"; };
FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPopoverView.swift; sourceTree = "<group>"; }; FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPopoverView.swift; sourceTree = "<group>"; };
FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = "<group>"; }; FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = "<group>"; };
FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileImportView.swift; sourceTree = "<group>"; }; FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileImportView.swift; sourceTree = "<group>"; };
@ -369,6 +368,7 @@
FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; }; FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
FFD784012B91C1B4000F62A6 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; }; FFD784012B91C1B4000F62A6 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
FFD784032B91C280000F62A6 /* EmptyActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyActivityView.swift; sourceTree = "<group>"; }; FFD784032B91C280000F62A6 /* EmptyActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyActivityView.swift; sourceTree = "<group>"; };
FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeferredViewModifier.swift; sourceTree = "<group>"; }; FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeferredViewModifier.swift; sourceTree = "<group>"; };
FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalPlayer.swift; sourceTree = "<group>"; }; FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalPlayer.swift; sourceTree = "<group>"; };
FFF8ACD32B92392C008466FA /* SourceFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceFileManager.swift; sourceTree = "<group>"; }; FFF8ACD32B92392C008466FA /* SourceFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceFileManager.swift; sourceTree = "<group>"; };
@ -499,6 +499,7 @@
C4A47D622B6D3D6500ADC637 /* Club.swift */, C4A47D622B6D3D6500ADC637 /* Club.swift */,
FF8F263E2BAD7D5C00650388 /* Event.swift */, FF8F263E2BAD7D5C00650388 /* Event.swift */,
FF1DC5522BAB354A00FD8220 /* MockData.swift */, FF1DC5522BAB354A00FD8220 /* MockData.swift */,
FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */,
FF6EC9012B94799200EA7F5A /* Coredata */, FF6EC9012B94799200EA7F5A /* Coredata */,
FF6EC9022B9479B900EA7F5A /* Federal */, FF6EC9022B9479B900EA7F5A /* Federal */,
); );
@ -789,7 +790,6 @@
FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */, FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */,
FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */, FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */,
FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */, FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */,
FF089EB72BB00ABF00F0AEC7 /* InscriptionTipsView.swift */,
FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */, FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */,
); );
path = Components; path = Components;
@ -1090,6 +1090,7 @@
FF967CF22BAECC0B00A9A3BD /* TeamScore.swift in Sources */, FF967CF22BAECC0B00A9A3BD /* TeamScore.swift in Sources */,
FFD783FD2B91B9ED000F62A6 /* AgendaDestinationPickerView.swift in Sources */, FFD783FD2B91B9ED000F62A6 /* AgendaDestinationPickerView.swift in Sources */,
FF6EC9002B94794700EA7F5A /* PresentationContext.swift in Sources */, FF6EC9002B94794700EA7F5A /* PresentationContext.swift in Sources */,
FFDB1C6D2BB2A02000F1E467 /* AppSettings.swift in Sources */,
FF0EC5202BB16F680056B6D1 /* SwiftParser.swift in Sources */, FF0EC5202BB16F680056B6D1 /* SwiftParser.swift in Sources */,
C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */, C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */,
FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */, FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */,
@ -1156,7 +1157,6 @@
C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */, C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */,
FF967D012BAEF0B400A9A3BD /* MatchSummaryView.swift in Sources */, FF967D012BAEF0B400A9A3BD /* MatchSummaryView.swift in Sources */,
FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */, FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */,
FF089EB82BB00ABF00F0AEC7 /* InscriptionTipsView.swift in Sources */,
FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */, FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */,
FF967D092BAF3D4000A9A3BD /* TeamDetailView.swift in Sources */, FF967D092BAF3D4000A9A3BD /* TeamDetailView.swift in Sources */,
FF8F26382BAD523300650388 /* PadelRule.swift in Sources */, FF8F26382BAD523300650388 /* PadelRule.swift in Sources */,

@ -0,0 +1,20 @@
//
// AppSettings.swift
// PadelClub
//
// Created by Razmig Sarkissian on 26/03/2024.
//
import Foundation
import LeStorage
class AppSettings: MicroStorable {
static var fileName: String { "appsettings.json" }
required init() {
}
// var id: String = Store.randomId()
}

@ -88,6 +88,10 @@ class Tournament : ModelObject, Storable {
case build case build
} }
func hasEnded() -> Bool {
endDate != nil
}
func state() -> Tournament.State { func state() -> Tournament.State {
if groupStageCount > 0 && groupStages().isEmpty == false { if groupStageCount > 0 && groupStages().isEmpty == false {
return .build return .build
@ -95,6 +99,10 @@ class Tournament : ModelObject, Storable {
return .initial return .initial
} }
func inscriptionClosed() -> Bool {
closedRegistrationDate != nil
}
func groupStages() -> [GroupStage] { func groupStages() -> [GroupStage] {
Store.main.filter { $0.tournament == self.id }.sorted(by: \.index) Store.main.filter { $0.tournament == self.id }.sorted(by: \.index)
} }
@ -189,13 +197,10 @@ class Tournament : ModelObject, Storable {
guard let newDate else { return } guard let newDate else { return }
rankSourceDate = newDate rankSourceDate = newDate
let maleDataURLs = SourceFile.allFiles(true).filter { $0.dateFromPath == newDate } let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate)
let femaleDataURLs = SourceFile.allFiles(false).filter { $0.dateFromPath == newDate } let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate)
let lastRankWoman = femaleDataURLs.compactMap { $0.getUnrankedValue() }.first
let lastRankMan = maleDataURLs.compactMap { $0.getUnrankedValue() }.sorted().last
let dataURLs = maleDataURLs + femaleDataURLs let dataURLs = SourceFileManager.shared.allFiles.filter({ $0.dateFromPath == newDate })
let sources = dataURLs.map { CSVParser(url: $0) } let sources = dataURLs.map { CSVParser(url: $0) }
await unsortedPlayers().concurrentForEach { player in await unsortedPlayers().concurrentForEach { player in

@ -15,7 +15,7 @@ class FileImportManager {
return false return false
} }
do { do {
return try SourceFile.allFilesSortedByDate(false).first(where: { return try SourceFileManager.shared.allFilesSortedByDate(false).first(where: {
let fileContent = try String(contentsOf: $0) let fileContent = try String(contentsOf: $0)
return fileContent.contains(";\(license);") return fileContent.contains(";\(license);")
}) != nil }) != nil
@ -30,7 +30,7 @@ class FileImportManager {
return false return false
} }
do { do {
return try SourceFile.allFilesSortedByDate(true).first(where: { return try SourceFileManager.shared.allFilesSortedByDate(true).first(where: {
let fileContent = try String(contentsOf: $0) let fileContent = try String(contentsOf: $0)
return fileContent.contains(";\(license);") return fileContent.contains(";\(license);")
}) != nil }) != nil
@ -253,7 +253,7 @@ class FileImportManager {
} }
func importDataFromFFT() async -> String? { func importDataFromFFT() async -> String? {
if let importingDate = SourceFile.mostRecentDateAvailable { if let importingDate = SourceFileManager.shared.mostRecentDateAvailable {
for source in SourceFile.allCases { for source in SourceFile.allCases {
for fileURL in source.currentURLs { for fileURL in source.currentURLs {
let p = readCSV(inputFile: fileURL) let p = readCSV(inputFile: fileURL)

@ -10,8 +10,17 @@ import Foundation
class SourceFileManager { class SourceFileManager {
static let shared = SourceFileManager() static let shared = SourceFileManager()
var lastDataSource: String? {
UserDefaults.standard.string(forKey: "lastDataSource")
}
func lastDataSourceDate() -> Date? {
guard let lastDataSource else { return nil }
return URL.importDateFormatter.date(from: lastDataSource)
}
func fetchData() async { func fetchData() async {
if let mostRecent = SourceFile.mostRecentDateAvailable, let current = Calendar.current.date(byAdding: .month, value: 1, to: mostRecent), current > mostRecent { if let mostRecent = mostRecentDateAvailable, let current = Calendar.current.date(byAdding: .month, value: 1, to: mostRecent), current > mostRecent {
await fetchData(fromDate: current) await fetchData(fromDate: current)
} else { } else {
await fetchData(fromDate: Date()) await fetchData(fromDate: Date())
@ -50,7 +59,7 @@ class SourceFileManager {
} catch { } catch {
print("downloadRankingData", error) print("downloadRankingData", error)
if SourceFile.mostRecentDateAvailable == nil { if mostRecentDateAvailable == nil {
if let previousDate = Calendar.current.date(byAdding: .month, value: -1, to: current) { if let previousDate = Calendar.current.date(byAdding: .month, value: -1, to: current) {
await fetchData(fromDate: previousDate) await fetchData(fromDate: previousDate)
} }
@ -65,7 +74,7 @@ class SourceFileManager {
URL.importDateFormatter.date(from: $0) URL.importDateFormatter.date(from: $0)
} }
.filter { date in .filter { date in
SourceFile.allFiles.contains(where: { $0.dateFromPath == date }) == false allFiles.contains(where: { $0.dateFromPath == date }) == false
} }
await dates.concurrentForEach { date in await dates.concurrentForEach { date in
@ -96,17 +105,18 @@ class SourceFileManager {
} }
return months return months
} }
}
enum SourceFile: String, CaseIterable { func getUnrankValue(forMale: Bool, rankSourceDate: Date?) -> Int? {
case dames = "DAMES" let _rankSourceDate = rankSourceDate ?? mostRecentDateAvailable
case messieurs = "MESSIEURS" let urls = allFiles(forMale).filter { $0.dateFromPath == _rankSourceDate }
return urls.compactMap { $0.getUnrankedValue() }.sorted().last
}
static var mostRecentDateAvailable: Date? { var mostRecentDateAvailable: Date? {
allFiles(false).first?.dateFromPath allFiles(false).first?.dateFromPath
} }
static func removeAllFilesFromServer() { func removeAllFilesFromServer() {
let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let allFiles = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil) let allFiles = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil)
allFiles.filter { $0.pathExtension == "csv" }.forEach { url in allFiles.filter { $0.pathExtension == "csv" }.forEach { url in
@ -114,7 +124,7 @@ enum SourceFile: String, CaseIterable {
} }
} }
static var allFiles: [URL] { var allFiles: [URL] {
let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let allFiles = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil).filter({ url in let allFiles = try! FileManager.default.contentsOfDirectory(at: docDir, includingPropertiesForKeys: nil).filter({ url in
url.pathExtension == "csv" url.pathExtension == "csv"
@ -123,15 +133,20 @@ enum SourceFile: String, CaseIterable {
return (allFiles + (Bundle.main.urls(forResourcesWithExtension: "csv", subdirectory: nil) ?? [])).sorted(by: \.dateFromPath).reversed() return (allFiles + (Bundle.main.urls(forResourcesWithExtension: "csv", subdirectory: nil) ?? [])).sorted(by: \.dateFromPath).reversed()
} }
static func allFiles(_ isManPlayer: Bool) -> [URL] { func allFiles(_ isManPlayer: Bool) -> [URL] {
allFiles.filter({ url in allFiles.filter({ url in
url.path().contains(isManPlayer ? SourceFile.messieurs.rawValue : SourceFile.dames.rawValue) url.path().contains(isManPlayer ? SourceFile.messieurs.rawValue : SourceFile.dames.rawValue)
}) })
} }
static func allFilesSortedByDate(_ isManPlayer: Bool) -> [URL] { func allFilesSortedByDate(_ isManPlayer: Bool) -> [URL] {
return allFiles(isManPlayer) return allFiles(isManPlayer)
} }
}
enum SourceFile: String, CaseIterable {
case dames = "DAMES"
case messieurs = "MESSIEURS"
var filesFromServer: [URL] { var filesFromServer: [URL] {
let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let docDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)

@ -157,7 +157,7 @@ struct InscriptionManagerCreateInputTip: Tip {
var message: Text? { var message: Text? {
Text("Si le joueur est introuvable, cela indique qu'il n'a jamais fait de compétition, rajoutez-le rapidement manuellement. Padel Club calcul le rang du non-classé ce mois-ci automatiquement.") Text("Si le joueur est introuvable, cela indique qu'il n'a jamais fait de compétition, rajoutez-le rapidement manuellement. Padel Club calcul le rang du non-classé de ce mois-ci automatiquement.")
} }
var image: Image? { var image: Image? {

@ -25,16 +25,6 @@ struct MainView: View {
animation: .default) animation: .default)
private var players: FetchedResults<ImportedPlayer> private var players: FetchedResults<ImportedPlayer>
var _mostRecentDateAvailable: Date? {
SourceFile.mostRecentDateAvailable
}
var _lastDataSourceDate: Date? {
guard let lastDataSource else { return nil }
return URL.importDateFormatter.date(from: lastDataSource)
}
var body: some View { var body: some View {
TabView { TabView {
if dataStore.tournaments.isEmpty { if dataStore.tournaments.isEmpty {
@ -91,18 +81,18 @@ struct MainView: View {
if importingFiles { if importingFiles {
HStack(spacing: 20) { HStack(spacing: 20) {
ProgressView() ProgressView()
if let _mostRecentDateAvailable { if let mostRecentDateAvailable = SourceFileManager.shared.mostRecentDateAvailable {
if _mostRecentDateAvailable > _lastDataSourceDate ?? .distantPast { if mostRecentDateAvailable > SourceFileManager.shared.lastDataSourceDate() ?? .distantPast {
Text("import " + _mostRecentDateAvailable.monthYearFormatted) Text("import " + mostRecentDateAvailable.monthYearFormatted)
} }
} }
} }
} else if let _mostRecentDateAvailable { } else if let mostRecentDateAvailable = SourceFileManager.shared.mostRecentDateAvailable {
if _mostRecentDateAvailable > _lastDataSourceDate ?? .distantPast { if mostRecentDateAvailable > SourceFileManager.shared.lastDataSourceDate() ?? .distantPast {
Label(_mostRecentDateAvailable.monthYearFormatted + " disponible", systemImage: "exclamationmark.triangle") Label(mostRecentDateAvailable.monthYearFormatted + " disponible", systemImage: "exclamationmark.triangle")
.labelStyle(.titleAndIcon) .labelStyle(.titleAndIcon)
} else { } else {
Label(_mostRecentDateAvailable.monthYearFormatted, systemImage: "checkmark") Label(mostRecentDateAvailable.monthYearFormatted, systemImage: "checkmark")
.labelStyle(.titleAndIcon) .labelStyle(.titleAndIcon)
} }
} }
@ -118,7 +108,7 @@ struct MainView: View {
checkingFilesAttempt += 1 checkingFilesAttempt += 1
checkingFiles = false checkingFiles = false
if let _mostRecentDateAvailable, _mostRecentDateAvailable > _lastDataSourceDate ?? .distantPast { if let mostRecentDateAvailable = SourceFileManager.shared.mostRecentDateAvailable, mostRecentDateAvailable > SourceFileManager.shared.lastDataSourceDate() ?? .distantPast {
_startImporting() _startImporting()
} }
} }

@ -23,7 +23,7 @@ struct PadelClubView: View {
var _mostRecentDateAvailable: Date? { var _mostRecentDateAvailable: Date? {
SourceFile.mostRecentDateAvailable SourceFileManager.shared.mostRecentDateAvailable
} }
var _lastDataSourceDate: Date? { var _lastDataSourceDate: Date? {
@ -93,7 +93,7 @@ struct PadelClubView: View {
print("check files on internet") print("check files on internet")
print("check if any files on internet are more recent than here") print("check if any files on internet are more recent than here")
checkingFiles = true checkingFiles = true
await fetchData() await SourceFileManager.shared.fetchData()
checkingFilesAttempt += 1 checkingFilesAttempt += 1
checkingFiles = false checkingFiles = false
} }
@ -105,56 +105,6 @@ struct PadelClubView: View {
importingFiles = false importingFiles = false
} }
} }
private func fetchData() async {
if let mostRecent = SourceFile.mostRecentDateAvailable, let current = Calendar.current.date(byAdding: .month, value: 1, to: mostRecent), current > mostRecent {
await fetchData(fromDate: current)
} else {
await fetchData(fromDate: Date())
}
}
private func _removeAllData(fromDate current: Date) {
let lastStringDate = URL.importDateFormatter.string(from: current)
let files = ["MESSIEURS", "MESSIEURS-2", "MESSIEURS-3", "MESSIEURS-4", "DAMES"]
files.forEach { fileName in
NetworkManager.shared.removeRankingData(lastDateString: lastStringDate, fileName: fileName)
}
}
private func fetchData(fromDate current: Date) async {
let lastStringDate = URL.importDateFormatter.string(from: current)
let files = ["MESSIEURS", "MESSIEURS-2", "MESSIEURS-3", "MESSIEURS-4", "DAMES"]
do {
try await withThrowingTaskGroup(of: Void.self) { group in // Mark 1
for file in files {
group.addTask {
try await NetworkManager.shared.downloadRankingData(lastDateString: lastStringDate, fileName: file)
}
}
try await group.waitForAll()
}
if current < Date() {
if let nextCurrent = Calendar.current.date(byAdding: .month, value: 1, to: current) {
await fetchData(fromDate: nextCurrent)
}
}
} catch {
print("downloadRankingData", error)
if _mostRecentDateAvailable == nil {
if let previousDate = Calendar.current.date(byAdding: .month, value: -1, to: current) {
await fetchData(fromDate: previousDate)
}
}
}
}
} }
#Preview { #Preview {

@ -106,7 +106,7 @@ struct SelectablePlayerListView: View {
} }
.id(importingFiles) .id(importingFiles)
.overlay { .overlay {
if let importedFile = SourceFile.mostRecentDateAvailable, importingFiles { if let importedFile = SourceFileManager.shared.mostRecentDateAvailable, importingFiles {
ContentUnavailableView("Importation en cours", systemImage: "square.and.arrow.down", description: Text("Padel Club récupère les données de \(importedFile.monthYearFormatted)")) ContentUnavailableView("Importation en cours", systemImage: "square.and.arrow.down", description: Text("Padel Club récupère les données de \(importedFile.monthYearFormatted)"))
} }

@ -1,86 +0,0 @@
//
// InscriptionTipsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 24/03/2024.
//
import SwiftUI
import TipKit
struct InscriptionTipsView: View {
@Environment(Tournament.self) private var tournament: Tournament
var body: some View {
List {
Section {
let fileTip = InscriptionManagerFileInputTip()
TipView(fileTip) { action in
if action.id == "website" {
} else if action.id == "add-team-file" {
}
}
.tipStyle(tint: nil)
}
Section {
let pasteTip = InscriptionManagerPasteInputTip()
TipView(pasteTip) { action in
if let paste = UIPasteboard.general.string {
//self.pasteField = paste
}
}
.tipStyle(tint: nil)
}
Section {
let searchTip = InscriptionManagerSearchInputTip()
TipView(searchTip) { action in
//presentPlayerCreation = true
}
.tipStyle(tint: nil)
}
Section {
let createTip = InscriptionManagerCreateInputTip()
TipView(createTip) { action in
//presentPlayerSelection = true
}
.tipStyle(tint: nil)
}
Section {
ContentUnavailableView("Aucune équipe", systemImage: "person.2.slash", description: Text("Vous n'avez encore aucune équipe dans votre liste d'attente."))
}
// if let mostRecentDate, let currentRankSourceDate, currentRankSourceDate < mostRecentDate, tournament.isOver == false {
//
// if #available(iOS 17.0, *) {
// Section {
// let tip = InscriptionManagerRankUpdateTip()
// TipView(tip) { action in
// self.currentRankSourceDate = mostRecentDate
// }
// .tipStyle(tint: nil)
// }
// }
//
// rankingDateSourcePickerView(showDateInLabel: false)
// } else if tournament.currentRankSourceDate == nil {
// rankingDateSourcePickerView(showDateInLabel: false)
// }
//
}
}
}
#Preview {
InscriptionTipsView()
.environment(Tournament.mock())
}

@ -32,6 +32,12 @@ struct InscriptionManagerView: View {
let slideToDeleteTip = SlideToDeleteTip() let slideToDeleteTip = SlideToDeleteTip()
let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip() let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip()
let fileTip = InscriptionManagerFileInputTip()
let pasteTip = InscriptionManagerPasteInputTip()
let searchTip = InscriptionManagerSearchInputTip()
let createTip = InscriptionManagerCreateInputTip()
let rankUpdateTip = InscriptionManagerRankUpdateTip()
let categoryOption: PlayerFilterOption let categoryOption: PlayerFilterOption
let filterable: Bool let filterable: Bool
@ -222,7 +228,7 @@ struct InscriptionManagerView: View {
private func _teamRegisteredView() -> some View { private func _teamRegisteredView() -> some View {
List { List {
Section { Section {
rankingDateSourcePickerView(showDateInLabel: true) _rankHandlerView()
let duplicates = tournament.duplicates() let duplicates = tournament.duplicates()
DisclosureGroup { DisclosureGroup {
@ -271,7 +277,7 @@ struct InscriptionManagerView: View {
RowButtonView(title: "Créer une équipe") { RowButtonView(title: "Créer une équipe") {
Task { Task {
await MainActor.run() { await MainActor.run() {
// fetchPlayers.nsPredicate = _pastePredicate(pasteField: searchField, mostRecentDate: tournament.rankSourceDate) fetchPlayers.nsPredicate = _pastePredicate(pasteField: searchField, mostRecentDate: tournament.rankSourceDate)
pasteString = searchField pasteString = searchField
} }
} }
@ -327,7 +333,7 @@ struct InscriptionManagerView: View {
if testCreatedPlayers.isEmpty == false || pasteString != nil || editedTeam != nil { if testCreatedPlayers.isEmpty == false || pasteString != nil || editedTeam != nil {
_buildingTeamView() _buildingTeamView()
} else if tournament.unsortedTeams().isEmpty { } else if tournament.unsortedTeams().isEmpty {
InscriptionTipsView() _inscriptionTipsView()
} else { } else {
_teamRegisteredView() _teamRegisteredView()
} }
@ -377,15 +383,63 @@ struct InscriptionManagerView: View {
} }
} }
ToolbarItem(placement: .topBarTrailing) { if editedTeam == nil {
ToolbarItem(placement: .navigationBarTrailing) {
Menu { Menu {
if tournament.inscriptionClosed() == false {
Menu {
//sortingTypePickerView
} label: {
Text("Méthode de sélection")
Text(tournament.teamSortingType.localizedLabel())
}
Divider()
rankingDateSourcePickerView(showDateInLabel: true)
if tournament.teamSortingType == .inscriptionDate {
Divider()
//prioritizeClubMembersButton
}
Divider()
Button {
let count = tournament.unsortedTeams().count
if tournament.teamCount > count {
tournament.teamCount = count
}
tournament.closedRegistrationDate = Date()
_save()
} label: {
Label("Clôturer", systemImage: "lock")
}
Divider()
// ShareLink(item: tournament.pasteDataForImporting) {
// Text("Exporter les paires")
// }
Button { Button {
presentImportView = true presentImportView = true
} label: { } label: {
Label("Importer", systemImage: "square.and.arrow.down") Label("Importer beach-padel", systemImage: "square.and.arrow.down")
}
if let url = URL(string: "beach-padel.app.fft.fr") {
Link(destination: url) {
Label("beach-padel.app.fft.fr", systemImage: "safari")
} }
}
} else {
Button {
tournament.closedRegistrationDate = nil
_save()
} label: { } label: {
Label("Ré-ouvrir", systemImage: "lock.open")
}
}
} label: {
if tournament.inscriptionClosed() == false {
LabelOptions() LabelOptions()
} else {
Label("Clôturer", systemImage: "lock")
}
}
} }
} }
} }
@ -415,7 +469,7 @@ struct InscriptionManagerView: View {
guard let first = strings.first else { return } guard let first = strings.first else { return }
Task { Task {
await MainActor.run() { await MainActor.run() {
// fetchPlayers.nsPredicate = _pastePredicate(pasteField: first, mostRecentDate: tournament.rankSourceDate) fetchPlayers.nsPredicate = _pastePredicate(pasteField: first, mostRecentDate: tournament.rankSourceDate)
pasteString = first pasteString = first
} }
} }
@ -450,13 +504,13 @@ struct InscriptionManagerView: View {
Text("inconnu").tag(nil as Date?) Text("inconnu").tag(nil as Date?)
} }
let dates = Array(Set(SourceFile.allFilesSortedByDate(tournament.tournamentCategory.rankingDataSourceMale).map({ $0.dateFromPath }))).sorted().reversed() let dates = Array(Set(SourceFileManager.shared.allFilesSortedByDate(tournament.tournamentCategory.rankingDataSourceMale).map({ $0.dateFromPath }))).sorted().reversed()
ForEach(dates, id: \.self) { date in ForEach(dates, id: \.self) { date in
Text(date.monthYearFormatted).tag(date as Date?) Text(date.monthYearFormatted).tag(date as Date?)
} }
} label: { } label: {
Text("Classement mensuel utilisé") Text("Classement utilisé")
if showDateInLabel { if showDateInLabel {
if let currentRankSourceDate { if let currentRankSourceDate {
Text(currentRankSourceDate.monthYearFormatted) Text(currentRankSourceDate.monthYearFormatted)
@ -492,6 +546,72 @@ struct InscriptionManagerView: View {
return .all return .all
} }
} }
@ViewBuilder
func _inscriptionTipsView() -> some View {
List {
Section {
TipView(fileTip) { action in
if action.id == "website" {
} else if action.id == "add-team-file" {
}
}
.tipStyle(tint: nil)
}
Section {
TipView(pasteTip) { action in
if let paste = UIPasteboard.general.string {
self.pasteString = paste
}
}
.tipStyle(tint: nil)
}
Section {
TipView(searchTip) { action in
presentPlayerCreation = true
}
.tipStyle(tint: nil)
}
Section {
TipView(createTip) { action in
presentPlayerSearch = true
}
.tipStyle(tint: nil)
}
Section {
ContentUnavailableView("Aucune équipe", systemImage: "person.2.slash", description: Text("Vous n'avez encore aucune équipe dans votre liste d'attente."))
}
_rankHandlerView()
}
}
@ViewBuilder
func _rankHandlerView() -> some View {
if let mostRecentDate = SourceFileManager.shared.lastDataSourceDate(), let currentRankSourceDate, currentRankSourceDate < mostRecentDate, tournament.hasEnded() == false {
Section {
TipView(rankUpdateTip) { action in
self.currentRankSourceDate = mostRecentDate
}
.tipStyle(tint: nil)
}
rankingDateSourcePickerView(showDateInLabel: false)
} else if tournament.rankSourceDate == nil {
rankingDateSourcePickerView(showDateInLabel: false)
}
}
func _save() {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
} }
#Preview { #Preview {

@ -23,14 +23,11 @@ struct TournamentView: View {
if tournament.missingUnrankedValue() { if tournament.missingUnrankedValue() {
Button("update NC") { Button("update NC") {
Task { tournament.femaleUnrankedValue = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: tournament.rankSourceDate)
tournament.maleUnrankedValue = await FederalPlayer.lastRank(mostRecentDateAvailable: _lastDataSourceDate, man: true) tournament.maleUnrankedValue = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: tournament.rankSourceDate)
tournament.femaleUnrankedValue = await FederalPlayer.lastRank(mostRecentDateAvailable: _lastDataSourceDate, man: false)
try? dataStore.tournaments.addOrUpdate(instance: tournament) try? dataStore.tournaments.addOrUpdate(instance: tournament)
} }
} }
}
NavigationLink(value: Screen.inscription) { NavigationLink(value: Screen.inscription) {

Loading…
Cancel
Save