change folders to the "feature" mindset
This commit is contained in:
@@ -49,19 +49,19 @@ struct SetListItem: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
//#Preview {
|
||||||
@Previewable @State var set = WorkoutItem(set: [
|
// @Previewable @State var set = WorkoutItem(set: [
|
||||||
WorkoutItem(Exercise("Squat")),
|
// WorkoutItem(Exercise("Squat")),
|
||||||
WorkoutItem(Exercise("Squat")),
|
// WorkoutItem(Exercise("Squat")),
|
||||||
WorkoutItem(Exercise("Squat"))])
|
// WorkoutItem(Exercise("Squat"))])
|
||||||
List {
|
// List {
|
||||||
SetListItem(workout: Workout(name: "RR"), set: $set)
|
// SetListItem(workout: Workout(name: "RR"), set: $set)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
#Preview("Empty Database") {
|
//#Preview("Empty Database") {
|
||||||
@Previewable @State var set = WorkoutItem(set: [])
|
// @Previewable @State var set = WorkoutItem(set: [])
|
||||||
List {
|
// List {
|
||||||
SetListItem(workout: Workout(name: "RR"), set: $set)
|
// SetListItem(workout: Workout(name: "RR"), set: $set)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|||||||
@@ -49,8 +49,7 @@ struct ContentView: View {
|
|||||||
Section {
|
Section {
|
||||||
NavigationLink(
|
NavigationLink(
|
||||||
destination: ActiveWorkoutSession(
|
destination: ActiveWorkoutSession(
|
||||||
activeWorkoutSession: $activeWorkoutSession,
|
activeWorkoutSession: $activeWorkoutSession)
|
||||||
activeWorkout: activeWorkoutSession.workout)
|
|
||||||
) {
|
) {
|
||||||
HStack {
|
HStack {
|
||||||
Label("Back to Session", systemImage: "memories")
|
Label("Back to Session", systemImage: "memories")
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ struct WorkoutLog: View {
|
|||||||
ForEach(workoutSessions) { session in
|
ForEach(workoutSessions) { session in
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(session.startDate.ISO8601Format())
|
Text(session.startDate.ISO8601Format())
|
||||||
Text(session.workout.name)
|
Text(session.name)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
}
|
}
|
||||||
}.onDelete(perform: deleteWorkoutSession)
|
}.onDelete(perform: deleteWorkoutSession)
|
||||||
@@ -37,14 +37,14 @@ final class WorkoutItem: Nameable, Positionable {
|
|||||||
var id = UUID()
|
var id = UUID()
|
||||||
var name: String
|
var name: String
|
||||||
var workout: Workout?
|
var workout: Workout?
|
||||||
|
// TODO: Make WorkoutItem a protocol so the type can be distinguished by the class type
|
||||||
var workoutItemType: WorkoutItemType // Differentiates between exercise/rest/set
|
var workoutItemType: WorkoutItemType // Differentiates between exercise/rest/set
|
||||||
var position: Int = 0
|
var position: Int = 0
|
||||||
var set: [WorkoutItem] = []
|
var set: [WorkoutItem] = []
|
||||||
|
|
||||||
// Exercise has to be optional to allow Rest and Set to be a WorkoutItem (without being an Exercise).
|
var exercise: Exercise // Do Push-up | Run Marathon
|
||||||
var exerciseData: Exercise? // Do Push-up | Run Marathon
|
var plannedReps: Int // 8 times | 1 time
|
||||||
var plannedReps: Int // 8 times | 1 time
|
var plannedValue: Double // With 10 | 42,187
|
||||||
var plannedValue: Double // With 10 | 42,187
|
|
||||||
var metric: ExerciseMetric? // kg (weight) | km (distance)
|
var metric: ExerciseMetric? // kg (weight) | km (distance)
|
||||||
|
|
||||||
enum WorkoutItemType: Codable {
|
enum WorkoutItemType: Codable {
|
||||||
@@ -54,7 +54,7 @@ final class WorkoutItem: Nameable, Positionable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(_ exercise: Exercise) {
|
init(_ exercise: Exercise) {
|
||||||
self.exerciseData = exercise
|
self.exercise = exercise
|
||||||
self.workoutItemType = .exercise
|
self.workoutItemType = .exercise
|
||||||
|
|
||||||
// Push-up
|
// Push-up
|
||||||
@@ -67,27 +67,25 @@ final class WorkoutItem: Nameable, Positionable {
|
|||||||
self.metric = exercise.metric
|
self.metric = exercise.metric
|
||||||
}
|
}
|
||||||
|
|
||||||
init(set: [WorkoutItem] = []) {
|
// init(set: [WorkoutItem] = []) {
|
||||||
self.workoutItemType = .set
|
// self.workoutItemType = .set
|
||||||
self.name = "Set"
|
// self.name = "Set"
|
||||||
self.plannedReps = 3
|
// self.plannedReps = 3
|
||||||
self.plannedValue = 0
|
// self.plannedValue = 0
|
||||||
set.forEach(addChild)
|
// set.forEach(addChild)
|
||||||
}
|
// }
|
||||||
|
// init(rest: Double) {
|
||||||
|
// self.workoutItemType = .rest
|
||||||
|
// self.name = "Rest"
|
||||||
|
// self.plannedReps = 1
|
||||||
|
// self.plannedValue = rest
|
||||||
|
// self.metric = .time
|
||||||
|
// }
|
||||||
func addChild(_ child: WorkoutItem) {
|
func addChild(_ child: WorkoutItem) {
|
||||||
if self.workoutItemType == .set {
|
if self.workoutItemType == .set {
|
||||||
self.set.append(child)
|
self.set.append(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(rest: Double) {
|
|
||||||
self.workoutItemType = .rest
|
|
||||||
self.name = "Rest"
|
|
||||||
self.plannedReps = 1
|
|
||||||
self.plannedValue = rest
|
|
||||||
self.metric = .time
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension WorkoutItem {
|
extension WorkoutItem {
|
||||||
@@ -19,17 +19,17 @@ struct WorkoutItemLibrarySheet: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
List {
|
List {
|
||||||
Section(header: Text("Utilities")) {
|
// Section(header: Text("Utilities")) {
|
||||||
AddItemButton(label: "Set") {
|
// AddItemButton(label: "Set") {
|
||||||
addWorkoutItemtoWorkout(WorkoutItem(set: [
|
// addWorkoutItemtoWorkout(WorkoutItem(set: [
|
||||||
WorkoutItem(Exercise("Set item 1")),
|
// WorkoutItem(Exercise("Set item 1")),
|
||||||
WorkoutItem(Exercise("Set item 2"))
|
// WorkoutItem(Exercise("Set item 2"))
|
||||||
]))
|
// ]))
|
||||||
}
|
// }
|
||||||
AddItemButton(label: "Rest") {
|
// AddItemButton(label: "Rest") {
|
||||||
addWorkoutItemtoWorkout(WorkoutItem(rest: 45))
|
// addWorkoutItemtoWorkout(WorkoutItem(rest: 45))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
Section(header: Text("Excersises")) {
|
Section(header: Text("Excersises")) {
|
||||||
if !exercises.isEmpty {
|
if !exercises.isEmpty {
|
||||||
ForEach(exercises) { exercise in
|
ForEach(exercises) { exercise in
|
||||||
@@ -34,18 +34,20 @@ struct WorkoutLibrary: View {
|
|||||||
NavigationLink {
|
NavigationLink {
|
||||||
WorkoutDetail(activeWorkoutSession: $activeWorkoutSession, workout: workout)
|
WorkoutDetail(activeWorkoutSession: $activeWorkoutSession, workout: workout)
|
||||||
} label: {
|
} label: {
|
||||||
Button(action: {
|
Image(systemName: workout.workoutIconSystemName)
|
||||||
activeWorkoutSession = workout.start()
|
.foregroundStyle(workout.workoutIconColorName.color)
|
||||||
}) {
|
|
||||||
Image(systemName: "play.fill")
|
|
||||||
.foregroundStyle(.green)
|
|
||||||
}
|
|
||||||
// TODO: Decide if icon should appear here/create custom view
|
|
||||||
// Image(systemName: workout.workoutIconSystemName)
|
|
||||||
// .foregroundStyle(workout.workoutIconColorName.color)
|
|
||||||
Text(workout.name)
|
Text(workout.name)
|
||||||
}
|
}
|
||||||
|
.swipeActions(edge: .leading) {
|
||||||
|
Button {
|
||||||
|
activeWorkoutSession = workout.start()
|
||||||
|
} label: {
|
||||||
|
Label("Start", systemImage: "play")
|
||||||
|
.tint(.green)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.onDelete(perform: deleteWorkout)
|
.onDelete(perform: deleteWorkout)
|
||||||
if filteredItems.isEmpty {
|
if filteredItems.isEmpty {
|
||||||
ContentUnavailableView.search(text: searchText)
|
ContentUnavailableView.search(text: searchText)
|
||||||
@@ -37,12 +37,12 @@ struct WorkoutListItem: View {
|
|||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
List {
|
List {
|
||||||
WorkoutListItem(Workout(name: "RR"), WorkoutItem(set: [
|
// WorkoutListItem(Workout(name: "RR"), WorkoutItem(set: [
|
||||||
WorkoutItem(Exercise("Squat")),
|
// WorkoutItem(Exercise("Squat")),
|
||||||
WorkoutItem(Exercise("Squat")),
|
// WorkoutItem(Exercise("Squat")),
|
||||||
WorkoutItem(Exercise("Squat"))]))
|
// WorkoutItem(Exercise("Squat"))]))
|
||||||
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Push-ups")))
|
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Push-ups")))
|
||||||
WorkoutListItem(Workout(name: "RR"), WorkoutItem(rest: 15))
|
// WorkoutListItem(Workout(name: "RR"), WorkoutItem(rest: 15))
|
||||||
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Push-ups")))
|
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Push-ups")))
|
||||||
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Sprint", .distance)))
|
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Sprint", .distance)))
|
||||||
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Run", .time)))
|
WorkoutListItem(Workout(name: "RR"), WorkoutItem(Exercise("Run", .time)))
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SwiftData
|
import SwiftData
|
||||||
|
|
||||||
|
// This view can only be viewed, if there exists a WorkoutSession that can be considered active (a person working out).
|
||||||
struct ActiveWorkoutSession: View {
|
struct ActiveWorkoutSession: View {
|
||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
|
|
||||||
@@ -10,17 +11,14 @@ struct ActiveWorkoutSession: View {
|
|||||||
@Query(sort: \WorkoutSession.name) var workoutSessions: [WorkoutSession]
|
@Query(sort: \WorkoutSession.name) var workoutSessions: [WorkoutSession]
|
||||||
@Binding var activeWorkoutSession: WorkoutSession?
|
@Binding var activeWorkoutSession: WorkoutSession?
|
||||||
|
|
||||||
@Query(sort: \Workout.name) var workouts: [Workout]
|
|
||||||
@State var activeWorkout: Workout
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
List {
|
List {
|
||||||
Section(header: Text("Workout")) {
|
Section(header: Text("Workout")) {
|
||||||
Text(activeWorkout.name)
|
Text(activeWorkoutSession!.name)
|
||||||
}
|
}
|
||||||
Section(header: Text("Exercises")) {
|
Section(header: Text("Exercises")) {
|
||||||
ForEach(getActiveWorkoutItems(activeWorkout: activeWorkout)) { workoutItem in
|
ForEach(activeWorkoutSession!.workoutSessionItems) { workoutItem in
|
||||||
ActiveWorkoutSessionListItem(workoutItem: workoutItem)
|
ActiveWorkoutSessionListItem(workoutItem: workoutItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +26,6 @@ struct ActiveWorkoutSession: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle("Session")
|
.navigationTitle("Session")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
if (isWorkingOut) {
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
isWorkingOut = false
|
isWorkingOut = false
|
||||||
activeWorkoutSession?.stop()
|
activeWorkoutSession?.stop()
|
||||||
@@ -41,21 +38,6 @@ struct ActiveWorkoutSession: View {
|
|||||||
.bold()
|
.bold()
|
||||||
.fontDesign(.rounded)
|
.fontDesign(.rounded)
|
||||||
.tint(.red)
|
.tint(.red)
|
||||||
}
|
|
||||||
else {
|
|
||||||
Button(action: {
|
|
||||||
isWorkingOut = true
|
|
||||||
activeWorkoutSession = activeWorkout.start()
|
|
||||||
}) {
|
|
||||||
HStack {
|
|
||||||
Image(systemName: "play.fill")
|
|
||||||
Text("Start")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.bold()
|
|
||||||
.fontDesign(.rounded)
|
|
||||||
.tint(.green)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +58,7 @@ struct ActiveWorkoutSession: View {
|
|||||||
@Previewable @State var workout = Workout.sampleData.first!
|
@Previewable @State var workout = Workout.sampleData.first!
|
||||||
|
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
ActiveWorkoutSession(activeWorkoutSession: $activeWorkoutSession, activeWorkout: workout)
|
ActiveWorkoutSession(activeWorkoutSession: $activeWorkoutSession)
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Defaults.shared.isWorkingOut = false
|
Defaults.shared.isWorkingOut = false
|
||||||
@@ -8,14 +8,10 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ActiveWorkoutSessionListItem: View {
|
struct ActiveWorkoutSessionListItem: View {
|
||||||
var workoutItem: WorkoutItem
|
var workoutItem: WorkoutSessionItem
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
switch workoutItem.workoutItemType {
|
|
||||||
case .set:
|
|
||||||
Text(workoutItem.name)
|
|
||||||
case .exercise:
|
|
||||||
Text(workoutItem.name)
|
Text(workoutItem.name)
|
||||||
Spacer()
|
Spacer()
|
||||||
Button(action: {
|
Button(action: {
|
||||||
@@ -24,17 +20,15 @@ struct ActiveWorkoutSessionListItem: View {
|
|||||||
Image(systemName: "info.circle")
|
Image(systemName: "info.circle")
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
}
|
}
|
||||||
case .rest:
|
|
||||||
Text("Pause")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
|
@Previewable @State var workoutSession = WorkoutSession(start: Workout.sampleData.first!)
|
||||||
List {
|
List {
|
||||||
ForEach(WorkoutItem.sampleDataRecommendedRoutine) { item in
|
ForEach(WorkoutItem.sampleDataRecommendedRoutine) { item in
|
||||||
ActiveWorkoutSessionListItem(workoutItem: item)
|
ActiveWorkoutSessionListItem(workoutItem: WorkoutSessionItem(workoutSession: workoutSession, planned: item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,16 +11,16 @@ import SwiftData
|
|||||||
@Model
|
@Model
|
||||||
final class WorkoutSession: Nameable {
|
final class WorkoutSession: Nameable {
|
||||||
var id = UUID()
|
var id = UUID()
|
||||||
var name = ""
|
var name: String
|
||||||
// The Workout is what *should* happen
|
// TODO: Think about if a refrence to the workout makes sense; a Workout could change.
|
||||||
var workout: Workout {
|
// var workout: Workout
|
||||||
didSet {
|
var workoutSessionItems: [WorkoutSessionItem] = []
|
||||||
self.name = workout?.name ?? "Unknown Workout"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init(start with: Workout) {
|
init(start workout: Workout) {
|
||||||
self.workout = with
|
self.name = workout.name
|
||||||
|
for workoutItem in workout.getWorkoutItems() {
|
||||||
|
workoutSessionItems.append(WorkoutSessionItem(workoutSession: self, planned: workoutItem))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// State
|
// State
|
||||||
@@ -69,9 +69,7 @@ final class WorkoutSession: Nameable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nextExercise() {
|
func nextExercise() {
|
||||||
if currentExercise < workout.getWorkoutItems().count - 1 {
|
|
||||||
currentExercise += 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -- Workout Information
|
// MARK: -- Workout Information
|
||||||
@@ -84,7 +82,7 @@ final class WorkoutSession: Nameable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getTotalExerciseCount() -> Double {
|
func getTotalExerciseCount() -> Double {
|
||||||
return Double(workout.getWorkoutItems().count)
|
return 100.0
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentExerciseIndex() -> Double {
|
func getCurrentExerciseIndex() -> Double {
|
||||||
@@ -96,10 +94,10 @@ final class WorkoutSession: Nameable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentExerciseName() -> String {
|
func getCurrentExerciseName() -> String {
|
||||||
return workout.getWorkoutItems()[Int(currentExercise)].name
|
return "Hello"
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentExerciseMetric() -> String {
|
func getCurrentExerciseMetric() -> String {
|
||||||
return String(workout.getWorkoutItems()[Int(currentExercise)].plannedReps)
|
return "Hello"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// WorkoutSessionItem.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 15.10.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
@Model
|
||||||
|
final class WorkoutSessionItem: Nameable, Positionable {
|
||||||
|
var id = UUID()
|
||||||
|
var position: Int
|
||||||
|
var name: String
|
||||||
|
|
||||||
|
var workoutSession: WorkoutSession
|
||||||
|
|
||||||
|
var plannedReps: Int // 8 times | 1 time
|
||||||
|
var plannedValue: Double // With 10 | 42,187
|
||||||
|
var metric: ExerciseMetric? // kg (weight) | km (distance)
|
||||||
|
|
||||||
|
var actualReps: Int?
|
||||||
|
var actualValue: Double?
|
||||||
|
|
||||||
|
init(workoutSession: WorkoutSession, planned: WorkoutItem) {
|
||||||
|
self.workoutSession = workoutSession
|
||||||
|
self.name = planned.exercise.name
|
||||||
|
self.position = planned.position
|
||||||
|
self.plannedReps = planned.plannedReps
|
||||||
|
self.plannedValue = planned.plannedValue
|
||||||
|
self.metric = planned.metric
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// WorkoutSessionItem.swift
|
|
||||||
// WorkoutsPlus
|
|
||||||
//
|
|
||||||
// Created by Felix Förtsch on 15.10.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftData
|
|
||||||
|
|
||||||
@Model
|
|
||||||
final class WorkoutSessionItem {
|
|
||||||
var id = UUID()
|
|
||||||
|
|
||||||
var workoutSession: WorkoutSession
|
|
||||||
|
|
||||||
var exerciseData: WorkoutItem
|
|
||||||
var actualReps: Int?
|
|
||||||
var actualValue: Double?
|
|
||||||
|
|
||||||
init(workoutSession: WorkoutSession, planned: WorkoutItem) {
|
|
||||||
self.workoutSession = workoutSession
|
|
||||||
self.exerciseData = planned
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,10 +3,10 @@ erDiagram
|
|||||||
Exercise 1 .. 0+ Equipment : uses
|
Exercise 1 .. 0+ Equipment : uses
|
||||||
Exercise 1 -- 0+ WorkoutItem : "provides data for"
|
Exercise 1 -- 0+ WorkoutItem : "provides data for"
|
||||||
Workout 1 .. 0+ WorkoutItem : collects
|
Workout 1 .. 0+ WorkoutItem : collects
|
||||||
Workout 1 .. 0+ ViewModel : "provides data for"
|
Workout 1 .. 0+ WorkoutSession : "starts"
|
||||||
WorkoutItem 1 -- 1 WorkoutSessionItem : "provides data for"
|
WorkoutItem 1 -- 1 WorkoutSessionItem : "provides data for"
|
||||||
WorkoutSession 1 -- 1+ WorkoutSessionItem : collects
|
WorkoutSession 1 -- 1+ WorkoutSessionItem : collects
|
||||||
ViewModel 1 -- 1+ WorkoutSession : "creates, starts"
|
ContentView 1 -- 1 WorkoutSession : "holds onto"
|
||||||
|
|
||||||
Exercise {
|
Exercise {
|
||||||
string name
|
string name
|
||||||
@@ -29,7 +29,7 @@ erDiagram
|
|||||||
int actualReps
|
int actualReps
|
||||||
double actualValue
|
double actualValue
|
||||||
}
|
}
|
||||||
ViewModel {
|
ContentView {
|
||||||
Workout workout
|
Workout workout
|
||||||
WorkoutSession workoutSession
|
WorkoutSession workoutSession
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user