Fix 28 issues from deep audit and UI audit + redesign changes
Some checks failed
Apple Platform CI / smoke-and-tests (push) Has been cancelled

Deep audit (issues 2-14):
- Add missing WCSession handlers for applicationContext and userInfo
- Fix BoundedFIFOQueue race condition with serial dispatch queue
- Fix timer race condition with main thread guarantee
- Fix watch pause state divergence — phone is now source of truth
- Fix wrong notification posted on logout (createdNewWorkout → userLoggedOut)
- Fix POST status check to accept any 2xx (was exact match)
- Fix @StateObject → @ObservedObject for injected viewModel
- Add pull-to-refresh to CompletedWorkoutsView
- Fix typos: RefreshUserInfoFetcable, defualtPackageModle
- Replace string concatenation with interpolation
- Replace 6 @StateObject with @ObservedObject for BridgeModule.shared
- Replace 7 hardcoded AVPlayer URLs with BaseURLs.currentBaseURL

UI audit (issues 1-15):
- Fix GeometryReader eating VStack space — replaced with .overlay
- Fix refreshable continuation resuming before fetch completes
- Remove duplicate @State workouts — derive from DataStore
- Decouple leaf views from BridgeModule (pass discrete values)
- Convert selectedIds from Array to Set for O(1) lookups
- Extract .sorted() from var body into computed properties
- Move search filter out of ForEach render loop
- Replace import SwiftUI with import Combine in non-UI classes
- Mark all @State properties private
- Extract L/R exercise auto-add logic to WorkoutViewModel
- Use enumerated() instead of .indices in ForEach
- Make AddSupersetView frame flexible instead of fixed 300pt
- Hoist Set construction out of per-exercise filter loop
- Move ViewModel network fetch from init to load()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-23 10:24:06 -06:00
parent 921829f2c3
commit 5d39dcb66f
60 changed files with 1783 additions and 1346 deletions

View File

@@ -14,77 +14,63 @@ struct AddExerciseView: View {
case muscles
case equipment
}
@State var selectedMuscles = [Muscle]()
@State var selectedEquipment = [Equipment]()
@State var filteredExercises = [Exercise]()
@StateObject var bridgeModule = BridgeModule.shared
@ObservedObject var bridgeModule = BridgeModule.shared
let selectedExercise: ((Exercise) -> Void)
var body: some View {
VStack {
VStack(spacing: 0) {
AllExerciseView(filteredExercises: $filteredExercises,
selectedExercise: { excercise in
selectedExercise(excercise)
})
.padding(.top)
.padding(.top, WerkoutTheme.md)
Divider()
.overlay(WerkoutTheme.divider)
HStack {
AllMusclesView(selectedMuscles: $selectedMuscles)
.frame(maxWidth: .infinity)
Divider()
.overlay(WerkoutTheme.divider)
AllEquipmentView(selectedEquipment: $selectedEquipment)
.frame(maxWidth: .infinity)
}
.padding(.top)
.padding(.top, WerkoutTheme.sm)
.frame(height: 44)
}
.onChange(of: selectedMuscles, perform: { _ in
.background(WerkoutTheme.background)
.onChange(of: selectedMuscles) { _, _ in
filterExercises()
}) .onChange(of: selectedEquipment, perform: { _ in
}
.onChange(of: selectedEquipment) { _, _ in
filterExercises()
})
}
}
func filterExercises() {
guard let exercises = DataStore.shared.allExercise else {
filteredExercises = []
return
}
let filtered = exercises.filter({ exercise in
var hasCorrectMuscles = false
if selectedMuscles.count == 0 {
hasCorrectMuscles = true
} else {
let exerciseMuscleIds = exercise.muscles.map({ $0.muscle ?? -1 })
let selctedMuscleIds = selectedMuscles.map({ $0.id })
// if one items match
if exerciseMuscleIds.contains(where: selctedMuscleIds.contains) {
// if all items match
hasCorrectMuscles = true
}
}
var hasCorrectEquipment = false
if selectedEquipment.count == 0 {
hasCorrectEquipment = true
} else {
let exerciseEquipmentIds = exercise.equipment.map({ $0.equipment ?? -1 })
let selctedEquipmentIds = selectedEquipment.map({ $0.id })
// if one items match
if exerciseEquipmentIds.contains(where: selctedEquipmentIds.contains) {
// if all items match
hasCorrectEquipment = true
}
}
return hasCorrectMuscles && hasCorrectEquipment
})
filteredExercises = filtered
let selectedMuscleIds = Set(selectedMuscles.map { $0.id })
let selectedEquipmentIds = Set(selectedEquipment.map { $0.id })
filteredExercises = exercises.filter { exercise in
let muscleOK = selectedMuscleIds.isEmpty ||
exercise.muscles.contains(where: { selectedMuscleIds.contains($0.muscle ?? -1) })
let equipmentOK = selectedEquipmentIds.isEmpty ||
exercise.equipment.contains(where: { selectedEquipmentIds.contains($0.equipment ?? -1) })
return muscleOK && equipmentOK
}
}
}