diff --git a/Werkout_ios.xcodeproj/project.pbxproj b/Werkout_ios.xcodeproj/project.pbxproj index 20af24d..fae7c1a 100644 --- a/Werkout_ios.xcodeproj/project.pbxproj +++ b/Werkout_ios.xcodeproj/project.pbxproj @@ -17,6 +17,10 @@ 1C485C8D2A49D95700A6F896 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A822A42347D0042FFBD /* Extensions.swift */; }; 1C4AFF152A60F25F0027710B /* ThotStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF142A60F25E0027710B /* ThotStyle.swift */; }; 1C4AFF162A60F27E0027710B /* ThotStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF142A60F25E0027710B /* ThotStyle.swift */; }; + 1C4AFF182A65CD290027710B /* Superset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF172A65CD290027710B /* Superset.swift */; }; + 1C4AFF192A65CD6F0027710B /* Superset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF172A65CD290027710B /* Superset.swift */; }; + 1C4AFF1B2A65FB190027710B /* CurrentWorkoutInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */; }; + 1C4AFF1C2A65FB2B0027710B /* CurrentWorkoutInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */; }; 1C5190C22A57CA5F00885849 /* OvalTextFieldStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C12A57CA5F00885849 /* OvalTextFieldStyle.swift */; }; 1C5190C42A589CAC00885849 /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C32A589CAC00885849 /* InfoView.swift */; }; 1C5190C62A589CC100885849 /* ActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C52A589CC100885849 /* ActionsView.swift */; }; @@ -129,6 +133,8 @@ 1C485C892A492BB400A6F896 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 1C485C8B2A49D65600A6F896 /* WorkoutHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutHistoryView.swift; sourceTree = ""; }; 1C4AFF142A60F25E0027710B /* ThotStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThotStyle.swift; sourceTree = ""; }; + 1C4AFF172A65CD290027710B /* Superset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Superset.swift; sourceTree = ""; }; + 1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentWorkoutInfo.swift; sourceTree = ""; }; 1C5190C12A57CA5F00885849 /* OvalTextFieldStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OvalTextFieldStyle.swift; sourceTree = ""; }; 1C5190C32A589CAC00885849 /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = ""; }; 1C5190C52A589CC100885849 /* ActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionsView.swift; sourceTree = ""; }; @@ -269,6 +275,7 @@ 1CF65A822A42347D0042FFBD /* Extensions.swift */, 1CF65A272A3972840042FFBD /* Persistence.swift */, 1CD0C6662A5CA19600970E52 /* BaseURLs.swift */, + 1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */, 1CF65A4F2A3A1EA90042FFBD /* BridgeModule.swift */, 1CF65A802A412AA30042FFBD /* DataStore.swift */, 1CF65AB92A4894430042FFBD /* UserStore.swift */, @@ -314,6 +321,7 @@ 1CAF4D892A5132F900B00E50 /* PlannedWorkout.swift */, 1CF65A462A39FB6C0042FFBD /* RegisteredUser.swift */, 1CF65A422A39FB410042FFBD /* Workout.swift */, + 1C4AFF172A65CD290027710B /* Superset.swift */, ); path = APIModels; sourceTree = ""; @@ -583,6 +591,7 @@ 1CF65A852A43E8060042FFBD /* CompletedWorkout.swift in Sources */, 1CF65A6E2A3F60480042FFBD /* CreateViewModels.swift in Sources */, 1CF65A4C2A39FDA20042FFBD /* WorkoutDetailView.swift in Sources */, + 1C4AFF182A65CD290027710B /* Superset.swift in Sources */, 1CF65A8E2A44B78B0042FFBD /* CompletedWorkoutView.swift in Sources */, 1CF65A432A39FB410042FFBD /* Workout.swift in Sources */, 1CF65A502A3A1EA90042FFBD /* BridgeModule.swift in Sources */, @@ -597,6 +606,7 @@ 1CF65A2D2A3972840042FFBD /* MainView.swift in Sources */, 1CF65A7D2A41275D0042FFBD /* Network.swift in Sources */, 1C485C8A2A492BB400A6F896 /* LoginView.swift in Sources */, + 1C4AFF1B2A65FB190027710B /* CurrentWorkoutInfo.swift in Sources */, 1CF65A732A3F60D20042FFBD /* CreateExerciseActionsView.swift in Sources */, 1CF65A832A42347D0042FFBD /* Extensions.swift in Sources */, 1CF65A282A3972840042FFBD /* Persistence.swift in Sources */, @@ -619,6 +629,7 @@ files = ( 1CF65A982A452D270042FFBD /* ContentView.swift in Sources */, 1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */, + 1C4AFF192A65CD6F0027710B /* Superset.swift in Sources */, 1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */, 1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */, 1C4AFF162A60F27E0027710B /* ThotStyle.swift in Sources */, @@ -627,6 +638,7 @@ 1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */, 1CD0C6682A5CA1A200970E52 /* BaseURLs.swift in Sources */, 1CF65AA72A452D9C0042FFBD /* Muscle.swift in Sources */, + 1C4AFF1C2A65FB2B0027710B /* CurrentWorkoutInfo.swift in Sources */, 1C485C8D2A49D95700A6F896 /* Extensions.swift in Sources */, 1CF65AAB2A452DAC0042FFBD /* PreviewData.swift in Sources */, 1CF65AA52A452D9C0042FFBD /* CompletedWorkout.swift in Sources */, diff --git a/Werkout_ios/APIModels/Equipment.swift b/Werkout_ios/APIModels/Equipment.swift index bd4b657..d8f9366 100644 --- a/Werkout_ios/APIModels/Equipment.swift +++ b/Werkout_ios/APIModels/Equipment.swift @@ -6,28 +6,18 @@ // import Foundation + struct Equipment: Codable { let id: Int - let createdAt, updatedAt: String - let category, name: String? + let name, createdAt, updatedAt: String + let is_weight: Bool? + let category: String? enum CodingKeys: String, CodingKey { - case id + case id, name case createdAt = "created_at" case updatedAt = "updated_at" - case category, name - } -} - -struct ExerciseEquipment: Codable, Hashable { - let id: Int - let createdAt, updatedAt: String - let exercise, equipment: Int - - enum CodingKeys: String, CodingKey { - case id - case createdAt = "created_at" - case updatedAt = "updated_at" - case equipment, exercise + case is_weight + case category } } diff --git a/Werkout_ios/APIModels/Exercise.swift b/Werkout_ios/APIModels/Exercise.swift index aab61e5..cb3fb41 100644 --- a/Werkout_ios/APIModels/Exercise.swift +++ b/Werkout_ios/APIModels/Exercise.swift @@ -7,32 +7,39 @@ import Foundation -struct ExerciseElement: Codable, Equatable { - let workout: Int - let exercise: ExerciseExercise +struct SupersetExercise: Identifiable, Codable, Equatable, Hashable { + var id = UUID() + + let workout: Int? + let exercise: Exercise let weight: Int? let reps: Int? let duration: Int? let durationAudio: String? let weightAudio: String? let createdAt: String + let order, superset: Int enum CodingKeys: String, CodingKey { - case workout, exercise, weight, reps, duration + case workout, exercise, weight, reps, duration, order, superset case durationAudio = "duration_audio" case weightAudio = "weight_audio" case createdAt = "created_at" } + + public func hash(into hasher: inout Hasher) { + return hasher.combine(id) + } } -struct ExerciseExercise: Codable, Hashable, Identifiable { - static func == (lhs: ExerciseExercise, rhs: ExerciseExercise) -> Bool { +struct Exercise: Identifiable, Codable, Equatable { + static func == (lhs: Exercise, rhs: Exercise) -> Bool { lhs.id == rhs.id } let id: Int - let muscles: [ExerciseMuscle] - let equipment: [ExerciseEquipment] + let equipment: [Equipment] + let muscles: [Muscle] let audioURL, videoURL, createdAt, updatedAt: String let name, description, side: String let isTwoDumbbells, isTrackableDistance, isAlternating, isWeight: Bool diff --git a/Werkout_ios/APIModels/Muscle.swift b/Werkout_ios/APIModels/Muscle.swift index 55f2e33..a9d3378 100644 --- a/Werkout_ios/APIModels/Muscle.swift +++ b/Werkout_ios/APIModels/Muscle.swift @@ -9,18 +9,11 @@ import Foundation struct Muscle: Codable { let id: Int - let name: String -} - -struct ExerciseMuscle: Codable, Hashable { - let id: Int - let createdAt, updatedAt: String - let exercise, muscle: Int + let name, createdAt, updatedAt: String enum CodingKeys: String, CodingKey { - case id + case id, name case createdAt = "created_at" case updatedAt = "updated_at" - case exercise, muscle } } diff --git a/Werkout_ios/APIModels/Superset.swift b/Werkout_ios/APIModels/Superset.swift new file mode 100644 index 0000000..60db92b --- /dev/null +++ b/Werkout_ios/APIModels/Superset.swift @@ -0,0 +1,26 @@ +// +// Superset.swift +// Werkout_ios +// +// Created by Trey Tartt on 7/17/23. +// + +import Foundation + +struct Superset: Codable, Identifiable, Hashable { + let id: Int + let exercises: [SupersetExercise] + let createdAt, updatedAt, name: String? + let rounds, order, workout: Int + + enum CodingKeys: String, CodingKey { + case id, exercises + case createdAt = "created_at" + case updatedAt = "updated_at" + case name, rounds, order, workout + } + + public func hash(into hasher: inout Hasher) { + return hasher.combine(id) + } +} diff --git a/Werkout_ios/APIModels/Workout.swift b/Werkout_ios/APIModels/Workout.swift index d2ff0b2..804327e 100644 --- a/Werkout_ios/APIModels/Workout.swift +++ b/Werkout_ios/APIModels/Workout.swift @@ -15,17 +15,15 @@ struct Workout: Codable, Identifiable, Equatable { let id: Int let name: String let description: String? - let exercises: [ExerciseElement] + let supersets: [Superset]? let registeredUser: RegisteredUser? + let femaleVideos, maleVideos, bothVideos: [String]? let muscles: [String]? let equipment: [String]? let exercise_count: Int? - let maleVideos: [String]? - let femaleVideos: [String]? - let bothVideos: [String]? enum CodingKeys: String, CodingKey { - case name, description, exercises, id, muscles, equipment, exercise_count + case id, name, description, supersets, exercise_count, muscles, equipment case registeredUser = "registered_user" case maleVideos = "male_videos" case femaleVideos = "female_videos" @@ -34,31 +32,18 @@ struct Workout: Codable, Identifiable, Equatable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - if let exercises = try container.decodeIfPresent([ExerciseElement].self, forKey: .exercises) { - self.exercises = exercises - } else { - self.exercises = [ExerciseElement]() - } - - self.name = try container.decode(String.self, forKey: .name) + + self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "NA" self.description = try container.decodeIfPresent(String.self, forKey: .description) self.registeredUser = try container.decodeIfPresent(RegisteredUser.self, forKey: .registeredUser) self.id = try container.decode(Int.self, forKey: .id) - self.muscles = try container.decodeIfPresent([String].self, forKey: .muscles) - self.equipment = try container.decodeIfPresent([String].self, forKey: .equipment) - self.exercise_count = try container.decodeIfPresent(Int.self, forKey: .exercise_count) self.femaleVideos = try container.decodeIfPresent([String].self, forKey: .femaleVideos) self.maleVideos = try container.decodeIfPresent([String].self, forKey: .maleVideos) self.bothVideos = try container.decodeIfPresent([String].self, forKey: .bothVideos) - } - - var exercisesSortedByCreated_at: [ExerciseElement] { - return self.exercises.sorted(by: { - if let lhsDate = $0.createdAt.dateFromServerDate, - let rhsDate = $1.createdAt.dateFromServerDate { - return lhsDate < rhsDate - } - return false - }) + self.supersets = try container.decodeIfPresent([Superset].self, forKey: .supersets) + + self.equipment = try container.decodeIfPresent([String].self, forKey: .equipment) + self.muscles = try container.decodeIfPresent([String].self, forKey: .muscles) + self.exercise_count = try container.decodeIfPresent(Int.self, forKey: .exercise_count) } } diff --git a/Werkout_ios/BaseURLs.swift b/Werkout_ios/BaseURLs.swift index c43c8e6..537f209 100644 --- a/Werkout_ios/BaseURLs.swift +++ b/Werkout_ios/BaseURLs.swift @@ -12,6 +12,6 @@ enum BaseURLs: String { case dev = "https://dev.werkout.fitness" static var currentBaseURL: String { - return BaseURLs.dev.rawValue + return BaseURLs.local.rawValue } } diff --git a/Werkout_ios/BridgeModule.swift b/Werkout_ios/BridgeModule.swift index a63f426..934d862 100644 --- a/Werkout_ios/BridgeModule.swift +++ b/Werkout_ios/BridgeModule.swift @@ -36,17 +36,14 @@ class BridgeModule: NSObject, ObservableObject { @Published var currentWorkoutRunTimeInSeconds: Int = -1 private var currentWorkoutRunTimer: Timer? - var currentWorkout: Workout? public private(set) var workoutStartDate: Date? private var currentExerciseTimer: Timer? - public private(set) var currentExerciseIdx: Int = -1 { - didSet { - self.currentExercisePositionString = "\(self.currentExerciseIdx+1)/\(self.currentWorkout?.exercises.count ?? 0)" - } - } + + @Published public private(set) var currentExerciseInfo = CurrentWorkoutInfo() + @Published var previewWorkout: Workout? + @Published var currentExerciseTimeLeft: Int = 0 - @Published var currentExercise: ExerciseElement? var currentExercisePositionString: String? private var isWatchConnected = false @@ -59,33 +56,33 @@ class BridgeModule: NSObject, ObservableObject { var audioPlayer: AVAudioPlayer? func start(workout: Workout) { - self.currentWorkout = workout + currentExerciseInfo.complete = { + self.completeWorkout() + } + + currentExerciseInfo.start(workout: workout) currentWorkoutRunTimeInSeconds = 0 currentWorkoutRunTimer?.invalidate() currentWorkoutRunTimer = nil - currentExerciseIdx = 0 - let exercise = workout.exercises[currentExerciseIdx] - updateCurrent(exercise: exercise) - startWorkoutTimer() - workoutStartDate = Date() - isInWorkout = true - - if WCSession.isSupported() { - WCSession.default.delegate = self - WCSession.default.activate() + if let superetExercise = currentExerciseInfo.currentExercise { + updateCurrent(exercise: superetExercise) + startWorkoutTimer() + workoutStartDate = Date() + isInWorkout = true + + if WCSession.isSupported() { + WCSession.default.delegate = self + WCSession.default.activate() + } } - } - func goToExerciseAt(index: Int) { - guard let currentWorkout = currentWorkout else { - return + func goToExerciseAt(section: Int, row: Int) { + if let superetExercise = currentExerciseInfo.goToWorkoutAt(supersetIndex: section, + exerciseIndex: row) { + updateCurrent(exercise: superetExercise) } - - currentExerciseIdx = index - let exercise = currentWorkout.exercises[index] - updateCurrent(exercise: exercise) } func resetCurrentWorkout() { @@ -98,11 +95,8 @@ class BridgeModule: NSObject, ObservableObject { self.currentExerciseTimer = nil self.currentWorkoutRunTimeInSeconds = -1 - self.currentExerciseIdx = -1 - - self.currentExercise = nil - self.currentWorkout = nil - + self.currentExerciseInfo.reset() + self.isInWorkout = false self.workoutStartDate = nil self.workoutEndDate = nil @@ -162,38 +156,24 @@ class BridgeModule: NSObject, ObservableObject { } func nextExercise() { - currentExerciseIdx += 1 - if let currentWorkout = currentWorkout { - if currentExerciseIdx < currentWorkout.exercises.count { - let nextExercise = currentWorkout.exercises[currentExerciseIdx] - updateCurrent(exercise: nextExercise) - } else { - completeWorkout() - } + if let nextSupersetExercise = currentExerciseInfo.nextExercise { + updateCurrent(exercise: nextSupersetExercise) + } else { + completeWorkout() } } func previousExercise() { - currentExerciseIdx -= 1 - if currentExerciseIdx < 0 { - currentExerciseIdx = 0 - } - if let currentWorkout = currentWorkout { - if currentExerciseIdx < currentWorkout.exercises.count { - let nextExercise = currentWorkout.exercises[currentExerciseIdx] - updateCurrent(exercise: nextExercise) - } else { - completeWorkout() - } + if let nextSupersetExercise = currentExerciseInfo.previousExercise { + updateCurrent(exercise: nextSupersetExercise) + } else { + completeWorkout() } } func restartExercise() { - if let currentWorkout = currentWorkout { - if currentExerciseIdx < currentWorkout.exercises.count { - let nextExercise = currentWorkout.exercises[currentExerciseIdx] - updateCurrent(exercise: nextExercise) - } + if let currentExercise = currentExerciseInfo.currentExercise { + updateCurrent(exercise: currentExercise) } } @@ -201,15 +181,12 @@ class BridgeModule: NSObject, ObservableObject { currentWorkoutRunTimeInSeconds += 1 } - func updateCurrent(exercise: ExerciseElement) { + func updateCurrent(exercise: SupersetExercise) { DispatchQueue.main.async { self.currentExerciseTimer?.invalidate() self.currentExerciseTimer = nil - - self.currentExercise = exercise - - if let duration = exercise.duration, - duration > 0 { + + if let duration = exercise.duration, duration > 0 { self.startExerciseTimerWith(duration: duration) } self.sendCurrentExerciseToWatch() @@ -281,25 +258,25 @@ extension BridgeModule: WCSessionDelegate { } func sendCurrentExerciseToWatch() { - if let duration = currentExercise?.duration, + if let currentExercise = currentExerciseInfo.currentExercise, + let duration = currentExercise.duration , duration > 0 { - let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date()) + let watchModel = WatchPackageModel(currentExerciseName: currentExercise.exercise.name, currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date()) let model = PhoneToWatchActions.inExercise(watchModel) let data = try! JSONEncoder().encode(model) send(data) } else { - var intWatchDispaly = -1 - if let reps = self.currentExercise?.reps, + if let currentExercise = currentExerciseInfo.currentExercise, + let reps = currentExercise.reps, reps > 0 { - intWatchDispaly = reps + + // if not a timer we need to set the watch display with number of reps + // if timer it will set when timer updates + let watchModel = WatchPackageModel(currentExerciseName: currentExercise.exercise.name, currentTimeLeft: reps, workoutStartDate: self.workoutStartDate ?? Date()) + let model = PhoneToWatchActions.inExercise(watchModel) + let data = try! JSONEncoder().encode(model) + self.send(data) } - - // if not a timer we need to set the watch display with number of reps - // if timer it will set when timer updates - let watchModel = WatchPackageModel(currentExerciseName: self.currentExercise?.exercise.name ?? "-", currentTimeLeft: intWatchDispaly, workoutStartDate: self.workoutStartDate ?? Date()) - let model = PhoneToWatchActions.inExercise(watchModel) - let data = try! JSONEncoder().encode(model) - self.send(data) } } diff --git a/Werkout_ios/CurrentWorkoutInfo.swift b/Werkout_ios/CurrentWorkoutInfo.swift new file mode 100644 index 0000000..b336902 --- /dev/null +++ b/Werkout_ios/CurrentWorkoutInfo.swift @@ -0,0 +1,96 @@ +// +// CurrentWorkoutInfo.swift +// Werkout_ios +// +// Created by Trey Tartt on 7/17/23. +// + +import Foundation + +class CurrentWorkoutInfo { + var supersetIndex: Int = 0 + var exerciseIndex: Int = 0 + var workout: Workout? + var complete: (() -> Void)? + + var currentRound = 1 + + var superset: [Superset] { + return workout?.supersets?.sorted(by: { $0.order < $1.order }) ?? [Superset]() + } + + var currentExercise: SupersetExercise? { + guard let supersets = workout?.supersets else { return nil } + let superset = supersets[supersetIndex] + let exercise = superset.exercises[exerciseIndex] + return exercise + } + + var nextExercise: SupersetExercise? { + guard let workout = workout else { return nil } + guard let supersets = workout.supersets else { return nil } + + exerciseIndex += 1 + let currentSuperSet = supersets[supersetIndex] + + if exerciseIndex >= currentSuperSet.exercises.count { + currentRound += 1 + + if currentRound > currentSuperSet.rounds { + supersetIndex += 1 + currentRound = 1 + + if supersetIndex >= supersets.count { + complete?() + return nil + } + } + + exerciseIndex = 0 + } + + let superset = supersets[supersetIndex] + let exercise = superset.exercises[exerciseIndex] + return exercise + } + + var previousExercise: SupersetExercise? { + guard let workout = workout else { return nil } + guard let supersets = workout.supersets else { return nil } + + exerciseIndex -= 1 + if exerciseIndex < 0 { + if currentRound > 1 { + currentRound -= 1 + } else { + if supersetIndex > 1 { + supersetIndex -= 1 + } + } + exerciseIndex = 0 + } + + let superset = supersets[supersetIndex] + let exercise = superset.exercises[exerciseIndex] + return exercise + } + + func start(workout: Workout) { + reset() + self.workout = workout + } + + func reset() { + supersetIndex = 0 + exerciseIndex = 0 + currentRound = 1 + self.workout = nil + } + + func goToWorkoutAt(supersetIndex: Int, exerciseIndex: Int) -> SupersetExercise? { + self.supersetIndex = supersetIndex + self.exerciseIndex = exerciseIndex + + return currentExercise + } +} diff --git a/Werkout_ios/DataStore.swift b/Werkout_ios/DataStore.swift index da83411..ab4b7e8 100644 --- a/Werkout_ios/DataStore.swift +++ b/Werkout_ios/DataStore.swift @@ -19,7 +19,7 @@ class DataStore: ObservableObject { public private(set) var allWorkouts: [Workout]? public private(set) var allMuscles: [Muscle]? public private(set) var allEquipment: [Equipment]? - public private(set) var allExercise: [ExerciseExercise]? + public private(set) var allExercise: [Exercise]? @Published public private(set) var status = DataStoreStatus.idle diff --git a/Werkout_ios/JSON/WorkoutDetail.json b/Werkout_ios/JSON/WorkoutDetail.json index e35de21..1304612 100644 --- a/Werkout_ios/JSON/WorkoutDetail.json +++ b/Werkout_ios/JSON/WorkoutDetail.json @@ -2,151 +2,208 @@ "id": 21, "name": "Ipad", "description": "description", - "exercises": [ + "supersets": [ { - "workout": 21, - "exercise": { - "id": 520, - "muscles": [ - { - "id": 7264, - "created_at": "2023-06-14T17:05:39.760515Z", - "updated_at": "2023-06-14T17:05:39.761372Z", - "exercise": 520, - "muscle": 16 + "id": 1, + "exercises": [ + { + "id": 1, + "exercise": { + "id": 520, + "muscles": [ + { + "id": 7264, + "name": "hip flexor", + "created_at": "2023-06-14T17:05:39.760515Z", + "updated_at": "2023-06-14T17:05:39.761372Z", + "exercise": 520, + "muscle": 16 + }, + { + "id": 7265, + "name": "glutes", + "created_at": "2023-06-14T17:05:39.762342Z", + "updated_at": "2023-06-14T17:05:39.762814Z", + "exercise": 520, + "muscle": 4 + } + ], + "equipment": [ + { + "id": 941, + "name": "Wall", + "created_at": "2023-06-13T02:28:04.289213Z", + "updated_at": "2023-06-13T02:28:04.290354Z", + "exercise": 520, + "equipment": 1106 + } + ], + "audio_url": "exercise_audio/1-Step_Wall_March.m4a", + "video_url": "exercise_videos/1-Step_Wall_March.mp4", + "created_at": "2023-06-11T22:50:19.020826Z", + "updated_at": "2023-06-11T22:50:19.020834Z", + "name": "1-Step Wall March", + "description": "Keeping a tall posture, lean forward with both arms straight ahead, palms on the wall. While pushing your toes back through the floor, drive one knee up towards your ribs. Pause, then quickly repeat the same movement with the opposite leg. ", + "side": "", + "is_two_dumbbells": false, + "is_trackable_distance": false, + "is_alternating": true, + "is_weight": true, + "is_distance": false, + "is_duration": true, + "is_reps": true, + "joints_used": "ankle,knee,hip,wrist,shoulder,elbow", + "movement_patterns": "plyometric", + "equipment_required": "Wall", + "muscle_groups": "hip flexor,glutes", + "synonyms": "" }, - { - "id": 7265, - "created_at": "2023-06-14T17:05:39.762342Z", - "updated_at": "2023-06-14T17:05:39.762814Z", - "exercise": 520, - "muscle": 4 - } - ], - "equipment": [ - { - "id": 941, - "created_at": "2023-06-13T02:28:04.289213Z", - "updated_at": "2023-06-13T02:28:04.290354Z", - "exercise": 520, - "equipment": 1106 - } - ], - "audio_url": "exercise_audio/1-Step_Wall_March.m4a", - "video_url": "exercise_videos/1-Step_Wall_March.mp4", - "created_at": "2023-06-11T22:50:19.020826Z", - "updated_at": "2023-06-11T22:50:19.020834Z", - "name": "1-Step Wall March", - "description": "Keeping a tall posture, lean forward with both arms straight ahead, palms on the wall. While pushing your toes back through the floor, drive one knee up towards your ribs. Pause, then quickly repeat the same movement with the opposite leg. ", - "side": "", - "is_two_dumbbells": false, - "is_trackable_distance": false, - "is_alternating": true, - "is_weight": true, - "is_distance": false, - "is_duration": true, - "is_reps": true, - "joints_used": "ankle,knee,hip,wrist,shoulder,elbow", - "movement_patterns": "plyometric", - "equipment_required": "Wall", - "muscle_groups": "hip flexor,glutes", - "synonyms": "" - }, - "weight": 0, - "reps": 0, - "duration": null, - "duration_audio": null, - "weight_audio": "/media/quantities_audio/for_0_pounds.m4a", - "created_at": "2023-06-20T21:03:00.127620Z" + "created_at": "2023-07-17T18:56:36.984049Z", + "updated_at": "2023-07-17T19:06:00.534838Z", + "weight": null, + "reps": null, + "duration": 30, + "order": 1, + "superset": 1 + }, + { + "id": 2, + "exercise": { + "id": 992, + "muscles": [ + { + "id": 7270, + "name": "hamstrings", + "created_at": "2023-06-14T17:05:39.769351Z", + "updated_at": "2023-06-14T17:05:39.769758Z", + "exercise": 992, + "muscle": 6 + }, + { + "id": 7271, + "name": "glutes", + "created_at": "2023-06-14T17:05:39.770480Z", + "updated_at": "2023-06-14T17:05:39.771111Z", + "exercise": 992, + "muscle": 4 + } + ], + "equipment": [ + { + "id": 944, + "name": "Dumbbell", + "created_at": "2023-06-13T02:28:04.294180Z", + "updated_at": "2023-06-13T02:28:04.294658Z", + "exercise": 992, + "equipment": 1091 + } + ], + "audio_url": "exercise_audio/2_Dumbbell_Single-Leg_Deadlift.m4a", + "video_url": "exercise_videos/2_Dumbbell_Single-Leg_Deadlift.mp4", + "created_at": "2023-06-11T22:50:19.197099Z", + "updated_at": "2023-06-11T22:50:19.197105Z", + "name": "2 Dumbbell Single-Leg Deadlift", + "description": "Holding a dumbbell in each hand, with your right leg on the ground, hinge at your hips and let your body see-saw down until you are parallel with the ground. Snap back to a standing position.", + "side": "right_leg", + "is_two_dumbbells": true, + "is_trackable_distance": false, + "is_alternating": false, + "is_weight": true, + "is_distance": false, + "is_duration": true, + "is_reps": true, + "joints_used": "ankle,lumbar spine,hip,knee,wrist", + "movement_patterns": "lower pull,lower pull - hip hinge", + "equipment_required": "Dumbbell", + "muscle_groups": "hamstrings,glutes", + "synonyms": "2 Dumbbell Single Leg Deadlift" + }, + "created_at": "2023-07-17T18:56:36.984523Z", + "updated_at": "2023-07-17T19:06:00.535334Z", + "weight": 30, + "reps": null, + "duration": null, + "order": 2, + "superset": 1 + } + ], + "created_at": "2023-07-17T18:56:36.983159Z", + "updated_at": "2023-07-17T19:06:00.534027Z", + "name": "test superste", + "rounds": 3, + "order": 1, + "workout": 21 }, { - "workout": 21, - "exercise": { - "id": 37, - "muscles": [ - { - "id": 7272, - "created_at": "2023-06-14T17:05:39.772195Z", - "updated_at": "2023-06-14T17:05:39.772765Z", - "exercise": 37, - "muscle": 4 + "id": 2, + "exercises": [ + { + "id": 3, + "exercise": { + "id": 992, + "muscles": [ + { + "id": 7270, + "name": "hamstrings", + "created_at": "2023-06-14T17:05:39.769351Z", + "updated_at": "2023-06-14T17:05:39.769758Z", + "exercise": 992, + "muscle": 6 + }, + { + "id": 7271, + "name": "glutes", + "created_at": "2023-06-14T17:05:39.770480Z", + "updated_at": "2023-06-14T17:05:39.771111Z", + "exercise": 992, + "muscle": 4 + } + ], + "equipment": [ + { + "id": 944, + "name": "Dumbbell", + "created_at": "2023-06-13T02:28:04.294180Z", + "updated_at": "2023-06-13T02:28:04.294658Z", + "exercise": 992, + "equipment": 1091 + } + ], + "audio_url": "exercise_audio/2_Dumbbell_Single-Leg_Deadlift.m4a", + "video_url": "exercise_videos/2_Dumbbell_Single-Leg_Deadlift.mp4", + "created_at": "2023-06-11T22:50:19.197099Z", + "updated_at": "2023-06-11T22:50:19.197105Z", + "name": "2 Dumbbell Single-Leg Deadlift", + "description": "Holding a dumbbell in each hand, with your right leg on the ground, hinge at your hips and let your body see-saw down until you are parallel with the ground. Snap back to a standing position.", + "side": "right_leg", + "is_two_dumbbells": true, + "is_trackable_distance": false, + "is_alternating": false, + "is_weight": true, + "is_distance": false, + "is_duration": true, + "is_reps": true, + "joints_used": "ankle,lumbar spine,hip,knee,wrist", + "movement_patterns": "lower pull,lower pull - hip hinge", + "equipment_required": "Dumbbell", + "muscle_groups": "hamstrings,glutes", + "synonyms": "2 Dumbbell Single Leg Deadlift" }, - { - "id": 7273, - "created_at": "2023-06-14T17:05:39.773621Z", - "updated_at": "2023-06-14T17:05:39.774079Z", - "exercise": 37, - "muscle": 6 - } - ], - "equipment": [ - { - "id": 945, - "created_at": "2023-06-13T02:28:04.295672Z", - "updated_at": "2023-06-13T02:28:04.296237Z", - "exercise": 37, - "equipment": 1088 - } - ], - "audio_url": "exercise_audio/2_Kettlebell_Clean.m4a", - "video_url": "exercise_videos/2_Kettlebell_Clean.mp4", - "created_at": "2023-06-11T22:50:18.813591Z", - "updated_at": "2023-06-11T22:50:18.813600Z", - "name": "2 Kettlebell Clean", - "description": "Standing with feet in a shoulder width stance, grab the handles of the bells and swing them back between the legs while keeping a neutral spine.\nExtend the hips, swing the bells forward, and allow them to flip around your wrists to end in the front rack position.", - "side": "", - "is_two_dumbbells": false, - "is_trackable_distance": false, - "is_alternating": false, - "is_weight": true, - "is_distance": false, - "is_duration": true, - "is_reps": true, - "joints_used": "shoulder,ankle,knee,hip,elbow", - "movement_patterns": "lower pull - hip hinge,upper pull", - "equipment_required": "Kettlebell", - "muscle_groups": "glutes,hamstrings", - "synonyms": "" - }, - "weight": 0, - "reps": 0, - "duration": null, - "duration_audio": null, - "weight_audio": "/media/quantities_audio/for_0_pounds.m4a", - "created_at": "2023-06-20T21:03:00.131018Z" - }, - { - "workout": 21, - "exercise": { - "id": 798, - "muscles": [], - "equipment": [], - "audio_url": "exercise_audio/Recover.m4a", - "video_url": "exercise_videos/Recover.mp4", - "created_at": "2023-06-11T22:50:19.127914Z", - "updated_at": "2023-06-11T22:50:19.127921Z", - "name": "Recover", - "description": "Use this time to catch your breath. It will help you get more out of what's next", - "side": "", - "is_two_dumbbells": false, - "is_trackable_distance": false, - "is_alternating": false, - "is_weight": false, - "is_distance": false, - "is_duration": true, - "is_reps": false, - "joints_used": "", - "movement_patterns": "", - "equipment_required": "", - "muscle_groups": "", - "synonyms": null - }, - "weight": 0, - "reps": 0, - "duration": null, - "duration_audio": null, - "weight_audio": "/media/quantities_audio/for_0_pounds.m4a", - "created_at": "2023-06-20T21:03:00.134981Z" + "created_at": "2023-07-17T18:58:35.585418Z", + "updated_at": "2023-07-17T18:58:35.585435Z", + "weight": 11, + "reps": null, + "duration": null, + "order": 1, + "superset": 2 + } + ], + "created_at": "2023-07-17T18:58:35.584036Z", + "updated_at": "2023-07-17T19:03:57.175639Z", + "name": "two", + "rounds": 3, + "order": 2, + "workout": 21 } ], "registered_user": { diff --git a/Werkout_ios/Network/Fetchables.swift b/Werkout_ios/Network/Fetchables.swift index a94a092..93249a7 100644 --- a/Werkout_ios/Network/Fetchables.swift +++ b/Werkout_ios/Network/Fetchables.swift @@ -32,7 +32,7 @@ class AllEquipmentFetchable: Fetchable { } class AllExerciseFetchable: Fetchable { - typealias Response = [ExerciseExercise] + typealias Response = [Exercise] var endPoint: String = "/exercise/all/" } diff --git a/Werkout_ios/Preview Content/PreviewData.swift b/Werkout_ios/Preview Content/PreviewData.swift index 04da291..4bd51ec 100644 --- a/Werkout_ios/Preview Content/PreviewData.swift +++ b/Werkout_ios/Preview Content/PreviewData.swift @@ -30,11 +30,11 @@ class PreviewData { } } - class func parseExercises() -> [ExerciseExercise] { + class func parseExercises() -> [Exercise] { if let filepath = Bundle.main.path(forResource: "Exercises", ofType: "json") { do { let data = try Data(NSData(contentsOfFile: filepath)) - let exercises = try JSONDecoder().decode([ExerciseExercise].self, from: data) + let exercises = try JSONDecoder().decode([Exercise].self, from: data) return exercises } catch { print(error) diff --git a/Werkout_ios/Views/AddExercise/AddExerciseView.swift b/Werkout_ios/Views/AddExercise/AddExerciseView.swift index 3032251..b2a5193 100644 --- a/Werkout_ios/Views/AddExercise/AddExerciseView.swift +++ b/Werkout_ios/Views/AddExercise/AddExerciseView.swift @@ -18,15 +18,15 @@ struct AddExerciseView: View { @State var selectedMuscles = [Muscle]() @State var selectedEquipment = [Equipment]() - @State var filteredExercises = [ExerciseExercise]() + @State var filteredExercises = [Exercise]() @StateObject var bridgeModule = BridgeModule.shared @Environment(\.dismiss) var dismiss - var selectedExercise: ((ExerciseExercise) -> Void) + var selectedExercise: ((Exercise) -> Void) @State var createWorkoutItemPickerViewModel: CreateWorkoutItemPickerViewModel? @State var createWorkoutItemPickerViewType: CreateWorkoutItemPickerViewType? @State var searchString: String = "" - @State var videoExercise: ExerciseExercise? { + @State var videoExercise: Exercise? { didSet { if let viddd = self.videoExercise?.videoURL, let url = URL(string: BaseURLs.currentBaseURL + viddd) { @@ -111,19 +111,19 @@ struct AddExerciseView: View { func filterExercises() { if selectedMuscles.count == 0 { - filteredExercises = [ExerciseExercise]() + filteredExercises = [Exercise]() return } if selectedEquipment.count == 0 { - filteredExercises = [ExerciseExercise]() + filteredExercises = [Exercise]() return } guard let exercises = DataStore.shared.allExercise, let muscles = DataStore.shared.allMuscles, let equipment = DataStore.shared.allEquipment else { - filteredExercises = [ExerciseExercise]() + filteredExercises = [Exercise]() return } @@ -132,7 +132,7 @@ struct AddExerciseView: View { if selectedMuscles.count == muscles.count { hasCorrectMuscles = true } else { - let exerciseMuscleIds = exercise.muscles.map({ $0.muscle }) + let exerciseMuscleIds = exercise.muscles.map({ $0.id }) let selctedMuscleIds = selectedMuscles.map({ $0.id }) // if one items match if exerciseMuscleIds.contains(where: selctedMuscleIds.contains) { @@ -146,7 +146,7 @@ struct AddExerciseView: View { if selectedEquipment.count == equipment.count { hasCorrectEquipment = true } else { - let exerciseEquipmentIds = exercise.equipment.map({ $0.equipment }) + let exerciseEquipmentIds = exercise.equipment.map({ $0.id }) let selctedEquipmentIds = selectedEquipment.map({ $0.id }) // if one items match if exerciseEquipmentIds.contains(where: selctedEquipmentIds.contains) { @@ -198,7 +198,7 @@ struct AddExerciseView: View { var createWorkoutItemPickerModels = [CreateWorkoutItemPickerModel]() equipment.forEach({ let model = CreateWorkoutItemPickerModel(id: $0.id, - name: $0.name?.lowercased() ?? "-") + name: $0.name.lowercased()) createWorkoutItemPickerModels.append(model) }) createWorkoutItemPickerModels = createWorkoutItemPickerModels.sorted(by: { diff --git a/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift b/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift index 7195170..e196c8c 100644 --- a/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift +++ b/Werkout_ios/Views/AllWorkouts/AllWorkoutsView.swift @@ -35,13 +35,13 @@ struct AllWorkoutsView: View { @State private var showWorkoutDetail = false @State private var selectedWorkout: Workout? { didSet { - bridgeModule.currentWorkout = selectedWorkout + bridgeModule.currentExerciseInfo.workout = selectedWorkout } } @State private var selectedPlannedWorkout: Workout? { didSet { - bridgeModule.currentWorkout = selectedPlannedWorkout + bridgeModule.currentExerciseInfo.workout = selectedPlannedWorkout } } @@ -58,7 +58,7 @@ struct AllWorkoutsView: View { AllWorkoutPickerView(mainViews: MainViewTypes.allCases, selectedSegment: $selectedSegment, showCurrentWorkout: { - selectedWorkout = bridgeModule.currentWorkout + selectedWorkout = bridgeModule.currentExerciseInfo.workout }) switch selectedSegment { @@ -87,12 +87,13 @@ struct AllWorkoutsView: View { maybeUpdateShit() } .sheet(item: $selectedWorkout) { item in - let viewModel = WorkoutDetailViewModel(workout: item) + var isPreview = item.id == bridgeModule.currentExerciseInfo.workout?.id + let viewModel = WorkoutDetailViewModel(workout: item, isPreview: isPreview) WorkoutDetailView(viewModel: viewModel) } .sheet(item: $selectedPlannedWorkout) { item in - let viewModel = WorkoutDetailViewModel(workout: item) - WorkoutDetailView(viewModel: viewModel, showAddToCalendar: false) + let viewModel = WorkoutDetailViewModel(workout: item, isPreview: true) + WorkoutDetailView(viewModel: viewModel) } .sheet(isPresented: $showLoginView) { LoginView(completion: { diff --git a/Werkout_ios/Views/CreateWorkout/CreateViewModels.swift b/Werkout_ios/Views/CreateWorkout/CreateViewModels.swift index 4690092..1c39e26 100644 --- a/Werkout_ios/Views/CreateWorkout/CreateViewModels.swift +++ b/Werkout_ios/Views/CreateWorkout/CreateViewModels.swift @@ -9,12 +9,12 @@ import SwiftUI class CreateWorkoutExercise: ObservableObject, Identifiable { let id = UUID() - var exercise: ExerciseExercise + var exercise: Exercise @Published var reps: Int = 0 @Published var duration: Int = 0 @Published var weight: Int = 0 - init(exercise: ExerciseExercise, reps: Int = 0, duration: Int = 0, weight: Int = 0) { + init(exercise: Exercise, reps: Int = 0, duration: Int = 0, weight: Int = 0) { self.exercise = exercise self.reps = reps self.duration = duration diff --git a/Werkout_ios/Views/ExternalWorkoutDetailView.swift b/Werkout_ios/Views/ExternalWorkoutDetailView.swift index a2b988a..72a8251 100644 --- a/Werkout_ios/Views/ExternalWorkoutDetailView.swift +++ b/Werkout_ios/Views/ExternalWorkoutDetailView.swift @@ -15,7 +15,8 @@ struct ExternalWorkoutDetailView: View { var body: some View { ZStack { - if let workout = bridgeModule.currentWorkout { + if let workout = bridgeModule.currentExerciseInfo.workout, + let exercise = bridgeModule.currentExerciseInfo.currentExercise { GeometryReader { metrics in VStack { HStack { @@ -26,8 +27,8 @@ struct ExternalWorkoutDetailView: View { } VStack { - ExtExerciseList(workout: workout, - currentExerciseIdx: bridgeModule.currentExerciseIdx) + ExtExerciseList(workout: workout, + currentExercise: exercise) if let currentExercisePositionString = bridgeModule.currentExercisePositionString { Text(currentExercisePositionString) @@ -53,18 +54,20 @@ struct ExternalWorkoutDetailView: View { .scaledToFill() } } - .onChange(of: bridgeModule.currentExercise, perform: { newValue in - if let videoURL = VideoURLCreator.videoURL( - thotStyle: thotStyle, - defaultVideoURLStr: bridgeModule.currentExercise?.exercise.videoURL, - exerciseName: bridgeModule.currentExercise?.exercise.name, - workout: bridgeModule.currentWorkout) { - avPlayer = AVPlayer(url: videoURL) - avPlayer.play() + .onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in + if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise { + if let videoURL = VideoURLCreator.videoURL( + thotStyle: thotStyle, + defaultVideoURLStr: currentExtercise.exercise.videoURL, + exerciseName: currentExtercise.exercise.name, + workout: bridgeModule.currentExerciseInfo.workout) { + avPlayer = AVPlayer(url: videoURL) + avPlayer.play() + } } }) .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(bridgeModule.currentWorkout == nil ? Color(red: 157/255, green: 138/255, blue: 255/255) : Color(uiColor: .systemBackground)) + .background(bridgeModule.currentExerciseInfo.workout == nil ? Color(red: 157/255, green: 138/255, blue: 255/255) : Color(uiColor: .systemBackground)) } } @@ -73,7 +76,7 @@ struct TitleView: View { var body: some View { HStack { - if let workout = bridgeModule.currentWorkout { + if let workout = bridgeModule.currentExerciseInfo.workout { Text(workout.name) .font(Font.system(size: 100)) .frame(maxWidth: .infinity, alignment: .leading) @@ -91,36 +94,64 @@ struct TitleView: View { struct ExtExerciseList: View { var workout: Workout - var currentExerciseIdx: Int + var currentExercise: SupersetExercise var body: some View { - ScrollViewReader { proxy in - List() { - ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in - let obj = workout.exercisesSortedByCreated_at[i] - HStack { - if i == currentExerciseIdx { - Image(systemName: "checkmark") - .font(Font.system(size: 55)) - .minimumScaleFactor(0.01) - .lineLimit(1) - .foregroundColor(.green) - } + if let supersets = workout.supersets { + ScrollViewReader { proxy in + List() { + ForEach(supersets.indices, id: \.self) { supersetIndex in + let superset = supersets[supersetIndex] - Text(obj.exercise.name) - .font(Font.system(size: 55)) - .minimumScaleFactor(0.01) - .lineLimit(3) - .padding() - .id(i) + 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) + .id(exerciseIndex) + .font(Font.system(size: 55)) + .minimumScaleFactor(0.01) + .lineLimit(3) + .padding() + .id(exerciseIndex) + + Spacer() + } + } + }, 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 { + proxy.scrollTo(newValue, anchor: .top) + } + }) } - .onChange(of: currentExerciseIdx, perform: { newValue in - withAnimation { - proxy.scrollTo(newValue, anchor: .top) - } - }) } } } @@ -131,7 +162,7 @@ struct ExtCountdownView: View { var body: some View { GeometryReader { metrics in VStack { - if let currenExercise = bridgeModule.currentExercise { + if let currenExercise = bridgeModule.currentExerciseInfo.currentExercise { HStack { Text(currenExercise.exercise.name) .font(.system(size: 200)) @@ -203,15 +234,15 @@ struct ExtCountdownView: View { } } -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 - }() ) - } -} +//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 +// }() ) +// } +//} diff --git a/Werkout_ios/Views/MainView.swift b/Werkout_ios/Views/MainView.swift index d838c93..426991b 100644 --- a/Werkout_ios/Views/MainView.swift +++ b/Werkout_ios/Views/MainView.swift @@ -15,7 +15,7 @@ struct MainView: View { var body: some View { ZStack { if let workout = workout { - let vm = WorkoutDetailViewModel(workout: workout) + let vm = WorkoutDetailViewModel(workout: workout, isPreview: true) WorkoutDetailView(viewModel: vm) } else { Text("no workout selected") diff --git a/Werkout_ios/Views/WorkoutDetail/CountdownView.swift b/Werkout_ios/Views/WorkoutDetail/CountdownView.swift index a52f275..356df39 100644 --- a/Werkout_ios/Views/WorkoutDetail/CountdownView.swift +++ b/Werkout_ios/Views/WorkoutDetail/CountdownView.swift @@ -11,7 +11,7 @@ struct CountdownView: View { @StateObject var bridgeModule = BridgeModule.shared var body: some View { - if let duration = bridgeModule.currentExercise?.duration, + if let duration = bridgeModule.currentExerciseInfo.currentExercise?.duration, duration > 0 { HStack { if bridgeModule.currentExerciseTimeLeft >= 0 && duration > bridgeModule.currentExerciseTimeLeft { diff --git a/Werkout_ios/Views/WorkoutDetail/ExerciseListView.swift b/Werkout_ios/Views/WorkoutDetail/ExerciseListView.swift index 1c43b40..43e2b66 100644 --- a/Werkout_ios/Views/WorkoutDetail/ExerciseListView.swift +++ b/Werkout_ios/Views/WorkoutDetail/ExerciseListView.swift @@ -11,16 +11,16 @@ import AVKit struct ExerciseListView: View { @AppStorage("thotStyle") private var thotStyle: ThotStyle = .never @ObservedObject var bridgeModule = BridgeModule.shared - var workout: Workout @State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!) + var workout: Workout - @State var videoExercise: ExerciseExercise? { + @State var videoExercise: Exercise? { didSet { if let videoURL = VideoURLCreator.videoURL( thotStyle: thotStyle, defaultVideoURLStr: self.videoExercise?.videoURL, exerciseName: self.videoExercise?.name, - workout: bridgeModule.currentWorkout) { + workout: bridgeModule.currentExerciseInfo.workout) { avPlayer = AVPlayer(url: videoURL) avPlayer.play() } @@ -28,79 +28,97 @@ struct ExerciseListView: View { } var body: some View { - ScrollViewReader { proxy in - List() { - ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in - let obj = workout.exercisesSortedByCreated_at[i] - HStack { - if i == bridgeModule.currentExerciseIdx { - Image(systemName: "checkmark") - .foregroundColor(.green) - } - - Text(obj.exercise.name) - .id(i) - - Spacer() - - if let reps = obj.reps, - reps > 0 { - HStack { - Image(systemName: "number") - .foregroundColor(.white) - .frame(width: 20, alignment: .leading) - Text("\(reps)") - .foregroundColor(.white) - .frame(width: 30, alignment: .trailing) + 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 == bridgeModule.currentExerciseInfo.currentExercise?.id { + Image(systemName: "checkmark") + .foregroundColor(.green) + } + + Text(supersetExecercise.exercise.name) + .id(exerciseIndex) + + 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()) + .onTapGesture { + if bridgeModule.isInWorkout { + bridgeModule.goToExerciseAt(section: supersetIndex, row: exerciseIndex) + } else { + // videoExercise = obj.exercise + } + // .onChange(of: bridgeModule.currentExerciseIdx, perform: { newValue in + // withAnimation { + // proxy.scrollTo(newValue, anchor: .top) + // } + // }) + } + .sheet(item: $videoExercise) { exercise in + PlayerView(player: $avPlayer) + .onAppear{ + avPlayer.play() + } + } } - .padding([.top, .bottom], 5) - .padding([.leading], 10) - .padding([.trailing], 15) - .background(.blue) - .cornerRadius(5, corners: [.topLeft, .bottomLeft]) - .frame(alignment: .trailing) - } - - if let duration = obj.duration, - duration > 0 { + }, header: { HStack { - Image(systemName: "stopwatch") - .foregroundColor(.white) - .frame(width: 20, alignment: .leading) - Text("\(duration)") - .foregroundColor(.white) - .frame(width: 30, alignment: .trailing) + Text(superset.name ?? "--") + .foregroundColor(Color("appColor")) + .bold() + Spacer() + Text("\(superset.rounds) rounds") + .foregroundColor(Color("appColor")) + .bold() } - .padding([.top, .bottom], 5) - .padding([.leading], 10) - .padding([.trailing], 15) - .background(.green) - .cornerRadius(5, corners: [.topLeft, .bottomLeft]) - } - } - .padding(.trailing, -20) - .contentShape(Rectangle()) - .onTapGesture { - if bridgeModule.isInWorkout { - bridgeModule.goToExerciseAt(index: i) - } else { - videoExercise = obj.exercise - } + }) } } } - .onChange(of: bridgeModule.currentExerciseIdx, perform: { newValue in - withAnimation { - proxy.scrollTo(newValue, anchor: .top) - } - }) - } - .sheet(item: $videoExercise) { exercise in - PlayerView(player: $avPlayer) - .onAppear{ - avPlayer.play() - } } } } diff --git a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift index 776f47c..798bb66 100644 --- a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift +++ b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailView.swift @@ -23,7 +23,6 @@ struct WorkoutDetailView: View { @State var presentedSheet: Sheet? @State var workoutToPlan: Workout? - var showAddToCalendar = true var body: some View { ZStack { @@ -80,7 +79,7 @@ struct WorkoutDetailView: View { bridgeModule.completeWorkout() }, planWorkout: { workout in workoutToPlan = workout - }, workout: workout, showAddToCalendar: showAddToCalendar) + }, workout: workout, showAddToCalendar: viewModel.isPreview) .frame(height: 44) } @@ -101,24 +100,28 @@ struct WorkoutDetailView: View { } } } - .onChange(of: bridgeModule.currentExercise, perform: { newValue in - if let videoURL = VideoURLCreator.videoURL( - thotStyle: thotStyle, - defaultVideoURLStr: newValue?.exercise.videoURL, - exerciseName: newValue?.exercise.name, - workout: bridgeModule.currentWorkout) { - avPlayer = AVPlayer(url: videoURL) - avPlayer.play() + .onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in + if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise { + if let videoURL = VideoURLCreator.videoURL( + thotStyle: thotStyle, + defaultVideoURLStr: currentExtercise.exercise.videoURL, + exerciseName: currentExtercise.exercise.name, + workout: bridgeModule.currentExerciseInfo.workout) { + avPlayer = AVPlayer(url: videoURL) + avPlayer.play() + } } }) .onAppear{ - if let videoURL = VideoURLCreator.videoURL( - thotStyle: thotStyle, - defaultVideoURLStr: bridgeModule.currentExercise?.exercise.videoURL, - exerciseName: bridgeModule.currentExercise?.exercise.name, - workout: bridgeModule.currentWorkout) { - avPlayer = AVPlayer(url: videoURL) - avPlayer.play() + if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise { + if let videoURL = VideoURLCreator.videoURL( + thotStyle: thotStyle, + defaultVideoURLStr: currentExtercise.exercise.videoURL, + exerciseName: currentExtercise.exercise.name, + workout: bridgeModule.currentExerciseInfo.workout) { + avPlayer = AVPlayer(url: videoURL) + avPlayer.play() + } } bridgeModule.completedWorkout = { @@ -132,7 +135,7 @@ struct WorkoutDetailView: View { } func createWorkoutData() -> [String:Any]? { - guard let workoutid = bridgeModule.currentWorkout?.id, + guard let workoutid = bridgeModule.currentExerciseInfo.workout?.id, let startTime = bridgeModule.workoutStartDate?.timeFormatForUpload, let endTime = bridgeModule.workoutEndDate?.timeFormatForUpload else { return nil @@ -154,6 +157,6 @@ struct WorkoutDetailView: View { 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))) + WorkoutDetailView(viewModel: WorkoutDetailViewModel(workout: WorkoutDetailView_Previews.workoutDetail, status: .showWorkout(WorkoutDetailView_Previews.workoutDetail), isPreview: true)) } } diff --git a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailViewModel.swift b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailViewModel.swift index ff2f801..e9880d1 100644 --- a/Werkout_ios/Views/WorkoutDetail/WorkoutDetailViewModel.swift +++ b/Werkout_ios/Views/WorkoutDetail/WorkoutDetailViewModel.swift @@ -13,10 +13,13 @@ class WorkoutDetailViewModel: ObservableObject { case loading case showWorkout(Workout) } - @Published var status: WorkoutDetailViewModelStatus - init(workout: Workout, status: WorkoutDetailViewModelStatus? = nil) { + @Published var status: WorkoutDetailViewModelStatus + let isPreview: Bool + + init(workout: Workout, status: WorkoutDetailViewModelStatus? = nil, isPreview: Bool) { self.status = .loading + self.isPreview = isPreview if let passedStatus = status { self.status = passedStatus diff --git a/Werkout_ios/Views/WorkoutHistoryView.swift b/Werkout_ios/Views/WorkoutHistoryView.swift index f15cc92..5ff72b9 100644 --- a/Werkout_ios/Views/WorkoutHistoryView.swift +++ b/Werkout_ios/Views/WorkoutHistoryView.swift @@ -79,8 +79,8 @@ struct WorkoutHistoryView: View { } } .sheet(item: $selectedPlannedWorkout) { item in - let viewModel = WorkoutDetailViewModel(workout: item) - WorkoutDetailView(viewModel: viewModel, showAddToCalendar: true) + let viewModel = WorkoutDetailViewModel(workout: item, isPreview: true) + WorkoutDetailView(viewModel: viewModel) } } }