// // MainView.swift // Werkout_ios // // Created by Trey Tartt on 6/14/23. // import SwiftUI struct WorkoutDetailView: View { @StateObject var viewModel: WorkoutDetailViewModel var bridgeModule = BridgeModule.shared @Environment(\.dismiss) var dismiss enum Sheet: Identifiable { case completedWorkout([String: Any]) var id: String { return UUID().uuidString } } @State var presentedSheet: Sheet? @State var workoutToPlan: Workout? var showAddToCalendar = true var body: some View { ZStack { switch viewModel.status { case .loading: Text("Loading") case .showWorkout(let workout): VStack { InfoView(workout: workout) Divider() CurrentWorkoutElapsedTimeView() .padding(.top) CountdownView() ExerciseListView(workout: workout) ActionsView(completedWorkout: { bridgeModule.completeWorkout() }, planWorkout: { workout in workoutToPlan = workout }, workout: workout, showAddToCalendar: showAddToCalendar) .frame(height: 44) } .sheet(item: $presentedSheet) { item in switch item { case .completedWorkout(let data): CompletedWorkoutView(postData: data, workout: workout, completedWorkoutDismissed: { uploaded in if uploaded { dismiss() } }) } } .sheet(item: $workoutToPlan) { workout in PlanWorkoutView(workout: workout, addedPlannedWorkout: { dismiss() }) } .interactiveDismissDisabled() } } .onAppear{ bridgeModule.completedWorkout = { if let workoutData = createWorkoutData() { presentedSheet = .completedWorkout(workoutData) bridgeModule.resetCurrentWorkout() } } } } func createWorkoutData() -> [String:Any]? { guard let workoutid = bridgeModule.currentWorkout?.id, let startTime = bridgeModule.workoutStartDate?.timeFormatForUpload, let endTime = bridgeModule.workoutEndDate?.timeFormatForUpload else { return nil } let postBody = [ "difficulty": 1, "workout_start_time": startTime, "workout_end_time": endTime, "workout": workoutid, "total_time": bridgeModule.currentWorkoutRunTimeInSeconds, "total_calories": bridgeModule.totalCaloire ?? -1, "heart_rates": bridgeModule.heartRates ?? [Int]() ] as [String : Any] return postBody } } struct InfoView: View { @ObservedObject var bridgeModule = BridgeModule.shared var workout: Workout var body: some View { VStack { if bridgeModule.isInWorkout == false { Text(workout.name) .frame(maxWidth: .infinity, alignment: .leading) .font(.title3) .padding() if let desc = workout.description { Text(desc) .frame(maxWidth: .infinity, alignment: .leading) .font(.body) .padding([.leading, .trailing]) } } } } } struct ActionsView: View { @ObservedObject var bridgeModule = BridgeModule.shared var completedWorkout: (() -> Void)? var planWorkout: ((Workout) -> Void)? var workout: Workout @Environment(\.dismiss) var dismiss var showAddToCalendar: Bool var body: some View { HStack { if bridgeModule.isInWorkout == false { Button(action: { bridgeModule.resetCurrentWorkout() dismiss() }, label: { Image(systemName: "xmark.octagon.fill") .font(.title) .frame(maxWidth: .infinity, maxHeight: .infinity) }) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.red) .foregroundColor(.white) if showAddToCalendar { Button(action: { planWorkout?(workout) }, label: { Image(systemName: "calendar.badge.plus") .font(.title) .frame(maxWidth: .infinity, maxHeight: .infinity) }) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.blue) .foregroundColor(.white) } Button(action: { startWorkout() }, label: { Image(systemName: "arrowtriangle.forward.fill") .font(.title) .frame(maxWidth: .infinity, maxHeight: .infinity) }) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.green) .foregroundColor(.white) } else { Button(action: { nextExercise() }, label: { Image(systemName: "arrow.forward") .font(.title) .frame(maxWidth: .infinity, maxHeight: .infinity) }) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.yellow) .foregroundColor(.white) Button(action: { completedWorkout?() }, label: { Image(systemName: "checkmark") .font(.title) .frame(maxWidth: .infinity, maxHeight: .infinity) }) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.blue) .foregroundColor(.white) } } } func nextExercise() { bridgeModule.nextExercise() } func startWorkout() { bridgeModule.start(workout: workout) } } struct CurrentWorkoutElapsedTimeView: View { @ObservedObject var bridgeModule = BridgeModule.shared var body: some View { if bridgeModule.currentWorkoutRunTimeInSeconds > -1 { Text("\(bridgeModule.currentWorkoutRunTimeInSeconds)") .font(.title2) } } } struct ExerciseListView: View { @ObservedObject var bridgeModule = BridgeModule.shared var workout: Workout var body: some View { List() { ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in let obj = workout.exercisesSortedByCreated_at[i] VStack { HStack { if i == bridgeModule.currentExerciseIdx { Image(systemName: "checkmark") .foregroundColor(.green) } Text(obj.exercise.name) .onTapGesture { bridgeModule.goToExerciseAt(index: i) } Spacer() } if i == bridgeModule.currentExerciseIdx { HStack { if obj.exercise.isReps { Text("is reps") .frame(maxWidth: .infinity, maxHeight: .infinity) } if obj.exercise.isWeight { Text("is weight") .frame(maxWidth: .infinity, maxHeight: .infinity) } if obj.exercise.isDuration { Text("is duration") .frame(maxWidth: .infinity, maxHeight: .infinity) } } } } } } } } struct CountdownView: View { @StateObject var bridgeModule = BridgeModule.shared var body: some View { VStack { if let duration = bridgeModule.currentExercise?.duration { HStack { ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration)) Text("\(bridgeModule.currentExerciseTimeLeft)") }.padding(16) } } } } struct WorkoutDetailView_Previews: PreviewProvider { static let workoutDetail = PreviewData.workout() static var previews: some View { WorkoutDetailView(viewModel: WorkoutDetailViewModel(workout: WorkoutDetailView_Previews.workoutDetail, status: .showWorkout(WorkoutDetailView_Previews.workoutDetail))) } }