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. 104
      PadelClub/Views/Club/ClubDetailView.swift
  7. 3
      PadelClub/Views/Club/ClubImportView.swift
  8. 7
      PadelClub/Views/Club/ClubRowView.swift
  9. 28
      PadelClub/Views/Club/ClubSearchView.swift
  10. 47
      PadelClub/Views/Club/ClubsView.swift
  11. 13
      PadelClub/Views/Club/CreateClubView.swift
  12. 26
      PadelClub/Views/Components/BarButtonView.swift
  13. 30
      PadelClub/Views/Event/EventCreationView.swift
  14. 7
      PadelClub/Views/Tournament/Shared/TournamentCellView.swift
  15. 54
      PadelClub/Views/Tournament/TournamentView.swift

@ -1847,7 +1847,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 10;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1885,7 +1885,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 9;
CURRENT_PROJECT_VERSION = 10;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6;

@ -25,7 +25,7 @@ extension 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 edition
case lockedForEditing
case selection
}
enum DisplayStyle {

@ -53,7 +53,7 @@ class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
}
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? {
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? {
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 {

@ -9,17 +9,20 @@ import SwiftUI
import LeStorage
struct ClubDetailView: View {
@Bindable var club: Club
var displayContext: DisplayContext
@EnvironmentObject var dataStore: DataStore
@Environment(\.dismiss) var dismiss
@FocusState var focusedField: Club.CodingKeys?
@State private var acronymMode: Club.AcronymMode = .automatic
@State private var city: 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)
self.displayContext = displayContext
self.selection = selection
_acronymMode = State(wrappedValue: club.shortNameMode())
_city = State(wrappedValue: club.city ?? "")
_zipCode = State(wrappedValue: club.zipCode ?? "")
@ -27,41 +30,22 @@ struct ClubDetailView: View {
var body: some View {
Form {
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)
.autocorrectionDisabled()
.keyboardType(.alphabet)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.focused($focusedField, equals: ._name)
.submitLabel( displayContext == .addition ? .next : .done)
.onSubmit {
if club.acronym.isEmpty {
club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines)
focusedField = ._city
}
if displayContext == .addition {
focusedField = ._acronym
}
TextField("Nom du club", text: $club.name)
.autocorrectionDisabled()
.keyboardType(.alphabet)
.frame(maxWidth: .infinity)
.focused($focusedField, equals: ._name)
.submitLabel( displayContext == .addition ? .next : .done)
.onSubmit {
if club.acronym.isEmpty {
club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines)
focusedField = ._city
}
} label: {
Text("Nom du club")
}
.onTapGesture {
focusedField = ._name
}
if displayContext == .addition {
focusedField = ._acronym
}
}
LabeledContent {
if acronymMode == .automatic {
Text(club.acronym)
@ -106,8 +90,16 @@ struct ClubDetailView: View {
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 {
TextField("Ville", text: $city)
.autocorrectionDisabled()
@ -146,16 +138,13 @@ struct ClubDetailView: View {
.onTapGesture {
focusedField = ._zipCode
}
} footer: {
if displayContext == .lockedForEditing {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)
}
}
} 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.")
}
.disabled(displayContext == .lockedForEditing)
}
.disabled(displayContext == .lockedForEditing)
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 {
Section {
RowButtonView("Supprimer ce club", role: .destructive) {

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

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

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

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

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

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

@ -57,7 +57,7 @@ struct EventCreationView: View {
}
} label: {
if let selectedClub {
ClubRowView(club: selectedClub)
ClubRowView(club: selectedClub, displayContext: .selection)
} else {
Text("Choisir un club")
}
@ -70,20 +70,13 @@ struct EventCreationView: View {
LabeledContent {
Text(tournaments.count.formatted())
} 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 {
case .approvedTournament:
approvedTournamentEditorView
@ -134,10 +127,23 @@ struct EventCreationView: View {
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")
.navigationBarTitleDisplayMode(.large)
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.onAppear {
if selectedClub == nil {
selectedClub = dataStore.user.clubsObjects().first
}
}
}
}

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

@ -155,37 +155,39 @@ struct TournamentView: View {
}
}
ToolbarItem(placement: .topBarTrailing) {
Menu {
if presentationContext == .agenda {
Button {
navigation.openTournamentInOrganizer(tournament)
} label: {
Label("Voir dans le gestionnaire", systemImage: "line.diagonal.arrow")
if presentationContext == .agenda || tournament.state() == .build {
ToolbarItem(placement: .topBarTrailing) {
Menu {
if presentationContext == .agenda {
Button {
navigation.openTournamentInOrganizer(tournament)
} label: {
Label("Voir dans le gestionnaire", systemImage: "line.diagonal.arrow")
}
}
}
Divider()
if tournament.state() == .build {
NavigationLink(value: Screen.event) {
Text("Gestion de l'événement")
}
Divider()
if tournament.state() == .build {
NavigationLink(value: Screen.event) {
Text("Gestion de l'événement")
}
NavigationLink(value: Screen.settings) {
LabelSettings()
}
NavigationLink(value: Screen.structure) {
LabelStructure()
}
NavigationLink(value: Screen.rankings) {
Text("Classement")
}
NavigationLink(value: Screen.broadcast) {
Text("Publication")
NavigationLink(value: Screen.settings) {
LabelSettings()
}
NavigationLink(value: Screen.structure) {
LabelStructure()
}
NavigationLink(value: Screen.rankings) {
Text("Classement")
}
NavigationLink(value: Screen.broadcast) {
Text("Publication")
}
}
} label: {
LabelOptions()
}
} label: {
LabelOptions()
}
}
}

Loading…
Cancel
Save