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 */; }; 1C485C8D2A49D95700A6F896 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF65A822A42347D0042FFBD /* Extensions.swift */; };
1C4AFF152A60F25F0027710B /* ThotStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF142A60F25E0027710B /* ThotStyle.swift */; }; 1C4AFF152A60F25F0027710B /* ThotStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF142A60F25E0027710B /* ThotStyle.swift */; };
1C4AFF162A60F27E0027710B /* 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 */; }; 1C5190C22A57CA5F00885849 /* OvalTextFieldStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C12A57CA5F00885849 /* OvalTextFieldStyle.swift */; };
1C5190C42A589CAC00885849 /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C32A589CAC00885849 /* InfoView.swift */; }; 1C5190C42A589CAC00885849 /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C32A589CAC00885849 /* InfoView.swift */; };
1C5190C62A589CC100885849 /* ActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C52A589CC100885849 /* ActionsView.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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 1C5190C52A589CC100885849 /* ActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionsView.swift; sourceTree = "<group>"; };
@@ -269,6 +275,7 @@
1CF65A822A42347D0042FFBD /* Extensions.swift */, 1CF65A822A42347D0042FFBD /* Extensions.swift */,
1CF65A272A3972840042FFBD /* Persistence.swift */, 1CF65A272A3972840042FFBD /* Persistence.swift */,
1CD0C6662A5CA19600970E52 /* BaseURLs.swift */, 1CD0C6662A5CA19600970E52 /* BaseURLs.swift */,
1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */,
1CF65A4F2A3A1EA90042FFBD /* BridgeModule.swift */, 1CF65A4F2A3A1EA90042FFBD /* BridgeModule.swift */,
1CF65A802A412AA30042FFBD /* DataStore.swift */, 1CF65A802A412AA30042FFBD /* DataStore.swift */,
1CF65AB92A4894430042FFBD /* UserStore.swift */, 1CF65AB92A4894430042FFBD /* UserStore.swift */,
@@ -314,6 +321,7 @@
1CAF4D892A5132F900B00E50 /* PlannedWorkout.swift */, 1CAF4D892A5132F900B00E50 /* PlannedWorkout.swift */,
1CF65A462A39FB6C0042FFBD /* RegisteredUser.swift */, 1CF65A462A39FB6C0042FFBD /* RegisteredUser.swift */,
1CF65A422A39FB410042FFBD /* Workout.swift */, 1CF65A422A39FB410042FFBD /* Workout.swift */,
1C4AFF172A65CD290027710B /* Superset.swift */,
); );
path = APIModels; path = APIModels;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -583,6 +591,7 @@
1CF65A852A43E8060042FFBD /* CompletedWorkout.swift in Sources */, 1CF65A852A43E8060042FFBD /* CompletedWorkout.swift in Sources */,
1CF65A6E2A3F60480042FFBD /* CreateViewModels.swift in Sources */, 1CF65A6E2A3F60480042FFBD /* CreateViewModels.swift in Sources */,
1CF65A4C2A39FDA20042FFBD /* WorkoutDetailView.swift in Sources */, 1CF65A4C2A39FDA20042FFBD /* WorkoutDetailView.swift in Sources */,
1C4AFF182A65CD290027710B /* Superset.swift in Sources */,
1CF65A8E2A44B78B0042FFBD /* CompletedWorkoutView.swift in Sources */, 1CF65A8E2A44B78B0042FFBD /* CompletedWorkoutView.swift in Sources */,
1CF65A432A39FB410042FFBD /* Workout.swift in Sources */, 1CF65A432A39FB410042FFBD /* Workout.swift in Sources */,
1CF65A502A3A1EA90042FFBD /* BridgeModule.swift in Sources */, 1CF65A502A3A1EA90042FFBD /* BridgeModule.swift in Sources */,
@@ -597,6 +606,7 @@
1CF65A2D2A3972840042FFBD /* MainView.swift in Sources */, 1CF65A2D2A3972840042FFBD /* MainView.swift in Sources */,
1CF65A7D2A41275D0042FFBD /* Network.swift in Sources */, 1CF65A7D2A41275D0042FFBD /* Network.swift in Sources */,
1C485C8A2A492BB400A6F896 /* LoginView.swift in Sources */, 1C485C8A2A492BB400A6F896 /* LoginView.swift in Sources */,
1C4AFF1B2A65FB190027710B /* CurrentWorkoutInfo.swift in Sources */,
1CF65A732A3F60D20042FFBD /* CreateExerciseActionsView.swift in Sources */, 1CF65A732A3F60D20042FFBD /* CreateExerciseActionsView.swift in Sources */,
1CF65A832A42347D0042FFBD /* Extensions.swift in Sources */, 1CF65A832A42347D0042FFBD /* Extensions.swift in Sources */,
1CF65A282A3972840042FFBD /* Persistence.swift in Sources */, 1CF65A282A3972840042FFBD /* Persistence.swift in Sources */,
@@ -619,6 +629,7 @@
files = ( files = (
1CF65A982A452D270042FFBD /* ContentView.swift in Sources */, 1CF65A982A452D270042FFBD /* ContentView.swift in Sources */,
1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */, 1CF65A962A452D270042FFBD /* Werkout_watchApp.swift in Sources */,
1C4AFF192A65CD6F0027710B /* Superset.swift in Sources */,
1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */, 1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */,
1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */, 1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */,
1C4AFF162A60F27E0027710B /* ThotStyle.swift in Sources */, 1C4AFF162A60F27E0027710B /* ThotStyle.swift in Sources */,
@@ -627,6 +638,7 @@
1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */, 1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */,
1CD0C6682A5CA1A200970E52 /* BaseURLs.swift in Sources */, 1CD0C6682A5CA1A200970E52 /* BaseURLs.swift in Sources */,
1CF65AA72A452D9C0042FFBD /* Muscle.swift in Sources */, 1CF65AA72A452D9C0042FFBD /* Muscle.swift in Sources */,
1C4AFF1C2A65FB2B0027710B /* CurrentWorkoutInfo.swift in Sources */,
1C485C8D2A49D95700A6F896 /* Extensions.swift in Sources */, 1C485C8D2A49D95700A6F896 /* Extensions.swift in Sources */,
1CF65AAB2A452DAC0042FFBD /* PreviewData.swift in Sources */, 1CF65AAB2A452DAC0042FFBD /* PreviewData.swift in Sources */,
1CF65AA52A452D9C0042FFBD /* CompletedWorkout.swift in Sources */, 1CF65AA52A452D9C0042FFBD /* CompletedWorkout.swift in Sources */,

View File

@@ -6,28 +6,18 @@
// //
import Foundation import Foundation
struct Equipment: Codable { struct Equipment: Codable {
let id: Int let id: Int
let createdAt, updatedAt: String let name, createdAt, updatedAt: String
let category, name: String? let is_weight: Bool?
let category: String?
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case id case id, name
case createdAt = "created_at" case createdAt = "created_at"
case updatedAt = "updated_at" case updatedAt = "updated_at"
case category, name case is_weight
} case category
}
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
} }
} }

