wip
This commit is contained in:
BIN
Workouts/Assets.xcassets/AppIcon.appiconset/1024 1.png
Normal file
BIN
Workouts/Assets.xcassets/AppIcon.appiconset/1024 1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
BIN
Workouts/Assets.xcassets/AppIcon.appiconset/1024 2.png
Normal file
BIN
Workouts/Assets.xcassets/AppIcon.appiconset/1024 2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
BIN
Workouts/Assets.xcassets/AppIcon.appiconset/1024.png
Normal file
BIN
Workouts/Assets.xcassets/AppIcon.appiconset/1024.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "DumbBellIcon.png",
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
@ -13,7 +13,7 @@
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "DumbBellIcon 1.png",
|
||||
"filename" : "1024 1.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
@ -25,7 +25,7 @@
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"filename" : "DumbBellIcon 2.png",
|
||||
"filename" : "1024 2.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 90 KiB |
Binary file not shown.
Before Width: | Height: | Size: 90 KiB |
Binary file not shown.
Before Width: | Height: | Size: 90 KiB |
@ -36,11 +36,11 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
NavigationStack {
|
||||
Text("Achivements")
|
||||
Text("Achievements")
|
||||
.navigationTitle("Achievements")
|
||||
}
|
||||
.tabItem {
|
||||
Label("Achivements", systemImage: "star.fill")
|
||||
Label("Achievements", systemImage: "star.fill")
|
||||
}
|
||||
|
||||
// SettingsView()
|
||||
|
@ -3,8 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -4,10 +4,12 @@ import SwiftData
|
||||
@Model
|
||||
final class Exercise {
|
||||
var name: String = ""
|
||||
var loadType: Int = LoadType.weight.rawValue
|
||||
var order: Int = 0
|
||||
var sets: Int = 0
|
||||
var reps: Int = 0
|
||||
var weight: Int = 0
|
||||
var duration: Date = Date.distantPast
|
||||
var weightLastUpdated: Date = Date()
|
||||
var weightReminderTimeIntervalWeeks: Int = 2
|
||||
|
||||
@ -24,3 +26,18 @@ final class Exercise {
|
||||
self.weightReminderTimeIntervalWeeks = weightReminderTimeIntervalWeeks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum LoadType: Int, CaseIterable {
|
||||
case none = 0
|
||||
case weight = 1
|
||||
case duration = 2
|
||||
|
||||
var name: String {
|
||||
switch self {
|
||||
case .none: "None"
|
||||
case .weight: "Weight"
|
||||
case .duration: "Duration"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
Workouts/Models/ExerciseType.swift
Normal file
10
Workouts/Models/ExerciseType.swift
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// ExerciseType.swift
|
||||
// Workouts
|
||||
//
|
||||
// Created by rzen on 7/26/25 at 9:16 AM.
|
||||
//
|
||||
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
|
||||
//
|
||||
|
||||
|
77
Workouts/Resources/bodyweight-starter.exercises.yaml
Normal file
77
Workouts/Resources/bodyweight-starter.exercises.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
name: Starter Set - Bodyweight
|
||||
source: Home or Minimal Equipment
|
||||
exercises:
|
||||
- name: Pull-Up
|
||||
descr: Grip a bar with hands shoulder-width or wider. Pull your chin above the bar, engaging your back and arms. Lower with control.
|
||||
type: Bodyweight
|
||||
split: Upper Body
|
||||
|
||||
- name: Inverted Row
|
||||
descr: Lie underneath a bar or sturdy edge, pull your chest toward the bar while keeping your body straight. Squeeze shoulder blades together.
|
||||
type: Bodyweight
|
||||
split: Upper Body
|
||||
|
||||
- name: Pike Push-Up
|
||||
descr: Begin in downward dog position. Lower your head toward the ground by bending your elbows, then press back up. Focus on shoulder engagement.
|
||||
type: Bodyweight
|
||||
split: Upper Body
|
||||
|
||||
- name: Push-Up
|
||||
descr: With hands just outside shoulder width, lower your body until elbows are at 90°, then push back up. Keep your body in a straight line.
|
||||
type: Bodyweight
|
||||
split: Upper Body
|
||||
|
||||
- name: Tricep Dip
|
||||
descr: Using a chair or bench, lower your body by bending your elbows behind you, then press back up. Keep elbows tight to your body.
|
||||
type: Bodyweight
|
||||
split: Upper Body
|
||||
|
||||
- name: Towel Curl
|
||||
descr: Sit on the floor with a towel looped under your feet. Pull against the towel using biceps, optionally resisting with your legs.
|
||||
type: Bodyweight
|
||||
split: Upper Body
|
||||
|
||||
- name: Crunch
|
||||
descr: Lie on your back with knees bent. Curl your upper back off the floor using your abdominal muscles, then return slowly.
|
||||
type: Bodyweight
|
||||
split: Core
|
||||
|
||||
- name: Russian Twist
|
||||
descr: Sit with knees bent and feet off the ground. Twist your torso side to side while keeping your abs engaged.
|
||||
type: Bodyweight
|
||||
split: Core
|
||||
|
||||
- name: Bodyweight Squat
|
||||
descr: Stand with feet shoulder-width apart. Lower your hips back and down, keeping your heels on the floor. Rise back to standing.
|
||||
type: Bodyweight
|
||||
split: Lower Body
|
||||
|
||||
- name: Wall Sit
|
||||
descr: Lean your back against a wall and lower into a seated position. Hold as long as possible while maintaining good form.
|
||||
type: Bodyweight
|
||||
split: Lower Body
|
||||
|
||||
- name: Glute Bridge
|
||||
descr: Lie on your back with knees bent. Push through your heels to lift your hips, then lower slowly. Focus on hamstrings and glutes.
|
||||
type: Bodyweight
|
||||
split: Lower Body
|
||||
|
||||
- name: Hamstring Walkout
|
||||
descr: Start in a glute bridge, then slowly walk your heels outward and back in, maintaining control and tension in the hamstrings.
|
||||
type: Bodyweight
|
||||
split: Lower Body
|
||||
|
||||
- name: Side-Lying Leg Raise (Inner)
|
||||
descr: Lie on your side with bottom leg straight. Raise the bottom leg upward, engaging the inner thigh.
|
||||
type: Bodyweight
|
||||
split: Lower Body
|
||||
|
||||
- name: Side-Lying Leg Raise (Outer)
|
||||
descr: Lie on your side with top leg straight. Raise the top leg upward to engage the outer thigh and glute.
|
||||
type: Bodyweight
|
||||
split: Lower Body
|
||||
|
||||
- name: Calf Raise
|
||||
descr: Stand on the balls of your feet (on flat ground or a step). Raise your heels, then lower slowly for a full stretch.
|
||||
type: Bodyweight
|
||||
split: Lower Body
|
28
Workouts/Utils/Constants.swift
Normal file
28
Workouts/Utils/Constants.swift
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// Constants.swift
|
||||
// Workouts
|
||||
//
|
||||
// Created by Claude on 8/8/25.
|
||||
//
|
||||
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Application-wide constants
|
||||
struct Constants {
|
||||
|
||||
/// Default values for new exercises
|
||||
struct ExerciseDefaults {
|
||||
static let sets = 3
|
||||
static let reps = 10
|
||||
static let weight = 40
|
||||
static let weightReminderTimeIntervalWeeks = 2
|
||||
}
|
||||
|
||||
/// UI Constants
|
||||
struct UI {
|
||||
static let defaultSplitColor = "indigo"
|
||||
static let defaultSplitSystemImage = "dumbbell.fill"
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ListItem: View {
|
||||
var systemName: String?
|
||||
var title: String?
|
||||
var text: String?
|
||||
var subtitle: String?
|
||||
@ -18,6 +19,9 @@ struct ListItem: View {
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if let systemName = systemName {
|
||||
Image(systemName: systemName)
|
||||
}
|
||||
VStack (alignment: .leading) {
|
||||
if let title = title {
|
||||
Text("\(title)")
|
||||
|
@ -17,7 +17,17 @@ struct ExerciseAddEditView: View {
|
||||
@State var model: Exercise
|
||||
|
||||
@State var originalWeight: Int? = nil
|
||||
@State var loadType: LoadType = .none
|
||||
|
||||
@State private var minutes = 0
|
||||
@State private var seconds = 0
|
||||
|
||||
@State private var weight_tens = 0
|
||||
@State private var weight = 0
|
||||
|
||||
@State private var reps = 0
|
||||
@State private var sets = 0
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
@ -40,23 +50,88 @@ struct ExerciseAddEditView: View {
|
||||
}
|
||||
|
||||
Section (header: Text("Sets/Reps")) {
|
||||
Stepper("Sets: \(model.sets)", value: $model.sets, in: 1...10)
|
||||
Stepper("Reps: \(model.reps)", value: $model.reps, in: 1...50)
|
||||
HStack (alignment: .bottom) {
|
||||
VStack (alignment: .center) {
|
||||
Text("Sets")
|
||||
Picker("", selection: $sets) {
|
||||
ForEach(0..<20) { sets in
|
||||
Text("\(sets)").tag(sets)
|
||||
}
|
||||
}
|
||||
.frame(height: 100)
|
||||
.pickerStyle(.wheel)
|
||||
}
|
||||
VStack (alignment: .center) {
|
||||
Text("Reps")
|
||||
Picker("", selection: $reps) {
|
||||
ForEach(0..<100) { reps in
|
||||
Text("\(reps)").tag(reps)
|
||||
}
|
||||
}
|
||||
.frame(height: 100)
|
||||
.pickerStyle(.wheel)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
sets = model.sets
|
||||
reps = model.reps
|
||||
}
|
||||
|
||||
// Weight section
|
||||
Section (header: Text("Weight")) {
|
||||
HStack {
|
||||
VStack(alignment: .center) {
|
||||
Text("\(model.weight) lbs")
|
||||
.font(.headline)
|
||||
Section (header: Text("Load Type"), footer: Text("For bodyweight exercises choose None. For resistance or weight training selected Weight. For exercises that are time oriented (like plank or meditation select Time.")) {
|
||||
Picker("", selection: $loadType) {
|
||||
ForEach (LoadType.allCases, id: \.self) { load in
|
||||
Text(load.name)
|
||||
.tag(load)
|
||||
}
|
||||
Spacer()
|
||||
VStack(alignment: .trailing) {
|
||||
Stepper("±1", value: $model.weight, in: 0...1000)
|
||||
Stepper("±5", value: $model.weight, in: 0...1000, step: 5)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
if loadType == .weight {
|
||||
Section (header: Text("Weight")) {
|
||||
HStack {
|
||||
HStack {
|
||||
Picker("", selection: $weight_tens) {
|
||||
ForEach(0..<100) { lbs in
|
||||
Text("\(lbs*10)").tag(lbs*10)
|
||||
}
|
||||
}
|
||||
.frame(height: 100)
|
||||
.pickerStyle(.wheel)
|
||||
|
||||
Picker("", selection: $weight) {
|
||||
ForEach(0..<10) { lbs in
|
||||
Text("\(lbs)").tag(lbs)
|
||||
}
|
||||
}
|
||||
.frame(height: 100)
|
||||
.pickerStyle(.wheel)
|
||||
|
||||
Text("lbs")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if loadType == .duration {
|
||||
Section (header: Text("Duration")) {
|
||||
|
||||
HStack {
|
||||
Picker("Minutes", selection: $minutes) {
|
||||
ForEach(0..<60) { minute in
|
||||
Text("\(minute) min").tag(minute)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
|
||||
Picker("Seconds", selection: $seconds) {
|
||||
ForEach(0..<60) { second in
|
||||
Text("\(second) sec").tag(second)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.wheel)
|
||||
}
|
||||
.frame(width: 130)
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +148,10 @@ struct ExerciseAddEditView: View {
|
||||
}
|
||||
.onAppear {
|
||||
originalWeight = model.weight
|
||||
weight_tens = model.weight / 10
|
||||
weight = model.weight - weight_tens * 10
|
||||
minutes = Int(model.duration.timeIntervalSince1970) / 60
|
||||
seconds = Int(model.duration.timeIntervalSince1970) - minutes * 60
|
||||
}
|
||||
.sheet(isPresented: $showingExercisePicker) {
|
||||
ExercisePickerView { exerciseNames in
|
||||
@ -94,6 +173,10 @@ struct ExerciseAddEditView: View {
|
||||
model.weightLastUpdated = Date()
|
||||
}
|
||||
}
|
||||
model.duration = Date(timeIntervalSince1970: Double(minutes * 60 + seconds))
|
||||
model.weight = weight_tens + weight
|
||||
model.sets = sets
|
||||
model.reps = reps
|
||||
try? modelContext.save()
|
||||
dismiss()
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ struct SplitDetailView: View {
|
||||
|
||||
@State var split: Split
|
||||
|
||||
@State private var showingAddSheet: Bool = false
|
||||
@State private var showingExerciseAddSheet: Bool = false
|
||||
@State private var showingSplitEditSheet: Bool = false
|
||||
@State private var itemToEdit: Exercise? = nil
|
||||
@State private var itemToDelete: Exercise? = nil
|
||||
@State private var createdWorkout: Workout? = nil
|
||||
@ -72,24 +73,29 @@ struct SplitDetailView: View {
|
||||
})
|
||||
|
||||
Button {
|
||||
showingAddSheet = true
|
||||
showingExerciseAddSheet = true
|
||||
} label: {
|
||||
ListItem(title: "Add Exercise")
|
||||
ListItem(systemName: "plus.circle", title: "Add Exercise")
|
||||
}
|
||||
|
||||
} else {
|
||||
Text("No exercises added yet.")
|
||||
Button(action: { showingAddSheet.toggle() }) {
|
||||
Button(action: { showingExerciseAddSheet.toggle() }) {
|
||||
ListItem(title: "Add Exercise")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button ("Delete This Split", role: .destructive) {
|
||||
showingDeleteConfirmation = true
|
||||
Section {
|
||||
Button ("Edit") {
|
||||
showingSplitEditSheet = true
|
||||
}
|
||||
|
||||
Button ("Delete", role: .destructive) {
|
||||
showingDeleteConfirmation = true
|
||||
}
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
.navigationTitle("\(split.name)")
|
||||
}
|
||||
@ -122,7 +128,7 @@ struct SplitDetailView: View {
|
||||
.navigationDestination(item: $createdWorkout, destination: { workout in
|
||||
WorkoutLogListView(workout: workout)
|
||||
})
|
||||
.sheet (isPresented: $showingAddSheet) {
|
||||
.sheet (isPresented: $showingExerciseAddSheet) {
|
||||
ExercisePickerView(onExerciseSelected: { exerciseNames in
|
||||
let splitId = split.persistentModelID
|
||||
print("exerciseNames: \(exerciseNames)")
|
||||
@ -164,7 +170,10 @@ struct SplitDetailView: View {
|
||||
try? modelContext.save()
|
||||
}, allowMultiSelect: true)
|
||||
}
|
||||
.sheet(item: $itemToEdit) { item in
|
||||
.sheet (isPresented: $showingSplitEditSheet) {
|
||||
SplitAddEditView(model: split)
|
||||
}
|
||||
.sheet (item: $itemToEdit) { item in
|
||||
ExerciseAddEditView(model: item)
|
||||
}
|
||||
.confirmationDialog(
|
||||
|
56
Workouts/Views/Splits/SplitListView.swift
Normal file
56
Workouts/Views/Splits/SplitListView.swift
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// SplitPickerView.swift
|
||||
// Workouts
|
||||
//
|
||||
// Created by rzen on 7/25/25 at 6:24 PM.
|
||||
//
|
||||
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
struct SplitListView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
|
||||
@State var splits: [Split]
|
||||
|
||||
@State private var allowSorting: Bool = true
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 16) {
|
||||
SortableForEach($splits, allowReordering: $allowSorting) { split, dragging in
|
||||
NavigationLink {
|
||||
SplitDetailView(split: split)
|
||||
} label: {
|
||||
SplitItem(
|
||||
name: split.name,
|
||||
color: Color.color(from: split.color),
|
||||
systemImageName: split.systemImage,
|
||||
exerciseCount: split.exercises?.count ?? 0
|
||||
)
|
||||
.overlay(dragging ? Color.white.opacity(0.8) : Color.clear)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.onAppear(perform: loadSplits)
|
||||
}
|
||||
|
||||
func loadSplits () {
|
||||
print("Loading splits")
|
||||
do {
|
||||
self.splits = try modelContext.fetch(FetchDescriptor<Split>(
|
||||
sortBy: [
|
||||
SortDescriptor(\Split.order),
|
||||
SortDescriptor(\Split.name)
|
||||
]
|
||||
))
|
||||
print("Loaded \(splits.count) splits")
|
||||
} catch {
|
||||
print("ERROR: failed to load splits \(error)")
|
||||
}
|
||||
}
|
||||
}
|
@ -17,54 +17,22 @@ struct SplitsView: View {
|
||||
@State var splits: [Split] = []
|
||||
|
||||
@State private var showingAddSheet: Bool = false
|
||||
@State private var allowSorting: Bool = true
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 16) {
|
||||
SortableForEach($splits, allowReordering: $allowSorting) { split, dragging in
|
||||
NavigationLink {
|
||||
SplitDetailView(split: split)
|
||||
} label: {
|
||||
SplitItem(
|
||||
name: split.name,
|
||||
color: Color.color(from: split.color),
|
||||
systemImageName: split.systemImage,
|
||||
exerciseCount: split.exercises?.count ?? 0
|
||||
)
|
||||
.overlay(dragging ? Color.white.opacity(0.8) : Color.clear)
|
||||
SplitListView(splits: splits)
|
||||
.navigationTitle("Splits")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button(action: { showingAddSheet.toggle() }) {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationTitle("Splits")
|
||||
.onAppear(perform: loadSplits)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button(action: { showingAddSheet.toggle() }) {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet (isPresented: $showingAddSheet) {
|
||||
SplitAddEditView(model: Split(name: "New Split"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func loadSplits () {
|
||||
do {
|
||||
self.splits = try modelContext.fetch(FetchDescriptor<Split>(
|
||||
sortBy: [
|
||||
SortDescriptor(\Split.order),
|
||||
SortDescriptor(\Split.name)
|
||||
]
|
||||
))
|
||||
} catch {
|
||||
print("ERROR: failed to load splits \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ struct WorkoutLogListView: View {
|
||||
CheckboxListItem(
|
||||
status: workoutLogStatus,
|
||||
title: log.exerciseName,
|
||||
subtitle: "\(log.sets) × \(log.reps) reps × \(log.weight) lbs"
|
||||
subtitle: getSubtitleText(for: log)
|
||||
)
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: false) {
|
||||
let status = log.status ?? WorkoutStatus.notStarted
|
||||
@ -200,6 +200,41 @@ struct WorkoutLogListView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func getSubtitleText(for log: WorkoutLog) -> String {
|
||||
let baseText = "\(log.sets) × \(log.reps) reps × \(log.weight) lbs"
|
||||
|
||||
if log.status == .inProgress, let currentStateIndex = log.currentStateIndex {
|
||||
let currentSet = getCurrentSetNumber(stateIndex: currentStateIndex, totalSets: log.sets)
|
||||
if currentSet > 0 {
|
||||
return "In Progress, Set #\(currentSet) • \(baseText)"
|
||||
}
|
||||
}
|
||||
|
||||
return baseText
|
||||
}
|
||||
|
||||
func getCurrentSetNumber(stateIndex: Int, totalSets: Int) -> Int {
|
||||
// Exercise states are structured as: intro(0) → set1(1) → rest1(2) → set2(3) → rest2(4) → ... → done
|
||||
// For each set number n, set state index = 2n-1, rest state index = 2n
|
||||
|
||||
if stateIndex <= 0 {
|
||||
return 0 // intro or invalid
|
||||
}
|
||||
|
||||
// Check if we're in a rest state (even indices > 0)
|
||||
let isRestState = stateIndex > 0 && stateIndex % 2 == 0
|
||||
|
||||
if isRestState {
|
||||
// During rest, show the next set number
|
||||
let nextSetNumber = (stateIndex / 2) + 1
|
||||
return min(nextSetNumber, totalSets)
|
||||
} else {
|
||||
// During set, show current set number
|
||||
let currentSetNumber = (stateIndex + 1) / 2
|
||||
return min(currentSetNumber, totalSets)
|
||||
}
|
||||
}
|
||||
|
||||
func getSetsRepsWeight(_ exerciseName: String, in modelContext: ModelContext) -> SetsRepsWeight {
|
||||
// Use a single expression predicate that works with SwiftData
|
||||
print("Searching for exercise name: \(exerciseName)")
|
||||
|
@ -20,11 +20,13 @@ struct WorkoutListView: View {
|
||||
|
||||
@Query(sort: [SortDescriptor(\Workout.start, order: .reverse)]) var workouts: [Workout]
|
||||
|
||||
// @State private var showingSplitPicker = false
|
||||
@State private var showingSplitPicker = false
|
||||
|
||||
@State private var itemToDelete: Workout? = nil
|
||||
@State private var itemToEdit: Workout? = nil
|
||||
|
||||
@State private var splits: [Split] = []
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
@ -60,6 +62,14 @@ struct WorkoutListView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Workouts")
|
||||
.onAppear (perform: loadSplits)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button ("Start New Split") {
|
||||
showingSplitPicker.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(item: $itemToEdit) { item in
|
||||
WorkoutEditView(workout: item)
|
||||
}
|
||||
@ -86,8 +96,14 @@ struct WorkoutListView: View {
|
||||
} message: {
|
||||
Text("Are you sure you want to delete this workout?")
|
||||
}
|
||||
// .sheet(isPresented: $showingSplitPicker) {
|
||||
// SplitPickerView { split in
|
||||
.sheet(isPresented: $showingSplitPicker) {
|
||||
|
||||
NavigationStack {
|
||||
SplitListView(splits: splits)
|
||||
.navigationTitle("Select a Split")
|
||||
}
|
||||
|
||||
// { split in
|
||||
// let workout = Workout(start: Date(), end: Date(), split: split)
|
||||
// modelContext.insert(workout)
|
||||
// if let exercises = split.exercises {
|
||||
@ -106,7 +122,20 @@ struct WorkoutListView: View {
|
||||
// }
|
||||
// try? modelContext.save()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadSplits () {
|
||||
do {
|
||||
self.splits = try modelContext.fetch(FetchDescriptor<Split>(
|
||||
sortBy: [
|
||||
SortDescriptor(\Split.order),
|
||||
SortDescriptor(\Split.name)
|
||||
]
|
||||
))
|
||||
} catch {
|
||||
print("ERROR: failed to load splits \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user