This commit is contained in:
2025-07-19 16:42:47 -04:00
parent 6e46775f58
commit e3c3f2c6f0
38 changed files with 556 additions and 367 deletions

View File

@ -0,0 +1,196 @@
//
// SplitExercisesListView.swift
// Workouts
//
// Created by rzen on 7/18/25 at 8:38AM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct ExerciseListView: View {
@Environment(\.modelContext) private var modelContext
@Environment(\.dismiss) private var dismiss
// Use a @Query to observe the Split and its exercises
@Query private var splits: [Split]
private var split: Split
@State private var showingAddSheet: Bool = false
@State private var itemToEdit: Exercise? = nil
@State private var itemToDelete: Exercise? = nil
@State private var createdWorkout: Workout? = nil
// Initialize with a Split and set up a query to observe it
init(split: Split) {
self.split = split
// Create a predicate to fetch only this specific split
let splitId = split.persistentModelID
self._splits = Query(filter: #Predicate<Split> { s in
s.persistentModelID == splitId
})
}
var body: some View {
// Use the first Split from our query if available, otherwise fall back to the original split
let currentSplit = splits.first ?? split
NavigationStack {
Form {
List {
if let assignments = currentSplit.exercises, !assignments.isEmpty {
let sortedAssignments = assignments.sorted(by: { $0.order == $1.order ? $0.name < $1.name : $0.order < $1.order })
ForEach(sortedAssignments) { item in
ListItem(
title: item.name,
subtitle: "\(item.sets) × \(item.reps) × \(item.weight) lbs \(item.order)"
)
.swipeActions {
Button {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
.onMove(perform: { indices, destination in
var exerciseArray = Array(sortedAssignments)
exerciseArray.move(fromOffsets: indices, toOffset: destination)
for (index, exercise) in exerciseArray.enumerated() {
exercise.order = index
}
if let modelContext = exerciseArray.first?.modelContext {
do {
try modelContext.save()
} catch {
print("Error saving after reordering: \(error)")
}
}
})
Button {
showingAddSheet = true
} label: {
ListItem(title: "Add Exercise")
}
} else {
Text("No exercises added yet.")
Button(action: { showingAddSheet.toggle() }) {
ListItem(title: "Add Exercise")
}
}
}
}
.navigationTitle("\(currentSplit.name)")
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("Start This Split") {
let split = currentSplit
let workout = Workout(start: Date(), end: Date(), split: split)
modelContext.insert(workout)
if let exercises = split.exercises {
for assignment in exercises {
let workoutLog = WorkoutLog(
workout: workout,
exerciseName: assignment.name,
date: Date(),
order: assignment.order,
sets: assignment.sets,
reps: assignment.reps,
weight: assignment.weight
)
modelContext.insert(workoutLog)
}
}
try? modelContext.save()
// Set the created workout to trigger navigation
createdWorkout = workout
}
}
}
.navigationDestination(item: $createdWorkout, destination: { workout in
WorkoutLogListView(workout: workout)
})
// .toolbar {
// ToolbarItem(placement: .navigationBarTrailing) {
// Button(action: { showingAddSheet.toggle() }) {
// Image(systemName: "plus")
// }
// }
// }
.sheet (isPresented: $showingAddSheet) {
ExercisePickerView(onExerciseSelected: { exerciseNames in
let splitId = currentSplit.persistentModelID
print("exerciseNames: \(exerciseNames)")
if exerciseNames.count == 1 {
itemToEdit = Exercise(
split: split,
exerciseName: exerciseNames.first ?? "Exercise.unnamed",
order: 0,
sets: 3,
reps: 10,
weight: 40
)
} else {
for exerciseName in exerciseNames {
var duplicateExercise: [Exercise]? = nil
do {
duplicateExercise = try modelContext.fetch(FetchDescriptor<Exercise>(predicate: #Predicate{ exercise in
exerciseName == exercise.name && splitId == exercise.split?.persistentModelID
}))
} catch {
print("ERROR: failed to fetch \(exerciseName)")
}
if let dup = duplicateExercise, dup.count > 0 {
print("Skipping duplicate \(exerciseName) found \(dup.count) duplicate(s)")
} else {
print("Creating \(exerciseName) for \(split.name)")
modelContext.insert(Exercise(
split: split,
exerciseName: exerciseName,
order: 0,
sets: 3,
reps: 10,
weight: 40
))
}
}
}
try? modelContext.save()
}, allowMultiSelect: true)
}
.sheet(item: $itemToEdit) { item in
ExerciseAddEditView(model: item)
}
.confirmationDialog(
"Delete Exercise?",
isPresented: .constant(itemToDelete != nil),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let item = itemToDelete {
withAnimation {
modelContext.delete(item)
try? modelContext.save()
itemToDelete = nil
}
}
}
}
}
}