1.5.0 Initial run as iOS in a monorepo
This commit is contained in:
216
ios/OpenClimb/Views/AddEdit/AddEditGymView.swift
Normal file
216
ios/OpenClimb/Views/AddEdit/AddEditGymView.swift
Normal file
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// AddEditGymView.swift
|
||||
// OpenClimb
|
||||
//
|
||||
// Created by OpenClimb on 2025-01-17.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AddEditGymView: View {
|
||||
let gymId: UUID?
|
||||
@EnvironmentObject var dataManager: ClimbingDataManager
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var name = ""
|
||||
@State private var location = ""
|
||||
@State private var notes = ""
|
||||
@State private var selectedClimbTypes = Set<ClimbType>()
|
||||
@State private var selectedDifficultySystems = Set<DifficultySystem>()
|
||||
@State private var customDifficultyGrades: [String] = []
|
||||
@State private var isEditing = false
|
||||
|
||||
private var existingGym: Gym? {
|
||||
guard let gymId = gymId else { return nil }
|
||||
return dataManager.gym(withId: gymId)
|
||||
}
|
||||
|
||||
private var availableDifficultySystems: [DifficultySystem] {
|
||||
if selectedClimbTypes.isEmpty {
|
||||
return []
|
||||
} else {
|
||||
return selectedClimbTypes.flatMap { climbType in
|
||||
DifficultySystem.systemsForClimbType(climbType)
|
||||
}.removingDuplicates()
|
||||
}
|
||||
}
|
||||
|
||||
init(gymId: UUID? = nil) {
|
||||
self.gymId = gymId
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
BasicInfoSection()
|
||||
ClimbTypesSection()
|
||||
DifficultySystemsSection()
|
||||
NotesSection()
|
||||
}
|
||||
.navigationTitle(isEditing ? "Edit Gym" : "Add Gym")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Save") {
|
||||
saveGym()
|
||||
}
|
||||
.disabled(!canSave)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
loadExistingGym()
|
||||
}
|
||||
.onChange(of: selectedClimbTypes) { _ in
|
||||
updateAvailableDifficultySystems()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func BasicInfoSection() -> some View {
|
||||
Section("Basic Information") {
|
||||
TextField("Gym Name", text: $name)
|
||||
|
||||
TextField("Location (Optional)", text: $location)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func ClimbTypesSection() -> some View {
|
||||
Section("Supported Climb Types") {
|
||||
ForEach(ClimbType.allCases, id: \.self) { climbType in
|
||||
HStack {
|
||||
Text(climbType.displayName)
|
||||
Spacer()
|
||||
if selectedClimbTypes.contains(climbType) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.blue)
|
||||
} else {
|
||||
Image(systemName: "circle")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
if selectedClimbTypes.contains(climbType) {
|
||||
selectedClimbTypes.remove(climbType)
|
||||
} else {
|
||||
selectedClimbTypes.insert(climbType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func DifficultySystemsSection() -> some View {
|
||||
Section("Difficulty Systems") {
|
||||
if selectedClimbTypes.isEmpty {
|
||||
Text("Select climb types first to see available difficulty systems")
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
} else {
|
||||
ForEach(availableDifficultySystems, id: \.self) { system in
|
||||
HStack {
|
||||
Text(system.displayName)
|
||||
Spacer()
|
||||
if selectedDifficultySystems.contains(system) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.blue)
|
||||
} else {
|
||||
Image(systemName: "circle")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
if selectedDifficultySystems.contains(system) {
|
||||
selectedDifficultySystems.remove(system)
|
||||
} else {
|
||||
selectedDifficultySystems.insert(system)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func NotesSection() -> some View {
|
||||
Section("Notes (Optional)") {
|
||||
TextEditor(text: $notes)
|
||||
.frame(minHeight: 100)
|
||||
}
|
||||
}
|
||||
|
||||
private var canSave: Bool {
|
||||
!name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !selectedClimbTypes.isEmpty
|
||||
&& !selectedDifficultySystems.isEmpty
|
||||
}
|
||||
|
||||
private func loadExistingGym() {
|
||||
if let gym = existingGym {
|
||||
isEditing = true
|
||||
name = gym.name
|
||||
location = gym.location ?? ""
|
||||
notes = gym.notes ?? ""
|
||||
selectedClimbTypes = Set(gym.supportedClimbTypes)
|
||||
selectedDifficultySystems = Set(gym.difficultySystems)
|
||||
customDifficultyGrades = gym.customDifficultyGrades
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAvailableDifficultySystems() {
|
||||
// Remove selected systems that are no longer available
|
||||
let availableSet = Set(availableDifficultySystems)
|
||||
selectedDifficultySystems = selectedDifficultySystems.intersection(availableSet)
|
||||
}
|
||||
|
||||
private func saveGym() {
|
||||
let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let trimmedLocation = location.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let trimmedNotes = notes.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if isEditing, let gym = existingGym {
|
||||
let updatedGym = gym.updated(
|
||||
name: trimmedName,
|
||||
location: trimmedLocation.isEmpty ? nil : trimmedLocation,
|
||||
supportedClimbTypes: Array(selectedClimbTypes),
|
||||
difficultySystems: Array(selectedDifficultySystems),
|
||||
customDifficultyGrades: customDifficultyGrades,
|
||||
notes: trimmedNotes.isEmpty ? nil : trimmedNotes
|
||||
)
|
||||
dataManager.updateGym(updatedGym)
|
||||
} else {
|
||||
let newGym = Gym(
|
||||
name: trimmedName,
|
||||
location: trimmedLocation.isEmpty ? nil : trimmedLocation,
|
||||
supportedClimbTypes: Array(selectedClimbTypes),
|
||||
difficultySystems: Array(selectedDifficultySystems),
|
||||
customDifficultyGrades: customDifficultyGrades,
|
||||
notes: trimmedNotes.isEmpty ? nil : trimmedNotes
|
||||
)
|
||||
dataManager.addGym(newGym)
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element: Hashable {
|
||||
func removingDuplicates() -> [Element] {
|
||||
var seen = Set<Element>()
|
||||
return filter { seen.insert($0).inserted }
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
AddEditGymView()
|
||||
.environmentObject(ClimbingDataManager.preview)
|
||||
}
|
||||
Reference in New Issue
Block a user