Merge branch 'main'

sync_v2
Raz 8 months ago
commit acb0be6ec3
  1. 16
      PadelClub.xcodeproj/project.pbxproj
  2. 2
      PadelClub/Data/DataStore.swift
  3. 2
      PadelClub/Data/README.md
  4. 30
      PadelClub/Data/Tournament.swift
  5. 53
      PadelClub/PadelClubApp.swift
  6. 40
      PadelClub/Utils/Patcher.swift
  7. 16
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  8. 14
      PadelClub/Views/Player/PlayerDetailView.swift

@ -3632,7 +3632,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
@ -3658,7 +3658,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2; MARKETING_VERSION = 1.2.1;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -3678,7 +3678,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -3703,7 +3703,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2; MARKETING_VERSION = 1.2.1;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -3795,7 +3795,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
@ -3821,7 +3821,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.1.1; MARKETING_VERSION = 1.2.1;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -3841,7 +3841,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -3866,7 +3866,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.1.1; MARKETING_VERSION = 1.2.1;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

@ -134,7 +134,7 @@ class DataStore: ObservableObject {
} }
if Store.main.fileCollectionsAllLoaded() { if Store.main.fileCollectionsAllLoaded() {
Patcher.applyAllWhenApplicable() AutomaticPatcher.applyAllWhenApplicable()
} }
} }

@ -3,6 +3,7 @@ Dans Swift:
- Dans Data > Gen > créer un nouveau fichier json pour la classe - Dans Data > Gen > créer un nouveau fichier json pour la classe
- Le paramètre "foreignKey" permet de générer une méthode qui récupère l'objet parent. Ajouter une étoile permet d'indiquer que l'on cherche l'objet dans le Store de l'objet et non le Store.main. - Le paramètre "foreignKey" permet de générer une méthode qui récupère l'objet parent. Ajouter une étoile permet d'indiquer que l'on cherche l'objet dans le Store de l'objet et non le Store.main.
- Pour générer les fichiers, on se place dans le répertoire de generator.py et on lance la commande : python generator.py -i . -o . - Pour générer les fichiers, on se place dans le répertoire de generator.py et on lance la commande : python generator.py -i . -o .
- il faut avoir inflect: pip install inflect
Dans Django: Dans Django:
- Les modèles de base doivent étendre BaseModel - Les modèles de base doivent étendre BaseModel
@ -29,5 +30,6 @@ Dans Django:
- L'ajouter aussi dans admin.py si nécéssaire - L'ajouter aussi dans admin.py si nécéssaire
- Faire le *makemigrations* + *migrate* - Faire le *makemigrations* + *migrate*
Note: Les nouvelles classes de model doivent étendre BaseModel ou SideStoreModel
Enfin, revenir dans Xcode, ouvrir ServerDataTests et lancer le test mis à jour Enfin, revenir dans Xcode, ouvrir ServerDataTests et lancer le test mis à jour

