Guard quiz Next/Previous against double taps

A rapid second tap on Next (easy to trigger with Apple Pencil) called
advance() twice before the view tree re-rendered, skipping a card.
Share a single isAdvancing flag between advance() and a new goBack()
helper, disable both buttons while the flag is set, and clear it after
350 ms so queued Pencil events are absorbed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-11 18:52:08 -05:00
parent 4e874f60d7
commit 3b8a8a7f1a

View File

@@ -18,6 +18,7 @@ struct CourseQuizView: View {
@State private var currentIndex = 0 @State private var currentIndex = 0
@State private var correctCount = 0 @State private var correctCount = 0
@State private var missedItems: [MissedCourseItem] = [] @State private var missedItems: [MissedCourseItem] = []
@State private var isAdvancing = false
// Per-question state // Per-question state
@State private var userAnswer = "" @State private var userAnswer = ""
@@ -311,16 +312,13 @@ struct CourseQuizView: View {
// Nav arrows // Nav arrows
HStack { HStack {
Button { Button {
guard currentIndex > 0 else { return } goBack()
currentIndex -= 1
resetQuestion()
prepareQuestion()
} label: { } label: {
Label("Previous", systemImage: "chevron.left") Label("Previous", systemImage: "chevron.left")
.font(.subheadline) .font(.subheadline)
} }
.tint(.secondary) .tint(.secondary)
.disabled(currentIndex == 0) .disabled(currentIndex == 0 || isAdvancing)
Spacer() Spacer()
@@ -332,6 +330,7 @@ struct CourseQuizView: View {
.labelStyle(.titleAndIcon) .labelStyle(.titleAndIcon)
} }
.tint(.blue) .tint(.blue)
.disabled(isAdvancing)
} }
.padding(.horizontal) .padding(.horizontal)
} }
@@ -498,6 +497,8 @@ struct CourseQuizView: View {
} }
private func advance() { private func advance() {
guard !isAdvancing else { return }
isAdvancing = true
currentIndex += 1 currentIndex += 1
if isComplete { if isComplete {
saveResult() saveResult()
@@ -505,6 +506,20 @@ struct CourseQuizView: View {
resetQuestion() resetQuestion()
prepareQuestion() prepareQuestion()
} }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
isAdvancing = false
}
}
private func goBack() {
guard !isAdvancing, currentIndex > 0 else { return }
isAdvancing = true
currentIndex -= 1
resetQuestion()
prepareQuestion()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
isAdvancing = false
}
} }
private func saveResult() { private func saveResult() {