This commit is contained in:
Trey t
2023-07-02 21:03:54 -05:00
parent 2344bcbd6a
commit 5c66ceff9f
6 changed files with 117 additions and 69 deletions

View File

@@ -27,3 +27,24 @@ struct CompletedWorkout: Codable {
case totalCalories = "total_calories" case totalCalories = "total_calories"
} }
} }
struct CompletedWorkoutPOSTReturn: Codable {
let id: Int
let workout: Int
let createdAt, updatedAt: String
let difficulty, totalTime: Int?
let workoutStartTime: String
let notes: String?
let totalCalories: Int?
enum CodingKeys: String, CodingKey {
case id, workout
case createdAt = "created_at"
case updatedAt = "updated_at"
case difficulty
case totalTime = "total_time"
case workoutStartTime = "workout_start_time"
case notes
case totalCalories = "total_calories"
}
}

View File

@@ -18,28 +18,28 @@ class BridgeModule: NSObject, ObservableObject {
static let shared = BridgeModule() static let shared = BridgeModule()
@Published var isShowingOnExternalDisplay = false @Published var isShowingOnExternalDisplay = false
private var timer: Timer? @Published var isInWorkout = false
@Published var timeLeft: Int = 0 var completedWorkout: (() -> Void)?
@Published var currentExercise: ExerciseElement?
var currentWorkout: Workout?
var currentExerciseIdx: Int = -1
var workoutStartDate: Date?
// workoutEndDate ties into WatchPackageModel.workoutEndDate which
// tells the watch app to stop the workout
var workoutEndDate: Date?
@Published var currentWorkoutRunTimeInSeconds: Int = -1 @Published var currentWorkoutRunTimeInSeconds: Int = -1
private var currentWorkoutRunTimer: Timer? private var currentWorkoutRunTimer: Timer?
@Published var isInWorkout = false var currentWorkout: Workout?
var completedWorkoutFromWatch: (() -> Void)? public private(set) var workoutStartDate: Date?
var totalCaloire: Float?
var heartRates: [Int]? private var currentExerciseTimer: Timer?
public private(set) var currentExerciseIdx: Int = -1
func start(workout: Workout, atExerciseIndex: Int = 0) { @Published var currentExerciseTimeLeft: Int = 0
@Published var currentExercise: ExerciseElement?
private var isWatchConnected = false
// workoutEndDate fills out WatchPackageModel.workoutEndDate which
// tells the watch app to stop the workout
public private(set) var workoutEndDate: Date?
public private(set) var totalCaloire: Float?
public private(set) var heartRates: [Int]?
func start(workout: Workout) {
self.currentWorkout = workout self.currentWorkout = workout
currentWorkoutRunTimeInSeconds = 0 currentWorkoutRunTimeInSeconds = 0
currentWorkoutRunTimer?.invalidate() currentWorkoutRunTimer?.invalidate()
@@ -58,23 +58,35 @@ class BridgeModule: NSObject, ObservableObject {
} }
} }
func goToExerciseAt(index: Int) {
guard let currentWorkout = currentWorkout else {
return
}
currentExerciseIdx = index
let exercise = currentWorkout.exercises[index]
updateCurrent(exercise: exercise)
}
func resetCurrentWorkout() { func resetCurrentWorkout() {
currentWorkoutRunTimeInSeconds = 0 DispatchQueue.main.async {
currentWorkoutRunTimer?.invalidate() self.currentWorkoutRunTimeInSeconds = 0
currentWorkoutRunTimer = nil self.currentWorkoutRunTimer?.invalidate()
self.currentWorkoutRunTimer = nil
currentWorkoutRunTimer?.invalidate()
currentWorkoutRunTimer = nil self.currentExerciseTimer?.invalidate()
self.currentExerciseTimer = nil
currentWorkoutRunTimeInSeconds = -1
currentExerciseIdx = -1 self.currentWorkoutRunTimeInSeconds = -1
self.currentExerciseIdx = -1
currentExercise = nil
currentWorkout = nil self.currentExercise = nil
self.currentWorkout = nil
isInWorkout = false
workoutStartDate = nil self.isInWorkout = false
workoutEndDate = nil self.workoutStartDate = nil
self.workoutEndDate = nil
}
let watchModel = WatchPackageModel(currentExerciseName: "", currentTimeLeft: -100, workoutStartDate: Date()) let watchModel = WatchPackageModel(currentExerciseName: "", currentTimeLeft: -100, workoutStartDate: Date())
let data = try! JSONEncoder().encode(watchModel) let data = try! JSONEncoder().encode(watchModel)
@@ -94,30 +106,26 @@ class BridgeModule: NSObject, ObservableObject {
private func startTimerWith(duration: Int) { private func startTimerWith(duration: Int) {
DispatchQueue.main.async { DispatchQueue.main.async {
self.timer?.invalidate() self.currentExerciseTimer?.invalidate()
self.timer = nil self.currentExerciseTimer = nil
self.timeLeft = duration self.currentExerciseTimeLeft = duration
self.timer = Timer.scheduledTimer(timeInterval: 1, self.currentExerciseTimer = Timer.scheduledTimer(timeInterval: 1,
target: self, target: self,
selector: #selector(self.updateCounter), selector: #selector(self.updateCurrentExerciseTimer),
userInfo: nil, userInfo: nil,
repeats: true) repeats: true)
self.timer?.fire() self.currentExerciseTimer?.fire()
} }
} }
@objc func updateCounter() { @objc func updateCurrentExerciseTimer() {
if timeLeft > 0 { if currentExerciseTimeLeft > 0 {
timeLeft -= 1 currentExerciseTimeLeft -= 1
let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date()) let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date())
let data = try! JSONEncoder().encode(watchModel) let data = try! JSONEncoder().encode(watchModel)
send(data) send(data)
} else { } else {
timer?.invalidate()
timer = nil
nextExercise() nextExercise()
} }
} }
@@ -129,7 +137,7 @@ class BridgeModule: NSObject, ObservableObject {
let nextExercise = currentWorkout.exercises[currentExerciseIdx] let nextExercise = currentWorkout.exercises[currentExerciseIdx]
updateCurrent(exercise: nextExercise) updateCurrent(exercise: nextExercise)
} else { } else {
completeWorkout()
} }
} }
} }
@@ -147,11 +155,22 @@ class BridgeModule: NSObject, ObservableObject {
} }
} }
} }
func completeWorkout() {
workoutEndDate = Date()
//if connected to watch
if WCSession.default.isReachable {
self.sendWorkoutCompleteToWatch()
} else {
completedWorkout?()
}
}
} }
extension BridgeModule: WCSessionDelegate { extension BridgeModule: WCSessionDelegate {
func sendWorkoutCompleteToWatch() { func sendWorkoutCompleteToWatch() {
let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: timeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date()) let watchModel = WatchPackageModel(currentExerciseName: currentExercise?.exercise.name ?? "-", currentTimeLeft: currentExerciseTimeLeft, workoutStartDate: workoutStartDate ?? Date(), workoutEndDate: Date())
let data = try! JSONEncoder().encode(watchModel) let data = try! JSONEncoder().encode(watchModel)
send(data) send(data)
} }
@@ -165,7 +184,7 @@ extension BridgeModule: WCSessionDelegate {
let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data) let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data)
totalCaloire = Float(model.totalBurnedEnergery) totalCaloire = Float(model.totalBurnedEnergery)
heartRates = model.allHeartRates heartRates = model.allHeartRates
completedWorkoutFromWatch?() completedWorkout?()
} }
} }
} }
@@ -173,16 +192,25 @@ extension BridgeModule: WCSessionDelegate {
func session(_ session: WCSession, func session(_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState, activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?) {} error: Error?) {
switch activationState {
case .notActivated:
print("notActivated")
case .inactive:
print("inactive")
case .activated:
print("activated")
}
}
#if os(iOS) #if os(iOS)
func sessionDidBecomeInactive(_ session: WCSession) {} func sessionDidBecomeInactive(_ session: WCSession) {
}
func sessionDidDeactivate(_ session: WCSession) { func sessionDidDeactivate(_ session: WCSession) {
session.activate() session.activate()
} }
#endif #endif
func send(_ data: Data) { func send(_ data: Data) {
guard WCSession.default.activationState == .activated else { guard WCSession.default.activationState == .activated else {
return return
@@ -196,8 +224,7 @@ extension BridgeModule: WCSessionDelegate {
return return
} }
#endif #endif
WCSession.default.sendMessageData(data, replyHandler: nil) WCSession.default.sendMessageData(data, replyHandler: nil) { error in
{ error in
print("Cannot send message: \(String(describing: error))") print("Cannot send message: \(String(describing: error))")
} }
} }

View File

@@ -62,7 +62,7 @@ class CompleteWorkoutFetchable: Postable {
var postableData: [String : Any]? var postableData: [String : Any]?
var successStatus = 201 var successStatus = 201
typealias Response = CompletedWorkout typealias Response = CompletedWorkoutPOSTReturn
var endPoint: String = "/workout/complete/" var endPoint: String = "/workout/complete/"
init(postData: [String: Any]) { init(postData: [String: Any]) {

View File

@@ -121,7 +121,8 @@ struct CompletedWorkoutView: View {
func heartRates() -> some View { func heartRates() -> some View {
VStack { VStack {
if let heartRates = postData["heart_rates"] as? [Int] { if let heartRates = postData["heart_rates"] as? [Int],
heartRates.count > 0 {
let avg = heartRates.reduce(0, +)/heartRates.count let avg = heartRates.reduce(0, +)/heartRates.count
HStack { HStack {
Image(systemName: "heart") Image(systemName: "heart")

View File

@@ -113,9 +113,9 @@ struct ExtCountdownView: View {
} }
HStack { HStack {
if let duration = currenExercise.duration { if let duration = currenExercise.duration {
ProgressView(value: Float(bridgeModule.timeLeft), total: Float(duration)) ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
.scaleEffect(x: 1, y: 6, anchor: .center) .scaleEffect(x: 1, y: 6, anchor: .center)
Text("\(bridgeModule.timeLeft)") Text("\(bridgeModule.currentExerciseTimeLeft)")
.font(Font.system(size: 75)) .font(Font.system(size: 75))
.padding([.leading, .trailing]) .padding([.leading, .trailing])
} else if let reps = currenExercise.reps { } else if let reps = currenExercise.reps {

View File

@@ -35,8 +35,7 @@ struct WorkoutDetailView: View {
CountdownView() CountdownView()
ExerciseListView(workout: workout) ExerciseListView(workout: workout)
ActionsView(completedWorkout: { ActionsView(completedWorkout: {
bridgeModule.workoutEndDate = Date() bridgeModule.completeWorkout()
bridgeModule.sendWorkoutCompleteToWatch()
}, planWorkout: { workout in }, planWorkout: { workout in
workoutToPlan = workout workoutToPlan = workout
}, workout: workout, showAddToCalendar: showAddToCalendar) }, workout: workout, showAddToCalendar: showAddToCalendar)
@@ -62,7 +61,7 @@ struct WorkoutDetailView: View {
} }
} }
.onAppear{ .onAppear{
bridgeModule.completedWorkoutFromWatch = { bridgeModule.completedWorkout = {
if let workoutData = createWorkoutData() { if let workoutData = createWorkoutData() {
presentedSheet = .completedWorkout(workoutData) presentedSheet = .completedWorkout(workoutData)
bridgeModule.resetCurrentWorkout() bridgeModule.resetCurrentWorkout()
@@ -223,7 +222,7 @@ struct ExerciseListView: View {
Text(obj.exercise.name) Text(obj.exercise.name)
.onTapGesture { .onTapGesture {
bridgeModule.start(workout: workout, atExerciseIndex: i) bridgeModule.goToExerciseAt(index: i)
} }
Spacer() Spacer()
@@ -257,8 +256,8 @@ struct CountdownView: View {
VStack { VStack {
if let duration = bridgeModule.currentExercise?.duration { if let duration = bridgeModule.currentExercise?.duration {
HStack { HStack {
ProgressView(value: Float(bridgeModule.timeLeft), total: Float(duration)) ProgressView(value: Float(bridgeModule.currentExerciseTimeLeft), total: Float(duration))
Text("\(bridgeModule.timeLeft)") Text("\(bridgeModule.currentExerciseTimeLeft)")
}.padding(16) }.padding(16)
} }
} }