diff --git a/Workouts/Views/Exercises/ExerciseListView.swift b/Workouts/Views/Exercises/ExerciseListView.swift deleted file mode 100644 index 1f673ff..0000000 --- a/Workouts/Views/Exercises/ExerciseListView.swift +++ /dev/null @@ -1,198 +0,0 @@ -// -// ExerciseListView.swift -// Workouts -// -// Created by rzen on 7/18/25 at 8:38 AM. -// -// Copyright 2025 Rouslan Zenetl. All Rights Reserved. -// - -import IndieSync -import SwiftUI -import SwiftData - -struct ExerciseListView: View { - @Environment(SyncEngine.self) private var sync - @Environment(\.dismiss) private var dismiss - - // Resolve the routine by id, not a captured entity: editing a seed's exercise from - // here clones the seed (new identity, old entity deleted), which would dangle a - // stored `Routine`. `currentRoutineID` follows that swap. - @State private var routineID: String - @Query private var routines: [Routine] - - @State private var showingAddSheet: Bool = false - @State private var itemToEdit: Exercise? = nil - @State private var itemToDelete: Exercise? = nil - @AppStorage("weightUnit") private var weightUnit: WeightUnit = .lb - - init(routine: Routine) { - _routineID = State(initialValue: routine.id) - } - - private var routine: Routine? { - let id = sync.currentRoutineID(for: routineID) - return routines.first { $0.id == id } - } - - var body: some View { - Group { - if let routine { - content(for: routine) - } else { - ContentUnavailableView("Routine Unavailable", systemImage: "dumbbell") - .task { dismiss() } - } - } - } - - @ViewBuilder - private func content(for routine: Routine) -> some View { - Form { - let sortedExercises = routine.exercisesArray - - if !sortedExercises.isEmpty { - ForEach(sortedExercises) { item in - ListItem( - title: item.name, - subtitle: "\(item.sets) × \(item.reps) × \(weightUnit.format(item.weight))" - ) - .swipeActions(edge: .leading) { - Button { - duplicateExercise(item) - } label: { - Label("Duplicate", systemImage: "plus.square.on.square") - } - .tint(.teal) - } - .swipeActions { - Button { - itemToDelete = item - } label: { - Label("Delete", systemImage: "trash") - } - .tint(.red) - Button { - itemToEdit = item - } label: { - Label("Edit", systemImage: "pencil") - } - .tint(.indigo) - } - } - .onMove(perform: moveExercises) - - Button { - showingAddSheet = true - } label: { - ListItem(title: "Add Exercise") - } - } else { - Text("No exercises added yet.") - Button(action: { showingAddSheet.toggle() }) { - ListItem(title: "Add Exercise") - } - } - } - .navigationTitle(routine.name) - .sheet(isPresented: $showingAddSheet) { - ExercisePickerView(onExerciseSelected: { exerciseNames in - addExercises(names: exerciseNames) - }, allowMultiSelect: true, - inRoutineCounts: Dictionary( - routine.exercisesArray.map { ($0.libraryExerciseName, 1) }, - uniquingKeysWith: + - )) - } - .sheet(item: $itemToEdit) { item in - ExerciseAddEditView(exercise: item, routine: routine) - } - .confirmationDialog( - "Delete Exercise?", - isPresented: Binding( - get: { itemToDelete != nil }, - set: { if !$0 { itemToDelete = nil } } - ), - titleVisibility: .visible, - presenting: itemToDelete - ) { item in - Button("Delete", role: .destructive) { - deleteExercise(item) - itemToDelete = nil - } - Button("Cancel", role: .cancel) { - itemToDelete = nil - } - } message: { item in - Text("Remove \"\(item.name)\" from this routine?") - } - } - - // MARK: - Helpers - - private func moveExercises(from source: IndexSet, to destination: Int) { - guard let routine else { return } - var exercises = routine.exercisesArray - exercises.move(fromOffsets: source, toOffset: destination) - var doc = RoutineDocument(from: routine) - doc.exercises = exercises.enumerated().map { i, ex in - var ed = ExerciseDocument(from: ex) - ed.order = i - return ed - } - doc.updatedAt = Date() - Task { await sync.save(routine: doc) } - } - - private func addExercises(names: [String]) { - guard let routine else { return } - var doc = RoutineDocument(from: routine) - let base = doc.exercises.count - let newDocs = names - .enumerated() - .map { i, exName -> ExerciseDocument in - // Seed from the library's authored `**Defaults:**` when available, - // falling back to a plain 3×10 weighted guess; weight always starts - // at 0 (there's no prior lift to seed it from). - let defaults = ExerciseInfoLibrary.info(for: exName)?.defaults - return ExerciseDocument( - id: ULID.make(), name: exName, order: base + i, - sets: defaults?.sets ?? 3, reps: defaults?.reps ?? 10, weight: 0, - loadType: (defaults?.loadType ?? .weight).rawValue, - durationSeconds: defaults?.durationSeconds ?? 0 - ) - } - doc.exercises.append(contentsOf: newDocs) - doc.updatedAt = Date() - Task { await sync.save(routine: doc) } - } - - private func deleteExercise(_ exercise: Exercise) { - guard let routine else { return } - var doc = RoutineDocument(from: routine) - doc.exercises.removeAll { $0.id == exercise.id } - for i in doc.exercises.indices { - doc.exercises[i].order = i - } - doc.updatedAt = Date() - Task { await sync.save(routine: doc) } - } - - /// Copy an exercise in place, right after itself — the interval-routine case - /// (a treadmill's "Warmup 5 min" / "Brisk Walk 10 min" / … segments all derive - /// from the same library exercise). The clone starts out an exact duplicate, - /// plan and any customized name included; renaming it is a follow-up edit. - private func duplicateExercise(_ exercise: Exercise) { - guard let routine else { return } - var doc = RoutineDocument(from: routine) - guard let idx = doc.exercises.firstIndex(where: { $0.id == exercise.id }) else { return } - var copy = doc.exercises[idx] - copy.id = ULID.make() - doc.exercises.insert(copy, at: idx + 1) - for i in doc.exercises.indices { - doc.exercises[i].order = i - } - doc.updatedAt = Date() - Task { await sync.save(routine: doc) } - } -} diff --git a/Workouts/Views/Routines/OrderableItem.swift b/Workouts/Views/Routines/OrderableItem.swift deleted file mode 100644 index 0be6265..0000000 --- a/Workouts/Views/Routines/OrderableItem.swift +++ /dev/null @@ -1,10 +0,0 @@ -// -// OrderableItem.swift -// Workouts -// -// Created by rzen on 7/18/25 at 5:19 PM. -// -// Copyright 2025 Rouslan Zenetl. All Rights Reserved. -// -// No longer used — reordering is now a SyncEngine document write (onMove → doc save). -// File kept to avoid Xcode project reference errors. diff --git a/Workouts/Views/Routines/RoutineAddEditView.swift b/Workouts/Views/Routines/RoutineAddEditView.swift index 0f0f5d6..087cd60 100644 --- a/Workouts/Views/Routines/RoutineAddEditView.swift +++ b/Workouts/Views/Routines/RoutineAddEditView.swift @@ -160,18 +160,7 @@ struct RoutineAddEditView: View { } } - if let routine = routine { - Section(header: Text("Exercises")) { - NavigationLink { - ExerciseListView(routine: routine) - } label: { - ListItem( - text: "Exercises", - count: routine.exercisesArray.count - ) - } - } - + if routine != nil { Section { Button("Delete Routine", role: .destructive) { showingDeleteConfirmation = true diff --git a/Workouts/Views/Routines/SortableForEach.swift b/Workouts/Views/Routines/SortableForEach.swift deleted file mode 100644 index c769f15..0000000 --- a/Workouts/Views/Routines/SortableForEach.swift +++ /dev/null @@ -1,10 +0,0 @@ -// -// SortableForEach.swift -// Workouts -// -// Created by rzen on 7/18/25 at 2:04 PM. -// -// Copyright 2025 Rouslan Zenetl. All Rights Reserved. -// -// No longer used — list reordering is handled with SwiftUI's native onMove modifier -// backed by SyncEngine document writes. File kept to avoid Xcode project reference errors.