diff --git a/WorkoutsPlus/Components/ExerciseListItem.swift b/WorkoutsPlus/Components/ExerciseListItem.swift new file mode 100644 index 0000000..8156a70 --- /dev/null +++ b/WorkoutsPlus/Components/ExerciseListItem.swift @@ -0,0 +1,48 @@ +// +// ExerciseListItem.swift +// WorkoutsPlus +// +// Created by Felix Förtsch on 02.09.24. +// + +import SwiftUI + +struct ExerciseListItem: View { + var workout: Workout + @State var exercise: WorkoutItem + + init(_ workout: Workout, _ exercise: WorkoutItem ) { + self.workout = workout + self.exercise = exercise + } + + var body: some View { + Button(action: { + // workout.addExercise(from: exercise) + }) { + HStack { + Text(String(exercise.reps)) + .font(.system(size: 14, weight: .bold)) + .foregroundStyle(.white) + .frame(width: 20, height: 10) + .padding(8) + .background(Color.blue) + .clipShape(RoundedRectangle(cornerRadius: 8)) + Text(exercise.name) + .foregroundStyle(.black) + Spacer() + Stepper( + value: $exercise.reps, + in: 0...100, + step: 1 + ) {} + } + } + } +} + +#Preview { + List { + ExerciseListItem(Workout(name: "RR"), WorkoutItem(from: Exercise("Push-ups"))) + } +} diff --git a/WorkoutsPlus/Components/SetListItem.swift b/WorkoutsPlus/Components/SetListItem.swift new file mode 100644 index 0000000..fb7a076 --- /dev/null +++ b/WorkoutsPlus/Components/SetListItem.swift @@ -0,0 +1,72 @@ +// +// SetListItem.swift +// WorkoutsPlus +// +// Created by Felix Förtsch on 02.09.24. +// + +import SwiftUI + +struct SetListItem: View { + var workout: Workout + @State var set: WorkoutItem + + init(_ workout: Workout, _ set: WorkoutItem ) { + self.workout = workout + self.set = set + } + + var body: some View { + HStack { + HStack { + Text(String(set.reps)) + .font(.system(size: 14, weight: .bold)) + .foregroundStyle(.white) + .frame(width: 20, height: 10) + .padding(8) + .background(Color.blue) + .clipShape(RoundedRectangle(cornerRadius: 8)) + Image(systemName: "repeat") + Text("Set") + } + .fontWeight(.bold) + Spacer() + Stepper( + value: $set.reps, + in: 0...100, + step: 1 + ) {} + Button(action: { + + }) { + Image(systemName: "plus.circle.fill") + .foregroundStyle(.green) + } + } + ForEach(set.workoutItems) { workoutItem in + ExerciseListItem(workout, workoutItem) + .padding(.leading) + } + } + + private func addExerciseToSet() { + + } +} + +#Preview { + let set = WorkoutItem(workoutItems: [ + WorkoutItem(10, "Squat"), + WorkoutItem(10, "Squat"), + WorkoutItem(10, "Squat")]) + List { + SetListItem(Workout(name: "RR"), set) + } +} + +#Preview("Empty Database") { + let set = WorkoutItem(workoutItems: []) + List { + SetListItem(Workout(name: "RR"), set) + } +} diff --git a/WorkoutsPlus/Exercise/Exercise.swift b/WorkoutsPlus/Exercise/Exercise.swift index ac5767a..64ea7f3 100644 --- a/WorkoutsPlus/Exercise/Exercise.swift +++ b/WorkoutsPlus/Exercise/Exercise.swift @@ -9,10 +9,11 @@ import Foundation import SwiftData @Model -final class Exercise { +final class Exercise: Identifiable { + var id = UUID() static var systemImage = "figure.run" @Attribute(.unique) var name: String - var metric: String = "reps" + // var metric: String = "reps" // var exerciseDescription: ExerciseDescription? var timestamp: Date = Date.now @@ -20,7 +21,9 @@ final class Exercise { init(_ name: String = "") { self.name = name } - +} + +extension Exercise { static let sampleData: [Exercise] = [ Exercise("Dips"), Exercise("Chin-ups"), diff --git a/WorkoutsPlus/SampleData.swift b/WorkoutsPlus/SampleData.swift index ee987c8..4d30ab6 100644 --- a/WorkoutsPlus/SampleData.swift +++ b/WorkoutsPlus/SampleData.swift @@ -39,9 +39,7 @@ class SampleData { context.insert(workoutItem) } - for workout in Workout.sampleData { - context.insert(workout) - } + context.insert(Workout.sampleData) do { try context.save() diff --git a/WorkoutsPlus/Workout/AddWorkout.swift b/WorkoutsPlus/Workout/AddWorkout.swift index 086ac3a..6b9c2db 100644 --- a/WorkoutsPlus/Workout/AddWorkout.swift +++ b/WorkoutsPlus/Workout/AddWorkout.swift @@ -36,6 +36,6 @@ struct AddWorkout: View { #Preview { Color.clear .sheet(isPresented: .constant(true)) { - AddWorkout(workout: Workout.sampleData.first!) + AddWorkout(workout: Workout.sampleData) } } diff --git a/WorkoutsPlus/Workout/AddExerciseToWorkout.swift b/WorkoutsPlus/Workout/AddWorkoutItemToWorkout.swift similarity index 53% rename from WorkoutsPlus/Workout/AddExerciseToWorkout.swift rename to WorkoutsPlus/Workout/AddWorkoutItemToWorkout.swift index dadcb3c..d046437 100644 --- a/WorkoutsPlus/Workout/AddExerciseToWorkout.swift +++ b/WorkoutsPlus/Workout/AddWorkoutItemToWorkout.swift @@ -8,7 +8,7 @@ import SwiftUI import SwiftData -struct AddExerciseToWorkout: View { +struct AddWorkoutItemToWorkout: View { @Environment(\.modelContext) private var modelContext @Query(sort: \Exercise.name) private var exercises: [Exercise] @@ -16,18 +16,21 @@ struct AddExerciseToWorkout: View { var body: some View { Group { - if !exercises.isEmpty { - List { - Section(header: Text("Excersises")) { - ForEach(exercises) { exercise in - AddExerciseToWorkoutListItem(exercise, workout) - } - } + List { + Section(header: Text("Utilities")) { + AddExerciseToWorkoutListItem(WorkoutItem(workoutItems: []), workout) } - } else { - ContentUnavailableView { - // TODO: Add Button that allows adding an exercise - Label("No Exercises", systemImage: Exercise.systemImage) + Section(header: Text("Excersises")) { + if !exercises.isEmpty { + ForEach(exercises) { exercise in + + AddExerciseToWorkoutListItem(WorkoutItem(from: exercise), workout) + }} else { + ContentUnavailableView { + // TODO: Add Button that allows adding an exercise + Label("No Exercises", systemImage: Exercise.systemImage) + } + } } } } @@ -37,20 +40,20 @@ struct AddExerciseToWorkout: View { struct AddExerciseToWorkoutListItem: View { @Environment(\.modelContext) private var modelContext - var exercise: Exercise + var workoutItem: WorkoutItem var workout: Workout - init(_ exercise: Exercise, _ workout: Workout) { - self.exercise = exercise + init(_ workoutItem: WorkoutItem, _ workout: Workout) { + self.workoutItem = workoutItem self.workout = workout } var body: some View { Button(action: { - workout.addExercise(exercise) + workout.add(workoutItem: workoutItem) }) { HStack { - Text(exercise.name) + Text(workoutItem.name) .foregroundStyle(.black) Spacer() Image(systemName: "plus.circle.fill") @@ -61,11 +64,11 @@ struct AddExerciseToWorkoutListItem: View { } #Preview("With Sample Data") { - AddExerciseToWorkout(workout: Workout.sampleData.first!) + AddWorkoutItemToWorkout(workout: Workout.sampleData) .modelContainer(SampleData.shared.modelContainer) } #Preview("Empty Database") { - AddExerciseToWorkout(workout: Workout.sampleData.first!) + AddWorkoutItemToWorkout(workout: Workout.sampleData) .modelContainer(for: Exercise.self, inMemory: true) } diff --git a/WorkoutsPlus/Workout/Workout.swift b/WorkoutsPlus/Workout/Workout.swift index a4d0947..67e82ea 100644 --- a/WorkoutsPlus/Workout/Workout.swift +++ b/WorkoutsPlus/Workout/Workout.swift @@ -17,36 +17,31 @@ final class Workout: Identifiable { // Other properties and methods var timestamp: Date = Date.now - @Relationship(deleteRule: .cascade) var exercises: [WorkoutItem] = [] + @Relationship(deleteRule: .cascade) var workoutItems: [WorkoutItem] = [] init(name: String) { self.name = name } - func addExercise(_ exercise: Exercise) { - self.exercises.append(WorkoutItem(from: exercise)) + func add(workoutItem: WorkoutItem) { + self.workoutItems.append(workoutItem) updateExercisePositions() } -// func addExercise(_ exercise: WorkoutItem) { -// self.exercises.append(exercise) -// updateExercisePositions() -// } - - func addExercise(_ exercises: [WorkoutItem]) { - for exercise in exercises { - self.exercises.append(exercise) + func add(workoutItems: [WorkoutItem]) { + for workoutItem in workoutItems { + self.workoutItems.append(workoutItem) } updateExercisePositions() } - func moveExercise(from source: IndexSet, to destination: Int) { - exercises.move(fromOffsets: source, toOffset: destination) + func moveWorkoutItem(from source: IndexSet, to destination: Int) { + workoutItems.move(fromOffsets: source, toOffset: destination) updateExercisePositions() } private func updateExercisePositions() { - for (index, exercise) in exercises.enumerated() { + for (index, exercise) in workoutItems.enumerated() { exercise.position = index } } @@ -56,11 +51,17 @@ final class Workout: Identifiable { extension Workout { private convenience init(name: String, exercises: [WorkoutItem]) { self.init(name: name) - self.exercises = exercises + self.workoutItems = exercises } - static let sampleData: [Workout] = [ - Workout(name: "Recommended Routine", exercises: WorkoutItem.sampleData), - Workout(name: "Marathon Plan", exercises: WorkoutItem.sampleData) - ] + static let sampleData: Workout = { + var workout = Workout(name: "Recommended Routine") + + for workoutItem in WorkoutItem.sampleData { + workout.add(workoutItem: workoutItem) + } + + return workout + }() + } diff --git a/WorkoutsPlus/Workout/WorkoutDetail.swift b/WorkoutsPlus/Workout/WorkoutDetail.swift index f005187..8c4bb62 100644 --- a/WorkoutsPlus/Workout/WorkoutDetail.swift +++ b/WorkoutsPlus/Workout/WorkoutDetail.swift @@ -23,18 +23,24 @@ struct WorkoutDetail: View { } Section(header: Text("Exercises")) { List { - ForEach(workout.exercises - .sorted(by: { $0.position < $1.position})) { exercise in - ExerciseListItem(workout, exercise) - } - .onDelete(perform: deleteExerciseFromWorkout) - .onMove(perform: move) + ForEach(workout.workoutItems + .sorted(by: { $0.position < $1.position})) { workoutItem in + switch workoutItem.workoutItemType { + case .exercise: + ExerciseListItem(workout, workoutItem) + case .set: + SetListItem(workout, workoutItem) + case .workout: + Text(workoutItem.name) + } + } + .onDelete(perform: deleteExerciseFromWorkout) + .onMove(perform: move) } .environment(\.editMode, .constant(.active)) // Always active drag mode - AddItemButton(label: "Exercise", action: addExerciseToWorkout) + AddItemButton(label: "Exercise", action: addWorkoutItemToWorkout) } } - .navigationBarTitle("Edit \(workout.name)") .toolbar { ToolbarItem() { @@ -43,14 +49,14 @@ struct WorkoutDetail: View { } .sheet(isPresented: $isPresenting) { NavigationStack { - AddExerciseToWorkout(workout: workout) + AddWorkoutItemToWorkout(workout: workout) } .presentationDetents([.medium, .large]) .presentationDragIndicator(.visible) } } - private func addExerciseToWorkout() { + private func addWorkoutItemToWorkout() { withAnimation { isPresenting = true } @@ -69,54 +75,20 @@ struct WorkoutDetail: View { private func deleteExerciseFromWorkout(offsets: IndexSet) { withAnimation { for index in offsets { - modelContext.delete(workout.exercises[index]) + modelContext.delete(workout.workoutItems[index]) } try? modelContext.save() } } private func move(from source: IndexSet, to destination: Int) { - workout.moveExercise(from: source, to: destination) - } -} - -struct ExerciseListItem: View { - var workout: Workout - @State var exercise: WorkoutItem - - init(_ workout: Workout, _ exercise: WorkoutItem ) { - self.workout = workout - self.exercise = exercise - } - - var body: some View { - Button(action: { - // workout.addExercise(from: exercise) - }) { - HStack { - Text(String(exercise.reps)) - .font(.system(size: 14, weight: .bold)) - .foregroundStyle(.white) - .frame(width: 20, height: 10) - .padding(8) - .background(Color.blue) - .clipShape(RoundedRectangle(cornerRadius: 8)) - Text(exercise.name) - .foregroundStyle(.black) - Spacer() - Stepper( - value: $exercise.reps, - in: 0...100, - step: 1 - ) {} - } - } + workout.moveWorkoutItem(from: source, to: destination) } } #Preview { NavigationStack { - WorkoutDetail(workout: Workout.sampleData.first!) + WorkoutDetail(workout: Workout.sampleData) .modelContainer(SampleData.shared.modelContainer) } } diff --git a/WorkoutsPlus/Workout/WorkoutItem.swift b/WorkoutsPlus/Workout/WorkoutItem.swift index 3ed6065..1543a10 100644 --- a/WorkoutsPlus/Workout/WorkoutItem.swift +++ b/WorkoutsPlus/Workout/WorkoutItem.swift @@ -12,18 +12,19 @@ import SwiftData final class WorkoutItem: Identifiable { var id = UUID() var name: String - var workoutItemType: WorkoutItemType - + var workout: Workout? - var children: [WorkoutItem] = [] + var workoutItemType: WorkoutItemType var position: Int = 0 + var reps: Int = 8 + + // EXERCISE var exercise: Exercise? { didSet { self.name = exercise?.name ?? "self.name" } } - var reps: Int = 0 init(_ reps: Int, _ exercise: String) { self.workoutItemType = .exercise @@ -40,8 +41,20 @@ final class WorkoutItem: Identifiable { self.exercise = exercise } + // SET + var workoutItems: [WorkoutItem] = [] + + init(workoutItems: [WorkoutItem] = []) { + self.name = "Set" + self.workoutItemType = .set + self.reps = 3 + workoutItems.forEach(addChild) + } + func addChild(_ child: WorkoutItem) { - self.children.append(child) + if self.workoutItemType == .set { + self.workoutItems.append(child) + } } }