WIP
This commit is contained in:
@@ -33,7 +33,7 @@ struct ExerciseExercise: Codable, Hashable, Identifiable {
|
||||
let id: Int
|
||||
let muscles: [ExerciseMuscle]
|
||||
let equipment: [ExerciseEquipment]
|
||||
let audioURL, videoURL, createdAt, updatedAt: String
|
||||
let audioURL, videoURL, createdAt, updatedAt, nsfwVideoURL: String
|
||||
let name, description, side: String
|
||||
let isTwoDumbbells, isTrackableDistance, isAlternating, isWeight: Bool
|
||||
let isDistance, isDuration, isReps: Bool
|
||||
@@ -59,5 +59,6 @@ struct ExerciseExercise: Codable, Hashable, Identifiable {
|
||||
case equipmentRequired = "equipment_required"
|
||||
case muscleGroups = "muscle_groups"
|
||||
case synonyms
|
||||
case nsfwVideoURL = "nsfw_video_url"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
import WatchConnectivity
|
||||
import AVFoundation
|
||||
|
||||
enum WatchActions: Codable {
|
||||
case nextExercise
|
||||
@@ -125,6 +126,14 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date())
|
||||
let data = try! JSONEncoder().encode(watchModel)
|
||||
send(data)
|
||||
|
||||
if currentExerciseTimeLeft == 0 {
|
||||
playFinished()
|
||||
} else {
|
||||
if currentExerciseTimeLeft < 4 {
|
||||
playBeep()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nextExercise()
|
||||
}
|
||||
@@ -166,6 +175,18 @@ class BridgeModule: NSObject, ObservableObject {
|
||||
completedWorkout?()
|
||||
}
|
||||
}
|
||||
|
||||
func playBeep() {
|
||||
#if os(iOS)
|
||||
AudioServicesPlaySystemSound(SystemSoundID(1052))
|
||||
#endif
|
||||
}
|
||||
|
||||
func playFinished() {
|
||||
#if os(iOS)
|
||||
AudioServicesPlaySystemSound(SystemSoundID(1070))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension BridgeModule: WCSessionDelegate {
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
workout history view
|
||||
-calorie upload
|
||||
|
||||
video view on iphone during workout
|
||||
-video view on iphone during workout
|
||||
|
||||
edit weights on workouts
|
||||
|
||||
@@ -12,6 +12,7 @@ struct AccountView: View {
|
||||
@State var completedWorkouts: [CompletedWorkout]?
|
||||
@ObservedObject var userStore = UserStore.shared
|
||||
@State var showCompletedWorkouts: Bool = false
|
||||
@AppStorage("showNSFWVideos") private var showNSFWVideos = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
@@ -60,6 +61,8 @@ struct AccountView: View {
|
||||
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
Toggle("Show NSFW Videos", isOn: $showNSFWVideos)
|
||||
Spacer()
|
||||
|
||||
Button("Logout", action: {
|
||||
|
||||
@@ -11,7 +11,8 @@ import AVKit
|
||||
struct ExternalWorkoutDetailView: View {
|
||||
@StateObject var bridgeModule = BridgeModule.shared
|
||||
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
||||
|
||||
@AppStorage("showNSFWVideos") private var showNSFWVideos = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let workout = bridgeModule.currentWorkout {
|
||||
@@ -23,7 +24,7 @@ struct ExternalWorkoutDetailView: View {
|
||||
|
||||
ExtExerciseList(workout: workout,
|
||||
currentExerciseIdx: bridgeModule.currentExerciseIdx)
|
||||
.frame(width: metrics.size.width * 0.4, height: metrics.size.height * 0.8)
|
||||
.frame(width: metrics.size.width * 0.3, height: metrics.size.height * 0.8)
|
||||
|
||||
}
|
||||
|
||||
@@ -38,9 +39,16 @@ struct ExternalWorkoutDetailView: View {
|
||||
}
|
||||
}
|
||||
.onChange(of: bridgeModule.currentExercise, perform: { newValue in
|
||||
if let viddd = newValue?.exercise.videoURL,
|
||||
let url = URL(string: BaseURLs.currentBaseURL + viddd) {
|
||||
avPlayer = AVPlayer(url: url)
|
||||
if showNSFWVideos {
|
||||
if let viddd = newValue?.exercise.nsfwVideoURL,
|
||||
let url = URL(string: BaseURLs.currentBaseURL + viddd) {
|
||||
avPlayer = AVPlayer(url: url)
|
||||
}
|
||||
} else {
|
||||
if let viddd = newValue?.exercise.videoURL,
|
||||
let url = URL(string: BaseURLs.currentBaseURL + viddd) {
|
||||
avPlayer = AVPlayer(url: url)
|
||||
}
|
||||
}
|
||||
})
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
@@ -74,21 +82,35 @@ struct ExtExerciseList: View {
|
||||
var currentExerciseIdx: Int
|
||||
|
||||
var body: some View {
|
||||
List() {
|
||||
ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in
|
||||
let obj = workout.exercisesSortedByCreated_at[i]
|
||||
HStack {
|
||||
if i == currentExerciseIdx {
|
||||
Image(systemName: "checkmark")
|
||||
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: 75))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
|
||||
Text(obj.exercise.name)
|
||||
.font(Font.system(size: 75))
|
||||
.foregroundColor(.green)
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.padding()
|
||||
.id(i)
|
||||
}
|
||||
|
||||
Text(obj.exercise.name)
|
||||
.font(Font.system(size: 75))
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.onChange(of: currentExerciseIdx, perform: { newValue in
|
||||
withAnimation {
|
||||
proxy.scrollTo(newValue, anchor: .top)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,32 +119,49 @@ struct ExtCountdownView: View {
|
||||
@StateObject var bridgeModule = BridgeModule.shared
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if let currenExercise = bridgeModule.currentExercise {
|
||||
HStack {
|
||||
Text(currenExercise.exercise.name)
|
||||
.font(Font.system(size: 100))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
GeometryReader { metrics in
|
||||
VStack {
|
||||
if let currenExercise = bridgeModule.currentExercise {
|
||||
HStack {
|
||||
Text(currenExercise.exercise.name)
|
||||
.font(.system(size: 200))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if bridgeModule.currentWorkoutRunTimeInSeconds > -1 {
|
||||
Text("\(bridgeModule.currentWorkoutRunTimeInSeconds)")
|
||||
.font(Font.system(size: 100))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.padding(.trailing, 100)
|
||||
}
|
||||
}
|
||||
.frame(height: metrics.size.height * 0.5)
|
||||
|
||||
if bridgeModule.currentWorkoutRunTimeInSeconds > -1 {
|
||||
Text("\(bridgeModule.currentWorkoutRunTimeInSeconds)")
|
||||
.font(Font.system(size: 100))
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.padding(.trailing, 100)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
if let duration = currenExercise.duration {
|
||||
ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
|
||||
.scaleEffect(x: 1, y: 6, anchor: .center)
|
||||
Text("\(bridgeModule.currentExerciseTimeLeft)")
|
||||
.font(Font.system(size: 75))
|
||||
.padding([.leading, .trailing])
|
||||
} else if let reps = currenExercise.reps {
|
||||
Text("\(reps)")
|
||||
.font(Font.system(size: 75))
|
||||
.padding([.leading, .trailing])
|
||||
HStack {
|
||||
if let duration = currenExercise.duration {
|
||||
ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
|
||||
.scaleEffect(x: 1, y: 6, anchor: .center)
|
||||
Text("\(bridgeModule.currentExerciseTimeLeft)")
|
||||
.font(Font.system(size: 75))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.padding([.leading, .trailing])
|
||||
} else if let reps = currenExercise.reps {
|
||||
Text("\(reps)")
|
||||
.font(Font.system(size: 75))
|
||||
.scaledToFit()
|
||||
.minimumScaleFactor(0.01)
|
||||
.lineLimit(1)
|
||||
.padding([.leading, .trailing])
|
||||
}
|
||||
}
|
||||
.frame(height: metrics.size.height * 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ struct VideoPlayerView: View {
|
||||
|
||||
VideoPlayer(player: avPlayer)
|
||||
.onAppear{
|
||||
_ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: .default, options: .mixWithOthers)
|
||||
|
||||
avPlayer.play()
|
||||
}
|
||||
}
|
||||
@@ -38,8 +40,13 @@ struct VideoPlayerView: View {
|
||||
.onReceive(pub) { (output) in
|
||||
avPlayer.pause()
|
||||
avPlayer.seek(to: .zero)
|
||||
_ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: .default, options: .mixWithOthers)
|
||||
|
||||
avPlayer.play()
|
||||
}
|
||||
.onChange(of: avPlayer, perform: { newValue in
|
||||
avPlayer.play()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,15 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AVKit
|
||||
|
||||
struct WorkoutDetailView: View {
|
||||
@StateObject var viewModel: WorkoutDetailViewModel
|
||||
var bridgeModule = BridgeModule.shared
|
||||
@State var avPlayer = AVPlayer(url: URL(string: "https://dev.werkout.fitness/media/exercise_videos/2_Dumbbell_Lateral_Lunges.mp4")!)
|
||||
|
||||
@StateObject var bridgeModule = BridgeModule.shared
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@AppStorage("showNSFWVideos") private var showNSFWVideos = false
|
||||
|
||||
enum Sheet: Identifiable {
|
||||
case completedWorkout([String: Any])
|
||||
@@ -28,11 +32,21 @@ struct WorkoutDetailView: View {
|
||||
Text("Loading")
|
||||
case .showWorkout(let workout):
|
||||
VStack {
|
||||
if bridgeModule.isInWorkout {
|
||||
HStack {
|
||||
CurrentWorkoutElapsedTimeView()
|
||||
CountdownView()
|
||||
}
|
||||
.padding()
|
||||
|
||||
GeometryReader { metrics in
|
||||
WorkoutDetailVideoPlayerView(avPlayer: $avPlayer)
|
||||
.frame(width: metrics.size.width * 1, height: metrics.size.height * 1)
|
||||
}
|
||||
}
|
||||
InfoView(workout: workout)
|
||||
Divider()
|
||||
CurrentWorkoutElapsedTimeView()
|
||||
.padding(.top)
|
||||
CountdownView()
|
||||
.padding([.leading, .trailing])
|
||||
ExerciseListView(workout: workout)
|
||||
ActionsView(completedWorkout: {
|
||||
bridgeModule.completeWorkout()
|
||||
@@ -60,6 +74,19 @@ struct WorkoutDetailView: View {
|
||||
.interactiveDismissDisabled()
|
||||
}
|
||||
}
|
||||
.onChange(of: bridgeModule.currentExercise, perform: { newValue in
|
||||
if showNSFWVideos {
|
||||
if let viddd = newValue?.exercise.nsfwVideoURL,
|
||||
let url = URL(string: BaseURLs.currentBaseURL + viddd) {
|
||||
avPlayer = AVPlayer(url: url)
|
||||
}
|
||||
} else {
|
||||
if let viddd = newValue?.exercise.videoURL,
|
||||
let url = URL(string: BaseURLs.currentBaseURL + viddd) {
|
||||
avPlayer = AVPlayer(url: url)
|
||||
}
|
||||
}
|
||||
})
|
||||
.onAppear{
|
||||
bridgeModule.completedWorkout = {
|
||||
if let workoutData = createWorkoutData() {
|
||||
@@ -68,6 +95,7 @@ struct WorkoutDetailView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func createWorkoutData() -> [String:Any]? {
|
||||
@@ -90,6 +118,15 @@ struct WorkoutDetailView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct WorkoutDetailVideoPlayerView: View {
|
||||
@ObservedObject var bridgeModule = BridgeModule.shared
|
||||
@Binding var avPlayer: AVPlayer
|
||||
|
||||
var body: some View {
|
||||
VideoPlayerView(avPlayer: $avPlayer, showDoneButton: false)
|
||||
}
|
||||
}
|
||||
|
||||
struct InfoView: View {
|
||||
@ObservedObject var bridgeModule = BridgeModule.shared
|
||||
var workout: Workout
|
||||
@@ -209,43 +246,44 @@ struct ExerciseListView: View {
|
||||
@ObservedObject var bridgeModule = BridgeModule.shared
|
||||
var workout: Workout
|
||||
|
||||
|
||||
var body: some View {
|
||||
List() {
|
||||
ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in
|
||||
let obj = workout.exercisesSortedByCreated_at[i]
|
||||
VStack {
|
||||
HStack {
|
||||
if i == bridgeModule.currentExerciseIdx {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
|
||||
Text(obj.exercise.name)
|
||||
.onTapGesture {
|
||||
bridgeModule.goToExerciseAt(index: i)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
if i == bridgeModule.currentExerciseIdx {
|
||||
ForEach(workout.exercisesSortedByCreated_at.indices, id: \.self) { i in
|
||||
let obj = workout.exercisesSortedByCreated_at[i]
|
||||
VStack {
|
||||
HStack {
|
||||
if obj.exercise.isReps {
|
||||
Text("is reps")
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
if i == bridgeModule.currentExerciseIdx {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
if obj.exercise.isWeight {
|
||||
Text("is weight")
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
if obj.exercise.isDuration {
|
||||
Text("is duration")
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
|
||||
Text(obj.exercise.name)
|
||||
.onTapGesture {
|
||||
bridgeModule.goToExerciseAt(index: i)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
if i == bridgeModule.currentExerciseIdx {
|
||||
HStack {
|
||||
if obj.exercise.isReps {
|
||||
Text("is reps")
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
if obj.exercise.isWeight {
|
||||
Text("is weight")
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
if obj.exercise.isDuration {
|
||||
Text("is duration")
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,12 +291,11 @@ struct CountdownView: View {
|
||||
@StateObject var bridgeModule = BridgeModule.shared
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if let duration = bridgeModule.currentExercise?.duration {
|
||||
HStack {
|
||||
ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
|
||||
Text("\(bridgeModule.currentExerciseTimeLeft)")
|
||||
}.padding(16)
|
||||
if let duration = bridgeModule.currentExercise?.duration {
|
||||
HStack {
|
||||
ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
|
||||
Text("\(bridgeModule.currentExerciseTimeLeft)")
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import AVKit
|
||||
|
||||
@main
|
||||
struct Werkout_iosApp: App {
|
||||
@@ -62,6 +63,7 @@ struct Werkout_iosApp: App {
|
||||
.tag(3)
|
||||
}
|
||||
.onAppear{
|
||||
_ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: .default, options: .mixWithOthers)
|
||||
// UserStore.shared.logout()
|
||||
}
|
||||
.onReceive(pub) { (output) in
|
||||
|
||||
Reference in New Issue
Block a user