Preventing Double Scans
August 7, 2025 · AI Conversations
if (cordova.platformId === 'android') {
var permissions = cordova.plugins.permissions;
permissions.checkPermission(permissions.CAMERA, function (status) {
if (!status.hasPermission) {
// Request permission
permissions.requestPermission(
permissions.CAMERA,
function (status) {
if (status.hasPermission) {
navigator.splashscreen.hide(); // Hide splash after permission
initializeApp(); // Proceed with your app
} else {
navigator.app.exitApp(); // Exit if denied
}
},
function () {
navigator.app.exitApp(); // Exit on request error
}
);
} else {
navigator.splashscreen.hide();
initializeApp(); // Permission already granted
}
});
} else {
navigator.splashscreen.hide(); // For browser/iOS fallback
initializeApp();
}
}, false);
function initializeApp() {
window.TOKEN = 'fossettscircustoken';
window.eventId = -1;
window.eventDateId = -1;
window.ApiUrl = 'https://staging.fossettscircus.com/boxoffice/index.php/api/';
var scanConfig = {
preferFrontCamera : false, // iOS and Android
showFlipCameraButton : true, // iOS and Android
showTorchButton : true, // iOS and Android
torchOn: false, // Android, launch with the torch switched on (if available)
saveHistory: false, // Android, save scan history (default false)
prompt : "Place a code inside the scan area", // Android
resultDisplayDuration: 500, // Android, display scanned text for X ms. 0 suppresses it entirely, default 1500
formats : "QR_CODE", // default: all but PDF_417 and RSS_EXPANDED
//orientation : "landscape", // Android only (portrait|landscape), default unset so it rotates with the device
disableAnimations : true, // iOS
disableSuccessBeep: false // iOS and Android
};
$(document).ready(function()
{
$(document).on('page:init', function (e) {
const pageEl = e.target;
const pageName = $(pageEl).attr('data-name');
const $navbarPlaceholder = $(pageEl).find('#navbar-placeholder');
if ($navbarPlaceholder.length) {
$navbarPlaceholder.load('pages/navbar.html', function () {
// After loading navbar, toggle the reload icon
const $reloadIcon = $('#reload-icon', pageEl); // scope to the current page
if (pageName === 'staff-tickets') {
$reloadIcon.show();
} else {
$reloadIcon.hide();
}
});
}
});
$(document).on('page:init','.page[data-name="event-details"]',function (e) {
const page = e.detail;
const title = page.route.query.title || 'No Title';
const location = page.route.query.location || '';
const time = page.route.query.time || '';
const code = page.route.query.code || '';
console.log(title, location, time, code);
page.$el.find('.event-title').text(title);
page.$el.find('.event-location').text(location);
page.$el.find('.event-time').text(time);
page.$el.find('input[name="code"]').val(code);
var sendCode = function(code,type)
{
if(type=='qr')
f7App.dialog.preloader('Scanning code…');
else
f7App.dialog.preloader('Sending code…');
$('.scan').prop('disabled', true);
$.ajax({
url: `${ApiUrl}staff_scan`,
method: 'POST',
data: {
token: TOKEN,
event_date_id: eventDateId,
order_code: code
},
success: function(result,status,xhr)
{
var apiResult = JSON.parse(result);
if ( apiResult.result )
{
f7App.dialog.alert(apiResult.message, 'Scan Success');
}
else
{
f7App.dialog.alert(apiResult.message, 'Scan Failed');
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert(`${result.text} could not be sent to boxoffice, please try again`, 'Scan Failed');
},
complete: function()
{
$('.scan').prop('disabled', false);
//$('.manually').prop('disabled', false);
f7App.dialog.close();
}
});
}
var scanSuccess = function(result)
{
if (! result.cancelled )
{
if (eventDateId > -1)
{
sendCode(result.text,'qr');
}
else
{
f7App.dialog.alert('You must select an event date to scan tickets', 'Scan Failed');
}
}
else
{
f7App.dialog.alert('Scan was cancelled', 'Scan Failed');
}
//f7App.popup.open('#modal-scan-result');
};
var scanError = function (error)
{
f7App.dialog.alert("Scanning failed: " + error, 'Scan Failed');
//f7App.popup.open('#modal-scan-result');
};
$(document).on('click', '.scan', function()
{
cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});
});
$(document).on('page:init', '.page[data-name="home"]', function () {
});
$(document).on('page:init', '.page[data-name="staff-pay-terminal"]', function () {
$(document).on('input', '#amountInput', function () {
const amount = parseFloat($(this).val());
const btn = $('#chargeBtn');
if (amount > 0) {
btn.removeClass('disabled:opacity-50').addClass('color-green');
btn.text('Charge €' + amount.toFixed(2));
} else {
btn.addClass('disabled:opacity-50').removeClass('color-green');
btn.text('Charge €0.00');
}
});
})
$(document).on('page:init', '.page[data-name="staff-tickets"]', function () {
loadEvents('yes');
$(document).on('click', '.reload-events-staff', function()
{
loadEvents('yes');
});
});
$(document).on('page:init', '.page[data-name="staff-check-orders"]', function () {
});
$(document).on('page:init', '.page[data-name="staff-dashboard"]', function () {
});
});
}
function loadEvents(staff)
{
f7App.dialog.preloader('Loading events…');
$.ajax({
url: `${ApiUrl}today`,
method: 'POST',
data: {
token: TOKEN
},
success: function(result,status,xhr)
{
console.log(result);
var events = JSON.parse(result);
if (events.result) {
$('.customer-screen div.events').html('');
$('.staff-screen div.events').html('');
const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
$.each(events.data, function(i, val)
{
var eventDatetime = new Date(val.event_datetime);
var dateOptions = { year: '2-digit', month: '2-digit', day: '2-digit' };
var timeOptions = { hour12: true, hour: '2-digit', minute: '2-digit' };
var dayname=weekday[eventDatetime.getDay()];
var code=val.event_id +'.'+ val.event_date_id +'.';
$('.staff-screen div.events').append(`
${val.venue_full}
${dayname} at ${eventDatetime.toLocaleDateString('en-GB', dateOptions)} –
${eventDatetime.toLocaleTimeString('en-US', timeOptions)}
0/500
`);
});
}else{
$('.events').html(`${events.message}`);
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert('Something went wrong. Please try again.', 'Error');
},
complete: function()
{
f7App.dialog.close();
}
});
}
Its always show scan failed, scanning is already running alert
– A scan attempt is made while another scan is already in progress.
– The scan doesn't cleanly close (e.g., popup is left open, or success/error callback doesn't finish properly).
– `.scan()` is being triggered multiple times very quickly (e.g., from multiple click events).
—
### 🔍 Problem in Your Code
Inside this code:
$(document).on('click', '.scan', function() {
cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});
You're **not preventing multiple rapid clicks**, so if the user taps the scan button more than once quickly, it **re-launches the scanner** before the previous one finishes.
—
### ✅ Recommended Fixes
#### 1. **Prevent Double Scans**
Add a global `isScanning` flag to prevent overlapping scan attempts:
let isScanning = false;
$(document).on('click', '.scan', function () {
if (isScanning) return;
isScanning = true;
cordova.plugins.barcodeScanner.scan(
function (result) {
isScanning = false;
scanSuccess(result);
},
function (error) {
isScanning = false;
scanError(error);
},
scanConfig
);
});
#### 2. **Disable the Button Temporarily**
Also visually disable the button while scanning:
$('.scan').prop('disabled', true);
cordova.plugins.barcodeScanner.scan(
function (result) {
$('.scan').prop('disabled', false);
scanSuccess(result);
},
function (error) {
$('.scan').prop('disabled', false);
scanError(error);
},
scanConfig
);
—
### Optional: Avoid Binding Clicks Multiple Times
Make sure `.scan` click handler isn't being attached more than once. You can replace:
$(document).on('click', '.scan', function () { ... });
with:
$(document).off('click', '.scan').on('click', '.scan', function () { ... });
This prevents duplicate bindings on each `page:init`.
—
### ✅ Final Summary
– Introduce an `isScanning` flag to prevent multiple concurrent scans.
– Re-enable scan button and reset the flag on success or error.
– Use `.off().on()` pattern to prevent multiple event bindings when navigating.
Let me know if you’d like me to clean up your `event-details` scan-related block with all fixes included.