Add the exercise reference library, animated exercise figures, and exercise categories
Exercise Library/ holds per-exercise reference docs (setup, cues, mistakes, progressions) with SVG visuals and a Python-rendered motion pipeline; Workouts/ExerciseFigure renders the bundled *.motion.json rigs as animated stick figures on the exercise screen. Exercises gain a warm-up/main-circuit category, timed exercises display hold time via planSummary, and a completed exercise reopens to a check screen instead of its timers.
This commit is contained in:
@@ -79,6 +79,11 @@ struct ExerciseProgressView: View {
|
||||
/// transient TabView snap-to-0, so it isn't reset on open.
|
||||
@State private var startsResumed: Bool
|
||||
|
||||
/// True when the exercise was already completed when this screen opened — it shows a
|
||||
/// static Completed page instead of dropping back into the timer flow. Fixed at init
|
||||
/// so completing the exercise from inside the flow doesn't swap the page mid-dismiss.
|
||||
@State private var startsCompleted: Bool
|
||||
|
||||
/// Forces the starting page (used only by the DEBUG screenshot host). When set it
|
||||
/// also suppresses the Ready page so the index is a plain work/rest cycle offset.
|
||||
private let debugInitialPage: Int?
|
||||
@@ -102,6 +107,7 @@ struct ExerciseProgressView: View {
|
||||
// set and re-asserts that page past the TabView's snap-to-0 on first layout.
|
||||
let ready = debugInitialPage == nil
|
||||
_startsResumed = State(initialValue: ready && !notStarted)
|
||||
_startsCompleted = State(initialValue: ready && log?.status == WorkoutStatus.completed.rawValue)
|
||||
|
||||
let base = ready ? 1 : 0
|
||||
// Resume on the first unfinished set's work page (clamped to the last set).
|
||||
@@ -182,6 +188,14 @@ struct ExerciseProgressView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if startsCompleted {
|
||||
CompletedPhaseView()
|
||||
} else {
|
||||
flowBody
|
||||
}
|
||||
}
|
||||
|
||||
private var flowBody: some View {
|
||||
TabView(selection: $currentPage) {
|
||||
ForEach(0..<totalPages, id: \.self) { index in
|
||||
page(for: index)
|
||||
@@ -727,6 +741,22 @@ private extension View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Completed Phase
|
||||
|
||||
/// Shown instead of the run flow when the exercise was already completed on open.
|
||||
private struct CompletedPhaseView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.font(.system(size: 56))
|
||||
.foregroundStyle(.green)
|
||||
Text("Completed")
|
||||
.font(.system(.title3, design: .rounded, weight: .heavy))
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Ready Phase
|
||||
|
||||
private struct ReadyPhaseView: View {
|
||||
|
||||
Reference in New Issue
Block a user