The UX redesign's first landing (spec in UX-REDESIGN.md): ContentView becomes a Today / Progress / Settings TabView, "Routine" replaces "Split" in every user-facing string and view name (code-level types keep their names), and workout starting moves to shared WorkoutStarter / StartedWorkoutNavigator plumbing. - New Progress tab: weekly goal streaks, workout trends, per-exercise weight progression, achievements, and the full history list (WorkoutLogsView -> WorkoutHistoryView). - Goals: stable categories workouts roll up to, managed from Settings. - New Meditation exercise + starter routine; timed sits record to Apple Health as Mind & Body sessions. Claude-Session: https://claude.ai/code/session_012qw2itfzKyEJ1HpsFt8Ex4
79 lines
3.0 KiB
Swift
79 lines
3.0 KiB
Swift
#if DEBUG
|
|
import SwiftUI
|
|
import SwiftData
|
|
|
|
/// DEBUG-only root used for App Store screenshot capture. Bypasses the iCloud gate and
|
|
/// renders one fully-formed screen (chosen by `--screen`) against the seeded cache, so
|
|
/// captures are deterministic with no UI automation. Never compiled into release.
|
|
struct ScreenshotRootView: View {
|
|
let services: AppServices
|
|
|
|
private var activeWorkout: Workout? {
|
|
let context = services.container.mainContext
|
|
var descriptor = FetchDescriptor<Workout>(sortBy: [SortDescriptor(\.start, order: .reverse)])
|
|
descriptor.fetchLimit = 25
|
|
let workouts = (try? context.fetch(descriptor)) ?? []
|
|
return workouts.first { $0.status == .inProgress } ?? workouts.first
|
|
}
|
|
|
|
var body: some View {
|
|
content
|
|
.environment(services)
|
|
.environment(services.syncEngine)
|
|
.environment(services.liveRunState)
|
|
.modelContainer(services.container)
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var content: some View {
|
|
// The full tab shell and the Progress tab need no particular workout,
|
|
// so they're handled before the active-workout unwrap.
|
|
if ScreenshotSeed.screen(default: "workouts") == "today" {
|
|
ContentView()
|
|
} else if ScreenshotSeed.screen(default: "workouts") == "progress" {
|
|
ProgressTabView()
|
|
} else if ScreenshotSeed.screen(default: "workouts") == "progress-bottom" {
|
|
ProgressTabView(initialAnchor: .bottom)
|
|
} else if ScreenshotSeed.screen(default: "workouts") == "exercise-trends" {
|
|
NavigationStack { ExerciseTrendsListView() }
|
|
} else if let workout = activeWorkout {
|
|
switch ScreenshotSeed.screen(default: "workouts") {
|
|
case "exercise":
|
|
let logID = WorkoutDocument(from: workout).logs.first { $0.exerciseName == "Seated Row" }?.id
|
|
NavigationStack { ExerciseView(workout: workout, logID: logID ?? "") }
|
|
case "run":
|
|
ScreenshotRunView(workout: workout)
|
|
case "settings":
|
|
SettingsView()
|
|
default:
|
|
NavigationStack { WorkoutLogListView(workout: workout) }
|
|
}
|
|
} else {
|
|
Color(.systemBackground)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The paged run flow for the in-progress exercise, showing the animated form guide
|
|
/// (with machine props) — the screenshot works off its own local document copy, so
|
|
/// nothing persists.
|
|
private struct ScreenshotRunView: View {
|
|
@State private var doc: WorkoutDocument
|
|
private let logID: String
|
|
|
|
init(workout: Workout) {
|
|
let document = WorkoutDocument(from: workout)
|
|
_doc = State(initialValue: document)
|
|
logID = document.logs.first { $0.exerciseName == "Seated Row" }?.id
|
|
?? document.logs.first?.id ?? ""
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ExerciseProgressView(doc: $doc, logID: logID, onChange: {},
|
|
onLive: { _ in }, onLiveEnded: {}, incomingFrame: nil)
|
|
}
|
|
}
|
|
}
|
|
#endif
|