feat: replace barcode-detector polyfill with local @zxing/browser for cross-browser barcode scanning
- Remove barcode-detector polyfill (made remote CDN calls to download zxing-wasm) - Use @zxing/library@0.22.0 + @zxing/browser@0.2.0 locally - Support PDF417, Code128, Code39, Code93, QR, EAN, UPC, ITF formats - Clean special characters from scanned results before CIP validation - Fix video ref timing issue with waitForVideo loop
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import React, { useState, useCallback, useRef } from 'react';
|
||||
import { BarcodeScanner, BarcodeFormat } from '@capacitor-mlkit/barcode-scanning';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import 'barcode-detector/polyfill';
|
||||
import { BrowserMultiFormatReader } from '@zxing/browser';
|
||||
import { BarcodeFormat as ZxingFormat, DecodeHintType } from '@zxing/library';
|
||||
import './ScannerView.css';
|
||||
|
||||
const CIP_REGEX = /^[A-Z0-9]{16}$/i;
|
||||
const CIP_REGEX = /^[A-Z0-9]{8,30}$/i;
|
||||
|
||||
function playBeep() {
|
||||
try {
|
||||
@@ -29,6 +30,12 @@ function ScannerView({ onClose, onSelectMedicine }) {
|
||||
const rafRef = useRef(null);
|
||||
const detectorRef = useRef(null);
|
||||
|
||||
// Log de diagnóstico al montar
|
||||
React.useEffect(() => {
|
||||
console.log('[Scanner] BarcodeDetector global:', typeof BarcodeDetector);
|
||||
console.log('[Scanner] Navegador:', navigator.userAgent);
|
||||
}, []);
|
||||
|
||||
const [phase, setPhase] = useState('idle');
|
||||
const [manualCip, setManualCip] = useState('');
|
||||
const [prescriptions, setPrescriptions] = useState([]);
|
||||
@@ -112,6 +119,7 @@ function ScannerView({ onClose, onSelectMedicine }) {
|
||||
|
||||
async function handleWebScan() {
|
||||
setErrorMsg('');
|
||||
console.log('[Scanner] Iniciando escaneo web...');
|
||||
try {
|
||||
if (!navigator.mediaDevices?.getUserMedia) {
|
||||
setErrorMsg('Cámara no disponible en este navegador.');
|
||||
@@ -119,58 +127,77 @@ function ScannerView({ onClose, onSelectMedicine }) {
|
||||
return;
|
||||
}
|
||||
|
||||
let detector;
|
||||
try {
|
||||
detector = new BarcodeDetector({ formats: ['pdf417', 'code_128', 'qr_code'] });
|
||||
} catch {
|
||||
setErrorMsg('La detección de códigos no está soportada en este navegador.');
|
||||
setPhase('error');
|
||||
return;
|
||||
}
|
||||
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: { facingMode: { ideal: 'environment' }, width: { ideal: 1280 }, height: { ideal: 720 } },
|
||||
});
|
||||
console.log('[Scanner] Stream de cámara obtenido');
|
||||
|
||||
streamRef.current = stream;
|
||||
detectorRef.current = detector;
|
||||
setPhase('scanning');
|
||||
|
||||
await new Promise((resolve) => {
|
||||
requestAnimationFrame(() => {
|
||||
const waitForVideo = () => {
|
||||
if (videoRef.current) {
|
||||
videoRef.current.srcObject = stream;
|
||||
videoRef.current.play().then(resolve).catch(resolve);
|
||||
videoRef.current.play().then(() => {
|
||||
console.log('[Scanner] Video reproduciéndose');
|
||||
console.log('[Scanner] Video dims:', videoRef.current.videoWidth, 'x', videoRef.current.videoHeight);
|
||||
resolve();
|
||||
}).catch((e) => {
|
||||
console.error('[Scanner] Error al reproducir video:', e);
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
requestAnimationFrame(waitForVideo);
|
||||
}
|
||||
});
|
||||
};
|
||||
requestAnimationFrame(waitForVideo);
|
||||
});
|
||||
|
||||
let found = false;
|
||||
async function scanFrame() {
|
||||
if (found || !videoRef.current || !detectorRef.current) return;
|
||||
try {
|
||||
const barcodes = await detectorRef.current.detect(videoRef.current);
|
||||
if (barcodes.length > 0) {
|
||||
const rawValue = barcodes[0].rawValue;
|
||||
if (rawValue && CIP_REGEX.test(rawValue)) {
|
||||
found = true;
|
||||
stopCamera();
|
||||
playBeep();
|
||||
setScannedCip(rawValue);
|
||||
setPhase('prescriptions');
|
||||
fetchPrescriptions(rawValue);
|
||||
return;
|
||||
}
|
||||
console.log('[Scanner] Usando zxing local (@zxing/browser)...');
|
||||
const hints = new Map();
|
||||
hints.set(DecodeHintType.POSSIBLE_FORMATS, [
|
||||
ZxingFormat.CODE_128,
|
||||
ZxingFormat.CODE_39,
|
||||
ZxingFormat.CODE_93,
|
||||
ZxingFormat.PDF_417,
|
||||
ZxingFormat.QR_CODE,
|
||||
ZxingFormat.EAN_13,
|
||||
ZxingFormat.EAN_8,
|
||||
ZxingFormat.UPC_A,
|
||||
ZxingFormat.UPC_E,
|
||||
ZxingFormat.ITF,
|
||||
]);
|
||||
hints.set(DecodeHintType.TRY_HARDER, true);
|
||||
const zxingReader = new BrowserMultiFormatReader(hints);
|
||||
detectorRef.current = zxingReader;
|
||||
|
||||
const formatNames = { 0:'AZTEC', 1:'CODABAR', 2:'CODE_39', 3:'CODE_93', 4:'CODE_128', 5:'DATA_MATRIX', 6:'EAN_8', 7:'EAN_13', 8:'ITF', 10:'PDF_417', 11:'QR_CODE', 12:'RSS_14', 14:'UPC_A', 15:'UPC_E' };
|
||||
|
||||
console.log('[Scanner] Iniciando decodeFromVideoElement...');
|
||||
const controls = await zxingReader.decodeFromVideoElement(videoRef.current, (result, error) => {
|
||||
if (result) {
|
||||
const rawValue = result.getText();
|
||||
const cleaned = rawValue.replace(/[^A-Z0-9]/gi, '');
|
||||
const format = result.getBarcodeFormat();
|
||||
console.log('[Scanner] zxing detectó! raw:', rawValue, '| limpio:', cleaned, '| formato:', formatNames[format] || format);
|
||||
if (cleaned && CIP_REGEX.test(cleaned)) {
|
||||
console.log('[Scanner] CIP válido ✓');
|
||||
controls.stop();
|
||||
stopCamera();
|
||||
playBeep();
|
||||
setScannedCip(cleaned);
|
||||
setPhase('prescriptions');
|
||||
fetchPrescriptions(cleaned);
|
||||
} else if (rawValue) {
|
||||
console.log('[Scanner] Código no cumple regex:', cleaned);
|
||||
}
|
||||
} catch { }
|
||||
if (!found) {
|
||||
rafRef.current = requestAnimationFrame(scanFrame);
|
||||
}
|
||||
}
|
||||
scanFrame();
|
||||
});
|
||||
console.log('[Scanner] Escaneando...');
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Scanner] Error general:', err);
|
||||
stopCamera();
|
||||
if (err.name === 'NotAllowedError') {
|
||||
setErrorMsg('Permiso de cámara denegado. Permite el acceso e intenta de nuevo.');
|
||||
|
||||
Reference in New Issue
Block a user