diff --git a/src/modules/activities/composables/useTicketScanner.ts b/src/modules/activities/composables/useTicketScanner.ts index 638b8bc..50006a1 100644 --- a/src/modules/activities/composables/useTicketScanner.ts +++ b/src/modules/activities/composables/useTicketScanner.ts @@ -39,6 +39,17 @@ export function useTicketScanner(activityId: Ref) { const isProcessing = ref(false) const lastScan = ref(null) + /** + * Set to `true` immediately after a decode resolves (success or + * failure) and stays true until the operator dismisses or taps + * "Scan next". While paused, further `onDecode` calls are dropped + * — the camera keeps streaming for instant resume but the result + * banner sticks so the door can confirm the outcome before + * moving on. Without this, the 5-fps decode loop instantly + * fires "already scanned this session" on the very ticket that + * just succeeded. + */ + const isPaused = ref(false) const scanned = useLocalStorage( () => `activities_scanned_${activityId.value}`, [], @@ -51,7 +62,7 @@ export function useTicketScanner(activityId: Ref) { } async function onDecode(qrText: string): Promise { - if (isProcessing.value) return + if (isProcessing.value || isPaused.value) return const ticketId = parseTicketId(qrText).trim() if (!ticketId) return @@ -60,6 +71,7 @@ export function useTicketScanner(activityId: Ref) { // of the camera for multiple decode frames. if (scanned.value.some(r => r.ticketId === ticketId)) { lastScan.value = { status: 'duplicate-session', ticketId } + isPaused.value = true return } @@ -90,24 +102,32 @@ export function useTicketScanner(activityId: Ref) { } } finally { isProcessing.value = false + // Pause the decode loop regardless of outcome. The operator + // taps "Scan next" to resume; this guarantees they see the + // banner and can correct (let the attendee in, deny entry, + // etc.) before the next QR comes into frame. + isPaused.value = true } } + function resume() { + lastScan.value = null + isPaused.value = false + } + function clearScanned() { scanned.value = [] lastScan.value = null - } - - function dismissLastScan() { - lastScan.value = null + isPaused.value = false } return { isProcessing, + isPaused, lastScan, scanned, onDecode, + resume, clearScanned, - dismissLastScan, } } diff --git a/src/modules/activities/views/ScanTicketsPage.vue b/src/modules/activities/views/ScanTicketsPage.vue index 4a2d74e..67c51f5 100644 --- a/src/modules/activities/views/ScanTicketsPage.vue +++ b/src/modules/activities/views/ScanTicketsPage.vue @@ -1,7 +1,7 @@