multistore
Razmig Sarkissian 1 year ago
parent b961b8919b
commit 27fa26f86e
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 8
      PadelClub/Data/Tournament.swift
  3. 2
      PadelClub/Utils/Tips.swift
  4. 5
      PadelClub/Utils/URLs.swift
  5. 18
      PadelClub/Views/Tournament/Screen/BroadcastView.swift
  6. 35
      PadelClub/Views/Tournament/Shared/TournamentBroadcastRowView.swift
  7. 41
      PadelClub/Views/Tournament/TournamentBuildView.swift
  8. 20
      PadelClub/Views/Tournament/TournamentInitView.swift
  9. 28
      PadelClub/Views/Tournament/TournamentInscriptionView.swift
  10. 14
      PadelClub/Views/Tournament/TournamentView.swift
  11. 82
      PadelClub/Views/User/LoginView.swift

@ -202,6 +202,7 @@
FFA1B1292BB71773006CE248 /* PadelClubButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA1B1282BB71773006CE248 /* PadelClubButtonView.swift */; }; FFA1B1292BB71773006CE248 /* PadelClubButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA1B1282BB71773006CE248 /* PadelClubButtonView.swift */; };
FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; }; FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; };
FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; }; FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; };
FFB1C98B2C10255100B154A7 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; };
FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; };
FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; };
FFBF065C2BBD2657009D6715 /* GroupStageTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */; }; FFBF065C2BBD2657009D6715 /* GroupStageTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */; };
@ -525,6 +526,7 @@
FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileImportManager.swift; sourceTree = "<group>"; }; FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileImportManager.swift; sourceTree = "<group>"; };
FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudConvert.swift; sourceTree = "<group>"; }; FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudConvert.swift; sourceTree = "<group>"; };
FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = "<group>"; };
FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = "<group>"; }; FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = "<group>"; };
FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = "<group>"; }; FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = "<group>"; };
FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageTeamView.swift; sourceTree = "<group>"; }; FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageTeamView.swift; sourceTree = "<group>"; };
@ -952,6 +954,7 @@
FF7091672B90F79F00AB08DA /* TournamentCellView.swift */, FF7091672B90F79F00AB08DA /* TournamentCellView.swift */,
FF7091692B90F95E00AB08DA /* DateBoxView.swift */, FF7091692B90F95E00AB08DA /* DateBoxView.swift */,
FFA1B1282BB71773006CE248 /* PadelClubButtonView.swift */, FFA1B1282BB71773006CE248 /* PadelClubButtonView.swift */,
FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */,
); );
path = Shared; path = Shared;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1483,6 +1486,7 @@
FF025AE12BD0EB9000A86CF8 /* TournamentClubSettingsView.swift in Sources */, FF025AE12BD0EB9000A86CF8 /* TournamentClubSettingsView.swift in Sources */,
FFBF065C2BBD2657009D6715 /* GroupStageTeamView.swift in Sources */, FFBF065C2BBD2657009D6715 /* GroupStageTeamView.swift in Sources */,
FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */, FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */,
FFB1C98B2C10255100B154A7 /* TournamentBroadcastRowView.swift in Sources */,
FF025ADF2BD0CE0A00A86CF8 /* TeamWeightView.swift in Sources */, FF025ADF2BD0CE0A00A86CF8 /* TeamWeightView.swift in Sources */,
FF9268012BCE94920080F940 /* SeedsCallingView.swift in Sources */, FF9268012BCE94920080F940 /* SeedsCallingView.swift in Sources */,
FF9268092BCEDC2C0080F940 /* CallView.swift in Sources */, FF9268092BCEDC2C0080F940 /* CallView.swift in Sources */,
@ -1855,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 = 39; CURRENT_PROJECT_VERSION = 40;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1893,7 +1897,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 = 39; CURRENT_PROJECT_VERSION = 40;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;

@ -446,8 +446,12 @@ class Tournament : ModelObject, Storable {
} }
func shareURL(_ pageLink: PageLink = .matches) -> URL? { func shareURL(_ pageLink: PageLink = .matches) -> URL? {
if pageLink == .broadcast, let club = club(), let broadcastCode = club.broadcastCode { if pageLink == .clubBroadcast {
return URLs.main.url.appending(path: "c/\(broadcastCode)") if let club = club(), let broadcastCode = club.broadcastCode {
return URLs.main.url.appending(path: "c/\(broadcastCode)")
} else {
return nil
}
} }
return URLs.main.url.appending(path: "tournament/\(id)").appending(path: pageLink.path) return URLs.main.url.appending(path: "tournament/\(id)").appending(path: pageLink.path)
} }