View File

@@ -7,32 +7,39 @@
import Foundation import Foundation
struct ExerciseElement: Codable, Equatable { struct SupersetExercise: Identifiable, Codable, Equatable, Hashable {
let workout: Int var id = UUID()
let exercise: ExerciseExercise
let workout: Int?
let exercise: Exercise
let weight: Int? let weight: Int?
let reps: Int? let reps: Int?
let duration: Int? let duration: Int?
let durationAudio: String? let durationAudio: String?
let weightAudio: String? let weightAudio: String?
let createdAt: String let createdAt: String
let order, superset: Int
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case workout, exercise, weight, reps, duration case workout, exercise, weight, reps, duration, order, superset
case durationAudio = "duration_audio" case durationAudio = "duration_audio"
case weightAudio = "weight_audio" case weightAudio = "weight_audio"
case createdAt = "created_at" case createdAt = "created_at"
} }
public func hash(into hasher: inout Hasher) {
return hasher.combine(id)
}
} }
struct ExerciseExercise: Codable, Hashable, Identifiable { struct Exercise: Identifiable, Codable, Equatable {
static func == (lhs: ExerciseExercise, rhs: ExerciseExercise) -> Bool { static func == (lhs: Exercise, rhs: Exercise) -> Bool {
lhs.id == rhs.id lhs.id == rhs.id
} }
let id: Int let id: Int
let muscles: [ExerciseMuscle] let equipment: [Equipment]
let equipment: [ExerciseEquipment] let muscles: [Muscle]
let audioURL, videoURL, createdAt, updatedAt: String let audioURL, videoURL, createdAt, updatedAt: String
let name, description, side: String let name, description, side: String
let isTwoDumbbells, isTrackableDistance, isAlternating, isWeight: Bool let isTwoDumbbells, isTrackableDistance, isAlternating, isWeight: Bool

View File

@@ -9,18 +9,11 @@ import Foundation
struct Muscle: Codable { struct Muscle: Codable {
let id: Int let id: Int
let name: String let name, createdAt, updatedAt: String
}
struct ExerciseMuscle: Codable, Hashable {
let id: Int
let createdAt, updatedAt: String
let exercise, muscle: Int
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case id case id, name
case createdAt = "created_at" case createdAt = "created_at"
case updatedAt = "updated_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 id: Int
let name: String let name: String
let description: String? let description: String?
let exercises: [ExerciseElement] let supersets: [Superset]?
let registeredUser: RegisteredUser? let registeredUser: RegisteredUser?
let femaleVideos, maleVideos, bothVideos: [String]?
let muscles: [String]? let muscles: [String]?
let equipment: [String]? let equipment: [String]?
let exercise_count: Int? let exercise_count: Int?
let maleVideos: [String]?
let femaleVideos: [String]?
let bothVideos: [String]?
enum CodingKeys: String, CodingKey { 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 registeredUser = "registered_user"
case maleVideos = "male_videos" case maleVideos = "male_videos"
case femaleVideos = "female_videos" case femaleVideos = "female_videos"
@@ -34,31 +32,18 @@ struct Workout: Codable, Identifiable, Equatable {
init(from decoder: Decoder) throws { init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) 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.description = try container.decodeIfPresent(String.self, forKey: .description)
self.registeredUser = try container.decodeIfPresent(RegisteredUser.self, forKey: .registeredUser) self.registeredUser = try container.decodeIfPresent(RegisteredUser.self, forKey: .registeredUser)
self.id = try container.decode(Int.self, forKey: .id) 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.femaleVideos = try container.decodeIfPresent([String].self, forKey: .femaleVideos)
self.maleVideos = try container.decodeIfPresent([String].self, forKey: .maleVideos) self.maleVideos = try container.decodeIfPresent([String].self, forKey: .maleVideos)
self.bothVideos = try container.decodeIfPresent([String].self, forKey: .bothVideos) self.bothVideos = try container.decodeIfPresent([String].self, forKey: .bothVideos)
} self.supersets = try container.decodeIfPresent([Superset].self, forKey: .supersets)
var exercisesSortedByCreated_at: [ExerciseElement] { self.equipment = try container.decodeIfPresent([String].self, forKey: .equipment)
return self.exercises.sorted(by: { self.muscles = try container.decodeIfPresent([String].self, forKey: .muscles)
if let lhsDate = $0.createdAt.dateFromServerDate, self.exercise_count = try container.decodeIfPresent(Int.self, forKey: .exercise_count)
let rhsDate = $1.createdAt.dateFromServerDate {
return lhsDate < rhsDate
}
return false
})
} }
} }

View File

@@ -12,6 +12,6 @@ enum BaseURLs: String {
case dev = "https://dev.werkout.fitness" case dev = "https://dev.werkout.fitness"
static var currentBaseURL: String { 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 @Published var currentWorkoutRunTimeInSeconds: Int = -1
private var currentWorkoutRunTimer: Timer? private var currentWorkoutRunTimer: Timer?
var currentWorkout: Workout?
public private(set) var workoutStartDate: Date? public private(set) var workoutStartDate: Date?
private var currentExerciseTimer: Timer? private var currentExerciseTimer: Timer?
public private(set) var currentExerciseIdx: Int = -1 {
didSet { @Published public private(set) var currentExerciseInfo = CurrentWorkoutInfo()
self.currentExercisePositionString = "\(self.currentExerciseIdx+1)/\(self.currentWorkout?.exercises.count ?? 0)" @Published var previewWorkout: Workout?
}
}
@Published var currentExerciseTimeLeft: Int = 0 @Published var currentExerciseTimeLeft: Int = 0
@Published var currentExercise: ExerciseElement?
var currentExercisePositionString: String? var currentExercisePositionString: String?
private var isWatchConnected = false private var isWatchConnected = false
@@ -59,14 +56,17 @@ class BridgeModule: NSObject, ObservableObject {
var audioPlayer: AVAudioPlayer? var audioPlayer: AVAudioPlayer?
func start(workout: Workout) { func start(workout: Workout) {
self.currentWorkout = workout currentExerciseInfo.complete = {
self.completeWorkout()
}
currentExerciseInfo.start(workout: workout)
currentWorkoutRunTimeInSeconds = 0 currentWorkoutRunTimeInSeconds = 0
currentWorkoutRunTimer?.invalidate() currentWorkoutRunTimer?.invalidate()
currentWorkoutRunTimer = nil currentWorkoutRunTimer = nil
currentExerciseIdx = 0 if let superetExercise = currentExerciseInfo.currentExercise {
let exercise = workout.exercises[currentExerciseIdx] updateCurrent(exercise: superetExercise)
updateCurrent(exercise: exercise)
startWorkoutTimer() startWorkoutTimer()
workoutStartDate = Date() workoutStartDate = Date()
isInWorkout = true isInWorkout = true
@@ -75,17 +75,14 @@ class BridgeModule: NSObject, ObservableObject {
WCSession.default.delegate = self WCSession.default.delegate = self
WCSession.default.activate() WCSession.default.activate()
} }
}
} }
func goToExerciseAt(index: Int) { func goToExerciseAt(section: Int, row: Int) {
guard let currentWorkout = currentWorkout else { if let superetExercise = currentExerciseInfo.goToWorkoutAt(supersetIndex: section,
return exerciseIndex: row) {
updateCurrent(exercise: superetExercise)
} }
currentExerciseIdx = index
let exercise = currentWorkout.exercises[index]
updateCurrent(exercise: exercise)
} }
func resetCurrentWorkout() { func resetCurrentWorkout() {
@@ -98,10 +95,7 @@ class BridgeModule: NSObject, ObservableObject {
self.currentExerciseTimer = nil self.currentExerciseTimer = nil
self.currentWorkoutRunTimeInSeconds = -1 self.currentWorkoutRunTimeInSeconds = -1
self.currentExerciseIdx = -1 self.currentExerciseInfo.reset()
self.currentExercise = nil
self.currentWorkout = nil
self.isInWorkout = false self.isInWorkout = false
self.workoutStartDate = nil self.workoutStartDate = nil
@@ -162,38 +156,24 @@ class BridgeModule: NSObject, ObservableObject {
} }
func nextExercise() { func nextExercise() {
currentExerciseIdx += 1 if let nextSupersetExercise = currentExerciseInfo.nextExercise {
if let currentWorkout = currentWorkout { updateCurrent(exercise: nextSupersetExercise)
if currentExerciseIdx < currentWorkout.exercises.count {
let nextExercise = currentWorkout.exercises[currentExerciseIdx]
updateCurrent(exercise: nextExercise)
} else { } else {
completeWorkout() completeWorkout()
} }
} }
}
func previousExercise() { func previousExercise() {
currentExerciseIdx -= 1 if let nextSupersetExercise = currentExerciseInfo.previousExercise {
if currentExerciseIdx < 0 { updateCurrent(exercise: nextSupersetExercise)
currentExerciseIdx = 0
}
if let currentWorkout = currentWorkout {
if currentExerciseIdx < currentWorkout.exercises.count {
let nextExercise = currentWorkout.exercises[currentExerciseIdx]
updateCurrent(exercise: nextExercise)
} else { } else {
completeWorkout() completeWorkout()
} }
} }
}
func restartExercise() { func restartExercise() {
if let currentWorkout = currentWorkout { if let currentExercise = currentExerciseInfo.currentExercise {
if currentExerciseIdx < currentWorkout.exercises.count { updateCurrent(exercise: currentExercise)
let nextExercise = currentWorkout.exercises[currentExerciseIdx]
updateCurrent(exercise: nextExercise)
}
} }
} }
@@ -201,15 +181,12 @@ class BridgeModule: NSObject, ObservableObject {
currentWorkoutRunTimeInSeconds += 1 currentWorkoutRunTimeInSeconds += 1
} }
func updateCurrent(exercise: ExerciseElement) { func updateCurrent(exercise: SupersetExercise) {
DispatchQueue.main.async { DispatchQueue.main.async {
self.currentExerciseTimer?.invalidate() self.currentExerciseTimer?.invalidate()
self.currentExerciseTimer = nil 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.startExerciseTimerWith(duration: duration)
} }
self.sendCurrentExerciseToWatch() self.sendCurrentExerciseToWatch()
@@ -281,27 +258,27 @@ extension BridgeModule: WCSessionDelegate {
} }
func sendCurrentExerciseToWatch() { func sendCurrentExerciseToWatch() {
if let duration = currentExercise?.duration, if let currentExercise = currentExerciseInfo.currentExercise,
let duration = currentExercise.duration ,
duration > 0 { 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 model = PhoneToWatchActions.inExercise(watchModel)
let data = try! JSONEncoder().encode(model) let data = try! JSONEncoder().encode(model)
send(data) send(data)
} else { } else {
var intWatchDispaly = -1 if let currentExercise = currentExerciseInfo.currentExercise,
if let reps = self.currentExercise?.reps, let reps = currentExercise.reps,
reps > 0 { reps > 0 {
intWatchDispaly = reps
}
// if not a timer we need to set the watch display with number of reps // if not a timer we need to set the watch display with number of reps
// if timer it will set when timer updates // if timer it will set when timer updates
let watchModel = WatchPackageModel(currentExerciseName: self.currentExercise?.exercise.name ?? "-", currentTimeLeft: intWatchDispaly, workoutStartDate: self.workoutStartDate ?? Date()) let watchModel = WatchPackageModel(currentExerciseName: currentExercise.exercise.name, currentTimeLeft: reps, workoutStartDate: self.workoutStartDate ?? Date())
let model = PhoneToWatchActions.inExercise(watchModel) let model = PhoneToWatchActions.inExercise(watchModel)
let data = try! JSONEncoder().encode(model) let data = try! JSONEncoder().encode(model)
self.send(data) self.send(data)
} }
} }
}
func session(_ session: WCSession, didReceiveMessageData messageData: Data) { func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
if let model = try? JSONDecoder().decode(WatchActions.self, from: messageData) { if let model = try? JSONDecoder().decode(WatchActions.self, from: messageData) {

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 allWorkouts: [Workout]?
public private(set) var allMuscles: [Muscle]? public private(set) var allMuscles: [Muscle]?
public private(set) var allEquipment: [Equipment]? 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 @Published public private(set) var status = DataStoreStatus.idle

View File

@@ -2,14 +2,18 @@
"id": 21, "id": 21,
"name": "Ipad", "name": "Ipad",
"description": "description", "description": "description",
"supersets": [
{
"id": 1,
"exercises": [ "exercises": [
{ {
"workout": 21, "id": 1,
"exercise": { "exercise": {
"id": 520, "id": 520,
"muscles": [ "muscles": [
{ {
"id": 7264, "id": 7264,
"name": "hip flexor",
"created_at": "2023-06-14T17:05:39.760515Z", "created_at": "2023-06-14T17:05:39.760515Z",
"updated_at": "2023-06-14T17:05:39.761372Z", "updated_at": "2023-06-14T17:05:39.761372Z",
"exercise": 520, "exercise": 520,
@@ -17,6 +21,7 @@
}, },
{ {
"id": 7265, "id": 7265,
"name": "glutes",
"created_at": "2023-06-14T17:05:39.762342Z", "created_at": "2023-06-14T17:05:39.762342Z",
"updated_at": "2023-06-14T17:05:39.762814Z", "updated_at": "2023-06-14T17:05:39.762814Z",
"exercise": 520, "exercise": 520,
@@ -26,6 +31,7 @@
"equipment": [ "equipment": [
{ {
"id": 941, "id": 941,
"name": "Wall",
"created_at": "2023-06-13T02:28:04.289213Z", "created_at": "2023-06-13T02:28:04.289213Z",
"updated_at": "2023-06-13T02:28:04.290354Z", "updated_at": "2023-06-13T02:28:04.290354Z",
"exercise": 520, "exercise": 520,
@@ -52,101 +58,152 @@
"muscle_groups": "hip flexor,glutes", "muscle_groups": "hip flexor,glutes",
"synonyms": "" "synonyms": ""
}, },
"weight": 0, "created_at": "2023-07-17T18:56:36.984049Z",
"reps": 0, "updated_at": "2023-07-17T19:06:00.534838Z",
"duration": null, "weight": null,
"duration_audio": null, "reps": null,
"weight_audio": "/media/quantities_audio/for_0_pounds.m4a", "duration": 30,
"created_at": "2023-06-20T21:03:00.127620Z" "order": 1,
"superset": 1
}, },
{ {
"workout": 21, "id": 2,
"exercise": { "exercise": {
"id": 37, "id": 992,
"muscles": [ "muscles": [
{ {
"id": 7272, "id": 7270,
"created_at": "2023-06-14T17:05:39.772195Z", "name": "hamstrings",
"updated_at": "2023-06-14T17:05:39.772765Z", "created_at": "2023-06-14T17:05:39.769351Z",
"exercise": 37, "updated_at": "2023-06-14T17:05:39.769758Z",
"muscle": 4 "exercise": 992,
"muscle": 6
}, },
{ {
"id": 7273, "id": 7271,
"created_at": "2023-06-14T17:05:39.773621Z", "name": "glutes",
"updated_at": "2023-06-14T17:05:39.774079Z", "created_at": "2023-06-14T17:05:39.770480Z",
"exercise": 37, "updated_at": "2023-06-14T17:05:39.771111Z",
"muscle": 6 "exercise": 992,
"muscle": 4
} }
], ],
"equipment": [ "equipment": [
{ {
"id": 945, "id": 944,
"created_at": "2023-06-13T02:28:04.295672Z", "name": "Dumbbell",
"updated_at": "2023-06-13T02:28:04.296237Z", "created_at": "2023-06-13T02:28:04.294180Z",
"exercise": 37, "updated_at": "2023-06-13T02:28:04.294658Z",
"equipment": 1088 "exercise": 992,
"equipment": 1091
} }
], ],
"audio_url": "exercise_audio/2_Kettlebell_Clean.m4a", "audio_url": "exercise_audio/2_Dumbbell_Single-Leg_Deadlift.m4a",
"video_url": "exercise_videos/2_Kettlebell_Clean.mp4", "video_url": "exercise_videos/2_Dumbbell_Single-Leg_Deadlift.mp4",
"created_at": "2023-06-11T22:50:18.813591Z", "created_at": "2023-06-11T22:50:19.197099Z",
"updated_at": "2023-06-11T22:50:18.813600Z", "updated_at": "2023-06-11T22:50:19.197105Z",
"name": "2 Kettlebell Clean", "name": "2 Dumbbell Single-Leg Deadlift",
"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.", "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": "", "side": "right_leg",
"is_two_dumbbells": false, "is_two_dumbbells": true,
"is_trackable_distance": false, "is_trackable_distance": false,
"is_alternating": false, "is_alternating": false,
"is_weight": true, "is_weight": true,
"is_distance": false, "is_distance": false,
"is_duration": true, "is_duration": true,
"is_reps": true, "is_reps": true,
"joints_used": "shoulder,ankle,knee,hip,elbow", "joints_used": "ankle,lumbar spine,hip,knee,wrist",
"movement_patterns": "lower pull - hip hinge,upper pull", "movement_patterns": "lower pull,lower pull - hip hinge",
"equipment_required": "Kettlebell", "equipment_required": "Dumbbell",
"muscle_groups": "glutes,hamstrings", "muscle_groups": "hamstrings,glutes",
"synonyms": "" "synonyms": "2 Dumbbell Single Leg Deadlift"
}, },
"weight": 0, "created_at": "2023-07-17T18:56:36.984523Z",
"reps": 0, "updated_at": "2023-07-17T19:06:00.535334Z",
"weight": 30,
"reps": null,
"duration": null, "duration": null,
"duration_audio": null, "order": 2,
"weight_audio": "/media/quantities_audio/for_0_pounds.m4a", "superset": 1
"created_at": "2023-06-20T21:03:00.131018Z" }
],
"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, "id": 2,
"exercises": [
{
"id": 3,
"exercise": { "exercise": {
"id": 798, "id": 992,
"muscles": [], "muscles": [
"equipment": [], {
"audio_url": "exercise_audio/Recover.m4a", "id": 7270,
"video_url": "exercise_videos/Recover.mp4", "name": "hamstrings",
"created_at": "2023-06-11T22:50:19.127914Z", "created_at": "2023-06-14T17:05:39.769351Z",
"updated_at": "2023-06-11T22:50:19.127921Z", "updated_at": "2023-06-14T17:05:39.769758Z",
"name": "Recover", "exercise": 992,
"description": "Use this time to catch your breath. It will help you get more out of what's next", "muscle": 6
"side": "", },
"is_two_dumbbells": false, {
"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_trackable_distance": false,
"is_alternating": false, "is_alternating": false,
"is_weight": false, "is_weight": true,
"is_distance": false, "is_distance": false,
"is_duration": true, "is_duration": true,
"is_reps": false, "is_reps": true,
"joints_used": "", "joints_used": "ankle,lumbar spine,hip,knee,wrist",
"movement_patterns": "", "movement_patterns": "lower pull,lower pull - hip hinge",
"equipment_required": "", "equipment_required": "Dumbbell",
"muscle_groups": "", "muscle_groups": "hamstrings,glutes",
"synonyms": null "synonyms": "2 Dumbbell Single Leg Deadlift"
}, },
"weight": 0, "created_at": "2023-07-17T18:58:35.585418Z",
"reps": 0, "updated_at": "2023-07-17T18:58:35.585435Z",
"weight": 11,
"reps": null,
"duration": null, "duration": null,
"duration_audio": null, "order": 1,
"weight_audio": "/media/quantities_audio/for_0_pounds.m4a", "superset": 2
"created_at": "2023-06-20T21:03:00.134981Z" }
],
"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": { "registered_user": {

View File

@@ -32,7 +32,7 @@ class AllEquipmentFetchable: Fetchable {
} }
class AllExerciseFetchable: Fetchable { class AllExerciseFetchable: Fetchable {
typealias Response = [ExerciseExercise] typealias Response = [Exercise]
var endPoint: String = "/exercise/all/" 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") { if let filepath = Bundle.main.path(forResource: "Exercises", ofType: "json") {
do { do {
let data = try Data(NSData(contentsOfFile: filepath)) 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 return exercises
} catch { } catch {
print(error) print(error)

View File

@@ -18,15 +18,15 @@ struct AddExerciseView: View {
@State var selectedMuscles = [Muscle]() @State var selectedMuscles = [Muscle]()
@State var selectedEquipment = [Equipment]() @State var selectedEquipment = [Equipment]()
@State var filteredExercises = [ExerciseExercise]() @State var filteredExercises = [Exercise]()
@StateObject var bridgeModule = BridgeModule.shared @StateObject var bridgeModule = BridgeModule.shared
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
var selectedExercise: ((ExerciseExercise) -> Void) var selectedExercise: ((Exercise) -> Void)
@State var createWorkoutItemPickerViewModel: CreateWorkoutItemPickerViewModel? @State var createWorkoutItemPickerViewModel: CreateWorkoutItemPickerViewModel?
@State var createWorkoutItemPickerViewType: CreateWorkoutItemPickerViewType? @State var createWorkoutItemPickerViewType: CreateWorkoutItemPickerViewType?
@State var searchString: String = "" @State var searchString: String = ""
@State var videoExercise: ExerciseExercise? { @State var videoExercise: Exercise? {
didSet { didSet {
if let viddd = self.videoExercise?.videoURL, if let viddd = self.videoExercise?.videoURL,
let url = URL(string: BaseURLs.currentBaseURL + viddd) { let url = URL(string: BaseURLs.currentBaseURL + viddd) {
@@ -111,19 +111,19 @@ struct AddExerciseView: View {
func filterExercises() { func filterExercises() {
if selectedMuscles.count == 0 { if selectedMuscles.count == 0 {
filteredExercises = [ExerciseExercise]() filteredExercises = [Exercise]()
return return
} }
if selectedEquipment.count == 0 { if selectedEquipment.count == 0 {
filteredExercises = [ExerciseExercise]() filteredExercises = [Exercise]()
return return
} }
guard let exercises = DataStore.shared.allExercise, guard let exercises = DataStore.shared.allExercise,
let muscles = DataStore.shared.allMuscles, let muscles = DataStore.shared.allMuscles,
let equipment = DataStore.shared.allEquipment else { let equipment = DataStore.shared.allEquipment else {
filteredExercises = [ExerciseExercise]() filteredExercises = [Exercise]()
return return
} }
@@ -132,7 +132,7 @@ struct AddExerciseView: View {
if selectedMuscles.count == muscles.count { if selectedMuscles.count == muscles.count {
hasCorrectMuscles = true hasCorrectMuscles = true
} else { } else {
let exerciseMuscleIds = exercise.muscles.map({ $0.muscle }) let exerciseMuscleIds = exercise.muscles.map({ $0.id })
let selctedMuscleIds = selectedMuscles.map({ $0.id }) let selctedMuscleIds = selectedMuscles.map({ $0.id })
// if one items match // if one items match
if exerciseMuscleIds.contains(where: selctedMuscleIds.contains) { if exerciseMuscleIds.contains(where: selctedMuscleIds.contains) {
@@ -146,7 +146,7 @@ struct AddExerciseView: View {
if selectedEquipment.count == equipment.count { if selectedEquipment.count == equipment.count {
hasCorrectEquipment = true hasCorrectEquipment = true
} else { } else {
let exerciseEquipmentIds = exercise.equipment.map({ $0.equipment }) let exerciseEquipmentIds = exercise.equipment.map({ $0.id })
let selctedEquipmentIds = selectedEquipment.map({ $0.id }) let selctedEquipmentIds = selectedEquipment.map({ $0.id })
// if one items match // if one items match
if exerciseEquipmentIds.contains(where: selctedEquipmentIds.contains) { if exerciseEquipmentIds.contains(where: selctedEquipmentIds.contains) {
@@ -198,7 +198,7 @@ struct AddExerciseView: View {
var createWorkoutItemPickerModels = [CreateWorkoutItemPickerModel]() var createWorkoutItemPickerModels = [CreateWorkoutItemPickerModel]()
equipment.forEach({ equipment.forEach({
let model = CreateWorkoutItemPickerModel(id: $0.id, let model = CreateWorkoutItemPickerModel(id: $0.id,
name: $0.name?.lowercased() ?? "-") name: $0.name.lowercased())
createWorkoutItemPickerModels.append(model) createWorkoutItemPickerModels.append(model)
}) })
createWorkoutItemPickerModels = createWorkoutItemPickerModels.sorted(by: { createWorkoutItemPickerModels = createWorkoutItemPickerModels.sorted(by: {

View File

@@ -35,13 +35,13 @@ struct AllWorkoutsView: View {
@State private var showWorkoutDetail = false @State private var showWorkoutDetail = false
@State private var selectedWorkout: Workout? { @State private var selectedWorkout: Workout? {
didSet { didSet {
bridgeModule.currentWorkout = selectedWorkout bridgeModule.currentExerciseInfo.workout = selectedWorkout
} }
} }
@State private var selectedPlannedWorkout: Workout? { @State private var selectedPlannedWorkout: Workout? {
didSet { didSet {
bridgeModule.currentWorkout = selectedPlannedWorkout bridgeModule.currentExerciseInfo.workout = selectedPlannedWorkout
} }
} }
@@ -58,7 +58,7 @@ struct AllWorkoutsView: View {
AllWorkoutPickerView(mainViews: MainViewTypes.allCases, AllWorkoutPickerView(mainViews: MainViewTypes.allCases,
selectedSegment: $selectedSegment, selectedSegment: $selectedSegment,
showCurrentWorkout: { showCurrentWorkout: {
selectedWorkout = bridgeModule.currentWorkout selectedWorkout = bridgeModule.currentExerciseInfo.workout
}) })
switch selectedSegment { switch selectedSegment {
@@ -87,12 +87,13 @@ struct AllWorkoutsView: View {
maybeUpdateShit() maybeUpdateShit()
} }
.sheet(item: $selectedWorkout) { item in .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) WorkoutDetailView(viewModel: viewModel)
} }
.sheet(item: $selectedPlannedWorkout) { item in .sheet(item: $selectedPlannedWorkout) { item in
let viewModel = WorkoutDetailViewModel(workout: item) let viewModel = WorkoutDetailViewModel(workout: item, isPreview: true)
WorkoutDetailView(viewModel: viewModel, showAddToCalendar: false) WorkoutDetailView(viewModel: viewModel)
} }
.sheet(isPresented: $showLoginView) { .sheet(isPresented: $showLoginView) {
LoginView(completion: { LoginView(completion: {

View File

@@ -9,12 +9,12 @@ import SwiftUI
class CreateWorkoutExercise: ObservableObject, Identifiable { class CreateWorkoutExercise: ObservableObject, Identifiable {
let id = UUID() let id = UUID()
var exercise: ExerciseExercise var exercise: Exercise
@Published var reps: Int = 0 @Published var reps: Int = 0
@Published var duration: Int = 0 @Published var duration: Int = 0
@Published var weight: 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.exercise = exercise
self.reps = reps self.reps = reps
self.duration = duration self.duration = duration

View File

@@ -15,7 +15,8 @@ struct ExternalWorkoutDetailView: View {
var body: some View { var body: some View {
ZStack { ZStack {
if let workout = bridgeModule.currentWorkout { if let workout = bridgeModule.currentExerciseInfo.workout,
let exercise = bridgeModule.currentExerciseInfo.currentExercise {
GeometryReader { metrics in GeometryReader { metrics in
VStack { VStack {
HStack { HStack {
@@ -27,7 +28,7 @@ struct ExternalWorkoutDetailView: View {
VStack { VStack {
ExtExerciseList(workout: workout, ExtExerciseList(workout: workout,
currentExerciseIdx: bridgeModule.currentExerciseIdx) currentExercise: exercise)
if let currentExercisePositionString = bridgeModule.currentExercisePositionString { if let currentExercisePositionString = bridgeModule.currentExercisePositionString {
Text(currentExercisePositionString) Text(currentExercisePositionString)
@@ -53,18 +54,20 @@ struct ExternalWorkoutDetailView: View {
.scaledToFill() .scaledToFill()
} }
} }
.onChange(of: bridgeModule.currentExercise, perform: { newValue in .onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
if let videoURL = VideoURLCreator.videoURL( if let videoURL = VideoURLCreator.videoURL(
thotStyle: thotStyle, thotStyle: thotStyle,
defaultVideoURLStr: bridgeModule.currentExercise?.exercise.videoURL, defaultVideoURLStr: currentExtercise.exercise.videoURL,
exerciseName: bridgeModule.currentExercise?.exercise.name, exerciseName: currentExtercise.exercise.name,
workout: bridgeModule.currentWorkout) { workout: bridgeModule.currentExerciseInfo.workout) {
avPlayer = AVPlayer(url: videoURL) avPlayer = AVPlayer(url: videoURL)
avPlayer.play() avPlayer.play()
} }
}
}) })
.frame(maxWidth: .infinity, maxHeight: .infinity) .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 { var body: some View {
HStack { HStack {
if let workout = bridgeModule.currentWorkout { if let workout = bridgeModule.currentExerciseInfo.workout {
Text(workout.name) Text(workout.name)
.font(Font.system(size: 100)) .font(Font.system(size: 100))
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
@@ -91,38 +94,66 @@ struct TitleView: View {
struct ExtExerciseList: View { struct ExtExerciseList: View {
var workout: Workout var workout: Workout
var currentExerciseIdx: Int var currentExercise: SupersetExercise
var body: some View { var body: some View {
if let supersets = workout.supersets {
ScrollViewReader { proxy in ScrollViewReader { proxy in
List() { List() {
ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in ForEach(supersets.indices, id: \.self) { supersetIndex in
let obj = workout.exercisesSortedByCreated_at[i] let superset = supersets[supersetIndex]
Section(content: {
ForEach(superset.exercises.indices, id: \.self) { exerciseIndex in
let supersetExecercise = superset.exercises[exerciseIndex]
HStack { HStack {
if i == currentExerciseIdx { if supersetExecercise.id == currentExercise.id {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.foregroundColor(.green)
.font(Font.system(size: 55)) .font(Font.system(size: 55))
.minimumScaleFactor(0.01) .minimumScaleFactor(0.01)
.lineLimit(1) .lineLimit(1)
.foregroundColor(.green) .foregroundColor(.green)
} }
Text(obj.exercise.name) Text(supersetExecercise.exercise.name)
.id(exerciseIndex)
.font(Font.system(size: 55)) .font(Font.system(size: 55))
.minimumScaleFactor(0.01) .minimumScaleFactor(0.01)
.lineLimit(3) .lineLimit(3)
.padding() .padding()
.id(i) .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: currentExerciseIdx, perform: { newValue in })
}
}
.onChange(of: currentExercise, perform: { newValue in
withAnimation { withAnimation {
proxy.scrollTo(newValue, anchor: .top) proxy.scrollTo(newValue, anchor: .top)
} }
}) })
} }
} }
}
} }
struct ExtCountdownView: View { struct ExtCountdownView: View {
@@ -131,7 +162,7 @@ struct ExtCountdownView: View {
var body: some View { var body: some View {
GeometryReader { metrics in GeometryReader { metrics in
VStack { VStack {
if let currenExercise = bridgeModule.currentExercise { if let currenExercise = bridgeModule.currentExerciseInfo.currentExercise {
HStack { HStack {
Text(currenExercise.exercise.name) Text(currenExercise.exercise.name)
.font(.system(size: 200)) .font(.system(size: 200))
@@ -203,15 +234,15 @@ struct ExtCountdownView: View {
} }
} }
struct ExternalWorkoutDetailView_Previews: PreviewProvider { //struct ExternalWorkoutDetailView_Previews: PreviewProvider {
static var bridge = BridgeModule.shared // static var bridge = BridgeModule.shared
//
static var previews: some View { // static var previews: some View {
ExternalWorkoutDetailView().environmentObject({ () -> BridgeModule in // ExternalWorkoutDetailView().environmentObject({ () -> BridgeModule in
let envObj = BridgeModule.shared // let envObj = BridgeModule.shared
envObj.currentWorkout = nil //PreviewData.workout() // envObj.currentWorkout = nil //PreviewData.workout()
bridge.currentExercise = PreviewData.workout().exercisesSortedByCreated_at.first! // bridge.currentExercise = PreviewData.workout().exercisesSortedByCreated_at.first!
return envObj // return envObj
}() ) // }() )
} // }
} //}

View File

@@ -15,7 +15,7 @@ struct MainView: View {
var body: some View { var body: some View {
ZStack { ZStack {
if let workout = workout { if let workout = workout {
let vm = WorkoutDetailViewModel(workout: workout) let vm = WorkoutDetailViewModel(workout: workout, isPreview: true)
WorkoutDetailView(viewModel: vm) WorkoutDetailView(viewModel: vm)
} else { } else {
Text("no workout selected") Text("no workout selected")

View File

@@ -11,7 +11,7 @@ struct CountdownView: View {
@StateObject var bridgeModule = BridgeModule.shared @StateObject var bridgeModule = BridgeModule.shared
var body: some View { var body: some View {
if let duration = bridgeModule.currentExercise?.duration, if let duration = bridgeModule.currentExerciseInfo.currentExercise?.duration,
duration > 0 { duration > 0 {
HStack { HStack {
if bridgeModule.currentExerciseTimeLeft >= 0 && duration > bridgeModule.currentExerciseTimeLeft { if bridgeModule.currentExerciseTimeLeft >= 0 && duration > bridgeModule.currentExerciseTimeLeft {

View File

@@ -11,16 +11,16 @@ import AVKit
struct ExerciseListView: View { struct ExerciseListView: View {
@AppStorage("thotStyle") private var thotStyle: ThotStyle = .never @AppStorage("thotStyle") private var thotStyle: ThotStyle = .never
@ObservedObject var bridgeModule = BridgeModule.shared @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")!) @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 { didSet {
if let videoURL = VideoURLCreator.videoURL( if let videoURL = VideoURLCreator.videoURL(
thotStyle: thotStyle, thotStyle: thotStyle,
defaultVideoURLStr: self.videoExercise?.videoURL, defaultVideoURLStr: self.videoExercise?.videoURL,
exerciseName: self.videoExercise?.name, exerciseName: self.videoExercise?.name,
workout: bridgeModule.currentWorkout) { workout: bridgeModule.currentExerciseInfo.workout) {
avPlayer = AVPlayer(url: videoURL) avPlayer = AVPlayer(url: videoURL)
avPlayer.play() avPlayer.play()
} }
@@ -28,22 +28,27 @@ struct ExerciseListView: View {
} }
var body: some View { var body: some View {
if let supersets = workout.supersets {
ScrollViewReader { proxy in ScrollViewReader { proxy in
List() { List() {
ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in ForEach(supersets.indices, id: \.self) { supersetIndex in
let obj = workout.exercisesSortedByCreated_at[i] let superset = supersets[supersetIndex]
Section(content: {
ForEach(superset.exercises.indices, id: \.self) { exerciseIndex in
let supersetExecercise = superset.exercises[exerciseIndex]
HStack { HStack {
if i == bridgeModule.currentExerciseIdx { if supersetExecercise.id == bridgeModule.currentExerciseInfo.currentExercise?.id {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.foregroundColor(.green) .foregroundColor(.green)
} }
Text(obj.exercise.name) Text(supersetExecercise.exercise.name)
.id(i) .id(exerciseIndex)
Spacer() Spacer()
if let reps = obj.reps, if let reps = supersetExecercise.reps,
reps > 0 { reps > 0 {
HStack { HStack {
Image(systemName: "number") Image(systemName: "number")
@@ -62,7 +67,7 @@ struct ExerciseListView: View {
.frame(alignment: .trailing) .frame(alignment: .trailing)
} }
if let duration = obj.duration, if let duration = supersetExecercise.duration,
duration > 0 { duration > 0 {
HStack { HStack {
Image(systemName: "stopwatch") Image(systemName: "stopwatch")
@@ -83,18 +88,15 @@ struct ExerciseListView: View {
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
if bridgeModule.isInWorkout { if bridgeModule.isInWorkout {
bridgeModule.goToExerciseAt(index: i) bridgeModule.goToExerciseAt(section: supersetIndex, row: exerciseIndex)
} else { } else {
videoExercise = obj.exercise // videoExercise = obj.exercise
} }
} // .onChange(of: bridgeModule.currentExerciseIdx, perform: { newValue in
} // withAnimation {
} // proxy.scrollTo(newValue, anchor: .top)
.onChange(of: bridgeModule.currentExerciseIdx, perform: { newValue in // }
withAnimation { // })
proxy.scrollTo(newValue, anchor: .top)
}
})
} }
.sheet(item: $videoExercise) { exercise in .sheet(item: $videoExercise) { exercise in
PlayerView(player: $avPlayer) PlayerView(player: $avPlayer)
@@ -103,6 +105,22 @@ struct ExerciseListView: View {
} }
} }
} }
}, header: {
HStack {
Text(superset.name ?? "--")
.foregroundColor(Color("appColor"))
.bold()
Spacer()
Text("\(superset.rounds) rounds")
.foregroundColor(Color("appColor"))
.bold()
}
})
}
}
}
}
}
} }
struct ExerciseListView_Previews: PreviewProvider { struct ExerciseListView_Previews: PreviewProvider {

View File

@@ -23,7 +23,6 @@ struct WorkoutDetailView: View {
@State var presentedSheet: Sheet? @State var presentedSheet: Sheet?
@State var workoutToPlan: Workout? @State var workoutToPlan: Workout?
var showAddToCalendar = true
var body: some View { var body: some View {
ZStack { ZStack {
@@ -80,7 +79,7 @@ struct WorkoutDetailView: View {
bridgeModule.completeWorkout() bridgeModule.completeWorkout()
}, planWorkout: { workout in }, planWorkout: { workout in
workoutToPlan = workout workoutToPlan = workout
}, workout: workout, showAddToCalendar: showAddToCalendar) }, workout: workout, showAddToCalendar: viewModel.isPreview)
.frame(height: 44) .frame(height: 44)
} }
@@ -101,25 +100,29 @@ struct WorkoutDetailView: View {
} }
} }
} }
.onChange(of: bridgeModule.currentExercise, perform: { newValue in .onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
if let videoURL = VideoURLCreator.videoURL( if let videoURL = VideoURLCreator.videoURL(
thotStyle: thotStyle, thotStyle: thotStyle,
defaultVideoURLStr: newValue?.exercise.videoURL, defaultVideoURLStr: currentExtercise.exercise.videoURL,
exerciseName: newValue?.exercise.name, exerciseName: currentExtercise.exercise.name,
workout: bridgeModule.currentWorkout) { workout: bridgeModule.currentExerciseInfo.workout) {
avPlayer = AVPlayer(url: videoURL) avPlayer = AVPlayer(url: videoURL)
avPlayer.play() avPlayer.play()
} }
}
}) })
.onAppear{ .onAppear{
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
if let videoURL = VideoURLCreator.videoURL( if let videoURL = VideoURLCreator.videoURL(
thotStyle: thotStyle, thotStyle: thotStyle,
defaultVideoURLStr: bridgeModule.currentExercise?.exercise.videoURL, defaultVideoURLStr: currentExtercise.exercise.videoURL,
exerciseName: bridgeModule.currentExercise?.exercise.name, exerciseName: currentExtercise.exercise.name,
workout: bridgeModule.currentWorkout) { workout: bridgeModule.currentExerciseInfo.workout) {
avPlayer = AVPlayer(url: videoURL) avPlayer = AVPlayer(url: videoURL)
avPlayer.play() avPlayer.play()
} }
}
bridgeModule.completedWorkout = { bridgeModule.completedWorkout = {
if let workoutData = createWorkoutData() { if let workoutData = createWorkoutData() {
@@ -132,7 +135,7 @@ struct WorkoutDetailView: View {
} }
func createWorkoutData() -> [String:Any]? { func createWorkoutData() -> [String:Any]? {
guard let workoutid = bridgeModule.currentWorkout?.id, guard let workoutid = bridgeModule.currentExerciseInfo.workout?.id,
let startTime = bridgeModule.workoutStartDate?.timeFormatForUpload, let startTime = bridgeModule.workoutStartDate?.timeFormatForUpload,
let endTime = bridgeModule.workoutEndDate?.timeFormatForUpload else { let endTime = bridgeModule.workoutEndDate?.timeFormatForUpload else {
return nil return nil
@@ -154,6 +157,6 @@ struct WorkoutDetailView: View {
struct WorkoutDetailView_Previews: PreviewProvider { struct WorkoutDetailView_Previews: PreviewProvider {
static let workoutDetail = PreviewData.workout() static let workoutDetail = PreviewData.workout()
static var previews: some View { 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 loading
case showWorkout(Workout) 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.status = .loading
self.isPreview = isPreview
if let passedStatus = status { if let passedStatus = status {
self.status = passedStatus self.status = passedStatus

View File

@@ -79,8 +79,8 @@ struct WorkoutHistoryView: View {
} }
} }
.sheet(item: $selectedPlannedWorkout) { item in .sheet(item: $selectedPlannedWorkout) { item in
let viewModel = WorkoutDetailViewModel(workout: item) let viewModel = WorkoutDetailViewModel(workout: item, isPreview: true)
WorkoutDetailView(viewModel: viewModel, showAddToCalendar: true) WorkoutDetailView(viewModel: viewModel)
} }
} }
} }