diff --git a/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js b/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js index 2266b39..4e60903 100644 --- a/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js +++ b/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js @@ -56,6 +56,10 @@ if (shutterBtn) shutterBtn.addEventListener('click', captureFrame); window.addEventListener('beforeunload', releaseCamera); + // If the user has already granted camera permission, silently pre-warm the stream + // so the next Scan Label click opens instantly without any browser prompt. + preWarmCamera(); + // ── Open / close ────────────────────────────────────────────────────── async function openScanner() { @@ -184,6 +188,24 @@ rafId = requestAnimationFrame(tick); } + // ── Camera pre-warm ─────────────────────────────────────────────────── + + // Check camera permission state without prompting. If already granted, start the stream + // silently on page load so Scan Label opens instantly with no browser prompt on this visit. + // The idle timer ensures the camera releases if the user never actually scans anything. + async function preWarmCamera() { + try { + if (!navigator.permissions) return; + const perm = await navigator.permissions.query({ name: 'camera' }); + if (perm.state !== 'granted') return; + stream = await navigator.mediaDevices.getUserMedia({ + video: { facingMode: 'environment', width: { ideal: 1280 }, height: { ideal: 720 } } + }); + // Start idle timer — release if user never opens the scanner + idleTimer = setTimeout(releaseCamera, IDLE_RELEASE_MS); + } catch { /* permission denied or getUserMedia failed — ignore */ } + } + function loadJsQR() { return new Promise((resolve, reject) => { if (window.jsQR) { resolve(); return; }