// // WorkoutLibrary.swift // WorkoutsPlus // // Created by Felix Förtsch on 10.08.24. // import SwiftUI import SwiftData struct WorkoutLibrary: View { @Environment(\.modelContext) private var modelContext @EnvironmentObject private var navigationManager: NavigationManager @Default(\.isWorkingOut) var isWorkingOut @Binding var activeWorkoutSession: WorkoutSession? @Query(sort: \Workout.name) private var workouts: [Workout] @State private var newWorkout: Workout = Workout(name: "") @State private var newWorkoutName: String = "" @State private var isAddingWorkout: Bool = false @FocusState private var isInputFieldFocused: Bool @State private var searchText: String = "" var filteredItems: [Workout] { if searchText.isEmpty { return workouts } else { return workouts.filter { $0.name.localizedCaseInsensitiveContains(searchText) } } } var body: some View { Group { List { ForEach(filteredItems) { workout in NavigationLink { WorkoutDetail(activeWorkoutSession: $activeWorkoutSession, workout: workout) } label: { Image(systemName: workout.workoutIconSystemName) .foregroundStyle(workout.workoutIconColorName.color) Text(workout.name) } .swipeActions(edge: .leading) { if !isWorkingOut { Button { activeWorkoutSession = workout.start() navigationManager.navigateFromRoot(to: .activeWorkoutSession) } label: { Label("Quick Start Workout", systemImage: "play") .tint(.green) } } } } .onDelete(perform: deleteWorkout) if filteredItems.isEmpty { ContentUnavailableView.search(text: searchText) } if isAddingWorkout { // TODO: On tap-out of the text field, it should lose focus TextField("New Workout", text: $newWorkoutName, onCommit: { save(workout: newWorkout) }) .textInputAutocapitalization(.words) .focused($isInputFieldFocused) } // TODO: When pressing the button again, it should check if something is being added already and if yes, save it. AddItemButton(label: "Workout", action: addWorkout) } .searchable(text: $searchText) } .navigationTitle("Workouts") .toolbar { ToolbarItem() { EditButton() } } } private func addWorkout() { withAnimation { newWorkout = Workout(name: "") newWorkoutName = "" isAddingWorkout = true isInputFieldFocused = true } } private func save(workout: Workout) { withAnimation { newWorkout.name = newWorkoutName if !workout.name.isEmpty { modelContext.insert(workout) try? modelContext.save() } isAddingWorkout = false } } private func deleteWorkout(offsets: IndexSet) { withAnimation { for index in offsets { modelContext.delete(workouts[index]) } try? modelContext.save() } } } #Preview("With Sample Data") { @Previewable @State var activeWorkoutSession: WorkoutSession? NavigationStack { WorkoutLibrary(activeWorkoutSession: $activeWorkoutSession) } .modelContainer(SampleData.shared.modelContainer) } #Preview("Empty Database") { @Previewable @State var activeWorkoutSession: WorkoutSession? NavigationStack { WorkoutLibrary(activeWorkoutSession: $activeWorkoutSession) } .modelContainer(for: Workout.self, inMemory: true) }