multistore
Razmig Sarkissian 2 years ago
parent a3c2db3823
commit eb7acc1299
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 2
      PadelClub/Data/MockData.swift
  3. 1
      PadelClub/Utils/DisplayContext.swift
  4. 2
      PadelClub/Utils/LocationManager.swift
  5. 10
      PadelClub/Utils/Tips.swift
  6. 66
      PadelClub/Views/Club/ClubDetailView.swift
  7. 3
      PadelClub/Views/Club/ClubImportView.swift
  8. 3
      PadelClub/Views/Club/ClubRowView.swift
  9. 28
      PadelClub/Views/Club/ClubSearchView.swift
  10. 45
      PadelClub/Views/Club/ClubsView.swift
  11. 13
      PadelClub/Views/Club/CreateClubView.swift
  12. 8
      PadelClub/Views/Components/BarButtonView.swift
  13. 30
      PadelClub/Views/Event/EventCreationView.swift
  14. 7
      PadelClub/Views/Tournament/Shared/TournamentCellView.swift
  15. 2
      PadelClub/Views/Tournament/TournamentView.swift

@ -1847,7 +1847,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 = 9; CURRENT_PROJECT_VERSION = 10;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1885,7 +1885,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 = 9; CURRENT_PROJECT_VERSION = 10;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;

@ -25,7 +25,7 @@ extension Club {
} }
static func newEmptyInstance() -> Club { static func newEmptyInstance() -> Club {
Club(name: "", acronym: "") Club(creator: DataStore.shared.user.id, name: "", acronym: "")
} }
} }

@ -11,6 +11,7 @@ enum DisplayContext {
case addition case addition
case edition case edition
case lockedForEditing case lockedForEditing
case selection
} }
enum DisplayStyle { enum DisplayStyle {

@ -53,7 +53,7 @@ class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
} }
func geocodeCity(cityOrZipcode: String, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) { func geocodeCity(cityOrZipcode: String, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().geocodeAddressString(cityOrZipcode, in: nil, completionHandler: completion) CLGeocoder().geocodeAddressString(cityOrZipcode, completionHandler: completion)
} }
} }

