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

@@ -9,9 +9,9 @@ import SwiftUI
import AVKit
struct ExternalWorkoutDetailView: View {
@StateObject var bridgeModule = BridgeModule.shared
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4") ?? URL(fileURLWithPath: "/dev/null"))
@State var smallAVPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4") ?? URL(fileURLWithPath: "/dev/null"))
@ObservedObject var bridgeModule = BridgeModule.shared
@State var avPlayer = AVPlayer(url: URL(string: BaseURLs.currentBaseURL + "/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4") ?? URL(fileURLWithPath: "/dev/null"))
@State var smallAVPlayer = AVPlayer(url: URL(string: BaseURLs.currentBaseURL + "/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4") ?? URL(fileURLWithPath: "/dev/null"))
@State private var currentVideoURL: URL?
@State private var currentSmallVideoURL: URL?
@AppStorage(Constants.extThotStyle) private var extThotStyle: ThotStyle = .never
@@ -32,23 +32,23 @@ struct ExternalWorkoutDetailView: View {
avPlayer.play()
}
}
VStack {
ExtExerciseList(workout: workout,
allSupersetExecerciseIndex: bridgeModule.currentWorkoutInfo.allSupersetExecerciseIndex)
}
.frame(width: metrics.size.width * 0.4, height: metrics.size.height * 0.8)
.padding([.top, .bottom], 20)
.padding([.top, .bottom], WerkoutTheme.lg)
}
HStack {
ExtCountdownView()
.padding(.leading, 50)
.padding(.bottom, 20)
.padding(.bottom, WerkoutTheme.lg)
if extShowNextVideo && extThotStyle != .off {
PlayerView(player: $smallAVPlayer)
.frame(width: metrics.size.width * 0.2,
.frame(width: metrics.size.width * 0.2,
height: metrics.size.height * 0.2)
.onAppear{
smallAVPlayer.isMuted = true
@@ -56,7 +56,7 @@ struct ExternalWorkoutDetailView: View {
}
}
}
.background(Color(uiColor: .tertiarySystemGroupedBackground))
.background(WerkoutTheme.surfaceCard)
}
}
} else {
@@ -65,43 +65,34 @@ struct ExternalWorkoutDetailView: View {
.edgesIgnoringSafeArea(.all)
.scaledToFill()
}
VStack {
HStack {
if bridgeModule.currentWorkoutRunTimeInSeconds > -1 {
Text(" \(Double(bridgeModule.currentWorkoutRunTimeInSeconds).asString(style: .positional)) ")
.font(Font.system(size: 120))
.font(WerkoutTheme.stat)
.minimumScaleFactor(0.01)
.lineLimit(1)
.padding()
.bold()
.foregroundColor(.white)
.background(
Capsule()
.strokeBorder(Color.black, lineWidth: 0.8)
.background(Color(uiColor: UIColor(red: 148/255,
green: 0,
blue: 211/255,
alpha: 0.5)))
.clipped()
)
.clipShape(Capsule())
.foregroundColor(WerkoutTheme.textPrimary)
.glassEffect(.regular.interactive())
.tint(WerkoutTheme.accent)
}
Spacer()
}
.padding(.leading, 50)
Spacer()
}
}
.onChange(of: bridgeModule.isInWorkout, perform: { _ in
.onChange(of: bridgeModule.isInWorkout) {
playVideos()
})
.onChange(of: bridgeModule.currentWorkoutInfo.allSupersetExecerciseIndex, perform: { _ in
}
.onChange(of: bridgeModule.currentWorkoutInfo.allSupersetExecerciseIndex) {
playVideos()
})
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(bridgeModule.currentWorkoutInfo.workout == nil ? Color(red: 157/255, green: 138/255, blue: 255/255) : Color(uiColor: .systemBackground))
.background(bridgeModule.currentWorkoutInfo.workout == nil ? WerkoutTheme.accent : WerkoutTheme.background)
.onReceive(NotificationCenter.default.publisher(
for: UIScene.willEnterForegroundNotification)) { _ in
avPlayer.play()
@@ -112,7 +103,7 @@ struct ExternalWorkoutDetailView: View {
smallAVPlayer.pause()
}
}
func playVideos() {
if let currentExtercise = bridgeModule.currentWorkoutInfo.currentExercise {
if let videoURL = VideoURLCreator.videoURL(
@@ -123,7 +114,7 @@ struct ExternalWorkoutDetailView: View {
workout: bridgeModule.currentWorkoutInfo.workout) {
updateMainPlayer(for: videoURL)
}
if let smallVideoURL = VideoURLCreator.videoURL(
thotStyle: .never,
gender: thotGenderOption,
@@ -163,7 +154,7 @@ struct ExternalWorkoutDetailView: View {
//struct ExternalWorkoutDetailView_Previews: PreviewProvider {
// static var bridge = BridgeModule.shared
//
//
// static var previews: some View {
// ExternalWorkoutDetailView().environmentObject({ () -> BridgeModule in
// let envObj = BridgeModule.shared