141 lines
4.6 KiB
Swift
141 lines
4.6 KiB
Swift
//
|
||
// WorkoutDetailView.swift
|
||
// Workouts
|
||
//
|
||
// Created by rzen on 7/22/25 at 9:54 PM.
|
||
//
|
||
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
|
||
//
|
||
|
||
import SwiftUI
|
||
import SwiftData
|
||
|
||
struct WorkoutDetailView: View {
|
||
let workout: Workout
|
||
@Environment(\.modelContext) private var modelContext
|
||
@Environment(\.dismiss) private var dismiss
|
||
|
||
@State private var showingCompletedDialog = false
|
||
@State private var selectedLog: WorkoutLog? = nil
|
||
@State private var navigateToExercise = false
|
||
|
||
var body: some View {
|
||
VStack(alignment: .center, spacing: 8) {
|
||
if let logs = workout.logs?.sorted(by: { $0.order < $1.order }), !logs.isEmpty {
|
||
List {
|
||
ForEach(logs) { log in
|
||
Button {
|
||
handleExerciseTap(log)
|
||
} label: {
|
||
WorkoutLogCardView(log: log)
|
||
}
|
||
.listRowBackground(
|
||
RoundedRectangle(cornerRadius: 12)
|
||
.fill(Color.secondary.opacity(0.2))
|
||
.padding(
|
||
EdgeInsets(
|
||
top: 4,
|
||
leading: 8,
|
||
bottom: 4,
|
||
trailing: 8
|
||
)
|
||
)
|
||
)
|
||
}
|
||
}
|
||
.listStyle(.carousel)
|
||
} else {
|
||
Text("No exercises in this workout")
|
||
.font(.body)
|
||
.foregroundStyle(.secondary)
|
||
.padding()
|
||
|
||
Spacer()
|
||
}
|
||
}
|
||
.navigationDestination(isPresented: $navigateToExercise) {
|
||
if let selectedLog = selectedLog {
|
||
ExerciseProgressControlView(log: selectedLog)
|
||
}
|
||
}
|
||
.alert("Exercise Completed", isPresented: $showingCompletedDialog) {
|
||
Button("Cancel", role: .cancel) {
|
||
// Do nothing, just dismiss
|
||
}
|
||
Button("Restart") {
|
||
if let log = selectedLog {
|
||
restartExercise(log)
|
||
}
|
||
}
|
||
Button("One More Set") {
|
||
if let log = selectedLog {
|
||
addOneMoreSet(log)
|
||
}
|
||
}
|
||
} message: {
|
||
Text("This exercise is already completed. What would you like to do?")
|
||
}
|
||
}
|
||
|
||
private func handleExerciseTap(_ log: WorkoutLog) {
|
||
selectedLog = log
|
||
|
||
switch log.status {
|
||
case .notStarted:
|
||
// Start from beginning
|
||
log.currentStateIndex = 0
|
||
try? modelContext.save()
|
||
navigateToExercise = true
|
||
|
||
case .inProgress:
|
||
// If we're in a rest state, advance to the next set
|
||
if let currentStateIndex = log.currentStateIndex, isRestState(currentStateIndex) {
|
||
log.currentStateIndex = currentStateIndex + 1
|
||
try? modelContext.save()
|
||
}
|
||
// Continue from current (possibly updated) position
|
||
navigateToExercise = true
|
||
|
||
case .completed:
|
||
// Show dialog for completed exercise
|
||
showingCompletedDialog = true
|
||
|
||
default:
|
||
// Default to not started behavior
|
||
log.currentStateIndex = 0
|
||
try? modelContext.save()
|
||
navigateToExercise = true
|
||
}
|
||
}
|
||
|
||
private func restartExercise(_ log: WorkoutLog) {
|
||
log.status = .notStarted
|
||
log.currentStateIndex = 0
|
||
log.elapsedSeconds = 0
|
||
try? modelContext.save()
|
||
navigateToExercise = true
|
||
}
|
||
|
||
private func addOneMoreSet(_ log: WorkoutLog) {
|
||
// Increment total sets
|
||
log.sets += 1
|
||
|
||
// Calculate the state index for the additional set
|
||
// States: intro(0) → set1(1) → rest1(2) → ... → setN(2N-1) → done(2N)
|
||
// For the additional set, we want to go to setN+1 which is at index 2N+1
|
||
let additionalSetStateIndex = (log.sets * 2) - 1
|
||
|
||
log.status = .inProgress
|
||
log.currentStateIndex = additionalSetStateIndex
|
||
log.elapsedSeconds = 0
|
||
|
||
try? modelContext.save()
|
||
navigateToExercise = true
|
||
}
|
||
|
||
private func isRestState(_ stateIndex: Int) -> Bool {
|
||
// Rest states are at even indices > 0
|
||
return stateIndex > 0 && stateIndex % 2 == 0
|
||
}
|
||
}
|