This commit is contained in:
Trey t
2023-07-17 19:26:11 -05:00
parent 593cc496cd
commit af538362e8
23 changed files with 637 additions and 438 deletions

View File

@@ -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 = "<group>"; };
1C485C8B2A49D65600A6F896 /* WorkoutHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutHistoryView.swift; sourceTree = "<group>"; };
1C4AFF142A60F25E0027710B /* ThotStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThotStyle.swift; sourceTree = "<group>"; };
1C4AFF172A65CD290027710B /* Superset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Superset.swift; sourceTree = "<group>"; };
1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentWorkoutInfo.swift; sourceTree = "<group>"; };
1C5190C12A57CA5F00885849 /* OvalTextFieldStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OvalTextFieldStyle.swift; sourceTree = "<group>"; };
1C5190C32A589CAC00885849 /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = "<group>"; };
1C5190C52A589CC100885849 /* ActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionsView.swift; sourceTree = "<group>"; };
@@ -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 = "<group>";
@@ -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 */,

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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": {

View File

@@ -32,7 +32,7 @@ class AllEquipmentFetchable: Fetchable {
}
class AllExerciseFetchable: Fetchable {
typealias Response = [ExerciseExercise]
typealias Response = [Exercise]
var endPoint: String = "/exercise/all/"
}

View File

@@ -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)

View File

@@ -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: {

View File

@@ -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: {

View File

@@ -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

View File

@@ -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
// }() )
// }
//}

View File

@@ -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")

View File

@@ -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 {

View File

@@ -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()
}
}
}
}

View File

@@ -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))
}
}

View File

@@ -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

View File

@@ -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)
}
}
}