Files
workouts/Worksouts Watch App/Views/ActiveWorkoutListView.swift

231 lines
6.9 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 {
VStack(spacing: 16) {
Text(log.exerciseName)
.font(.headline)
VStack(alignment: .leading, spacing: 8) {
HStack {
Text("Sets:")
.foregroundStyle(.secondary)
Spacer()
Text("\(log.sets)")
}
HStack {
Text("Reps:")
.foregroundStyle(.secondary)
Spacer()
Text("\(log.reps)")
}
HStack {
Text("Weight:")
.foregroundStyle(.secondary)
Spacer()
Text("\(log.weight)")
}
HStack {
Text("Status:")
.foregroundStyle(.secondary)
Spacer()
Text(log.status?.name ?? "Not Started")
.foregroundStyle(statusColor(for: log.status))
}
}
.padding()
.background(Color.secondary.opacity(0.1))
.cornerRadius(10)
NavigationLink {
ExerciseProgressControlView(log: log)
} label: {
Text("Start Exercise")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.tint(.accentColor)
Spacer()
}
.padding()
}
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)
//}