fix: add hasCompleted guard to prevent double-completion race condition
If a user confirms cash payment AND a beacon triggers auto-complete at the same time, two completion calls could fire. Added @State hasCompleted flag that gates all completion paths (manual complete, beacon auto-complete, and cash collection). Resets on error/cancel so user can retry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
db7fe31b8a
commit
54923ba341
1 changed files with 10 additions and 1 deletions
|
|
@ -28,6 +28,7 @@ struct TaskDetailScreen: View {
|
||||||
@State private var showCancelOrderAlert = false
|
@State private var showCancelOrderAlert = false
|
||||||
@State private var isCancelingOrder = false
|
@State private var isCancelingOrder = false
|
||||||
@State private var taskAccepted = false // Track if task was just accepted
|
@State private var taskAccepted = false // Track if task was just accepted
|
||||||
|
@State private var hasCompleted = false // Guard against double-completion
|
||||||
@State private var customerAvatarUrl: String? // Fetched separately if not in task details
|
@State private var customerAvatarUrl: String? // Fetched separately if not in task details
|
||||||
|
|
||||||
// Rating dialog
|
// Rating dialog
|
||||||
|
|
@ -100,6 +101,7 @@ struct TaskDetailScreen: View {
|
||||||
} else if result == "cancelled" || result == "error" {
|
} else if result == "cancelled" || result == "error" {
|
||||||
autoCompleting = false
|
autoCompleting = false
|
||||||
beaconDetected = false
|
beaconDetected = false
|
||||||
|
hasCompleted = false
|
||||||
beaconScanner?.resetSamples()
|
beaconScanner?.resetSamples()
|
||||||
beaconScanner?.startScanning()
|
beaconScanner?.startScanning()
|
||||||
}
|
}
|
||||||
|
|
@ -629,6 +631,7 @@ struct TaskDetailScreen: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
.tint(Color(red: 0.13, green: 0.55, blue: 0.13))
|
.tint(Color(red: 0.13, green: 0.55, blue: 0.13))
|
||||||
|
.disabled(hasCompleted)
|
||||||
} else {
|
} else {
|
||||||
Button { showCompleteAlert = true } label: {
|
Button { showCompleteAlert = true } label: {
|
||||||
Label("Complete Task", systemImage: "checkmark.circle.fill")
|
Label("Complete Task", systemImage: "checkmark.circle.fill")
|
||||||
|
|
@ -637,6 +640,7 @@ struct TaskDetailScreen: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
.tint(.green)
|
.tint(.green)
|
||||||
|
.disabled(hasCompleted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -723,9 +727,10 @@ struct TaskDetailScreen: View {
|
||||||
let scanner = BeaconScanner(
|
let scanner = BeaconScanner(
|
||||||
targetServicePointId: servicePointId,
|
targetServicePointId: servicePointId,
|
||||||
onBeaconDetected: { [self] _ in
|
onBeaconDetected: { [self] _ in
|
||||||
if !beaconDetected && !autoCompleting {
|
if !beaconDetected && !autoCompleting && !hasCompleted {
|
||||||
beaconDetected = true
|
beaconDetected = true
|
||||||
autoCompleting = true
|
autoCompleting = true
|
||||||
|
hasCompleted = true
|
||||||
beaconScanner?.stopScanning()
|
beaconScanner?.stopScanning()
|
||||||
showAutoCompleteDialog = true
|
showAutoCompleteDialog = true
|
||||||
}
|
}
|
||||||
|
|
@ -760,6 +765,8 @@ struct TaskDetailScreen: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func completeTask() {
|
private func completeTask() {
|
||||||
|
guard !hasCompleted else { return }
|
||||||
|
hasCompleted = true
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await APIService.shared.completeTask(taskId: task.taskId)
|
try await APIService.shared.completeTask(taskId: task.taskId)
|
||||||
|
|
@ -768,9 +775,11 @@ struct TaskDetailScreen: View {
|
||||||
if case .ratingRequired = apiError {
|
if case .ratingRequired = apiError {
|
||||||
showRatingDialog = true
|
showRatingDialog = true
|
||||||
} else {
|
} else {
|
||||||
|
hasCompleted = false
|
||||||
self.error = apiError.localizedDescription
|
self.error = apiError.localizedDescription
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
hasCompleted = false
|
||||||
self.error = error.localizedDescription
|
self.error = error.localizedDescription
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue