// // ExerciseProgressControlView.swift // Workouts // // Created by rzen on 7/20/25 at 7:19 PM. // // Copyright 2025 Rouslan Zenetl. All Rights Reserved. // import SwiftUI import SwiftData enum ExerciseState: Identifiable { case set(number: Int) case rest(afterSet: Int) case done var id: String { switch self { case .set(let number): return "set_\(number)" case .rest(let afterSet): return "rest_\(afterSet)" case .done: return "done" } } var isRest: Bool { if case .rest = self { return true } return false } var isSet: Bool { if case .set = self { return true } return false } var isDone: Bool { if case .done = self { return true } return false } } struct ExerciseProgressControlView: View { let log: WorkoutLog @Environment(\.dismiss) private var dismiss @Environment(\.modelContext) private var modelContext @State private var exerciseStates: [ExerciseState] = [] @State private var currentStateIndex: Int = 0 @State private var elapsedSeconds: Int = 0 @State private var timer: Timer? = nil @State private var previousStateIndex: Int = 0 var body: some View { TabView(selection: $currentStateIndex) { ForEach(Array(exerciseStates.enumerated()), id: \.element.id) { index, state in ExerciseStateView( state: state, elapsedSeconds: elapsedSeconds, onComplete: { moveToNextState() } ) .tag(index) } } .tabViewStyle(.page(indexDisplayMode: .never)) .onChange(of: currentStateIndex) { oldValue, newValue in if oldValue != newValue { // Reset timer when user swipes to a new state elapsedSeconds = 0 } } .onAppear { setupExerciseStates() startTimer() } .onDisappear { stopTimer() } } private func setupExerciseStates() { var states: [ExerciseState] = [] // Create states for each set and rest period for setNumber in 1...log.sets { states.append(.set(number: setNumber)) // Add rest period after each set except the last one if setNumber < log.sets { states.append(.rest(afterSet: setNumber)) } } // Add done state at the end states.append(.done) exerciseStates = states } private func startTimer() { timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in elapsedSeconds += 1 // Check if we need to provide haptic feedback during rest periods if let currentState = exerciseStates[safe: currentStateIndex] { if currentState.isRest { provideRestHapticFeedback() } else if currentState.isDone && elapsedSeconds >= 10 { // Auto-complete after 10 seconds on the DONE state completeExercise() } } } } private func stopTimer() { timer?.invalidate() timer = nil } private func moveToNextState() { if currentStateIndex < exerciseStates.count - 1 { withAnimation { currentStateIndex += 1 elapsedSeconds = 0 } } else { // We've reached the end (DONE state) completeExercise() } } private func provideRestHapticFeedback() { // Provide haptic feedback based on elapsed time if elapsedSeconds % 60 == 0 && elapsedSeconds > 0 { // Triple tap every 60 seconds HapticFeedback.tripleTap() } else if elapsedSeconds % 30 == 0 && elapsedSeconds > 0 { // Double tap every 30 seconds HapticFeedback.doubleTap() } else if elapsedSeconds % 10 == 0 && elapsedSeconds > 0 { // Single tap every 10 seconds HapticFeedback.success() } } private func completeExercise() { // Update the workout log status to completed log.status = .completed // Provide "tada" haptic feedback HapticFeedback.tripleTap() // Dismiss this view to return to WorkoutDetailView dismiss() } } struct ExerciseStateView: View { let state: ExerciseState let elapsedSeconds: Int let onComplete: () -> Void var body: some View { VStack(spacing: 20) { // Title based on state Text(stateTitle) .font(.title3) .fontWeight(.bold) // Timer display Text(timeFormatted) .font(.system(size: 48, weight: .semibold, design: .monospaced)) .foregroundStyle(state.isRest ? .orange : .accentColor) // Only show Done button and countdown for the final state if state.isDone { // Countdown message if elapsedSeconds < 10 { Text("Completing automatically in \(10 - elapsedSeconds) seconds") .font(.caption) .multilineTextAlignment(.center) .foregroundStyle(.secondary) } else { Text("Auto-completing...") .font(.caption) .foregroundStyle(.secondary) } // Done button Button(action: onComplete) { Text("Done") .font(.headline) .frame(maxWidth: .infinity) } .buttonStyle(.borderedProminent) .tint(.green) .padding(.horizontal) } } .padding() } private var stateTitle: String { switch state { case .set(let number): return "Set \(number) in progress" case .rest: return "Resting" case .done: return "Exercise Complete" } } private var buttonTitle: String { switch state { case .set: return "Complete Set" case .rest: return "Start Next Set" case .done: return "DONE" } } private var buttonColor: Color { switch state { case .set: return .accentColor case .rest: return .orange case .done: return .green } } private var timeFormatted: String { let minutes = elapsedSeconds / 60 let seconds = elapsedSeconds % 60 return String(format: "%02d:%02d", minutes, seconds) } } // Extension to safely access array elements extension Array { subscript(safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } } //#Preview { // let container = AppContainer.preview // let workout = Workout(start: Date(), end: nil, split: nil) // let log = WorkoutLog(workout: workout, exerciseName: "Bench Press", date: Date(), sets: 3, reps: 10, weight: 135) // // ExerciseProgressControlView(log: log) // .modelContainer(container) //}