fix repetitive save of WorkoutItem, add: Onboarding, Defaults, Settings, Trainer/Trainee skeletons, reorder files, remove all Bindable
This commit is contained in:
@@ -12,9 +12,7 @@ struct AddItemButton: View {
|
|||||||
var action: () -> Void
|
var action: () -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: {
|
Button(action: action) {
|
||||||
action()
|
|
||||||
}) {
|
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "plus.circle.fill")
|
Image(systemName: "plus.circle.fill")
|
||||||
.foregroundStyle(.green)
|
.foregroundStyle(.green)
|
||||||
|
|||||||
@@ -20,23 +20,7 @@ struct ExerciseListItem: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
// workout.addExercise(from: exercise)
|
// workout.addExercise(from: exercise)
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
StepperListItem(itemName: exercise.name, itemValue: $exercise.reps)
|
||||||
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
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
WorkoutsPlus/Components/StepperListItem.swift
Normal file
43
WorkoutsPlus/Components/StepperListItem.swift
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// StepperListItem.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 04.09.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct StepperListItem: View {
|
||||||
|
@State var itemName: String
|
||||||
|
@Binding var itemValue: Int
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Stepper(
|
||||||
|
value: $itemValue,
|
||||||
|
in: 0...100,
|
||||||
|
step: 1
|
||||||
|
) {
|
||||||
|
HStack {
|
||||||
|
Text(String(itemValue))
|
||||||
|
.font(.system(size: 14, weight: .bold))
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.frame(width: 20, height: 10)
|
||||||
|
.padding(8)
|
||||||
|
.background(Color.blue)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||||
|
Text(itemName)
|
||||||
|
.foregroundStyle(.black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
@Previewable @State var value = 8
|
||||||
|
|
||||||
|
List {
|
||||||
|
StepperListItem(itemName: "Short Name", itemValue: $value)
|
||||||
|
StepperListItem(itemName: "Very very very very long name with whitespace", itemValue: $value)
|
||||||
|
StepperListItem(itemName: "Veryveryverylonglonglonglongnamewithoutwithwhitespace", itemValue: $value)
|
||||||
|
}
|
||||||
|
}
|
||||||
45
WorkoutsPlus/Configuration/Defaults.swift
Normal file
45
WorkoutsPlus/Configuration/Defaults.swift
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Defaults.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 04.09.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public class Defaults: ObservableObject {
|
||||||
|
@AppStorage("isFirstAppStart") public var isFirstAppStart = true
|
||||||
|
@AppStorage("isOnboarding") public var isOnboarding = true
|
||||||
|
@AppStorage("userId") public var userId = UUID().uuidString
|
||||||
|
|
||||||
|
@AppStorage("sets") public var sets = 8
|
||||||
|
@AppStorage("reps") public var reps = 8
|
||||||
|
|
||||||
|
public static let shared = Defaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
@propertyWrapper
|
||||||
|
public struct Default<T>: DynamicProperty {
|
||||||
|
@ObservedObject private var defaults: Defaults
|
||||||
|
private let keyPath: ReferenceWritableKeyPath<Defaults, T>
|
||||||
|
|
||||||
|
public init(_ keyPath: ReferenceWritableKeyPath<Defaults, T>, defaults: Defaults = .shared) {
|
||||||
|
self.keyPath = keyPath
|
||||||
|
self.defaults = defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
public var wrappedValue: T {
|
||||||
|
get { defaults[keyPath: keyPath] }
|
||||||
|
nonmutating set { defaults[keyPath: keyPath] = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
public var projectedValue: Binding<T> {
|
||||||
|
Binding(
|
||||||
|
get: { defaults[keyPath: keyPath] },
|
||||||
|
set: { value in
|
||||||
|
defaults[keyPath: keyPath] = value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,11 @@ import SwiftData
|
|||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
|
@Default(\.isOnboarding) var isOnboarding
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TabView {
|
TabView {
|
||||||
WorkoutLog()
|
// WorkoutLog()
|
||||||
WorkoutLibrary()
|
WorkoutLibrary()
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Image(systemName: "figure.run.square.stack")
|
Image(systemName: "figure.run.square.stack")
|
||||||
@@ -24,7 +25,7 @@ struct ContentView: View {
|
|||||||
Image(systemName: "figure.run")
|
Image(systemName: "figure.run")
|
||||||
Text("Exercises")
|
Text("Exercises")
|
||||||
}
|
}
|
||||||
DebugExerciseList()
|
DebugList()
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Image(systemName: "hammer")
|
Image(systemName: "hammer")
|
||||||
Text("Debug")
|
Text("Debug")
|
||||||
@@ -35,6 +36,7 @@ struct ContentView: View {
|
|||||||
Text("Settings")
|
Text("Settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: $isOnboarding) { Onboarding() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
64
WorkoutsPlus/Debug/DebugList.swift
Normal file
64
WorkoutsPlus/Debug/DebugList.swift
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// DebugList.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 30.08.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
struct DebugList: View {
|
||||||
|
@Query(sort: \Exercise.name) private var exercises: [Exercise]
|
||||||
|
@Query(sort: \Workout.name) private var workouts: [Workout]
|
||||||
|
@Query(sort: \WorkoutItem.name) private var workoutItems: [WorkoutItem]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
List {
|
||||||
|
Section(header: Text("Exercises")) {
|
||||||
|
ForEach(exercises) { exercise in
|
||||||
|
DebugListItem(item: exercise)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section(header: Text("Workouts")) {
|
||||||
|
ForEach(workouts) { workout in
|
||||||
|
DebugListItem(item: workout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section(header: Text("WorkoutItems")) {
|
||||||
|
ForEach(workoutItems) { workoutItem in
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("\(workoutItem.name), pos: \(workoutItem.position), reps: \(workoutItem.reps)")
|
||||||
|
Text(workoutItem.id.uuidString)
|
||||||
|
.font(.system(size: 12, weight: .bold))
|
||||||
|
.fontDesign(.monospaced)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "wrench")
|
||||||
|
Text("Exercise Debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DebugListItem: View {
|
||||||
|
var item: any Nameable
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(item.name)
|
||||||
|
Text(item.id.uuidString)
|
||||||
|
.font(.system(size: 12, weight: .bold))
|
||||||
|
.fontDesign(.monospaced)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
DebugList()
|
||||||
|
.modelContainer(SampleData.shared.modelContainer)
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// DebugExerciseList.swift
|
|
||||||
// WorkoutsPlus
|
|
||||||
//
|
|
||||||
// Created by Felix Förtsch on 30.08.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import SwiftData
|
|
||||||
|
|
||||||
struct DebugExerciseList: View {
|
|
||||||
@Query(sort: \WorkoutItem.name) private var exercises: [WorkoutItem]
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
List {
|
|
||||||
ForEach(exercises) { exercise in
|
|
||||||
Text("\(exercise.name), pos: \(exercise.position)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tabItem {
|
|
||||||
Image(systemName: "figure.run.square.stack")
|
|
||||||
Text("Exercise Debug")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
DebugExerciseList()
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,7 @@ struct AddExercise: View {
|
|||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
@Bindable var exercise: Exercise
|
@State var exercise: Exercise
|
||||||
|
|
||||||
var body : some View {
|
var body : some View {
|
||||||
Form {
|
Form {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// ExerciseDetailsView.swift
|
// ExerciseDetail.swift
|
||||||
// WorkoutsPlus
|
// WorkoutsPlus
|
||||||
//
|
//
|
||||||
// Created by Felix Förtsch on 10.08.24.
|
// Created by Felix Förtsch on 10.08.24.
|
||||||
@@ -11,7 +11,7 @@ struct ExerciseDetail: View {
|
|||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
|
|
||||||
@Bindable var exercise: Exercise
|
@State var exercise: Exercise
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import Foundation
|
|||||||
import SwiftData
|
import SwiftData
|
||||||
|
|
||||||
@Model
|
@Model
|
||||||
final class Exercise: Identifiable {
|
final class Exercise: Nameable {
|
||||||
var id = UUID()
|
|
||||||
static var systemImage = "figure.run"
|
static var systemImage = "figure.run"
|
||||||
|
|
||||||
|
var id = UUID()
|
||||||
@Attribute(.unique) var name: String
|
@Attribute(.unique) var name: String
|
||||||
// var metric: String = "reps"
|
// var metric: String = "reps"
|
||||||
// var exerciseDescription: ExerciseDescription?
|
// var exerciseDescription: ExerciseDescription?
|
||||||
34
WorkoutsPlus/Models/Person.swift
Normal file
34
WorkoutsPlus/Models/Person.swift
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Person.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 03.09.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
@Model
|
||||||
|
class Trainer: Nameable {
|
||||||
|
var id = UUID()
|
||||||
|
var name: String = ""
|
||||||
|
|
||||||
|
var trainees: [Trainee] = []
|
||||||
|
|
||||||
|
init(name: String) {
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Model
|
||||||
|
class Trainee: Nameable {
|
||||||
|
var id = UUID()
|
||||||
|
var name: String = ""
|
||||||
|
|
||||||
|
var trainer: Trainer?
|
||||||
|
|
||||||
|
init(name: String, trainer: Trainer? = nil) {
|
||||||
|
self.name = name
|
||||||
|
self.trainer = trainer
|
||||||
|
}
|
||||||
|
}
|
||||||
18
WorkoutsPlus/Models/Protocols.swift
Normal file
18
WorkoutsPlus/Models/Protocols.swift
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// Protocols.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 04.09.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol Nameable: Identifiable {
|
||||||
|
var id: UUID { get }
|
||||||
|
var name: String { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol Positionable: Identifiable {
|
||||||
|
var id: UUID { get }
|
||||||
|
var position: Int { get }
|
||||||
|
}
|
||||||
@@ -9,11 +9,12 @@ import Foundation
|
|||||||
import SwiftData
|
import SwiftData
|
||||||
|
|
||||||
@Model
|
@Model
|
||||||
final class Workout: Identifiable {
|
final class Workout: Nameable {
|
||||||
var id = UUID()
|
|
||||||
var name: String
|
|
||||||
static var systemImage = "figure.run.square.stack"
|
static var systemImage = "figure.run.square.stack"
|
||||||
|
|
||||||
|
var id = UUID()
|
||||||
|
@Attribute(.unique) var name: String
|
||||||
|
|
||||||
// Other properties and methods
|
// Other properties and methods
|
||||||
var timestamp: Date = Date.now
|
var timestamp: Date = Date.now
|
||||||
|
|
||||||
@@ -25,22 +26,22 @@ final class Workout: Identifiable {
|
|||||||
|
|
||||||
func add(workoutItem: WorkoutItem) {
|
func add(workoutItem: WorkoutItem) {
|
||||||
self.workoutItems.append(workoutItem)
|
self.workoutItems.append(workoutItem)
|
||||||
updateExercisePositions()
|
updateWorkoutItemsPositions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(workoutItems: [WorkoutItem]) {
|
func add(workoutItems: [WorkoutItem]) {
|
||||||
for workoutItem in workoutItems {
|
for workoutItem in workoutItems {
|
||||||
self.workoutItems.append(workoutItem)
|
self.workoutItems.append(workoutItem)
|
||||||
}
|
}
|
||||||
updateExercisePositions()
|
updateWorkoutItemsPositions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveWorkoutItem(from source: IndexSet, to destination: Int) {
|
func moveWorkoutItem(from source: IndexSet, to destination: Int) {
|
||||||
workoutItems.move(fromOffsets: source, toOffset: destination)
|
workoutItems.move(fromOffsets: source, toOffset: destination)
|
||||||
updateExercisePositions()
|
updateWorkoutItemsPositions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateExercisePositions() {
|
private func updateWorkoutItemsPositions() {
|
||||||
for (index, exercise) in workoutItems.enumerated() {
|
for (index, exercise) in workoutItems.enumerated() {
|
||||||
exercise.position = index
|
exercise.position = index
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Exercise.swift
|
// WorkoutItem.swift
|
||||||
// WorkoutsPlus
|
// WorkoutsPlus
|
||||||
//
|
//
|
||||||
// Created by Felix Förtsch on 10.08.24.
|
// Created by Felix Förtsch on 10.08.24.
|
||||||
@@ -9,10 +9,10 @@ import Foundation
|
|||||||
import SwiftData
|
import SwiftData
|
||||||
|
|
||||||
@Model
|
@Model
|
||||||
final class WorkoutItem: Identifiable {
|
final class WorkoutItem: Nameable, Positionable {
|
||||||
var id = UUID()
|
var id = UUID()
|
||||||
var name: String
|
var name: String
|
||||||
|
|
||||||
var workout: Workout?
|
var workout: Workout?
|
||||||
var workoutItemType: WorkoutItemType
|
var workoutItemType: WorkoutItemType
|
||||||
var position: Int = 0
|
var position: Int = 0
|
||||||
@@ -20,11 +20,9 @@ final class WorkoutItem: Identifiable {
|
|||||||
var reps: Int = 8
|
var reps: Int = 8
|
||||||
|
|
||||||
// EXERCISE
|
// EXERCISE
|
||||||
var exercise: Exercise? {
|
var exercise: Exercise?
|
||||||
didSet {
|
// TODO: Think about what's happening when an exercise (template) is deleted or changed
|
||||||
self.name = exercise?.name ?? "self.name"
|
// { didSet { self.name = exercise?.name ?? "self.name" } }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init(_ reps: Int, _ exercise: String) {
|
init(_ reps: Int, _ exercise: String) {
|
||||||
self.workoutItemType = .exercise
|
self.workoutItemType = .exercise
|
||||||
82
WorkoutsPlus/Onboarding/Onboarding.swift
Normal file
82
WorkoutsPlus/Onboarding/Onboarding.swift
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// Onboarding.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 04.09.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct Onboarding: View {
|
||||||
|
@Default(\.isFirstAppStart) var isFirstAppStart
|
||||||
|
@Default(\.isOnboarding) var isOnboarding
|
||||||
|
@Default(\.userId) var userId
|
||||||
|
|
||||||
|
@State private var currentPage = 0
|
||||||
|
let totalPages = 3
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
TabView(selection: $currentPage) {
|
||||||
|
ForEach(0..<totalPages, id: \.self) { index in
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Image(systemName: "scribble.variable")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: 150, height: 150)
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
|
||||||
|
Text("userID: \(userId)")
|
||||||
|
.font(.largeTitle)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.padding(.top, 40)
|
||||||
|
|
||||||
|
Text("Erfahren Sie, wie MyApp Ihnen helfen kann, produktiver zu sein.")
|
||||||
|
.font(.title3)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
.padding(.top, 20)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
if currentPage > 0 {
|
||||||
|
Button("Zurück") {
|
||||||
|
currentPage -= 1
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button(currentPage == totalPages - 1 ? "Los geht's!" : "Weiter") {
|
||||||
|
if currentPage < totalPages - 1 {
|
||||||
|
currentPage += 1
|
||||||
|
} else {
|
||||||
|
if (isFirstAppStart) {
|
||||||
|
userId = UUID().uuidString
|
||||||
|
}
|
||||||
|
isFirstAppStart = false
|
||||||
|
isOnboarding = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
}
|
||||||
|
.padding(.bottom, 40)
|
||||||
|
}
|
||||||
|
.tag(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
Onboarding()
|
||||||
|
}
|
||||||
25
WorkoutsPlus/README.md
Normal file
25
WorkoutsPlus/README.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Workouts+
|
||||||
|
|
||||||
|
- JSON Import/Export
|
||||||
|
- Use Multipeer connect for sharing/tracking?
|
||||||
|
- Progression-Fotos: als eigene App? -> Generalisierung zu "Foto-Track"
|
||||||
|
- Karte mit öffentlichen Orten einbinden (AOK-Fitnesspark?)
|
||||||
|
|
||||||
|
## Workouts
|
||||||
|
- time-based: 60 s
|
||||||
|
- rep-based: 3x
|
||||||
|
- Loops
|
||||||
|
|
||||||
|
## Trainingspläne
|
||||||
|
|
||||||
|
Idee: Platformdenken
|
||||||
|
|
||||||
|
App ist für Sportler mit einem Trainer. Trainer können die App nutzen, um ihre Trainees mit Trainingsplänen zu versorgen.
|
||||||
|
Dazu muss der Trainer eine Möglichkeit bekommen bezahlt zu werden?
|
||||||
|
|
||||||
|
## Ernährungspläne
|
||||||
|
|
||||||
|
Visualisierung des Budgets (Balkendiagram, das man via DragNdrop auffüllen kann, um zu sehen, wie man hinkommt)
|
||||||
|
mglw mehrdimensional? eine Achse kcal eine Achse Protein?
|
||||||
|
|
||||||
|
Energiebudget -> im ersten Schritt einfach immer denselben Ernährungsplan
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// Settings.swift
|
|
||||||
// WorkoutsPlus
|
|
||||||
//
|
|
||||||
// Created by Felix Förtsch on 30.08.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct Settings: View {
|
|
||||||
var body: some View {
|
|
||||||
Text("Settings")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
Settings()
|
|
||||||
}
|
|
||||||
44
WorkoutsPlus/Settings/Settings.swift
Normal file
44
WorkoutsPlus/Settings/Settings.swift
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// Settings.swift
|
||||||
|
// WorkoutsPlus
|
||||||
|
//
|
||||||
|
// Created by Felix Förtsch on 30.08.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct Settings: View {
|
||||||
|
@Default(\.isFirstAppStart) var isFirstAppStart
|
||||||
|
@Default(\.isOnboarding) var isOnboarding
|
||||||
|
@Default(\.userId) var userId
|
||||||
|
@Default(\.reps) var reps
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
List {
|
||||||
|
Section(header: Text("User")) {
|
||||||
|
Text(userId)
|
||||||
|
TextField("name", text: $userId)
|
||||||
|
}
|
||||||
|
Section(header: Text("Defaults")) {
|
||||||
|
StepperListItem(itemName: "Rep Count", itemValue: $reps)
|
||||||
|
}
|
||||||
|
Text(String(reps))
|
||||||
|
Section(header: Text("Danger Zone")) {
|
||||||
|
|
||||||
|
Toggle(isOn: $isOnboarding) {
|
||||||
|
Text("isOnboarding")
|
||||||
|
}
|
||||||
|
Button("Reset App", role: .destructive, action: resetApp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func resetApp() {
|
||||||
|
isFirstAppStart = true
|
||||||
|
isOnboarding = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
Settings()
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// AddWorkoutView.swift
|
// AddWorkout.swift
|
||||||
// WorkoutsPlus
|
// WorkoutsPlus
|
||||||
//
|
//
|
||||||
// Created by Felix Förtsch on 17.08.24.
|
// Created by Felix Förtsch on 17.08.24.
|
||||||
@@ -11,7 +11,7 @@ struct AddWorkout: View {
|
|||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
@Bindable var workout: Workout
|
@State var workout: Workout
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
|
|||||||
@@ -12,54 +12,38 @@ struct AddWorkoutItemToWorkout: View {
|
|||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
@Query(sort: \Exercise.name) private var exercises: [Exercise]
|
@Query(sort: \Exercise.name) private var exercises: [Exercise]
|
||||||
|
|
||||||
@Bindable var workout: Workout
|
@State var workout: Workout
|
||||||
|
// TODO: Add (i) Button that allows editing an exercise (maybe requires NavigationStack?)
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
List {
|
List {
|
||||||
Section(header: Text("Utilities")) {
|
Section(header: Text("Utilities")) {
|
||||||
AddExerciseToWorkoutListItem(WorkoutItem(workoutItems: []), workout)
|
// AddExerciseToWorkoutListItem(WorkoutItem(workoutItems: []), workout)
|
||||||
}
|
}
|
||||||
Section(header: Text("Excersises")) {
|
Section(header: Text("Excersises")) {
|
||||||
if !exercises.isEmpty {
|
if !exercises.isEmpty {
|
||||||
ForEach(exercises) { exercise in
|
ForEach(exercises) { exercise in
|
||||||
|
AddItemButton(label: exercise.name) {
|
||||||
AddExerciseToWorkoutListItem(WorkoutItem(from: exercise), workout)
|
let workoutItem = WorkoutItem(from: exercise)
|
||||||
}} else {
|
addWorkoutItemtoWorkout(workoutItem)
|
||||||
ContentUnavailableView {
|
|
||||||
// TODO: Add Button that allows adding an exercise
|
|
||||||
Label("No Exercises", systemImage: Exercise.systemImage)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ContentUnavailableView {
|
||||||
|
// TODO: Add Button that allows adding an exercise
|
||||||
|
Label("No Exercises", systemImage: Exercise.systemImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct AddExerciseToWorkoutListItem: View {
|
|
||||||
@Environment(\.modelContext) private var modelContext
|
|
||||||
|
|
||||||
var workoutItem: WorkoutItem
|
private func addWorkoutItemtoWorkout(_ workoutItem: WorkoutItem) {
|
||||||
var workout: Workout
|
workout.add(workoutItem: workoutItem)
|
||||||
|
// TODO: Handle saving in a way the user knows when it's saved
|
||||||
init(_ workoutItem: WorkoutItem, _ workout: Workout) {
|
// modelContext.save()
|
||||||
self.workoutItem = workoutItem
|
|
||||||
self.workout = workout
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Button(action: {
|
|
||||||
workout.add(workoutItem: workoutItem)
|
|
||||||
}) {
|
|
||||||
HStack {
|
|
||||||
Text(workoutItem.name)
|
|
||||||
.foregroundStyle(.black)
|
|
||||||
Spacer()
|
|
||||||
Image(systemName: "plus.circle.fill")
|
|
||||||
.foregroundStyle(.green)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// WorkoutDetailsView.swift
|
// WorkoutDetail.swift
|
||||||
// WorkoutsPlus
|
// WorkoutsPlus
|
||||||
//
|
//
|
||||||
// Created by Felix Förtsch on 10.08.24.
|
// Created by Felix Förtsch on 10.08.24.
|
||||||
@@ -12,8 +12,8 @@ struct WorkoutDetail: View {
|
|||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
|
|
||||||
@Bindable var workout: Workout
|
@State var workout: Workout
|
||||||
@State private var isPresenting = false
|
@State private var isPresentingExerciseLibrary = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
@@ -47,18 +47,17 @@ struct WorkoutDetail: View {
|
|||||||
EditButton()
|
EditButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $isPresenting) {
|
.sheet(isPresented: $isPresentingExerciseLibrary) {
|
||||||
NavigationStack {
|
AddWorkoutItemToWorkout(workout: workout)
|
||||||
AddWorkoutItemToWorkout(workout: workout)
|
.presentationDetents([.medium, .large])
|
||||||
}
|
.presentationDragIndicator(.visible)
|
||||||
.presentationDetents([.medium, .large])
|
|
||||||
.presentationDragIndicator(.visible)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addWorkoutItemToWorkout() {
|
private func addWorkoutItemToWorkout() {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
isPresenting = true
|
isPresentingExerciseLibrary = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,3 +91,20 @@ struct WorkoutDetail: View {
|
|||||||
.modelContainer(SampleData.shared.modelContainer)
|
.modelContainer(SampleData.shared.modelContainer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Preview("Debug") {
|
||||||
|
TabView {
|
||||||
|
WorkoutDetail(workout: Workout.sampleData)
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "figure.run.square.stack")
|
||||||
|
Text("Workouts")
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugList()
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "hammer")
|
||||||
|
Text("Debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modelContainer(SampleData.shared.modelContainer)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// WorkoutLibraryView.swift
|
// WorkoutLibrary.swift
|
||||||
// WorkoutsPlus
|
// WorkoutsPlus
|
||||||
//
|
//
|
||||||
// Created by Felix Förtsch on 10.08.24.
|
// Created by Felix Förtsch on 10.08.24.
|
||||||
|
|||||||
Reference in New Issue
Block a user