@ -19,7 +19,7 @@ final class Tournament: BaseTournament {
func shouldRefreshTeams() -> Bool { func shouldRefreshTeams() -> Bool {
guard let lastTeamRefresh else { return true } guard let lastTeamRefresh else { return true }
return lastTeamRefresh.timeIntervalSinceNow < -60 return lastTeamRefresh.timeIntervalSinceNow < -600
} }
@ObservationIgnored @ObservationIgnored
@ -494,9 +494,20 @@ defer {
}) { }) {
return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: chunk) return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: chunk)
} else if fullLeftSeeds.count > 1, targetSpots > 1, fullLeftSeeds.count >= targetSpots { } else if fullLeftSeeds.count > 1, targetSpots > 1, fullLeftSeeds.count >= targetSpots {
let seeds = seeds() let currentSeeds = seeds()
if let firstIndex = seeds.firstIndex(where: { $0.isSeedable() }) { if let firstIndex = currentSeeds.firstIndex(where: { $0.isSeedable() }) {
return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: SeedInterval(first: firstIndex + 1, last: firstIndex + targetSpots))
if firstIndex < seededTeamsCount {
return nil
} else {
let sg = SeedInterval(first: seededTeamsCount + 1, last: seededTeamsCount + targetSpots)
let futureAvailableSeeds = self.seeds(inSeedGroup: sg)
if futureAvailableSeeds.count == targetSpots {
return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: sg)
} else {
return nil
}
}
} }
} }
} }
@ -2325,6 +2336,10 @@ defer {
// MARK: - Status // MARK: - Status
func shouldTournamentBeOver() async -> Bool { func shouldTournamentBeOver() async -> Bool {
return false
if tournamentStore?.store.fileCollectionsAllLoaded() == false {
return false
}
#if _DEBUGING_TIME //DEBUGING TIME #if _DEBUGING_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
@ -2366,26 +2381,21 @@ defer {
func refreshTeamList() async { func refreshTeamList() async {
guard StoreCenter.main.isAuthenticated else { return } guard StoreCenter.main.isAuthenticated else { return }
guard tournamentStore?.store.fileCollectionsAllLoaded() == true else { return }
guard shouldRefreshTeams(), refreshInProgress == false, enableOnlineRegistration, hasEnded() == false else { return } guard shouldRefreshTeams(), refreshInProgress == false, enableOnlineRegistration, hasEnded() == false else { return }
await MainActor.run {
refreshInProgress = true refreshInProgress = true
}
do { do {
try await self.tournamentStore?.playerRegistrations.loadDataFromServerIfAllowed(clear: true) try await self.tournamentStore?.playerRegistrations.loadDataFromServerIfAllowed(clear: true)
try await self.tournamentStore?.teamScores.loadDataFromServerIfAllowed(clear: true) try await self.tournamentStore?.teamScores.loadDataFromServerIfAllowed(clear: true)
try await self.tournamentStore?.teamRegistrations.loadDataFromServerIfAllowed(clear: true) try await self.tournamentStore?.teamRegistrations.loadDataFromServerIfAllowed(clear: true)
await MainActor.run {
refreshInProgress = false refreshInProgress = false
lastTeamRefresh = Date() lastTeamRefresh = Date()
}
} catch { } catch {
Logger.error(error) Logger.error(error)
await MainActor.run {
refreshInProgress = false refreshInProgress = false
lastTeamRefresh = Date() lastTeamRefresh = Date()
} }
} }
}
// MARK: - // MARK: -

@ -17,9 +17,10 @@ struct PadelClubApp: App {
@StateObject var dataStore = DataStore.shared @StateObject var dataStore = DataStore.shared
@State private var registrationError: RegistrationError? = nil @State private var registrationError: RegistrationError? = nil
@State private var importObserverViewModel = ImportObserver() @State private var importObserverViewModel = ImportObserver()
@State private var showDisconnectionAlert: Bool = false
@Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.horizontalSizeClass) var horizontalSizeClass
@State var blockApp = false @State var requiredVersion: String? = nil
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@ -65,8 +66,8 @@ struct PadelClubApp: App {
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
if self.blockApp { if let requiredVersion {
DownloadNewVersionView() DownloadNewVersionView(version: requiredVersion)
} else { } else {
MainView() MainView()
.environment(\.horizontalSizeClass, .compact) .environment(\.horizontalSizeClass, .compact)
@ -91,6 +92,10 @@ struct PadelClubApp: App {
.accentColor(.master) .accentColor(.master)
.onAppear { .onAppear {
self._checkVersion() self._checkVersion()
if ManualPatcher.patchIfPossible(.disconnect) == true {
self.showDisconnectionAlert = true
}
#if DEBUG #if DEBUG
print("Running in Debug mode") print("Running in Debug mode")
#elseif TESTFLIGHT #elseif TESTFLIGHT
@ -104,6 +109,9 @@ struct PadelClubApp: App {
self._onAppear() self._onAppear()
print(PersistenceController.getModelVersion()) print(PersistenceController.getModelVersion())
} }
.alert(isPresented: self.$showDisconnectionAlert, content: {
Alert(title: Text("Vous avez été déconnecté. Veuillez vous reconnecter pour récupérer vos données."))
})
.task { .task {
// try? Tips.resetDatastore() // try? Tips.resetDatastore()
@ -125,7 +133,9 @@ struct PadelClubApp: App {
Logger.log(">>> REQUIRED VERSION = \(requiredVersion)") Logger.log(">>> REQUIRED VERSION = \(requiredVersion)")
if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
await MainActor.run { await MainActor.run {
self.blockApp = VersionComparator.compare(cleanedRequired, currentVersion) == 1 if VersionComparator.compare(cleanedRequired, currentVersion) == 1 {
self.requiredVersion = cleanedRequired
}
} }
} }
} }
@ -211,22 +221,40 @@ struct PadelClubApp: App {
struct DownloadNewVersionView: View { struct DownloadNewVersionView: View {
var version: String
var body: some View { var body: some View {
VStack { VStack {
// AngledStripesBackground() // AngledStripesBackground()
Spacer() Spacer()
VStack(spacing: 20.0) {
Text("Veuillez télécharger la nouvelle version de Padel Club pour continuer à vous servir de l'app !") Text("Veuillez télécharger la nouvelle version de Padel Club pour continuer à vous servir de l'app !")
.padding(32.0) .fontWeight(.semibold)
.background(.logoYellow) .foregroundStyle(.white)
.padding()
.background(.logoRed)
.clipShape(.buttonBorder)
// .padding(32.0)
VStack(alignment: .center, spacing: 0.0
) {
Text("Version \(self.version)")
.fontWeight(.bold)
Image(systemName: "square.and.arrow.down").font(.title)
}.padding().background(.logoYellow)
.clipShape(.buttonBorder) .clipShape(.buttonBorder)
}.frame(maxWidth: .infinity)
.foregroundStyle(.logoBackground) .foregroundStyle(.logoBackground)
.fontWeight(.medium) .fontWeight(.medium)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.padding(.horizontal, 64.0) .padding(.horizontal, 36.0)
Image("logo").padding(.vertical, 50.0) Image("logo").padding(.vertical, 50.0)
Spacer() Spacer()
}.background(.logoBackground) }
.background(.logoBackground)
.onTapGesture { .onTapGesture {
UIApplication.shared.open(URLs.appStore.url) UIApplication.shared.open(URLs.appStore.url)
} }
@ -235,3 +263,12 @@ struct DownloadNewVersionView: View {
} }
struct DisconnectionAlertView: View {
var body: some View {
Text("Vous avez été déconnecté. Veuillez vous reconnecter pour récupérer vos données.").multilineTextAlignment(.center).padding()
}
}
#Preview {
DownloadNewVersionView(version: "1.2")
}

@ -8,6 +8,44 @@
import Foundation import Foundation
import LeStorage import LeStorage
enum ManualPatch: String {
case disconnect
var id: String {
return "padelclub.app.manual.patch.\(self.rawValue)"
}
}
class ManualPatcher {
static func patchIfPossible(_ patch: ManualPatch) -> Bool {
if UserDefaults.standard.value(forKey: patch.id) == nil {
do {
Logger.log(">>> Patches \(patch.rawValue)...")
let result = try self._applyPatch(patch)
UserDefaults.standard.setValue(true, forKey: patch.id)
return result
} catch {
Logger.error(error)
}
}
return false
}
fileprivate static func _applyPatch(_ patch: ManualPatch) throws -> Bool {
switch patch {
case .disconnect:
let rawToken = try? StoreCenter.main.rawTokenShouldNotBeUsed()
if StoreCenter.main.userName != nil || StoreCenter.main.userId != nil || rawToken != nil {
DataStore.shared.disconnect()
return true
} else {
return false
}
}
}
}
enum PatchError: Error { enum PatchError: Error {
case patchError(message: String) case patchError(message: String)
} }
@ -21,7 +59,7 @@ enum Patch: String, CaseIterable {
} }
} }
class Patcher { class AutomaticPatcher {
static func applyAllWhenApplicable() { static func applyAllWhenApplicable() {
for patch in Patch.allCases { for patch in Patch.allCases {

@ -121,14 +121,14 @@ struct UmpireView: View {
Text("Il s'agit des clubs qui sont utilisés pour récupérer les tournois tenup.") Text("Il s'agit des clubs qui sont utilisés pour récupérer les tournois tenup.")
} }
Section { // Section {
NavigationLink { // NavigationLink {
UmpireStatisticView() // UmpireStatisticView()
} label: { // } label: {
Text("Statistiques de participations") // Text("Statistiques de participations")
} // }
} // }
//
Section { Section {
@Bindable var user = dataStore.user @Bindable var user = dataStore.user

@ -220,13 +220,13 @@ struct PlayerDetailView: View {
} }
} }
Section { // Section {
NavigationLink { // NavigationLink {
PlayerStatisticView(player: player) // PlayerStatisticView(player: player)
} label: { // } label: {
Text("Statistiques de participations") // Text("Statistiques de participations")
} // }
} // }
} }
.onChange(of: [player.hasArrived, player.captain, player.coach]) { .onChange(of: [player.hasArrived, player.captain, player.coach]) {
_save() _save()

Loading…
Cancel
Save