388 lines
11 KiB
JavaScript
388 lines
11 KiB
JavaScript
// import React, { useRef, useState, useEffect } from "react";
|
|
// import { BarcodeScanner } from "@capacitor-mlkit/barcode-scanning";
|
|
// import { BsUpcScan } from "react-icons/bs";
|
|
// import { BrowserMultiFormatReader } from "@zxing/library";
|
|
// import { Capacitor } from "@capacitor/core";
|
|
// import { DefaultModal } from "../Components/Modal/DefaultModal";
|
|
|
|
// export default function BarCodeScan({ onScan }) {
|
|
// const isMobileApp = Capacitor.getPlatform() !== "web";
|
|
// const [scanning, setScanning] = useState(false);
|
|
// const [modalOpen, setModalOpen] = useState(false);
|
|
// const videoRef = useRef(null);
|
|
// const codeReaderRef = useRef(null);
|
|
// const streamRef = useRef(null);
|
|
|
|
// const startScan = async () => {
|
|
// if (scanning) return;
|
|
// setScanning(true);
|
|
|
|
// if (isMobileApp) {
|
|
// // Mobile: MLKit scanner
|
|
// try {
|
|
// const perm = await BarcodeScanner.requestPermissions();
|
|
// if (perm.camera !== "granted") {
|
|
// alert("Camera permission not granted");
|
|
// setScanning(false);
|
|
// return;
|
|
// }
|
|
|
|
// const { barcodes } = await BarcodeScanner.scan();
|
|
// if (barcodes && barcodes.length > 0) {
|
|
// onScan(barcodes[0].rawValue);
|
|
// }
|
|
// } catch (err) {
|
|
// alert("Error scanning barcode: " + err.message);
|
|
// } finally {
|
|
// setScanning(false);
|
|
// }
|
|
// } else {
|
|
// // Web: open modal and start camera
|
|
// setModalOpen(true);
|
|
// setTimeout(() => startWebScan(), 50); // wait for modal DOM
|
|
// }
|
|
// };
|
|
|
|
// const startWebScan = async () => {
|
|
// try {
|
|
// codeReaderRef.current = new BrowserMultiFormatReader();
|
|
// streamRef.current = await navigator.mediaDevices.getUserMedia({
|
|
// video: { facingMode: "environment" },
|
|
// });
|
|
// if (videoRef.current) {
|
|
// videoRef.current.srcObject = streamRef.current;
|
|
// videoRef.current.play();
|
|
// }
|
|
|
|
// codeReaderRef.current.decodeFromVideoDevice(
|
|
// null,
|
|
// videoRef.current,
|
|
// (result) => {
|
|
// if (result) {
|
|
// onScan(result.getText());
|
|
// stopWebScan();
|
|
// setModalOpen(false);
|
|
// }
|
|
// }
|
|
// );
|
|
// } catch (err) {
|
|
// alert("Error accessing camera: " + err.message);
|
|
// setScanning(false);
|
|
// setModalOpen(false);
|
|
// }
|
|
// };
|
|
|
|
// const stopWebScan = () => {
|
|
// if (streamRef.current) {
|
|
// streamRef.current.getTracks().forEach((track) => track.stop());
|
|
// streamRef.current = null;
|
|
// }
|
|
// if (codeReaderRef.current) {
|
|
// codeReaderRef.current.reset();
|
|
// codeReaderRef.current = null;
|
|
// }
|
|
// setScanning(false);
|
|
// };
|
|
|
|
// const closeModal = () => {
|
|
// stopWebScan();
|
|
// setModalOpen(false);
|
|
// };
|
|
|
|
// // Stop camera if component unmounts
|
|
// useEffect(() => () => stopWebScan(), []);
|
|
|
|
// return (
|
|
// <div style={{ display: "flex", justifyContent: "center", padding: 20 }}>
|
|
// {!scanning && (
|
|
// <BsUpcScan
|
|
// onClick={startScan}
|
|
// style={{ fontSize: 32, color: "#007bff", cursor: "pointer" }}
|
|
// />
|
|
// )}
|
|
|
|
// <DefaultModal
|
|
// title="Barcode Reader"
|
|
// width={500}
|
|
// open={modalOpen}
|
|
// footer={false}
|
|
// handleCancel={() => {
|
|
// closeModal();
|
|
// }}
|
|
// >
|
|
// <div
|
|
// style={{
|
|
// display: "flex",
|
|
// flexDirection: "column",
|
|
// alignItems: "center",
|
|
// justifyContent: "center",
|
|
// }}
|
|
// >
|
|
// <video
|
|
// ref={videoRef}
|
|
// style={{
|
|
// width: "100%",
|
|
// maxWidth: 400,
|
|
// borderRadius: 8,
|
|
// border: "2px solid #000",
|
|
// }}
|
|
// />
|
|
|
|
// <button
|
|
// onClick={closeModal}
|
|
// style={{
|
|
// marginTop: 20,
|
|
// padding: "10px 20px",
|
|
// fontSize: 16,
|
|
// borderRadius: 8,
|
|
// border: "none",
|
|
// cursor: "pointer",
|
|
// }}
|
|
// >
|
|
// Close
|
|
// </button>
|
|
// </div>
|
|
// </DefaultModal>
|
|
|
|
// </div>
|
|
// );
|
|
// }
|
|
|
|
import React, { useRef, useState, useEffect } from 'react';
|
|
import { BarcodeScanner } from '@capacitor-mlkit/barcode-scanning';
|
|
import { BsUpcScan } from 'react-icons/bs';
|
|
import { Capacitor } from '@capacitor/core';
|
|
import { DefaultModal } from '../Components/Modal/DefaultModal';
|
|
import QrScanner from 'qr-scanner/qr-scanner.min.js';
|
|
import { BrowserMultiFormatReader, BarcodeFormat } from '@zxing/library';
|
|
|
|
QrScanner.WORKER_PATH = new URL(
|
|
'qr-scanner/qr-scanner-worker.min.js',
|
|
import.meta.url
|
|
).toString();
|
|
|
|
export default function BarCodeScan({ onScan }) {
|
|
const isMobileApp = Capacitor.getPlatform() !== 'web';
|
|
const [scanning, setScanning] = useState(false);
|
|
const [modalOpen, setModalOpen] = useState(false);
|
|
|
|
const videoRef = useRef(null);
|
|
const qrScannerRef = useRef(null);
|
|
const barcodeReaderRef = useRef(null);
|
|
|
|
// -------------------------
|
|
// START SCAN
|
|
// -------------------------
|
|
const startScan = async () => {
|
|
if (scanning) return;
|
|
setScanning(true);
|
|
|
|
if (isMobileApp) {
|
|
try {
|
|
const perm = await BarcodeScanner.requestPermissions();
|
|
if (perm.camera !== 'granted') {
|
|
alert('Camera permission not granted');
|
|
setScanning(false);
|
|
return;
|
|
}
|
|
|
|
const { barcodes } = await BarcodeScanner.scan();
|
|
if (barcodes?.length > 0) {
|
|
onScan(barcodes[0].rawValue);
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
} finally {
|
|
setScanning(false);
|
|
}
|
|
} else {
|
|
setModalOpen(true);
|
|
}
|
|
};
|
|
|
|
// -------------------------
|
|
// START WEB SCAN (HYBRID)
|
|
// -------------------------
|
|
const startWebScan = async () => {
|
|
if (!videoRef.current) return;
|
|
|
|
// Start QR scanner (fast)
|
|
qrScannerRef.current = new QrScanner(
|
|
videoRef.current,
|
|
(result) => {
|
|
stopWebScan();
|
|
onScan(result.data);
|
|
setModalOpen(false);
|
|
},
|
|
{
|
|
maxScansPerSecond: 25,
|
|
}
|
|
);
|
|
|
|
await qrScannerRef.current.start();
|
|
|
|
// Start Barcode scanner (ZXing)
|
|
barcodeReaderRef.current = new BrowserMultiFormatReader(undefined, {
|
|
possibleFormats: [
|
|
BarcodeFormat.CODE_128,
|
|
BarcodeFormat.EAN_13,
|
|
BarcodeFormat.UPC_A,
|
|
BarcodeFormat.UPC_E,
|
|
],
|
|
});
|
|
|
|
barcodeReaderRef.current.timeBetweenDecodingAttempts = 50;
|
|
|
|
barcodeReaderRef.current.decodeFromVideoDevice(
|
|
null,
|
|
videoRef.current,
|
|
(result) => {
|
|
if (result) {
|
|
stopWebScan();
|
|
onScan(result.getText());
|
|
setModalOpen(false);
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
const stopWebScan = () => {
|
|
if (qrScannerRef.current) {
|
|
qrScannerRef.current.stop();
|
|
qrScannerRef.current.destroy();
|
|
qrScannerRef.current = null;
|
|
}
|
|
|
|
if (barcodeReaderRef.current) {
|
|
barcodeReaderRef.current.reset();
|
|
barcodeReaderRef.current = null;
|
|
}
|
|
|
|
setScanning(false);
|
|
};
|
|
|
|
const closeModal = () => {
|
|
stopWebScan();
|
|
setModalOpen(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (modalOpen) {
|
|
startWebScan();
|
|
}
|
|
}, [modalOpen]);
|
|
|
|
useEffect(() => {
|
|
return () => stopWebScan();
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
className="BarCodeScanMaster"
|
|
style={{ display: 'flex', justifyContent: 'center', padding: 20 }}
|
|
>
|
|
{!scanning && (
|
|
<BsUpcScan
|
|
onClick={startScan}
|
|
style={{
|
|
fontSize: 32,
|
|
color: '#007bff',
|
|
cursor: 'pointer',
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
<DefaultModal
|
|
title="QR / Barcode Reader"
|
|
width={500}
|
|
open={modalOpen}
|
|
footer={false}
|
|
handleCancel={closeModal}
|
|
>
|
|
<div
|
|
style={{
|
|
position: 'relative',
|
|
width: '100%',
|
|
maxWidth: 400,
|
|
margin: 'auto',
|
|
}}
|
|
>
|
|
{/* VIDEO */}
|
|
<video
|
|
ref={videoRef}
|
|
style={{
|
|
width: '100%',
|
|
borderRadius: 8,
|
|
}}
|
|
/>
|
|
|
|
{/* DARK OVERLAY */}
|
|
<div
|
|
style={{
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
width: '100%',
|
|
height: '100%',
|
|
background: 'rgba(0,0,0,0.5)',
|
|
borderRadius: 8,
|
|
}}
|
|
/>
|
|
|
|
{/* SCAN BOX */}
|
|
<div
|
|
style={{
|
|
position: 'absolute',
|
|
top: '50%',
|
|
left: '50%',
|
|
width: '70%',
|
|
height: '60%',
|
|
transform: 'translate(-50%, -50%)',
|
|
border: '3px solid #00ff00',
|
|
borderRadius: 12,
|
|
boxShadow: '0 0 0 2000px rgba(0,0,0,0.4)',
|
|
}}
|
|
/>
|
|
|
|
{/* SCAN LINE */}
|
|
<div
|
|
style={{
|
|
position: 'absolute',
|
|
left: '15%',
|
|
width: '70%',
|
|
height: 2,
|
|
background: 'red',
|
|
animation: 'scanAnimation 2s infinite linear',
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
onClick={closeModal}
|
|
style={{
|
|
marginTop: 20,
|
|
padding: '10px 20px',
|
|
fontSize: 16,
|
|
borderRadius: 8,
|
|
border: 'none',
|
|
cursor: 'pointer',
|
|
}}
|
|
>
|
|
Close
|
|
</button>
|
|
|
|
{/* Animation */}
|
|
<style>
|
|
{`
|
|
@keyframes scanAnimation {
|
|
0% { top: 25%; }
|
|
50% { top: 70%; }
|
|
100% { top: 25%; }
|
|
}
|
|
`}
|
|
</style>
|
|
</DefaultModal>
|
|
</div>
|
|
);
|
|
}
|