@ -304,20 +304,12 @@ struct MultiTournamentsEventTip: Tip {
} }
var message: Text? { var message: Text? {
Text("Padel Club permet de gérer plusieurs tournois ayant lieu en même temps. Un P100 homme et dame par le même week-end par exemple.") Text("Padel Club permet de gérer plusieurs tournois ayant lieu en même temps. Un P100 homme et dame le même week-end par exemple.")
} }
var image: Image? { var image: Image? {
Image(systemName: "trophy.circle") Image(systemName: "trophy.circle")
} }
var actions: [Action] {
Action(id: ActionKey.addEvent.rawValue, title: "Ajoutez une épreuve")
}
enum ActionKey: String {
case addEvent = "add-event"
}
} }
struct NotFoundAreWalkOutTip: Tip { struct NotFoundAreWalkOutTip: Tip {

@ -9,17 +9,20 @@ import SwiftUI
import LeStorage import LeStorage
struct ClubDetailView: View { struct ClubDetailView: View {
@Bindable var club: Club
var displayContext: DisplayContext
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Environment(\.dismiss) var dismiss
@FocusState var focusedField: Club.CodingKeys? @FocusState var focusedField: Club.CodingKeys?
@State private var acronymMode: Club.AcronymMode = .automatic @State private var acronymMode: Club.AcronymMode = .automatic
@State private var city: String @State private var city: String
@State private var zipCode: String @State private var zipCode: String
@Bindable var club: Club
var displayContext: DisplayContext
var selection: ((Club) -> ())? = nil
init(club: Club, displayContext: DisplayContext) { init(club: Club, displayContext: DisplayContext, selection: ((Club) -> ())? = nil) {
_club = Bindable(club) _club = Bindable(club)
self.displayContext = displayContext self.displayContext = displayContext
self.selection = selection
_acronymMode = State(wrappedValue: club.shortNameMode()) _acronymMode = State(wrappedValue: club.shortNameMode())
_city = State(wrappedValue: club.city ?? "") _city = State(wrappedValue: club.city ?? "")
_zipCode = State(wrappedValue: club.zipCode ?? "") _zipCode = State(wrappedValue: club.zipCode ?? "")
@ -27,23 +30,10 @@ struct ClubDetailView: View {
var body: some View { var body: some View {
Form { Form {
Section { Section {
NavigationLink {
ClubSearchView(displayContext: .edition, club: club)
} label: {
Label("Chercher dans la base fédérale", systemImage: "magnifyingglass")
}
} footer: {
Text("Vous pouvez chercher un club dans la base fédérale et importer les informations directement.")
}
Section {
LabeledContent {
TextField("Nom du club", text: $club.name) TextField("Nom du club", text: $club.name)
.autocorrectionDisabled() .autocorrectionDisabled()
.keyboardType(.alphabet) .keyboardType(.alphabet)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.focused($focusedField, equals: ._name) .focused($focusedField, equals: ._name)
.submitLabel( displayContext == .addition ? .next : .done) .submitLabel( displayContext == .addition ? .next : .done)
@ -56,12 +46,6 @@ struct ClubDetailView: View {
focusedField = ._acronym focusedField = ._acronym
} }
} }
} label: {
Text("Nom du club")
}
.onTapGesture {
focusedField = ._name
}
LabeledContent { LabeledContent {
if acronymMode == .automatic { if acronymMode == .automatic {
Text(club.acronym) Text(club.acronym)
@ -106,8 +90,16 @@ struct ClubDetailView: View {
club.acronym = "" club.acronym = ""
} }
} }
} footer: {
if displayContext == .lockedForEditing {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)
} else {
Text("Vous pouvez personaliser le nom court ou laisser celui généré par défaut.")
}
}
if club.code == nil { if club.code == nil {
Section {
LabeledContent { LabeledContent {
TextField("Ville", text: $city) TextField("Ville", text: $city)
.autocorrectionDisabled() .autocorrectionDisabled()
@ -146,16 +138,13 @@ struct ClubDetailView: View {
.onTapGesture { .onTapGesture {
focusedField = ._zipCode focusedField = ._zipCode
} }
}
} footer: { } footer: {
if displayContext == .lockedForEditing { if displayContext == .lockedForEditing {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)
} else {
Text("Vous pouvez personaliser le nom court ou laisser celui généré par défaut.")
} }
} }
.disabled(displayContext == .lockedForEditing) .disabled(displayContext == .lockedForEditing)
}
if let federalLink = club.federalLink() { if let federalLink = club.federalLink() {
@ -172,6 +161,31 @@ struct ClubDetailView: View {
} }
} }
if displayContext == .addition {
Section {
} header: {
HStack {
VStack {
Divider()
}
Text("ou")
VStack {
Divider()
}
}
}
Section {
NavigationLink {
ClubSearchView(displayContext: .edition, club: club)
} label: {
Label("Chercher dans la base fédérale", systemImage: "magnifyingglass")
}
} footer: {
Text("Vous pouvez chercher un club dans la base fédérale et importer les informations directement.")
}
}
if displayContext == .edition { if displayContext == .edition {
Section { Section {
RowButtonView("Supprimer ce club", role: .destructive) { RowButtonView("Supprimer ce club", role: .destructive) {

@ -9,10 +9,11 @@ import SwiftUI
struct ClubImportView: View { struct ClubImportView: View {
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
var selection: ((Club) -> ())? = nil
var body: some View { var body: some View {
NavigationStack { NavigationStack {
ClubSearchView(displayContext: .addition) ClubSearchView(displayContext: .addition, selection: selection)
.toolbar { .toolbar {
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
Button("Annuler", role: .cancel) { Button("Annuler", role: .cancel) {

@ -9,11 +9,14 @@ import SwiftUI
struct ClubRowView: View { struct ClubRowView: View {
let club: Club let club: Club
var displayContext: DisplayContext = .edition
var body: some View { var body: some View {
LabeledContent { LabeledContent {
if displayContext == .edition {
Image(systemName: club.isFavorite() ? "star.fill" : "star") Image(systemName: club.isFavorite() ? "star.fill" : "star")
.foregroundStyle(club.isFavorite() ? .green : .logoRed) .foregroundStyle(club.isFavorite() ? .green : .logoRed)
}
} label: { } label: {
Text(club.name) Text(club.name)
Text(club.acronym) Text(club.acronym)

@ -26,10 +26,20 @@ struct ClubSearchView: View {
@State private var getForwardCityList: [CLPlacemark] = [] @State private var getForwardCityList: [CLPlacemark] = []
@State private var searchPresented: Bool = false @State private var searchPresented: Bool = false
@State private var showingSettingsAlert = false @State private var showingSettingsAlert = false
@State private var presentClubCreationView: Bool = false @State private var newClub: Club?
var presentClubCreationView: Binding<Bool> { Binding(
get: { newClub != nil },
set: { isPresented in
if isPresented == false {
newClub = nil
}
}
)}
var displayContext: DisplayContext = .edition var displayContext: DisplayContext = .edition
var club: Club? var club: Club?
var selection: ((Club) -> ())? = nil
fileprivate class DebouncableViewModel: ObservableObject { fileprivate class DebouncableViewModel: ObservableObject {
@Published var debouncableText: String = "" @Published var debouncableText: String = ""
@ -162,7 +172,7 @@ struct ClubSearchView: View {
if searchAttempted { if searchAttempted {
RowButtonView("Créer un club manuellement") { RowButtonView("Créer un club manuellement") {
presentClubCreationView = true newClub = club ?? Club.newEmptyInstance()
} }
} }
} }
@ -171,10 +181,15 @@ struct ClubSearchView: View {
ContentUnavailableView("recherche en cours", systemImage: "mappin.and.ellipse", description: Text("recherche des clubs autour de vous")) ContentUnavailableView("recherche en cours", systemImage: "mappin.and.ellipse", description: Text("recherche des clubs autour de vous"))
} }
} }
.sheet(isPresented: $presentClubCreationView) { .sheet(isPresented: presentClubCreationView) {
CreateClubView() if let newClub {
CreateClubView(club: newClub) { club in
selection?(club)
dismiss()
}
.tint(.master) .tint(.master)
} }
}
.alert(isPresented: $showingSettingsAlert) { .alert(isPresented: $showingSettingsAlert) {
Alert( Alert(
title: Text("Réglages"), title: Text("Réglages"),
@ -295,8 +310,8 @@ struct ClubSearchView: View {
private func _importClub(clubToEdit: Club, clubMarker: ClubMarker) { private func _importClub(clubToEdit: Club, clubMarker: ClubMarker) {
if clubToEdit.creator == dataStore.user.id { if clubToEdit.creator == dataStore.user.id {
if clubToEdit.name.isEmpty { if clubToEdit.name.isEmpty {
clubToEdit.name = clubMarker.nom clubToEdit.name = clubMarker.nom.capitalized
clubToEdit.acronym = clubToEdit.automaticShortName() clubToEdit.acronym = clubToEdit.automaticShortName().capitalized
} }
clubToEdit.code = clubMarker.clubID clubToEdit.code = clubMarker.clubID
clubToEdit.latitude = clubMarker.lat clubToEdit.latitude = clubMarker.lat
@ -316,6 +331,7 @@ struct ClubSearchView: View {
} }
} }
dismiss() dismiss()
selection?(clubToEdit)
} }
private func _filteredClubs() -> [ClubMarker] { private func _filteredClubs() -> [ClubMarker] {

@ -12,20 +12,21 @@ import LeStorage
struct ClubsView: View { struct ClubsView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@State private var presentClubCreationView: Bool = false
@State private var presentClubSearchView: Bool = false @State private var presentClubSearchView: Bool = false
let tip = SlideToDeleteTip() @State private var newClub: Club?
var selection: ((Club) -> ())? = nil var selection: ((Club) -> ())? = nil
var presentClubCreationView: Binding<Bool> { Binding(
get: { newClub != nil },
set: { isPresented in
if isPresented == false {
newClub = nil
}
}
)}
var body: some View { var body: some View {
List { List {
//
// if dataStore.clubs.isEmpty == false && selection == nil {
// Section {
// TipView(tip)
// .tipStyle(tint: nil)
// }
// }
let clubs : [Club] = dataStore.user.clubsObjects(includeCreated: true) let clubs : [Club] = dataStore.user.clubsObjects(includeCreated: true)
ForEach(clubs) { club in ForEach(clubs) { club in
if let selection { if let selection {
@ -33,7 +34,7 @@ struct ClubsView: View {
selection(club) selection(club)
dismiss() dismiss()
} label: { } label: {
ClubRowView(club: club) ClubRowView(club: club, displayContext: .selection)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
} }
.contentShape(Rectangle()) .contentShape(Rectangle())
@ -62,7 +63,7 @@ struct ClubsView: View {
Text("Texte décrivant l'utilité d'un club et les features que cela apporte") Text("Texte décrivant l'utilité d'un club et les features que cela apporte")
} actions: { } actions: {
RowButtonView("Créer un nouveau club", systemImage: "plus.circle.fill") { RowButtonView("Créer un nouveau club", systemImage: "plus.circle.fill") {
presentClubCreationView = true newClub = Club.newEmptyInstance()
} }
RowButtonView("Chercher un club", systemImage: "magnifyingglass.circle.fill") { RowButtonView("Chercher un club", systemImage: "magnifyingglass.circle.fill") {
presentClubSearchView = true presentClubSearchView = true
@ -71,12 +72,26 @@ struct ClubsView: View {
} }
} }
.navigationTitle(selection == nil ? "Mes clubs" : "Choisir un club") .navigationTitle(selection == nil ? "Mes clubs" : "Choisir un club")
.sheet(isPresented: $presentClubCreationView) { .navigationBarTitleDisplayMode(.inline)
CreateClubView() .toolbarBackground(.visible, for: .navigationBar)
.sheet(isPresented: presentClubCreationView) {
if let newClub {
CreateClubView(club: newClub) { club in
if let selection {
selection(club)
dismiss()
}
}
.tint(.master) .tint(.master)
} }
}
.sheet(isPresented: $presentClubSearchView) { .sheet(isPresented: $presentClubSearchView) {
ClubImportView() ClubImportView() { club in
if let selection {
selection(club)
dismiss()
}
}
.tint(.master) .tint(.master)
} }
.toolbar { .toolbar {
@ -91,7 +106,7 @@ struct ClubsView: View {
} }
Button { Button {
presentClubCreationView = true newClub = Club.newEmptyInstance()
} label: { } label: {
Image(systemName: "plus.circle.fill") Image(systemName: "plus.circle.fill")
.resizable() .resizable()

@ -9,18 +9,14 @@ import SwiftUI
import LeStorage import LeStorage
struct CreateClubView: View { struct CreateClubView: View {
@Bindable var club: Club
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
var club: Club
init() { var selection: ((Club) -> ())? = nil
self.club = Club.newEmptyInstance()
}
var body: some View { var body: some View {
NavigationStack { NavigationStack {
ClubDetailView(club: club, displayContext: .addition) ClubDetailView(club: club, displayContext: .addition, selection: selection)
.task { .task {
do { do {
try await dataStore.clubs.loadDataFromServerIfAllowed() try await dataStore.clubs.loadDataFromServerIfAllowed()
@ -55,6 +51,7 @@ struct CreateClubView: View {
self.dataStore.saveUser() self.dataStore.saveUser()
} }
dismiss() dismiss()
selection?(existingOrCreatedClub)
} }
.disabled(club.isValid == false) .disabled(club.isValid == false)
} }
@ -64,6 +61,6 @@ struct CreateClubView: View {
} }
#Preview { #Preview {
CreateClubView() CreateClubView(club: Club.mock())
.environmentObject(DataStore.shared) .environmentObject(DataStore.shared)
} }

@ -22,6 +22,12 @@ struct BarButtonView: View {
Button(action: { Button(action: {
action() action()
}) { }) {
Image(systemName: icon)
.resizable()
.scaledToFit()
.frame(minHeight: 28)
/*
Label { Label {
Text(accessibilityLabel) Text(accessibilityLabel)
} icon: { } icon: {
@ -31,6 +37,8 @@ struct BarButtonView: View {
.frame(minHeight: 36) .frame(minHeight: 36)
} }
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
//todo: resizing not working when label used
*/
} }
} }
} }

@ -57,7 +57,7 @@ struct EventCreationView: View {
} }
} label: { } label: {
if let selectedClub { if let selectedClub {
ClubRowView(club: selectedClub) ClubRowView(club: selectedClub, displayContext: .selection)
} else { } else {
Text("Choisir un club") Text("Choisir un club")
} }
@ -70,20 +70,13 @@ struct EventCreationView: View {
LabeledContent { LabeledContent {
Text(tournaments.count.formatted()) Text(tournaments.count.formatted())
} label: { } label: {
Text("Nombre d'épreuves") Text("Nombre d'épreuve")
} }
} header: {
Text("")
} }
Section {
TipView(multiTournamentsEventTip) { action in
let tournament = Tournament.newEmptyInstance()
self.tournaments.append(tournament)
}
.tipStyle(tint: .orange)
}
switch eventType { switch eventType {
case .approvedTournament: case .approvedTournament:
approvedTournamentEditorView approvedTournamentEditorView
@ -134,10 +127,23 @@ struct EventCreationView: View {
dismiss() dismiss()
} }
} }
ToolbarItem(placement: .topBarTrailing) {
BarButtonView("Ajouter une épreuve", icon: "plus.circle.fill") {
let tournament = Tournament.newEmptyInstance()
self.tournaments.append(tournament)
}
.popoverTip(multiTournamentsEventTip)
}
} }
.navigationTitle("Nouvel événement") .navigationTitle("Nouvel événement")
.navigationBarTitleDisplayMode(.large) .navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.onAppear {
if selectedClub == nil {
selectedClub = dataStore.user.clubsObjects().first
}
}
} }
} }

@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import LeStorage
struct TournamentCellView: View { struct TournamentCellView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@ -123,7 +124,11 @@ struct TournamentCellView: View {
newTournament.dayDuration = federalTournament.dayDuration newTournament.dayDuration = federalTournament.dayDuration
newTournament.startDate = federalTournament.startDate.atBeginningOfDay(hourInt: 9) newTournament.startDate = federalTournament.startDate.atBeginningOfDay(hourInt: 9)
newTournament.setupFederalSettings() newTournament.setupFederalSettings()
try? dataStore.tournaments.addOrUpdate(instance: newTournament) do {
try dataStore.tournaments.addOrUpdate(instance: newTournament)
} catch {
Logger.error(error)
}
} }
} }
} }

@ -155,6 +155,7 @@ struct TournamentView: View {
} }
} }
if presentationContext == .agenda || tournament.state() == .build {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
Menu { Menu {
if presentationContext == .agenda { if presentationContext == .agenda {
@ -189,6 +190,7 @@ struct TournamentView: View {
} }
} }
} }
}
.onAppear { .onAppear {
Logger.log("Payment = \(String(describing: self.tournament.payment)), canceled = \(self.tournament.isCanceled)") Logger.log("Payment = \(String(describing: self.tournament.payment)), canceled = \(self.tournament.isCanceled)")
} }

Loading…
Cancel
Save