146 lines
3.7 KiB
Swift
146 lines
3.7 KiB
Swift
//
|
|
// Workout.swift
|
|
// WorkoutsPlus
|
|
//
|
|
// Created by Felix Förtsch on 10.08.24.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SwiftData
|
|
|
|
@Model
|
|
final class Workout: Nameable, Hashable {
|
|
static var systemImage = "figure.run.square.stack"
|
|
|
|
var id = UUID()
|
|
var creationDate = Date.now
|
|
|
|
// The name of my workout is: Recommended Routine, My Marathon Workout
|
|
@Attribute(.unique) var name: String
|
|
var defaultRestTime: TimeInterval = 60
|
|
var useDefaultRestTime: Bool = false
|
|
|
|
// Icon
|
|
var workoutIconSystemName = "figure.run"
|
|
var workoutIconColorName = ColorName.black
|
|
|
|
// TODO: Expected Duration (learn from past workouts with ML and predict workout duration from that
|
|
|
|
@Relationship(deleteRule: .cascade) private var workoutItems: [WorkoutItem] = []
|
|
func getWorkoutItems() -> [WorkoutItem] {
|
|
return workoutItems.sorted { $0.position < $1.position }
|
|
}
|
|
|
|
init(name: String) {
|
|
self.name = name
|
|
}
|
|
|
|
func start() -> WorkoutSession {
|
|
return WorkoutSession(start: self)
|
|
}
|
|
|
|
func add(workoutItem: WorkoutItem) {
|
|
self.workoutItems.append(workoutItem)
|
|
updateWorkoutItemsPositions()
|
|
}
|
|
|
|
func add(workoutItems: [WorkoutItem]) {
|
|
for workoutItem in workoutItems {
|
|
self.workoutItems.append(workoutItem)
|
|
}
|
|
updateWorkoutItemsPositions()
|
|
}
|
|
|
|
func moveWorkoutItem(from source: IndexSet, to destination: Int) {
|
|
workoutItems.move(fromOffsets: source, toOffset: destination)
|
|
updateWorkoutItemsPositions()
|
|
}
|
|
|
|
private func updateWorkoutItemsPositions() {
|
|
for (index, exercise) in workoutItems.enumerated() {
|
|
exercise.position = index
|
|
}
|
|
}
|
|
|
|
func isSelected(workout: Workout) -> Bool { self.id == workout.id }
|
|
}
|
|
|
|
extension Workout {
|
|
// This enum maps the system colors to enable storing them with the SwiftData model data as String.
|
|
// See Workout.workoutIconColorName for a usage example.
|
|
// TODO: Use a Macro to reduce this horrible contraption to something maintainable.
|
|
enum ColorName: String, Codable {
|
|
case black
|
|
case blue
|
|
case cyan
|
|
case gray
|
|
case green
|
|
case indigo
|
|
case mint
|
|
case orange
|
|
case pink
|
|
case purple
|
|
case red
|
|
case white
|
|
case yellow
|
|
|
|
var color: Color {
|
|
switch self {
|
|
case .black: return .black
|
|
case .blue: return .blue
|
|
case .cyan: return .cyan
|
|
case .gray: return .gray
|
|
case .green: return .green
|
|
case .indigo: return .indigo
|
|
case .mint: return .mint
|
|
case .orange: return .orange
|
|
case .pink: return .pink
|
|
case .purple: return .purple
|
|
case .red: return .red
|
|
case .white: return .white
|
|
case .yellow: return .yellow
|
|
}
|
|
}
|
|
|
|
static func fromColor(_ color: Color) -> ColorName? {
|
|
switch color {
|
|
case .black: return .black
|
|
case .blue: return .blue
|
|
case .cyan: return .cyan
|
|
case .gray: return .gray
|
|
case .green: return .green
|
|
case .indigo: return .indigo
|
|
case .mint: return .mint
|
|
case .orange: return .orange
|
|
case .pink: return .pink
|
|
case .purple: return .purple
|
|
case .red: return .red
|
|
case .white: return .white
|
|
case .yellow: return .yellow
|
|
default: return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Workout {
|
|
private convenience init(name: String, workoutItems: [WorkoutItem]) {
|
|
self.init(name: name)
|
|
self.workoutItems = workoutItems
|
|
}
|
|
|
|
static let sampleData: [Workout] = {
|
|
var rr = Workout(name: "Recommended Routine")
|
|
for workoutItem in WorkoutItem.sampleDataRecommendedRoutine {
|
|
rr.add(workoutItem: workoutItem)
|
|
}
|
|
|
|
var rings = Workout(name: "Fully Body Rings")
|
|
for workoutItem in WorkoutItem.sampleDataRings {
|
|
rings.add(workoutItem: workoutItem)
|
|
}
|
|
|
|
return [rr, rings]
|
|
}()
|
|
}
|