@ -418,7 +418,7 @@ struct CreateAccountTip: Tip {
} }
var message: Text? { var message: Text? {
let message = "Un compte est nécessaire pour publier le tournoi sur [Padel Club](\(URLs.main.rawValue)) et profiter de toutes du site, comme le mode TV pour transformer l'expérience de vos tournois !" let message = "Un compte est nécessaire pour publier le tournoi sur [Padel Club](\(URLs.main.rawValue)) et profiter de toutes les pages du site, comme le mode TV pour transformer l'expérience de vos tournois !"
return Text(.init(message)) return Text(.init(message))
} }

@ -28,7 +28,8 @@ enum PageLink: String, Identifiable, CaseIterable {
case groupStages = "Poules" case groupStages = "Poules"
case matches = "Matchs" case matches = "Matchs"
case rankings = "Classement" case rankings = "Classement"
case broadcast = "Broadcast" case broadcast = "Mode TV (Tournoi)"
case clubBroadcast = "Mode TV (Club)"
var id: String { self.rawValue } var id: String { self.rawValue }
@ -50,6 +51,8 @@ enum PageLink: String, Identifiable, CaseIterable {
return "group-stages" return "group-stages"
case .broadcast: case .broadcast:
return "broadcast" return "broadcast"
case .clubBroadcast:
return ""
} }
} }
} }

