Add machine comfort settings and tighten the document schemas
Machine-based exercises now carry ordered name/value comfort settings (seat height, back-rest position, ...) on both ExerciseDocument and, as a plan-time snapshot, WorkoutLogDocument — the optional array doubles as the machine flag. Editable from the exercise editor's new Machine section and from the workout row's settings sheet, whose mid-workout edits write back to the originating split (workout saved first, so seed clone-on-edit repointing can't clobber the log edit). Schema tightening rides the same rev: splits bump to v2 (weight-reminder fields and the unused exercise category removed), workouts to v3 (the derived `completed` flag removed; status is the single source). Starter seeds regenerated at v2 with unchanged ULIDs; SwiftData cache schema bumped to rebuild. SCHEMA.md documents the shapes. Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
This commit is contained in:
@@ -23,7 +23,6 @@ struct ExerciseAddEditView: View {
|
||||
|
||||
// Local editable state
|
||||
@State private var exerciseName: String
|
||||
@State private var originalWeight: Int
|
||||
@State private var loadType: LoadType
|
||||
@State private var minutes: Int
|
||||
@State private var seconds: Int
|
||||
@@ -31,9 +30,11 @@ struct ExerciseAddEditView: View {
|
||||
@State private var weightOnes: Int
|
||||
@State private var reps: Int
|
||||
@State private var sets: Int
|
||||
@State private var weightReminderWeeks: Int
|
||||
@State private var weightLastUpdated: Date?
|
||||
@State private var category: ExerciseCategory
|
||||
// Machine comfort settings. `machineEnabled` mirrors the optionality of the
|
||||
// stored `machineSettings` — ON persists a (possibly empty) array, OFF persists
|
||||
// nil. `machineSettings` holds the ordered rows while the toggle is on.
|
||||
@State private var machineEnabled: Bool
|
||||
@State private var machineSettings: [MachineSetting]
|
||||
@AppStorage("weightUnit") private var weightUnit: WeightUnit = .lb
|
||||
|
||||
init(exercise: Exercise, split: Split) {
|
||||
@@ -42,7 +43,6 @@ struct ExerciseAddEditView: View {
|
||||
|
||||
let w = exercise.weight
|
||||
_exerciseName = State(initialValue: exercise.name)
|
||||
_originalWeight = State(initialValue: w)
|
||||
_loadType = State(initialValue: exercise.loadTypeEnum)
|
||||
_minutes = State(initialValue: exercise.durationMinutes)
|
||||
_seconds = State(initialValue: exercise.durationSeconds)
|
||||
@@ -50,9 +50,8 @@ struct ExerciseAddEditView: View {
|
||||
_weightOnes = State(initialValue: w % 10)
|
||||
_reps = State(initialValue: exercise.reps)
|
||||
_sets = State(initialValue: exercise.sets)
|
||||
_weightReminderWeeks = State(initialValue: exercise.weightReminderTimeIntervalWeeks)
|
||||
_weightLastUpdated = State(initialValue: exercise.weightLastUpdated)
|
||||
_category = State(initialValue: exercise.categoryEnum)
|
||||
_machineEnabled = State(initialValue: exercise.machineSettings != nil)
|
||||
_machineSettings = State(initialValue: exercise.machineSettings ?? [])
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -100,19 +99,6 @@ struct ExerciseAddEditView: View {
|
||||
}
|
||||
}
|
||||
|
||||
Section(
|
||||
header: Text("Category"),
|
||||
footer: Text("The split screen groups its exercises by category, with the warm-up listed first.")
|
||||
) {
|
||||
Picker("", selection: $category) {
|
||||
ForEach(ExerciseCategory.displayOrder, id: \.self) { cat in
|
||||
Text(cat.displayName)
|
||||
.tag(cat)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
Section(
|
||||
header: Text("Load Type"),
|
||||
footer: Text("For bodyweight exercises choose None. For resistance or weight training select Weight. For exercises that are time oriented (like plank or meditation) select Time.")
|
||||
@@ -170,16 +156,17 @@ struct ExerciseAddEditView: View {
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Weight Increase")) {
|
||||
HStack {
|
||||
Text("Remind every \(weightReminderWeeks) weeks")
|
||||
Spacer()
|
||||
Stepper("", value: $weightReminderWeeks, in: 0...366)
|
||||
}
|
||||
if let lastUpdated = weightLastUpdated {
|
||||
Text("Last weight change \(Date().humanTimeInterval(to: lastUpdated)) ago")
|
||||
Section(
|
||||
header: Text("Machine"),
|
||||
footer: Text("Turn on for machine-based exercises to record comfort settings (seat height, back-rest incline, pin position…). During a workout you can adjust these and update this default.")
|
||||
) {
|
||||
Toggle("Machine-based", isOn: $machineEnabled.animation())
|
||||
|
||||
if machineEnabled {
|
||||
MachineSettingsEditor(settings: $machineSettings)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.sheet(isPresented: $showingExercisePicker) {
|
||||
ExercisePickerView { exerciseNames in
|
||||
@@ -206,7 +193,6 @@ struct ExerciseAddEditView: View {
|
||||
|
||||
private func saveExercise() {
|
||||
let newWeight = weightTens + weightOnes
|
||||
let updatedWeightDate: Date? = newWeight != originalWeight ? Date() : weightLastUpdated
|
||||
let durationSecs = minutes * 60 + seconds
|
||||
|
||||
var doc = SplitDocument(from: split)
|
||||
@@ -217,9 +203,8 @@ struct ExerciseAddEditView: View {
|
||||
doc.exercises[idx].weight = newWeight
|
||||
doc.exercises[idx].loadType = loadType.rawValue
|
||||
doc.exercises[idx].durationSeconds = durationSecs
|
||||
doc.exercises[idx].weightLastUpdated = updatedWeightDate
|
||||
doc.exercises[idx].weightReminderWeeks = weightReminderWeeks
|
||||
doc.exercises[idx].category = category.rawValue
|
||||
// ON → a (possibly empty) array; OFF → nil, discarding any prior settings.
|
||||
doc.exercises[idx].machineSettings = machineEnabled ? machineSettings : nil
|
||||
}
|
||||
doc.updatedAt = Date()
|
||||
Task { await sync.save(split: doc) }
|
||||
|
||||
@@ -226,14 +226,7 @@ struct ExerciseListView: View {
|
||||
guard let split else { return }
|
||||
let startDate = Date()
|
||||
let logs = split.exercisesArray.enumerated().map { i, ex in
|
||||
WorkoutLogDocument(
|
||||
id: ULID.make(), exerciseName: ex.name, order: i,
|
||||
sets: ex.sets, reps: ex.reps, weight: ex.weight,
|
||||
loadType: ex.loadType, durationSeconds: ex.durationTotalSeconds,
|
||||
currentStateIndex: 0, completed: false,
|
||||
status: WorkoutStatus.notStarted.rawValue,
|
||||
notes: nil, date: startDate
|
||||
)
|
||||
WorkoutLogDocument(planFrom: ExerciseDocument(from: ex), order: i, date: startDate)
|
||||
}
|
||||
let doc = WorkoutDocument(
|
||||
schemaVersion: WorkoutDocument.currentSchemaVersion,
|
||||
@@ -266,7 +259,7 @@ struct ExerciseListView: View {
|
||||
id: ULID.make(), name: exName, order: base + i,
|
||||
sets: 3, reps: 10, weight: 40,
|
||||
loadType: LoadType.weight.rawValue,
|
||||
durationSeconds: 0, weightLastUpdated: nil, weightReminderWeeks: 2
|
||||
durationSeconds: 0
|
||||
)
|
||||
}
|
||||
doc.exercises.append(contentsOf: newDocs)
|
||||
|
||||
Reference in New Issue
Block a user