Restructure into a three-tab app with Progress, goals, and Meditation
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
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// WorkoutStarter.swift
|
||||
// Workouts
|
||||
//
|
||||
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
|
||||
//
|
||||
|
||||
import IndieSync
|
||||
import Foundation
|
||||
|
||||
/// The one place a workout is created from a routine. Both start paths — the routine
|
||||
/// picker sheet and the Today board — call this so the plan-time log snapshot and
|
||||
/// the returned id stay identical per site.
|
||||
@MainActor
|
||||
enum WorkoutStarter {
|
||||
/// Builds and saves a fresh workout from a routine (plan-time log snapshot per
|
||||
/// exercise) and returns the new workout's id so the caller can navigate once the
|
||||
/// cache catches up. Deliberately does NOT touch the watch: the wrist session
|
||||
/// launches only when the first exercise actually starts (see
|
||||
/// `SyncEngine.onWorkoutBecameActive`), so a peek-then-back never spins one up.
|
||||
static func start(routine: Routine, sync: SyncEngine) async -> String {
|
||||
let startDate = Date()
|
||||
let logs = routine.exercisesArray.enumerated().map { index, exercise in
|
||||
WorkoutLogDocument(planFrom: ExerciseDocument(from: exercise), order: index, date: startDate)
|
||||
}
|
||||
|
||||
// A freshly started workout has no `end` — only completion stamps it.
|
||||
let doc = WorkoutDocument(
|
||||
schemaVersion: WorkoutDocument.currentSchemaVersion,
|
||||
id: ULID.make(),
|
||||
routineID: routine.id,
|
||||
routineName: routine.name,
|
||||
start: startDate,
|
||||
end: nil,
|
||||
status: WorkoutStatus.notStarted.rawValue,
|
||||
createdAt: startDate,
|
||||
updatedAt: startDate,
|
||||
logs: logs,
|
||||
restSeconds: routine.restSeconds, autoAdvance: routine.autoAdvance
|
||||
)
|
||||
|
||||
await sync.save(workout: doc)
|
||||
return doc.id
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user