This commit is contained in:
2025-08-08 21:09:11 -04:00
parent 2f044c3d9c
commit 7bcc5d656c
38 changed files with 776 additions and 159 deletions

View File

@ -19,6 +19,7 @@ struct ExerciseProgressControlView: View {
@State private var elapsedSeconds: Int = 0
@State private var timer: Timer? = nil
@State private var previousStateIndex: Int = 0
@State private var hapticCounter: Int = 0
var body: some View {
TabView(selection: $currentStateIndex) {
@ -32,21 +33,40 @@ struct ExerciseProgressControlView: View {
.tag(index)
} else if state.isRest {
ExerciseRestCard(elapsedSeconds: elapsedSeconds)
ExerciseRestCard(elapsedSeconds: elapsedSeconds, afterSet: state.afterSet ?? 0)
.tag(index)
} else if state.isDone {
ExerciseDoneCard(elapsedSeconds: elapsedSeconds, onComplete: completeExercise)
ExerciseDoneCard(elapsedSeconds: elapsedSeconds, onComplete: completeExercise, onOneMoreSet: addOneMoreSet)
.tag(index)
}
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
.gesture(
DragGesture()
.onEnded { value in
// Detect swipe left when on the Done card (last index)
if currentStateIndex == exerciseStates.count - 1 && value.translation.width < -50 {
// User swiped left from Done card - add one more set
addOneMoreSet()
}
}
)
.onChange(of: currentStateIndex) { oldValue, newValue in
if oldValue != newValue {
elapsedSeconds = 0
moveToNextState()
hapticCounter = 0 // Reset haptic pattern when changing phases
// Update the log's current state but don't auto-advance
log.currentStateIndex = currentStateIndex
log.elapsedSeconds = elapsedSeconds
try? modelContext.save()
// Update status based on current state
if currentStateIndex > 0 && currentStateIndex < exerciseStates.count - 1 {
log.status = .inProgress
}
}
}
.onAppear {
@ -54,6 +74,12 @@ struct ExerciseProgressControlView: View {
currentStateIndex = log.currentStateIndex ?? 0
startTimer()
}
.onChange(of: log.sets) { oldValue, newValue in
// Reconstruct exercise states if sets count changed
if oldValue != newValue {
setupExerciseStates()
}
}
.onDisappear {
stopTimer()
}
@ -76,11 +102,11 @@ struct ExerciseProgressControlView: View {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
elapsedSeconds += 1
// Check if we need to provide haptic feedback during rest periods
// Check if we need to provide haptic feedback
if currentStateIndex >= 0 && currentStateIndex < exerciseStates.count {
let currentState = exerciseStates[currentStateIndex]
if currentState.isRest {
provideRestHapticFeedback()
if currentState.isRest || currentState.isSet {
provideHapticFeedback()
} else if currentState.isDone && elapsedSeconds >= 10 {
// Auto-complete after 10 seconds on the DONE state
completeExercise()
@ -94,34 +120,53 @@ struct ExerciseProgressControlView: View {
timer = nil
}
private func moveToNextState() {
if currentStateIndex < exerciseStates.count - 1 {
elapsedSeconds = 0
withAnimation {
currentStateIndex += 1
log.currentStateIndex = currentStateIndex
log.elapsedSeconds = elapsedSeconds
log.status = .inProgress
try? modelContext.save()
private func provideHapticFeedback() {
// Provide haptic feedback every 15 seconds in a cycling pattern: 1 2 3 long
if elapsedSeconds % 15 == 0 && elapsedSeconds > 0 {
hapticCounter += 1
switch hapticCounter % 4 {
case 1:
// First 15 seconds: single tap
HapticFeedback.click()
case 2:
// Second 15 seconds: double tap
HapticFeedback.doubleTap()
case 3:
// Third 15 seconds: triple tap
HapticFeedback.tripleTap()
case 0:
// Fourth 15 seconds: long tap, then reset pattern
HapticFeedback.longTap()
default:
break
}
} else {
// We've reached the end (DONE state)
completeExercise()
}
}
private func provideRestHapticFeedback() {
// Provide haptic feedback based on elapsed time
if elapsedSeconds % 60 == 0 && elapsedSeconds > 0 {
// Triple tap every 60 seconds
HapticFeedback.tripleTap()
} else if elapsedSeconds % 30 == 0 && elapsedSeconds > 0 {
// Double tap every 30 seconds
HapticFeedback.doubleTap()
} else if elapsedSeconds % 10 == 0 && elapsedSeconds > 0 {
// Single tap every 10 seconds
HapticFeedback.success()
}
private func addOneMoreSet() {
// Increment total sets
log.sets += 1
// Reconstruct exercise states (will trigger onChange)
setupExerciseStates()
// Calculate the state index for the additional set
// States: intro(0) set1(1) rest1(2) ... setN(2N-1) done(2N)
// For the additional set, we want to go to setN which is at index 2N-1
let additionalSetStateIndex = (log.sets * 2) - 1
log.status = .inProgress
log.currentStateIndex = additionalSetStateIndex
log.elapsedSeconds = 0
elapsedSeconds = 0
hapticCounter = 0
// Update the current state index for the TabView
currentStateIndex = additionalSetStateIndex
try? modelContext.save()
}
private func completeExercise() {