Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,25 @@ extension WorkoutSessionView {

func completeCurrentSet(at date: Date) {
guard let currentSetIndex = viewModel.currentSetIndex else { return }
viewModel.toggleSetCompletion(
exerciseIndex: viewModel.currentExerciseIndex,
setIndex: currentSetIndex,
at: date
)
Task {
await completeSet(
exerciseIndex: viewModel.currentExerciseIndex,
setIndex: currentSetIndex,
at: date
)
}
}

func completeSet(exerciseIndex: Int, setIndex: Int, at date: Date) async {
let didComplete = await viewModel.completeSet(exerciseIndex: exerciseIndex, setIndex: setIndex, at: date)
guard didComplete else {
isShowingServerActionError = viewModel.serverActionErrorMessage != nil
return
}
completeSetSideEffects()
}

private func completeSetSideEffects() {
if settings.preferences.hapticsEnabled {
UIImpactFeedbackGenerator(style: .light).impactOccurred()
}
Expand All @@ -47,6 +61,27 @@ extension WorkoutSessionView {
}
}

func retryFailedServerAction() {
let failedAction = viewModel.failedServerAction
Task {
let didRetry = await viewModel.retryFailedServerAction()
guard didRetry else {
isShowingServerActionError = viewModel.serverActionErrorMessage != nil
return
}
if let failedAction, case .recordSet = failedAction {
completeSetSideEffects()
}
}
}

func advanceToNextExercise() {
Task {
let didAdvance = await viewModel.advanceToNextExercise()
isShowingServerActionError = !didAdvance && viewModel.serverActionErrorMessage != nil
}
}

func formattedDuration(_ seconds: Int) -> String {
String(format: "%02d:%02d", seconds / 60, seconds % 60)
}
Expand Down Expand Up @@ -133,6 +168,14 @@ extension WorkoutSessionView {
DebugOnboardingNetworking.makeExerciseReadService()
#else
nil
#endif
}

static func makeProgressService() -> WorkoutSessionProgressing? {
#if DEBUG
DebugOnboardingNetworking.makeWorkoutSessionService()
#else
nil
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension WorkoutSessionView {
nextExercise: viewModel.plan.exercises[viewModel.currentExerciseIndex + 1],
nextExerciseIndex: viewModel.currentExerciseIndex + 1,
totalExerciseCount: viewModel.plan.exercises.count,
onContinue: { _ = viewModel.moveToNextExercise() },
onContinue: advanceToNextExercise,
onFinish: { isConfirmingFinish = true }
)
} else if viewModel.isResting,
Expand Down Expand Up @@ -64,12 +64,26 @@ extension WorkoutSessionView {
}
},
onToggleSet: { setIndex in
viewModel.toggleSetCompletion(
exerciseIndex: viewModel.currentExerciseIndex,
setIndex: setIndex,
at: Date()
)
syncRestActivity()
if viewModel.serverWorkoutID == nil {
viewModel.toggleSetCompletion(
exerciseIndex: viewModel.currentExerciseIndex,
setIndex: setIndex,
at: Date()
)
syncRestActivity()
} else {
Task {
await completeSet(
exerciseIndex: viewModel.currentExerciseIndex,
setIndex: setIndex,
at: Date()
)
}
}
},
canToggleSet: { setIndex in
viewModel.serverWorkoutID == nil ||
(setIndex == viewModel.currentSetIndex && !viewModel.isSubmittingWorkoutAction)
},
onAdjustWeight: viewModel.adjustCurrentWeight,
onInputWeight: {
Expand All @@ -82,14 +96,18 @@ extension WorkoutSessionView {
viewModel.skipRest()
syncRestActivity()
},
onNextExercise: { _ = viewModel.moveToNextExercise() },
onNextExercise: advanceToNextExercise,
onFinish: { isConfirmingFinish = true },
previousRecordError: viewModel.previousRecordError(for: exercise.id),
isLoadingPreviousRecord: viewModel.isLoadingPreviousRecord(for: exercise.id),
onRetryPreviousRecord: { Task { await loadPreviousRecord(for: exercise.id) } }
)
} else {
GymupEmptyState(symbol: "dumbbell", title: "운동을 찾을 수 없어요", message: "루틴으로 돌아가 운동을 확인해 주세요.")
GymupEmptyState(
symbol: "dumbbell",
title: "운동을 찾을 수 없어요",
message: "루틴으로 돌아가 운동을 확인해 주세요."
)
}
}
}
Expand Down
30 changes: 26 additions & 4 deletions GYMUP/Sources/Features/WorkoutSession/WorkoutSessionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct WorkoutSessionView: View {
@State var isShowingFirstExercisePrompt = false
@State var promptedExerciseIDs: Set<String> = []
@State var isShowingWorkoutProcessing = false
@State var isShowingServerActionError = false
@State var isShowingWeightInput = false
@State var weightInputSetIndex: Int?
@State var liveActivitySessionID = UUID().uuidString
Expand Down Expand Up @@ -49,7 +50,8 @@ struct WorkoutSessionView: View {
startedAt: startedAt,
historyStore: historyStore,
previousRecordService: Self.makePreviousRecordService(),
serverWorkoutID: serverWorkoutID
serverWorkoutID: serverWorkoutID,
progressService: serverWorkoutID == nil ? nil : Self.makeProgressService()
)
)
self.historyStore = historyStore
Expand Down Expand Up @@ -159,10 +161,30 @@ struct WorkoutSessionView: View {
.onReceive(clock) { date in
Task { @MainActor in await handleRestTick(at: date) }
}
.alert("휴식 시간이 끝났어요", isPresented: $isShowingRestFinishedAlert) {
Button("다음 세트 진행", role: .cancel) {}
.alert(
isShowingServerActionError ? "서버 요청을 처리하지 못했어요" : "휴식 시간이 끝났어요",
isPresented: Binding(
get: { isShowingRestFinishedAlert || isShowingServerActionError },
set: { isPresented in
if !isPresented {
isShowingRestFinishedAlert = false
isShowingServerActionError = false
}
}
)
) {
if isShowingServerActionError {
Button("재시도") { retryFailedServerAction() }
Button("취소", role: .cancel) { viewModel.dismissServerActionError() }
} else {
Button("다음 세트 진행", role: .cancel) {}
}
} message: {
Text("준비가 되면 다음 세트를 시작해 주세요.")
if isShowingServerActionError {
Text(viewModel.serverActionErrorMessage ?? "다시 시도해 주세요.")
} else {
Text("준비가 되면 다음 세트를 시작해 주세요.")
}
}
.onChange(of: viewModel.currentExerciseIndex) { _, _ in
isShowingCurrentGuide = false
Expand Down
Loading
Loading