Files
workouts/Worksouts Watch App/Views/ActiveWorkoutListView.swift
2025-07-25 17:42:25 -04:00

220 lines
7.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ActiveWorkoutListView.swift
// Workouts
//
// Created by rzen on 7/20/25 at 6:35 PM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct ActiveWorkoutListView: View {
@Environment(\.modelContext) private var modelContext
let workouts: [Workout]
var body: some View {
List {
ForEach(workouts) { workout in
NavigationLink {
WorkoutDetailView(workout: workout)
} label: {
WorkoutCardView(workout: workout)
}
// .listRowSeparator(.hidden)
.listRowBackground(
RoundedRectangle(cornerRadius: 12)
.fill(Color.secondary.opacity(0.2))
.padding(
EdgeInsets(
top: 4,
leading: 8,
bottom: 4,
trailing: 8
)
)
)
}
}
.listStyle(.carousel)
// .navigationTitle("Workouts")
}
}
struct WorkoutCardView: View {
let workout: Workout
var body: some View {
VStack(alignment: .leading) {
// Split icon
if let split = workout.split {
Image(systemName: split.systemImage)
.font(.system(size: 48))
.foregroundStyle(split.getColor())
} else {
Image(systemName: "dumbbell.fill")
.font(.system(size: 24))
.foregroundStyle(.gray)
}
// VStack(alignment: .leading, spacing: 4) {
// Split name
Text(workout.split?.name ?? "Workout")
.font(.headline)
.foregroundStyle(.white)
// Workout status
Text(workout.status?.name ?? "Not Started")
.font(.caption)
.foregroundStyle(Color.accentColor)
// }
// Spacer()
}
// .padding(.vertical, 8)
}
}
struct WorkoutDetailView: View {
let workout: Workout
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
NavigationLink {
WorkoutLogDetailView(log: 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()
}
}
}
}
struct WorkoutLogCardView: View {
let log: WorkoutLog
var body: some View {
VStack(alignment: .leading, spacing: 8) {
// Exercise name
Text(log.exerciseName)
.font(.headline)
.lineLimit(1)
// Status
Text(log.status?.name ?? "Not Started")
.font(.caption)
.foregroundStyle(Color.accentColor)
// Sets, Reps, Weight
HStack(spacing: 12) {
Text("\(log.weight) lbs")
Spacer()
Text("\(log.sets) × \(log.reps)")
}
}
}
}
struct WorkoutLogDetailView: View {
let log: WorkoutLog
var body: some View {
NavigationLink {
ExerciseProgressControlView(log: log)
} label: {
VStack(alignment: .center) {
Text(log.exerciseName)
.font(.title)
.lineLimit(1) // Ensures it stays on one line
.minimumScaleFactor(0.5) // Scales down to 50% if needed
.layoutPriority(1) // Prioritize this view in tight layouts
HStack (alignment: .bottom) {
Text("\(log.weight)")
Text( "lbs")
.fontWeight(.light)
.padding([.trailing], 10)
Text("\(log.sets)")
Text("×")
.fontWeight(.light)
Text("\(log.reps)")
// Text("\(log.weight) lbs × \(log.sets) × \(log.reps)")
}
.font(.title3)
.lineLimit(1) // Ensures it stays on one line
.minimumScaleFactor(0.5) // Scales down to 50% if needed
.layoutPriority(1) // Prioritize this view in tight layouts
Text(log.status?.name ?? "Not Started")
.foregroundStyle(Color.accentColor)
Text("Tap to start")
.foregroundStyle(Color.accentColor)
}
.padding()
}
.buttonStyle(.plain)
}
private func statusColor(for status: WorkoutStatus?) -> Color {
guard let status = status else { return .secondary }
switch status {
case .notStarted:
return .secondary
case .inProgress:
return .blue
case .completed:
return .green
case .skipped:
return .red
}
}
}
//#Preview {
// let container = AppContainer.preview
// let split = Split(name: "Upper Body", color: "blue", systemImage: "figure.strengthtraining.traditional")
// let workout1 = Workout(start: Date(), end: nil, split: split)
// workout1.status = .inProgress
//
// let split2 = Split(name: "Lower Body", color: "red", systemImage: "figure.run")
// let workout2 = Workout(start: Date().addingTimeInterval(-3600), end: nil, split: split2)
// workout2.status = .notStarted
//
// return ActiveWorkoutListView(workouts: [workout1, workout2])
// .modelContainer(container)
//}