// // ExerciseListView.swift // Werkout_ios // // Created by Trey Tartt on 7/7/23. // import SwiftUI import AVKit struct ExerciseListView: View { @AppStorage(Constants.phoneThotStyle) private var phoneThotStyle: ThotStyle = .never @ObservedObject 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 private var previewVideoURL: URL? var workout: Workout @Binding var showExecersizeInfo: Bool @AppStorage(Constants.thotGenderOption) private var thotGenderOption: String = "female" @State var videoExercise: Exercise? { didSet { if let videoURL = VideoURLCreator.videoURL( thotStyle: phoneThotStyle, gender: thotGenderOption, defaultVideoURLStr: self.videoExercise?.videoURL, exerciseName: self.videoExercise?.name, workout: bridgeModule.currentWorkoutInfo.workout) { updatePreviewPlayer(for: videoURL) } } } var body: some View { let supersets = workout.supersets?.sorted(by: { $0.order < $1.order }) ?? [] if supersets.isEmpty == false { ScrollViewReader { proxy in List() { ForEach(supersets.indices, id: \.self) { supersetIndex in let superset = supersets[supersetIndex] Section(content: { ForEach(superset.exercises.indices, id: \.self) { exerciseIndex in let supersetExecercise = superset.exercises[exerciseIndex] let rowID = rowIdentifier( supersetIndex: supersetIndex, exerciseIndex: exerciseIndex, exercise: supersetExecercise ) VStack { Button(action: { if bridgeModule.isInWorkout { bridgeModule.currentWorkoutInfo.goToExerciseAt( supersetIndex: supersetIndex, exerciseIndex: exerciseIndex) } else { videoExercise = supersetExecercise.exercise } }, label: { HStack { if bridgeModule.isInWorkout && supersetIndex == bridgeModule.currentWorkoutInfo.supersetIndex && exerciseIndex == bridgeModule.currentWorkoutInfo.exerciseIndex { Image(systemName: "figure.run") .foregroundColor(Color("appColor")) } Text(supersetExecercise.exercise.extName) Spacer() if let reps = supersetExecercise.reps, reps > 0 { HStack { Image(systemName: "number") .foregroundColor(.white) .frame(width: 20, alignment: .leading) Text("\(reps)") .foregroundColor(.white) .frame(width: 30, alignment: .trailing) } .padding([.top, .bottom], 5) .padding([.leading], 10) .padding([.trailing], 15) .background(.blue) .cornerRadius(5, corners: [.topLeft, .bottomLeft]) .frame(alignment: .trailing) } if let duration = supersetExecercise.duration, duration > 0 { HStack { Image(systemName: "stopwatch") .foregroundColor(.white) .frame(width: 20, alignment: .leading) Text("\(duration)") .foregroundColor(.white) .frame(width: 30, alignment: .trailing) } .padding([.top, .bottom], 5) .padding([.leading], 10) .padding([.trailing], 15) .background(.green) .cornerRadius(5, corners: [.topLeft, .bottomLeft]) } } .padding(.trailing, -20) .contentShape(Rectangle()) }) .buttonStyle(.plain) .accessibilityLabel("Exercise \(supersetExecercise.exercise.extName)") .accessibilityHint(bridgeModule.isInWorkout ? "Jump to this exercise in the workout" : "Preview exercise video") if bridgeModule.isInWorkout && supersetIndex == bridgeModule.currentWorkoutInfo.supersetIndex && exerciseIndex == bridgeModule.currentWorkoutInfo.exerciseIndex && showExecersizeInfo { detailView(forExercise: supersetExecercise) } }.id(rowID) } }, header: { HStack { Text(superset.name ?? "--") .foregroundColor(Color("appColor")) .bold() Spacer() Text("\(superset.rounds) rounds") .foregroundColor(Color("appColor")) .bold() if let estimatedTime = superset.estimatedTime { Text("@ " + estimatedTime.asString(style: .abbreviated)) .foregroundColor(Color("appColor")) .bold() } } }) } } .onChange(of: bridgeModule.currentWorkoutInfo.allSupersetExecerciseIndex, perform: { newValue in if let newCurrentExercise = bridgeModule.currentWorkoutInfo.currentExercise { withAnimation { let currentSupersetIndex = bridgeModule.currentWorkoutInfo.supersetIndex let currentExerciseIndex = bridgeModule.currentWorkoutInfo.exerciseIndex proxy.scrollTo( rowIdentifier( supersetIndex: currentSupersetIndex, exerciseIndex: currentExerciseIndex, exercise: newCurrentExercise ), anchor: .top ) } } }) .sheet(item: $videoExercise) { exercise in PlayerView(player: $avPlayer) .onAppear{ avPlayer.isMuted = true avPlayer.play() } } .onDisappear { avPlayer.pause() } } } } private func rowIdentifier(supersetIndex: Int, exerciseIndex: Int, exercise: SupersetExercise) -> String { if let uniqueID = exercise.uniqueID, uniqueID.isEmpty == false { return uniqueID } if let id = exercise.id { return "exercise-\(id)" } return "superset-\(supersetIndex)-exercise-\(exerciseIndex)" } private func updatePreviewPlayer(for url: URL) { if previewVideoURL == url { avPlayer.seek(to: .zero) avPlayer.isMuted = true avPlayer.play() return } previewVideoURL = url avPlayer = AVPlayer(url: url) avPlayer.isMuted = true avPlayer.play() } func detailView(forExercise supersetExecercise: SupersetExercise) -> some View { VStack { Text(supersetExecercise.exercise.description) .frame(alignment: .leading) Divider() Text(supersetExecercise.exercise.muscles.map({ $0.name }).joined(separator: ", ")) .frame(alignment: .leading) } } } struct ExerciseListView_Previews: PreviewProvider { static var previews: some View { ExerciseListView(workout: PreviewData.workout(), showExecersizeInfo: .constant(true)) } }