@ -63,7 +63,7 @@ struct BroadcastView: View {
} }
if tournament.isPrivate == false { if tournament.isPrivate == false {
if let url = tournament.shareURL(.broadcast) { if let url = tournament.shareURL(.clubBroadcast) {
Section { Section {
Link(destination: url) { Link(destination: url) {
Text(url.absoluteString) Text(url.absoluteString)
@ -251,11 +251,17 @@ struct BroadcastView: View {
Text("Padel Club") Text("Padel Club")
} }
if let club = tournament.club(), let clubURL = club.shareURL() { let club = tournament.club()
LabeledContent { LabeledContent {
if let clubURL = club?.shareURL() {
actionForURL(clubURL) actionForURL(clubURL)
} label: { }
Text("Club") } label: {
Text("Club")
if let club {
Text(club.clubTitle())
} else {
Text("Aucun club indiqué pour ce tournoi")
} }
} }
@ -268,7 +274,7 @@ struct BroadcastView: View {
} }
} }
let links : [PageLink] = [.teams, .summons, .groupStages, .matches, .rankings] let links : [PageLink] = [.teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast]
Picker(selection: $pageLink) { Picker(selection: $pageLink) {
ForEach(links) { pageLink in ForEach(links) { pageLink in
Text(pageLink.localizedLabel()).tag(pageLink) Text(pageLink.localizedLabel()).tag(pageLink)

@ -0,0 +1,35 @@
//
// TournamentBroadcastRowView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 05/06/2024.
//
import SwiftUI
import LeStorage
struct TournamentBroadcastRowView: View {
var tournament: Tournament
var body: some View {
NavigationLink(value: Screen.broadcast) {
LabeledContent {
if Store.main.userId == nil {
Image(systemName: "xmark.circle.fill")
.foregroundStyle(.logoRed)
} else {
if tournament.isPrivate {
Text("tournoi privé").foregroundStyle(.logoRed)
} else {
Text("Automatique")
}
}
} label: {
Text("Publication")
if Store.main.userId == nil {
Text("Un compte Padel Club est nécessaire")
}
}
}
}
}

@ -17,6 +17,8 @@ struct TournamentBuildView: View {
@ViewBuilder @ViewBuilder
var body: some View { var body: some View {
let state = tournament.state()
if tournament.hasEnded() { if tournament.hasEnded() {
Section { Section {
NavigationLink(value: Screen.rankings) { NavigationLink(value: Screen.rankings) {
@ -70,9 +72,13 @@ struct TournamentBuildView: View {
} }
Section { Section {
if tournament.state() != .finished { if state == .running || state == .finished {
NavigationLink(value: Screen.schedule) { TournamentBroadcastRowView(tournament: tournament)
let tournamentStatus = scheduleStatus }
if state == .running || state == .finished {
NavigationLink(value: Screen.cashier) {
let tournamentStatus = cashierStatus
LabeledContent { LabeledContent {
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.completion) Text(tournamentStatus.completion)
@ -80,7 +86,7 @@ struct TournamentBuildView: View {
ProgressView() ProgressView()
} }
} label: { } label: {
Text("Horaires") Text("Encaissement")
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.label).lineLimit(1) Text(tournamentStatus.label).lineLimit(1)
} else { } else {
@ -89,11 +95,13 @@ struct TournamentBuildView: View {
} }
} }
.task { .task {
scheduleStatus = await tournament.scheduleStatus() cashierStatus = await tournament.cashierStatus()
} }
}
NavigationLink(value: Screen.call) { if state != .finished {
let tournamentStatus = callStatus NavigationLink(value: Screen.schedule) {
let tournamentStatus = scheduleStatus
LabeledContent { LabeledContent {
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.completion) Text(tournamentStatus.completion)
@ -101,7 +109,7 @@ struct TournamentBuildView: View {
ProgressView() ProgressView()
} }
} label: { } label: {
Text("Convocations") Text("Horaires")
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.label).lineLimit(1) Text(tournamentStatus.label).lineLimit(1)
} else { } else {
@ -110,13 +118,11 @@ struct TournamentBuildView: View {
} }
} }
.task { .task {
callStatus = await tournament.callStatus() scheduleStatus = await tournament.scheduleStatus()
} }
}
if tournament.state() == .running || tournament.state() == .finished { NavigationLink(value: Screen.call) {
NavigationLink(value: Screen.cashier) { let tournamentStatus = callStatus
let tournamentStatus = cashierStatus
LabeledContent { LabeledContent {
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.completion) Text(tournamentStatus.completion)
@ -124,7 +130,7 @@ struct TournamentBuildView: View {
ProgressView() ProgressView()
} }
} label: { } label: {
Text("Encaissement") Text("Convocations")
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.label).lineLimit(1) Text(tournamentStatus.label).lineLimit(1)
} else { } else {
@ -133,9 +139,14 @@ struct TournamentBuildView: View {
} }
} }
.task { .task {
cashierStatus = await tournament.cashierStatus() callStatus = await tournament.callStatus()
} }
} }
if state == .running || state == .finished {
TournamentInscriptionView(tournament: tournament)
}
} }
} }
} }

@ -42,25 +42,7 @@ struct TournamentInitView: View {
} }
} }
NavigationLink(value: Screen.broadcast) { TournamentBroadcastRowView(tournament: tournament)
LabeledContent {
if Store.main.userId == nil {
Image(systemName: "xmark.circle.fill")
.foregroundStyle(.logoRed)
} else {
if tournament.isPrivate {
Text("tournoi privé").foregroundStyle(.logoRed)
} else {
Text("Automatique")
}
}
} label: {
Text("Publication")
if Store.main.userId == nil {
Text("Un compte Padel Club est nécessaire")
}
}
}
NavigationLink(value: Screen.print) { NavigationLink(value: Screen.print) {
Text("Imprimer") Text("Imprimer")

@ -14,23 +14,21 @@ struct TournamentInscriptionView: View {
@ViewBuilder @ViewBuilder
var body: some View { var body: some View {
Section { NavigationLink(value: Screen.inscription) {
NavigationLink(value: Screen.inscription) { LabeledContent {
LabeledContent { Text(tournament.unsortedTeamsWithoutWO().count.formatted() + "/" + tournament.teamCount.formatted())
Text(tournament.unsortedTeamsWithoutWO().count.formatted() + "/" + tournament.teamCount.formatted()) } label: {
} label: { Text("Gestion des inscriptions")
Text("Gestion des inscriptions") if let closedRegistrationDate = tournament.closedRegistrationDate {
if let closedRegistrationDate = tournament.closedRegistrationDate { Text("clôturé le " + closedRegistrationDate.formatted(date: .abbreviated, time: .shortened))
Text("clôturé le " + closedRegistrationDate.formatted(date: .abbreviated, time: .shortened))
}
} }
} }
if let endOfInscriptionDate = tournament.mandatoryRegistrationCloseDate(), tournament.inscriptionClosed() == false && tournament.hasStarted() == false { }
LabeledContent { if let endOfInscriptionDate = tournament.mandatoryRegistrationCloseDate(), tournament.inscriptionClosed() == false && tournament.hasStarted() == false {
Text(endOfInscriptionDate.formatted(date: .abbreviated, time: .shortened)) LabeledContent {
} label: { Text(endOfInscriptionDate.formatted(date: .abbreviated, time: .shortened))
Text("Date limite") } label: {
} Text("Date limite")
} }
} }
} }

@ -56,14 +56,20 @@ struct TournamentView: View {
} }
} }
case .initial: case .initial:
TournamentInscriptionView(tournament: tournament) Section {
TournamentInscriptionView(tournament: tournament)
}
TournamentInitView(tournament: tournament) TournamentInitView(tournament: tournament)
case .build: case .build:
TournamentInscriptionView(tournament: tournament) Section {
TournamentInscriptionView(tournament: tournament)
}
TournamentInitView(tournament: tournament) TournamentInitView(tournament: tournament)
TournamentBuildView(tournament: tournament) TournamentBuildView(tournament: tournament)
case .running, .finished: case .running:
TournamentInscriptionView(tournament: tournament) TournamentRunningView(tournament: tournament)
TournamentBuildView(tournament: tournament)
case .finished:
TournamentBuildView(tournament: tournament) TournamentBuildView(tournament: tournament)
TournamentRunningView(tournament: tournament) TournamentRunningView(tournament: tournament)
} }

@ -11,6 +11,7 @@ import LeStorage
struct LoginView: View { struct LoginView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@EnvironmentObject var networkMonitor: NetworkMonitor
@State var username: String = "" @State var username: String = ""
@State var password: String = "" @State var password: String = ""
@ -20,6 +21,18 @@ struct LoginView: View {
@State var errorText: String? = nil @State var errorText: String? = nil
@State var showSubscriptionView: Bool = false
var loginFailed: Binding<Bool> {
Binding {
errorText != nil
} set: { newValue in
if newValue == false {
errorText = nil
}
}
}
var showEmailValidationMessage: Bool = false var showEmailValidationMessage: Bool = false
var handler: (User) -> () var handler: (User) -> ()
@ -41,26 +54,12 @@ struct LoginView: View {
} }
Section { Section {
Button(action: { RowButtonView("Connexion") {
self._login() await self._login()
}, label: {
if self.isLoading {
HStack {
Spacer()
ProgressView()
Spacer()
}
} else {
Text("Connexion")
.buttonStyle(.borderedProminent)
.tint(.orange)
.listRowBackground(Color.clear)
.frame(maxWidth: .infinity)
}
})
if let error = self.errorText {
Text(error).font(.callout).foregroundStyle(.red)
} }
// if let error = self.errorText {
// Text(error).font(.callout).foregroundStyle(.red)
// }
} }
if !self.showEmailValidationMessage { if !self.showEmailValidationMessage {
@ -86,6 +85,13 @@ struct LoginView: View {
} }
} }
.alert("Un problème est survenu", isPresented: loginFailed) {
Button("OK") {
}
} message: {
let message = [networkMonitor.connected == false ? "L'appareil n'est pas connecté à internet." as String? : nil, errorText].compacted().joined(separator: "\n")
Text(message)
}
.navigationTitle("Connexion") .navigationTitle("Connexion")
.onAppear { .onAppear {
#if DEBUG #if DEBUG
@ -99,29 +105,27 @@ struct LoginView: View {
} }
} }
fileprivate func _login() { fileprivate func _login() async {
self.errorText = nil // reset error self.errorText = nil // reset error
self.isLoading = true self.isLoading = true
Task { do {
do { let service = try Store.main.service()
let service = try Store.main.service() let user: User = try await service.login(
let user: User = try await service.login( username: self.username,
username: self.username, password: self.password)
password: self.password) self.dataStore.user = user
self.dataStore.user = user self.isLoading = false
self.isLoading = false self.handler(user)
self.handler(user) } catch {
} catch { self.isLoading = false
self.isLoading = false switch error {
switch error { case ServiceError.responseError(let reason):
case ServiceError.responseError(let reason): self.errorText = reason
self.errorText = reason default:
default: self.errorText = error.localizedDescription
self.errorText = error.localizedDescription
}
Logger.error(error)
} }
Logger.error(error)
} }
} }

Loading…
Cancel
Save