// // ExternalView.swift // Werkout_ios // // Created by Trey Tartt on 6/13/23. // 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")!) @State var smallAVPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!) @AppStorage(Constants.extThotStyle) private var extThotStyle: ThotStyle = .never @AppStorage(Constants.extShowBothVideos) private var extShowBothVideos: Bool = false var body: some View { ZStack { if let workout = bridgeModule.currentExerciseInfo.workout, let exercise = bridgeModule.currentExerciseInfo.currentExercise { GeometryReader { metrics in VStack { HStack { if extThotStyle != .off { PlayerView(player: $avPlayer) .frame(width: metrics.size.width * 0.5, height: metrics.size.height * 0.8) .onAppear{ avPlayer.play() } } VStack { ExtExerciseList(workout: workout, currentExercise: exercise) if let currentExercisePositionString = bridgeModule.currentExercisePositionString { Text(currentExercisePositionString) .font(Font.system(size: 75)) .scaledToFit() .minimumScaleFactor(0.01) .lineLimit(1) .padding() } } .frame(width: metrics.size.width * 0.4, height: metrics.size.height * 0.8) } HStack { if extShowBothVideos && extThotStyle != .off { ExtCountdownView() .frame(width: metrics.size.width * 0.8, height: metrics.size.height * 0.2) .padding(.leading, 50) .padding(.trailing, 5) PlayerView(player: $smallAVPlayer) .frame(width: metrics.size.width * 0.2, height: metrics.size.height * 0.2) .padding(.trailing, 50) .onAppear{ avPlayer.play() } } else { ExtCountdownView() .frame(width: metrics.size.width-50, height: metrics.size.height * 0.2) .padding([.leading, .trailing], 50) } } } } } else { Image("icon") .resizable() .edgesIgnoringSafeArea(.all) .scaledToFill() } } .onChange(of: bridgeModule.isInWorkout, perform: { newValue in if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise { if let videoURL = VideoURLCreator.videoURL( thotStyle: extThotStyle, defaultVideoURLStr: currentExtercise.exercise.videoURL, exerciseName: currentExtercise.exercise.name, workout: bridgeModule.currentExerciseInfo.workout) { avPlayer = AVPlayer(url: videoURL) avPlayer.play() if extShowBothVideos { if let smallVideoURL = VideoURLCreator.videoURL( thotStyle: VideoURLCreator.otherVideoType(forVideoURL: videoURL), defaultVideoURLStr: currentExtercise.exercise.videoURL, exerciseName: currentExtercise.exercise.name, workout: bridgeModule.currentExerciseInfo.workout) { smallAVPlayer = AVPlayer(url: smallVideoURL) smallAVPlayer.play() } } } } }) .onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise { if let videoURL = VideoURLCreator.videoURL( thotStyle: extThotStyle, defaultVideoURLStr: currentExtercise.exercise.videoURL, exerciseName: currentExtercise.exercise.name, workout: bridgeModule.currentExerciseInfo.workout) { avPlayer = AVPlayer(url: videoURL) avPlayer.play() if extShowBothVideos { if let smallVideoURL = VideoURLCreator.videoURL( thotStyle: VideoURLCreator.otherVideoType(forVideoURL: videoURL), defaultVideoURLStr: currentExtercise.exercise.videoURL, exerciseName: currentExtercise.exercise.name, workout: bridgeModule.currentExerciseInfo.workout) { smallAVPlayer = AVPlayer(url: smallVideoURL) smallAVPlayer.play() } } } } }) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(bridgeModule.currentExerciseInfo.workout == nil ? Color(red: 157/255, green: 138/255, blue: 255/255) : Color(uiColor: .systemBackground)) .onReceive(NotificationCenter.default.publisher( for: UIScene.willEnterForegroundNotification)) { _ in avPlayer.play() smallAVPlayer.play() } } } struct TitleView: View { @ObservedObject var bridgeModule = BridgeModule.shared var body: some View { HStack { if let workout = bridgeModule.currentExerciseInfo.workout { Text(workout.name) .font(Font.system(size: 100)) .frame(maxWidth: .infinity, alignment: .leading) } if bridgeModule.currentWorkoutRunTimeInSeconds > -1 { Text("\(bridgeModule.currentWorkoutRunTimeInSeconds)") .font(Font.system(size: 100)) .frame(maxWidth: .infinity, alignment: .trailing) .padding(.trailing, 100) } } } } struct ExtExerciseList: View { var workout: Workout var currentExercise: SupersetExercise var body: some View { if let supersets = workout.supersets { 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] HStack { if supersetExecercise.id == currentExercise.id { Image(systemName: "checkmark") .foregroundColor(.green) .font(Font.system(size: 55)) .minimumScaleFactor(0.01) .lineLimit(1) .foregroundColor(.green) } Text(supersetExecercise.exercise.name) .font(Font.system(size: 55)) .minimumScaleFactor(0.01) .lineLimit(3) .padding() Spacer() } .id(supersetExecercise.id) } }, header: { HStack { Text(superset.name ?? "--") .font(Font.system(size: 55)) .minimumScaleFactor(0.01) .lineLimit(3) .padding() Spacer() Text("\(superset.rounds) rounds") .font(Font.system(size: 55)) .minimumScaleFactor(0.01) .lineLimit(3) .padding() } }) } } .onChange(of: currentExercise, perform: { newValue in withAnimation { print(newValue.id) proxy.scrollTo(newValue.id, anchor: .top) } }) } } } } struct ExtCountdownView: View { @StateObject var bridgeModule = BridgeModule.shared var body: some View { GeometryReader { metrics in VStack { if let currenExercise = bridgeModule.currentExerciseInfo.currentExercise { HStack { Text(currenExercise.exercise.extName) .font(.system(size: 200)) .scaledToFit() .minimumScaleFactor(0.01) .lineLimit(1) .frame(maxWidth: .infinity, alignment: .leading) if bridgeModule.currentWorkoutRunTimeInSeconds > -1 { Text("\(Double(bridgeModule.currentWorkoutRunTimeInSeconds).asString(style: .positional))") .font(Font.system(size: 100)) .scaledToFit() .minimumScaleFactor(0.01) .lineLimit(1) .frame(maxWidth: .infinity, alignment: .trailing) .padding(.trailing, 100) } } .frame(height: metrics.size.height * 0.5) HStack { if let duration = currenExercise.duration, duration > 0 { ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration)) .scaleEffect(x: 1, y: 6, anchor: .center) Text("\(bridgeModule.currentExerciseTimeLeft)") .font(Font.system(size: 100)) .scaledToFit() .minimumScaleFactor(0.01) .lineLimit(1) .padding(.leading) .padding(.trailing, 100) } if let reps = currenExercise.reps, reps > 0 { Text(" X \(reps)") .font(Font.system(size: 100)) .scaledToFit() .minimumScaleFactor(0.01) .lineLimit(1) .frame(maxWidth: .infinity, alignment: .leading) } if let weight = currenExercise.weight, weight > 0 { Text(" @ \(weight)") .font(Font.system(size: 100)) .scaledToFit() .minimumScaleFactor(0.01) .lineLimit(1) .padding(.trailing, 100) .frame(maxWidth: .infinity, alignment: .leading) } } .frame(height: metrics.size.height * 0.5) } } } } } //struct ExternalWorkoutDetailView_Previews: PreviewProvider { // static var bridge = BridgeModule.shared // // static var previews: some View { // ExternalWorkoutDetailView().environmentObject({ () -> BridgeModule in // let envObj = BridgeModule.shared // envObj.currentWorkout = nil //PreviewData.workout() // bridge.currentExercise = PreviewData.workout().exercisesSortedByCreated_at.first! // return envObj // }() ) // } //}