Fix QR verification sending multiple notifications (#773)

The camera scanner was continuously detecting the same QR code (10-30+ times/second), causing multiple verification notifications to be sent. This happened because:

1. AVCaptureMetadataOutput fires repeatedly while QR is visible
2. Each detection triggered a new verification flow
3. After receiving response, pendingQRVerifications was cleared, allowing duplicate scans

Changes:
- Add deduplication using lastValid state to ignore re-scans of same QR code
- Only set lastValid after successful verification initiation
- Add onSuccess callback to close scanner after successful scan
- Automatically return to "My QR" view after verification starts
- Apply same logic to both iOS camera and macOS manual input paths

This ensures exactly one verification request per scan session.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
jack
2025-10-07 11:32:57 +02:00
committed by GitHub
co-authored by jack
parent d75700fa2b
commit c583949031
+31 -5
View File
@@ -109,6 +109,7 @@ struct ImageWrapper: View {
struct QRScanView: View {
@EnvironmentObject var viewModel: ChatViewModel
var isActive: Bool = true
var onSuccess: (() -> Void)? = nil // Called when verification succeeds
@State private var input = ""
@State private var result: String = "" // not shown for iOS scanner
@State private var lastValid: String = ""
@@ -131,10 +132,18 @@ struct QRScanView: View {
VStack(alignment: .leading, spacing: 12) {
#if os(iOS)
CameraScannerView(isActive: isActive) { code in
// Deduplicate: ignore if we just processed this exact QR code
guard code != lastValid else { return }
if let qr = VerificationService.shared.verifyScannedQR(code) {
let ok = viewModel.beginQRVerification(with: qr)
if !ok { /* already pending; continue scanning */ }
lastValid = code
if ok {
// Successfully initiated verification; remember this QR to prevent re-scanning
lastValid = code
// Close scanner and return to "My QR" view
onSuccess?()
}
// If !ok, peer not found or already pending - don't set lastValid so user can retry
} else {
// ignore invalid reads; continue scanning
}
@@ -148,9 +157,22 @@ struct QRScanView: View {
.frame(height: 100)
.border(Color.gray.opacity(0.4))
Button(Strings.validate) {
// Deduplicate: ignore if we just processed this exact QR
guard input != lastValid else {
result = Strings.requested("") // Already processed
return
}
if let qr = VerificationService.shared.verifyScannedQR(input) {
let ok = viewModel.beginQRVerification(with: qr)
result = ok ? Strings.requested(qr.nickname) : Strings.notFound
if ok {
result = Strings.requested(qr.nickname)
lastValid = input
// Close scanner and return to "My QR" view
onSuccess?()
} else {
result = Strings.notFound
}
} else {
result = Strings.invalid
}
@@ -308,12 +330,16 @@ struct VerificationSheetView: View {
.multilineTextAlignment(.center)
.foregroundColor(accentColor)
#if os(iOS)
QRScanView(isActive: showingScanner)
QRScanView(isActive: showingScanner, onSuccess: {
showingScanner = false
})
.environmentObject(viewModel)
.frame(height: 280)
.clipShape(RoundedRectangle(cornerRadius: 10))
#else
QRScanView()
QRScanView(onSuccess: {
showingScanner = false
})
.environmentObject(viewModel)
#endif
}