WIP
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct SupersetExercise: Identifiable, Codable, Equatable, Hashable {
|
struct SupersetExercise: Identifiable, Codable, Equatable, Hashable {
|
||||||
var id = UUID()
|
var id: Int
|
||||||
|
|
||||||
let workout: Int?
|
let workout: Int?
|
||||||
let exercise: Exercise
|
let exercise: Exercise
|
||||||
@@ -19,12 +19,15 @@ struct SupersetExercise: Identifiable, Codable, Equatable, Hashable {
|
|||||||
let weightAudio: String?
|
let weightAudio: String?
|
||||||
let createdAt: String
|
let createdAt: String
|
||||||
let order, superset: Int
|
let order, superset: Int
|
||||||
|
let uniqueID: String
|
||||||
|
let description: String?
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case workout, exercise, weight, reps, duration, order, superset
|
case workout, exercise, weight, reps, duration, order, superset, id, description
|
||||||
case durationAudio = "duration_audio"
|
case durationAudio = "duration_audio"
|
||||||
case weightAudio = "weight_audio"
|
case weightAudio = "weight_audio"
|
||||||
case createdAt = "created_at"
|
case createdAt = "created_at"
|
||||||
|
case uniqueID = "unique_id"
|
||||||
}
|
}
|
||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
@@ -67,4 +70,13 @@ struct Exercise: Identifiable, Codable, Equatable {
|
|||||||
case muscleGroups = "muscle_groups"
|
case muscleGroups = "muscle_groups"
|
||||||
case synonyms
|
case synonyms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extName: String {
|
||||||
|
if side.count > 0 {
|
||||||
|
var returnString = name + " - " + side
|
||||||
|
returnString = returnString.replacingOccurrences(of: "_", with: " ")
|
||||||
|
return returnString.capitalized
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.local.rawValue
|
return BaseURLs.dev.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,12 +133,8 @@ class BridgeModule: NSObject, ObservableObject {
|
|||||||
if currentExerciseTimeLeft > 0 {
|
if currentExerciseTimeLeft > 0 {
|
||||||
currentExerciseTimeLeft -= 1
|
currentExerciseTimeLeft -= 1
|
||||||
|
|
||||||
if currentExerciseTimeLeft == 0 {
|
if currentExerciseTimeLeft <= 3 {
|
||||||
playFinished()
|
playBeep()
|
||||||
} else {
|
|
||||||
if currentExerciseTimeLeft <= 3 {
|
|
||||||
playBeep()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sendCurrentExerciseToWatch()
|
sendCurrentExerciseToWatch()
|
||||||
} else {
|
} else {
|
||||||
@@ -158,6 +154,7 @@ class BridgeModule: NSObject, ObservableObject {
|
|||||||
func nextExercise() {
|
func nextExercise() {
|
||||||
if let nextSupersetExercise = currentExerciseInfo.nextExercise {
|
if let nextSupersetExercise = currentExerciseInfo.nextExercise {
|
||||||
updateCurrent(exercise: nextSupersetExercise)
|
updateCurrent(exercise: nextSupersetExercise)
|
||||||
|
playFinished()
|
||||||
} else {
|
} else {
|
||||||
completeWorkout()
|
completeWorkout()
|
||||||
}
|
}
|
||||||
@@ -196,6 +193,7 @@ class BridgeModule: NSObject, ObservableObject {
|
|||||||
func completeWorkout() {
|
func completeWorkout() {
|
||||||
self.currentExerciseTimer?.invalidate()
|
self.currentExerciseTimer?.invalidate()
|
||||||
self.currentExerciseTimer = nil
|
self.currentExerciseTimer = nil
|
||||||
|
self.isInWorkout = false
|
||||||
|
|
||||||
workoutEndDate = Date()
|
workoutEndDate = Date()
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,13 @@ class CurrentWorkoutInfo {
|
|||||||
|
|
||||||
var currentExercise: SupersetExercise? {
|
var currentExercise: SupersetExercise? {
|
||||||
guard let supersets = workout?.supersets else { return nil }
|
guard let supersets = workout?.supersets else { return nil }
|
||||||
|
|
||||||
|
if supersetIndex >= supersets.count { return nil }
|
||||||
let superset = supersets[supersetIndex]
|
let superset = supersets[supersetIndex]
|
||||||
|
|
||||||
|
// will be -1 for a moment while going to previous workout / superset
|
||||||
|
if exerciseIndex < 0 { return nil }
|
||||||
|
if exerciseIndex > superset.exercises.count { return nil }
|
||||||
let exercise = superset.exercises[exerciseIndex]
|
let exercise = superset.exercises[exerciseIndex]
|
||||||
return exercise
|
return exercise
|
||||||
}
|
}
|
||||||
@@ -62,12 +68,18 @@ class CurrentWorkoutInfo {
|
|||||||
if exerciseIndex < 0 {
|
if exerciseIndex < 0 {
|
||||||
if currentRound > 1 {
|
if currentRound > 1 {
|
||||||
currentRound -= 1
|
currentRound -= 1
|
||||||
|
let superset = supersets[supersetIndex]
|
||||||
|
exerciseIndex = superset.exercises.count-1
|
||||||
} else {
|
} else {
|
||||||
if supersetIndex > 1 {
|
if supersetIndex > 0 {
|
||||||
supersetIndex -= 1
|
supersetIndex -= 1
|
||||||
|
let superset = supersets[supersetIndex]
|
||||||
|
currentRound = superset.rounds
|
||||||
|
exerciseIndex = superset.exercises.count-1
|
||||||
|
} else {
|
||||||
|
exerciseIndex = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exerciseIndex = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let superset = supersets[supersetIndex]
|
let superset = supersets[supersetIndex]
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ struct AccountView: View {
|
|||||||
@State var showCompletedWorkouts: Bool = false
|
@State var showCompletedWorkouts: Bool = false
|
||||||
@AppStorage(Constants.phoneThotStyle) private var phoneThotStyle: ThotStyle = .never
|
@AppStorage(Constants.phoneThotStyle) private var phoneThotStyle: ThotStyle = .never
|
||||||
@AppStorage(Constants.extThotStyle) private var extThotStyle: ThotStyle = .never
|
@AppStorage(Constants.extThotStyle) private var extThotStyle: ThotStyle = .never
|
||||||
|
@AppStorage(Constants.extShowBothVideos) private var extShowBothVideos: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@@ -63,26 +64,36 @@ struct AccountView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
Text("Phone THOT Style:")
|
Group {
|
||||||
Picker("Phone THOT Style:", selection: $phoneThotStyle) {
|
Text("Phone THOT Style:")
|
||||||
ForEach(ThotStyle.allCases, id: \.self) { style in
|
Picker("Phone THOT Style:", selection: $phoneThotStyle) {
|
||||||
Text(style.stringValue())
|
ForEach(ThotStyle.allCases, id: \.self) { style in
|
||||||
.tag(phoneThotStyle.rawValue)
|
Text(style.stringValue())
|
||||||
|
.tag(phoneThotStyle.rawValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.pickerStyle(.segmented)
|
||||||
.pickerStyle(.segmented)
|
|
||||||
|
Divider()
|
||||||
Divider()
|
|
||||||
|
Text("External THOT Style:")
|
||||||
Text("External THOT Style:")
|
Picker("External THOT Style:", selection: $extThotStyle) {
|
||||||
Picker("External THOT Style:", selection: $extThotStyle) {
|
ForEach(ThotStyle.allCases, id: \.self) { style in
|
||||||
ForEach(ThotStyle.allCases, id: \.self) { style in
|
Text(style.stringValue())
|
||||||
Text(style.stringValue())
|
.tag(extThotStyle.rawValue)
|
||||||
.tag(extThotStyle.rawValue)
|
}
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.segmented)
|
||||||
}
|
}
|
||||||
.pickerStyle(.segmented)
|
|
||||||
|
Group {
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
Toggle(isOn: $extShowBothVideos, label: {
|
||||||
|
Text("Show both videos on external")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button("Logout", action: {
|
Button("Logout", action: {
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import AVKit
|
|||||||
struct ExternalWorkoutDetailView: View {
|
struct ExternalWorkoutDetailView: View {
|
||||||
@StateObject var bridgeModule = BridgeModule.shared
|
@StateObject var bridgeModule = BridgeModule.shared
|
||||||
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
||||||
|
@State var smallAVPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
||||||
@AppStorage(Constants.extThotStyle) private var extThotStyle: ThotStyle = .never
|
@AppStorage(Constants.extThotStyle) private var extThotStyle: ThotStyle = .never
|
||||||
|
@AppStorage(Constants.extShowBothVideos) private var extShowBothVideos: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let workout = bridgeModule.currentExerciseInfo.workout,
|
if let workout = bridgeModule.currentExerciseInfo.workout,
|
||||||
@@ -20,11 +22,13 @@ struct ExternalWorkoutDetailView: View {
|
|||||||
GeometryReader { metrics in
|
GeometryReader { metrics in
|
||||||
VStack {
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
PlayerView(player: $avPlayer)
|
if extThotStyle != .off {
|
||||||
.frame(width: metrics.size.width * 0.5, height: metrics.size.height * 0.8)
|
PlayerView(player: $avPlayer)
|
||||||
.onAppear{
|
.frame(width: metrics.size.width * 0.5, height: metrics.size.height * 0.8)
|
||||||
avPlayer.play()
|
.onAppear{
|
||||||
}
|
avPlayer.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
ExtExerciseList(workout: workout,
|
ExtExerciseList(workout: workout,
|
||||||
@@ -42,9 +46,25 @@ struct ExternalWorkoutDetailView: View {
|
|||||||
.frame(width: metrics.size.width * 0.4, height: metrics.size.height * 0.8)
|
.frame(width: metrics.size.width * 0.4, height: metrics.size.height * 0.8)
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtCountdownView()
|
HStack {
|
||||||
.frame(width: metrics.size.width-50, height: metrics.size.height * 0.2)
|
if extShowBothVideos && extThotStyle != .off {
|
||||||
.padding([.leading, .trailing], 50)
|
ExtCountdownView()
|
||||||
|
.frame(width: metrics.size.width * 0.8, height: metrics.size.height * 0.2)
|
||||||
|
.padding(.leading, 50)
|
||||||
|
.padding(.trailing, 5)
|
||||||
|
|
||||||
|
PlayerView(player: $smallAVPlayer)
|
||||||
|
.frame(width: metrics.size.width * 0.2, height: metrics.size.height * 0.2)
|
||||||
|
.padding(.trailing, 50)
|
||||||
|
.onAppear{
|
||||||
|
avPlayer.play()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ExtCountdownView()
|
||||||
|
.frame(width: metrics.size.width-50, height: metrics.size.height * 0.2)
|
||||||
|
.padding([.leading, .trailing], 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -54,6 +74,29 @@ struct ExternalWorkoutDetailView: View {
|
|||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onChange(of: bridgeModule.isInWorkout, perform: { newValue in
|
||||||
|
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||||
|
if let videoURL = VideoURLCreator.videoURL(
|
||||||
|
thotStyle: extThotStyle,
|
||||||
|
defaultVideoURLStr: currentExtercise.exercise.videoURL,
|
||||||
|
exerciseName: currentExtercise.exercise.name,
|
||||||
|
workout: bridgeModule.currentExerciseInfo.workout) {
|
||||||
|
avPlayer = AVPlayer(url: videoURL)
|
||||||
|
avPlayer.play()
|
||||||
|
|
||||||
|
if extShowBothVideos {
|
||||||
|
if let smallVideoURL = VideoURLCreator.videoURL(
|
||||||
|
thotStyle: VideoURLCreator.otherVideoType(forVideoURL: videoURL),
|
||||||
|
defaultVideoURLStr: currentExtercise.exercise.videoURL,
|
||||||
|
exerciseName: currentExtercise.exercise.name,
|
||||||
|
workout: bridgeModule.currentExerciseInfo.workout) {
|
||||||
|
smallAVPlayer = AVPlayer(url: smallVideoURL)
|
||||||
|
smallAVPlayer.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in
|
.onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in
|
||||||
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
|
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||||
if let videoURL = VideoURLCreator.videoURL(
|
if let videoURL = VideoURLCreator.videoURL(
|
||||||
@@ -63,11 +106,27 @@ struct ExternalWorkoutDetailView: View {
|
|||||||
workout: bridgeModule.currentExerciseInfo.workout) {
|
workout: bridgeModule.currentExerciseInfo.workout) {
|
||||||
avPlayer = AVPlayer(url: videoURL)
|
avPlayer = AVPlayer(url: videoURL)
|
||||||
avPlayer.play()
|
avPlayer.play()
|
||||||
|
|
||||||
|
if extShowBothVideos {
|
||||||
|
if let smallVideoURL = VideoURLCreator.videoURL(
|
||||||
|
thotStyle: VideoURLCreator.otherVideoType(forVideoURL: videoURL),
|
||||||
|
defaultVideoURLStr: currentExtercise.exercise.videoURL,
|
||||||
|
exerciseName: currentExtercise.exercise.name,
|
||||||
|
workout: bridgeModule.currentExerciseInfo.workout) {
|
||||||
|
smallAVPlayer = AVPlayer(url: smallVideoURL)
|
||||||
|
smallAVPlayer.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.background(bridgeModule.currentExerciseInfo.workout == 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))
|
||||||
|
.onReceive(NotificationCenter.default.publisher(
|
||||||
|
for: UIScene.willEnterForegroundNotification)) { _ in
|
||||||
|
avPlayer.play()
|
||||||
|
smallAVPlayer.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,17 +175,16 @@ struct ExtExerciseList: View {
|
|||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.foregroundColor(.green)
|
.foregroundColor(.green)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(supersetExecercise.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(exerciseIndex)
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.id(supersetExecercise.id)
|
||||||
}
|
}
|
||||||
}, header: {
|
}, header: {
|
||||||
HStack {
|
HStack {
|
||||||
@@ -148,7 +206,8 @@ struct ExtExerciseList: View {
|
|||||||
}
|
}
|
||||||
.onChange(of: currentExercise, perform: { newValue in
|
.onChange(of: currentExercise, perform: { newValue in
|
||||||
withAnimation {
|
withAnimation {
|
||||||
proxy.scrollTo(newValue, anchor: .top)
|
print(newValue.id)
|
||||||
|
proxy.scrollTo(newValue.id, anchor: .top)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -164,22 +223,13 @@ struct ExtCountdownView: View {
|
|||||||
VStack {
|
VStack {
|
||||||
if let currenExercise = bridgeModule.currentExerciseInfo.currentExercise {
|
if let currenExercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||||
HStack {
|
HStack {
|
||||||
Text(currenExercise.exercise.name)
|
Text(currenExercise.exercise.extName)
|
||||||
.font(.system(size: 200))
|
.font(.system(size: 200))
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.minimumScaleFactor(0.01)
|
.minimumScaleFactor(0.01)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
if currenExercise.exercise.side.count > 0 {
|
|
||||||
Text(" - " + currenExercise.exercise.side)
|
|
||||||
.font(.system(size: 200))
|
|
||||||
.scaledToFit()
|
|
||||||
.minimumScaleFactor(0.01)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bridgeModule.currentWorkoutRunTimeInSeconds > -1 {
|
if bridgeModule.currentWorkoutRunTimeInSeconds > -1 {
|
||||||
Text("\(Double(bridgeModule.currentWorkoutRunTimeInSeconds).asString(style: .positional))")
|
Text("\(Double(bridgeModule.currentWorkoutRunTimeInSeconds).asString(style: .positional))")
|
||||||
.font(Font.system(size: 100))
|
.font(Font.system(size: 100))
|
||||||
|
|||||||
@@ -82,6 +82,14 @@ struct PlayerView: UIViewRepresentable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoURLCreator {
|
class VideoURLCreator {
|
||||||
|
class func otherVideoType(forVideoURL videoURL: URL) -> ThotStyle {
|
||||||
|
var otherVideoStyle = ThotStyle.never
|
||||||
|
if videoURL.absoluteString.contains("exercise_videos") {
|
||||||
|
otherVideoStyle = .always
|
||||||
|
}
|
||||||
|
return otherVideoStyle
|
||||||
|
}
|
||||||
|
|
||||||
class func videoURL(thotStyle: ThotStyle, defaultVideoURLStr: String?, exerciseName: String?, workout: Workout?) -> URL? {
|
class func videoURL(thotStyle: ThotStyle, defaultVideoURLStr: String?, exerciseName: String?, workout: Workout?) -> URL? {
|
||||||
var urlString: String?
|
var urlString: String?
|
||||||
|
|
||||||
@@ -102,6 +110,8 @@ class VideoURLCreator {
|
|||||||
} else {
|
} else {
|
||||||
urlString = defaultVideoURLStr
|
urlString = defaultVideoURLStr
|
||||||
}
|
}
|
||||||
|
case .off:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let urlString = urlString,
|
if let urlString = urlString,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ enum ThotStyle: Int, CaseIterable {
|
|||||||
case never = 2
|
case never = 2
|
||||||
case recovery = 3
|
case recovery = 3
|
||||||
case random = 4
|
case random = 4
|
||||||
|
case off
|
||||||
|
|
||||||
func stringValue() -> String {
|
func stringValue() -> String {
|
||||||
switch(self) {
|
switch(self) {
|
||||||
@@ -23,6 +24,8 @@ enum ThotStyle: Int, CaseIterable {
|
|||||||
return "Recovery"
|
return "Recovery"
|
||||||
case .random:
|
case .random:
|
||||||
return "Random"
|
return "Random"
|
||||||
|
case .off:
|
||||||
|
return "Off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ struct ExerciseListView: View {
|
|||||||
@ObservedObject var bridgeModule = BridgeModule.shared
|
@ObservedObject var bridgeModule = BridgeModule.shared
|
||||||
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
||||||
var workout: Workout
|
var workout: Workout
|
||||||
|
@Binding var showExecersizeInfo: Bool
|
||||||
|
|
||||||
@State var videoExercise: Exercise? {
|
@State var videoExercise: Exercise? {
|
||||||
didSet {
|
didSet {
|
||||||
@@ -36,74 +37,72 @@ struct ExerciseListView: View {
|
|||||||
Section(content: {
|
Section(content: {
|
||||||
ForEach(superset.exercises.indices, id: \.self) { exerciseIndex in
|
ForEach(superset.exercises.indices, id: \.self) { exerciseIndex in
|
||||||
let supersetExecercise = superset.exercises[exerciseIndex]
|
let supersetExecercise = superset.exercises[exerciseIndex]
|
||||||
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
if supersetExecercise.id == bridgeModule.currentExerciseInfo.currentExercise?.id {
|
if bridgeModule.isInWorkout &&
|
||||||
Image(systemName: "checkmark")
|
supersetIndex == bridgeModule.currentExerciseInfo.supersetIndex &&
|
||||||
.foregroundColor(.green)
|
exerciseIndex == bridgeModule.currentExerciseInfo.exerciseIndex {
|
||||||
}
|
Image(systemName: "checkmark")
|
||||||
|
.foregroundColor(.green)
|
||||||
Text(supersetExecercise.exercise.name)
|
}
|
||||||
.id(exerciseIndex)
|
|
||||||
|
Text(supersetExecercise.exercise.extName)
|
||||||
Spacer()
|
|
||||||
|
Spacer()
|
||||||
if let reps = supersetExecercise.reps,
|
|
||||||
reps > 0 {
|
if let reps = supersetExecercise.reps,
|
||||||
HStack {
|
reps > 0 {
|
||||||
Image(systemName: "number")
|
HStack {
|
||||||
.foregroundColor(.white)
|
Image(systemName: "number")
|
||||||
.frame(width: 20, alignment: .leading)
|
.foregroundColor(.white)
|
||||||
Text("\(reps)")
|
.frame(width: 20, alignment: .leading)
|
||||||
.foregroundColor(.white)
|
Text("\(reps)")
|
||||||
.frame(width: 30, alignment: .trailing)
|
.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 = supersetExecercise.exercise
|
||||||
}
|
}
|
||||||
.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,
|
if bridgeModule.isInWorkout &&
|
||||||
duration > 0 {
|
supersetIndex == bridgeModule.currentExerciseInfo.supersetIndex &&
|
||||||
HStack {
|
exerciseIndex == bridgeModule.currentExerciseInfo.exerciseIndex &&
|
||||||
Image(systemName: "stopwatch")
|
showExecersizeInfo {
|
||||||
.foregroundColor(.white)
|
detailView(forExercise: supersetExecercise)
|
||||||
.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])
|
|
||||||
}
|
}
|
||||||
}
|
}.id(supersetExecercise.id)
|
||||||
.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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, header: {
|
}, header: {
|
||||||
HStack {
|
HStack {
|
||||||
@@ -118,13 +117,36 @@ struct ExerciseListView: View {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onChange(of: bridgeModule.currentExerciseInfo.exerciseIndex, perform: { newValue in
|
||||||
|
if let newCurrentExercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||||
|
withAnimation {
|
||||||
|
proxy.scrollTo(newCurrentExercise.id, anchor: .top)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sheet(item: $videoExercise) { exercise in
|
||||||
|
PlayerView(player: $avPlayer)
|
||||||
|
.onAppear{
|
||||||
|
avPlayer.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func detailView(forExercise supersetExecercise: SupersetExercise) -> some View {
|
||||||
|
VStack {
|
||||||
|
Text(supersetExecercise.exercise.description)
|
||||||
|
.frame(alignment: .leading)
|
||||||
|
Divider()
|
||||||
|
Text(supersetExecercise.exercise.muscles.map({ $0.name }).joined(separator: ", "))
|
||||||
|
.frame(alignment: .leading)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExerciseListView_Previews: PreviewProvider {
|
//struct ExerciseListView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
// static var previews: some View {
|
||||||
ExerciseListView(workout: PreviewData.workout())
|
// ExerciseListView(workout: PreviewData.workout(), showExecersizeInfo: )
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ struct WorkoutDetailView: View {
|
|||||||
|
|
||||||
@State var presentedSheet: Sheet?
|
@State var presentedSheet: Sheet?
|
||||||
@State var workoutToPlan: Workout?
|
@State var workoutToPlan: Workout?
|
||||||
|
@State var showExecersizeInfo: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
@@ -39,33 +40,47 @@ struct WorkoutDetailView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
GeometryReader { metrics in
|
if phoneThotStyle != .off {
|
||||||
ZStack {
|
GeometryReader { metrics in
|
||||||
PlayerView(player: $avPlayer)
|
ZStack {
|
||||||
.frame(width: metrics.size.width * 1, height: metrics.size.height * 1)
|
|
||||||
.onAppear{
|
PlayerView(player: $avPlayer)
|
||||||
avPlayer.play()
|
.frame(width: metrics.size.width * 1, height: metrics.size.height * 1)
|
||||||
}
|
.onAppear{
|
||||||
|
avPlayer.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
if let assetURL = ((avPlayer.currentItem?.asset) as? AVURLAsset)?.url {
|
if let assetURL = ((avPlayer.currentItem?.asset) as? AVURLAsset)?.url,
|
||||||
if assetURL.absoluteString.lowercased().contains("exercise_videos") {
|
let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise,
|
||||||
|
let otherVideoURL = VideoURLCreator.videoURL(
|
||||||
} else {
|
thotStyle: VideoURLCreator.otherVideoType(forVideoURL: assetURL),
|
||||||
|
defaultVideoURLStr: currentExtercise.exercise.videoURL,
|
||||||
}
|
exerciseName: currentExtercise.exercise.name,
|
||||||
// avPlayer = AVPlayer(url: videoURL)
|
workout: bridgeModule.currentExerciseInfo.workout) {
|
||||||
// avPlayer.play()
|
avPlayer = AVPlayer(url: otherVideoURL)
|
||||||
|
avPlayer.play()
|
||||||
}
|
}
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("Toggle THOT")
|
Image(systemName: "arrow.triangle.2.circlepath.camera.fill")
|
||||||
.padding()
|
.frame(width: 44, height: 44)
|
||||||
})
|
})
|
||||||
.background(.yellow)
|
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
.cornerRadius(4)
|
.cornerRadius(4)
|
||||||
.frame(width: 160, height: 60)
|
.frame(width: 160, height: 120)
|
||||||
.position(x: metrics.size.width - 80, y: metrics.size.height - 30)
|
.position(x: metrics.size.width - 22, y: metrics.size.height - 30)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
showExecersizeInfo.toggle()
|
||||||
|
}, label: {
|
||||||
|
Image(systemName: "info.circle.fill")
|
||||||
|
.frame(width: 44, height: 44)
|
||||||
|
})
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
.cornerRadius(4)
|
||||||
|
.frame(width: 120, height: 120)
|
||||||
|
.position(x: 22, y: metrics.size.height - 30)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +88,7 @@ struct WorkoutDetailView: View {
|
|||||||
InfoView(workout: workout)
|
InfoView(workout: workout)
|
||||||
.padding(.bottom)
|
.padding(.bottom)
|
||||||
|
|
||||||
ExerciseListView(workout: workout)
|
ExerciseListView(workout: workout, showExecersizeInfo: $showExecersizeInfo)
|
||||||
|
|
||||||
ActionsView(completedWorkout: {
|
ActionsView(completedWorkout: {
|
||||||
bridgeModule.completeWorkout()
|
bridgeModule.completeWorkout()
|
||||||
@@ -112,6 +127,18 @@ struct WorkoutDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.onChange(of: bridgeModule.isInWorkout, perform: { newValue in
|
||||||
|
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||||
|
if let videoURL = VideoURLCreator.videoURL(
|
||||||
|
thotStyle: phoneThotStyle,
|
||||||
|
defaultVideoURLStr: currentExtercise.exercise.videoURL,
|
||||||
|
exerciseName: currentExtercise.exercise.name,
|
||||||
|
workout: bridgeModule.currentExerciseInfo.workout) {
|
||||||
|
avPlayer = AVPlayer(url: videoURL)
|
||||||
|
avPlayer.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.onAppear{
|
.onAppear{
|
||||||
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
|
if let currentExtercise = bridgeModule.currentExerciseInfo.currentExercise {
|
||||||
if let videoURL = VideoURLCreator.videoURL(
|
if let videoURL = VideoURLCreator.videoURL(
|
||||||
@@ -131,7 +158,10 @@ struct WorkoutDetailView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onReceive(NotificationCenter.default.publisher(
|
||||||
|
for: UIScene.willEnterForegroundNotification)) { _ in
|
||||||
|
avPlayer.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createWorkoutData() -> [String:Any]? {
|
func createWorkoutData() -> [String:Any]? {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import AVKit
|
|||||||
struct Constants {
|
struct Constants {
|
||||||
static let phoneThotStyle = "phoneThotStyle"
|
static let phoneThotStyle = "phoneThotStyle"
|
||||||
static let extThotStyle = "extThotStyle"
|
static let extThotStyle = "extThotStyle"
|
||||||
|
static let extShowBothVideos = "extShowBothVideos"
|
||||||
}
|
}
|
||||||
|
|
||||||
@main
|
@main
|
||||||
|
|||||||
@@ -15,18 +15,17 @@ struct MainWatchView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
if vm.isInWorkout {
|
if vm.isInWorkout {
|
||||||
Text(vm.watchPackageModel.currentExerciseName)
|
Text(vm.watchPackageModel.currentExerciseName)
|
||||||
.font(Font.system(size: 55))
|
.font(.body)
|
||||||
.scaledToFit()
|
|
||||||
.minimumScaleFactor(0.01)
|
|
||||||
.lineLimit(1)
|
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(10)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
Divider()
|
Divider()
|
||||||
Text("\(vm.watchPackageModel.currentTimeLeft )")
|
Text("\(vm.watchPackageModel.currentTimeLeft )")
|
||||||
.font(Font.system(size: 55))
|
.font(.title)
|
||||||
.scaledToFit()
|
|
||||||
.minimumScaleFactor(0.01)
|
|
||||||
.lineLimit(1)
|
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(10)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user