Browse Source

update scanner

feature/axios_provider
cyril.tsui 5 months ago
parent
commit
c75a83efc3
2 changed files with 138 additions and 45 deletions
  1. +127
    -41
      src/components/QrCodeScanner/QrCodeScanner.tsx
  2. +11
    -4
      src/components/QrCodeScanner/QrCodeScannerModal.tsx

+ 127
- 41
src/components/QrCodeScanner/QrCodeScanner.tsx View File

@@ -1,8 +1,12 @@
import { Button, Card, CardContent, Grid, Modal, ModalProps, Stack, SxProps, Typography } from "@mui/material";
import { Html5Qrcode, Html5QrcodeCameraScanConfig, Html5QrcodeFullConfig, Html5QrcodeResult, Html5QrcodeScanner, QrcodeErrorCallback, QrcodeSuccessCallback } from "html5-qrcode";
import { Autocomplete, Box, Button, Card, CardContent, Grid, Modal, ModalProps, Stack, SxProps, TextField, Typography } from "@mui/material";
import { CameraDevice, Html5Qrcode, Html5QrcodeCameraScanConfig, Html5QrcodeFullConfig, Html5QrcodeResult, Html5QrcodeScanner, Html5QrcodeScannerState, QrcodeErrorCallback, QrcodeSuccessCallback } from "html5-qrcode";
import { Html5QrcodeError } from "html5-qrcode/esm/core";
import { Html5QrcodeScannerConfig } from "html5-qrcode/esm/html5-qrcode-scanner";
import React, { RefObject, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import QrCodeScannerIcon from '@mui/icons-material/QrCodeScanner';
import StopCircleOutlinedIcon from '@mui/icons-material/StopCircleOutlined';
import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined';

const scannerSx: React.CSSProperties = {
position: "absolute",
@@ -15,28 +19,36 @@ const scannerSx: React.CSSProperties = {
};

type QrCodeScannerProps = {
title?: string,
onScanSuccess: (result: string) => void,
onScanError?: (error: string) => void,
isOpen: boolean
isOpen: boolean,
onClose: () => void
}

const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
title,
onScanSuccess,
onScanError,
isOpen
isOpen,
onClose
}) => {
const { t } = useTranslation()
const [isScanned, setIsScanned] = useState<boolean>(false)
const [scanner, setScanner] = useState<Html5Qrcode | null>(null)
const [cameraList, setCameraList] = useState<CameraDevice[]>([])
const [selectedCameraId, setSelectedCameraId] = useState<string | null>(null)
const stringList = ["ABC: abc", "123:123", "ABC: abc", "123:123", "ABC: abc", "123:123"]

const scannerConfig: Html5QrcodeFullConfig = {
verbose: false
}

const cameraConfig: Html5QrcodeCameraScanConfig = {
fps: 10,
qrbox: { width: 400, height: 400 },
qrbox: { width: 250, height: 250 },
// aspectRatio: cardRef.current ? (cardRef.current.offsetWidth / cardRef.current.offsetHeight) : 1.78,
aspectRatio: 2.5
aspectRatio: (window.innerWidth / window.innerHeight) * 1.5 // can be better
};

// MediaTrackConstraintSet
@@ -44,26 +56,56 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
facingMode: "environment"
}

useEffect(() => {
setScanner(new Html5Qrcode(
"qr-reader",
scannerConfig
))
const handleScanStart = useCallback(() => {
if (scanner && selectedCameraId) {
if (scanner.getState() === Html5QrcodeScannerState.SCANNING) {
console.log("first")
scanner.stop()
}

scanner.start(
selectedCameraId,
cameraConfig,
handleScanSuccess,
handleScanError
)
}

}, [selectedCameraId, scanner])

const handleCameraList = useCallback(async () => {
const cameras = await Html5Qrcode.getCameras()
setCameraList(cameras)
if (cameras.length > 0) {
handleCameraChange(cameras[0].id)
}
}, [])

const handleCameraChange = useCallback((id: string) => {
setSelectedCameraId(id)
}, [])

const handleStartScan = useCallback(() => {
const switchScanStatus = useCallback(() => {
if (scanner) {
setIsScanned(false)
scanner.resume();
console.log(isScanned)
switch (isScanned) {
case true:
setIsScanned(false);
scanner.resume();
break;
case false:
setIsScanned(true);
scanner.pause(true);
break;
}
}
}, [scanner])
}, [scanner, isScanned])

const handleScanSuccess = useCallback<QrcodeSuccessCallback>((decodedText, result) => {
if (scanner) {
console.log(`Decoded text: ${decodedText}`);
// Handle the decoded text as needed
setIsScanned(true)
scanner.pause();
switchScanStatus()
onScanSuccess(decodedText)
}
}, [scanner, onScanSuccess])
@@ -76,55 +118,99 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
}
}, [scanner, onScanError])

const handleScanClose = useCallback(async () => {


const handleScanCloseButton = useCallback(async () => {
if (scanner) {
console.log("Cleaning up scanner...");
await scanner.stop()
await scanner.clear()
onClose()
}
}, [scanner])

// close modal without using Cancel Button
const handleScanClose = useCallback(async () => {
if (scanner && !isOpen) {
handleScanCloseButton()
}
}, [scanner, isOpen, handleScanCloseButton])

// -------------------------------------------------------//
useEffect(() => {
if (scanner) {
console.log("Scanner Instance:", scanner);
setScanner(new Html5Qrcode(
"qr-reader",
scannerConfig
))

scanner.start(
mediaTrackConstraintSet,
cameraConfig,
handleScanSuccess,
handleScanError
)
handleCameraList()
}, [])

return () => {
handleScanClose()
};
}
}, [scanner]);
useEffect(() => {
handleScanStart()
}, [scanner, selectedCameraId]);

useEffect(() => {
handleScanClose()
}, [isOpen])

return (
<>
<Stack spacing={2}>
<Typography variant="overline" display="block" marginBlockEnd={1} paddingLeft={2}>
{title ? <Typography variant="overline" display="block" marginBlockEnd={1} paddingLeft={2}>
{"Title"}
</Typography>
<Grid container columns={{ xs: 6, sm: 12 }}>
<Grid item xs={12} justifyContent={"center"}>
<div style={{textAlign: "center", margin: "auto", justifyContent: "center"}} id="qr-reader" hidden={isScanned} />
</Typography> : null}
<Grid container alignItems="center" rowSpacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={12}>
<div style={{ textAlign: "center", margin: "auto", justifyContent: "center" }} id="qr-reader" hidden={isScanned} />
</Grid>
{cameraList.length > 0 ? <Grid item xs={6} >
<Autocomplete
disableClearable
disableCloseOnSelect
noOptionsText={t("No Options")}
options={cameraList}
value={cameraList.find((camera) => camera.id === selectedCameraId)}
onChange={((event, value) => {
setSelectedCameraId(value.id)
})}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label={t("Selected Camera")}
/>
)}
/>
</Grid> : null}
{
stringList.map((string) => {
return <Grid item xs={7}>{string}</Grid>
})
}
</Grid>
<Stack direction="row" justifyContent={"flex-end"} spacing={2} sx={{ margin: 2 }}>
<Button
size="small"
onClick={handleStartScan}
onClick={switchScanStatus}
variant="contained"
sx={{ margin: 2 }}
startIcon={isScanned ? <QrCodeScannerIcon /> : <StopCircleOutlinedIcon />}
>
{isScanned ? t("Start Scanning") : t("Stop Scanning")}
</Button>
<Button
size="small"
onClick={handleScanCloseButton}
variant="contained"
color="error"
sx={{ margin: 2 }}
startIcon={<CloseOutlinedIcon />}
>
{isScanned ? "Re-Scan" : "Stop-Scanning"}
{t("Cancel")}
</Button>
</Stack>
</Stack>


</>
)
}


+ 11
- 4
src/components/QrCodeScanner/QrCodeScannerModal.tsx View File

@@ -1,6 +1,6 @@
import { Button, CardContent, Card, Modal, SxProps, ModalProps, Box, CardActions } from "@mui/material";
import QrCodeScanner from "./QrCodeScanner";
import { useCallback, useRef } from "react";
import { useCallback, useEffect, useRef, useState } from "react";

const modalSx: SxProps = {
position: "absolute",
@@ -13,6 +13,7 @@ const modalSx: SxProps = {
};

type QrCodeScannerModalProps = {
title?: string,
isOpen: boolean,
onClose: () => void,
onScanSuccess: (result: string) => void,
@@ -20,19 +21,25 @@ type QrCodeScannerModalProps = {
}

const QrCodeScannerModal: React.FC<QrCodeScannerModalProps> = ({
title,
isOpen,
onClose,
onScanSuccess,
onScanError,
}) => {

const [modalOpen, setModalOpen] = useState(isOpen) // pass to qr code scanner
useEffect(() => {
setModalOpen(isOpen)
}, [isOpen])

const onModalClose = useCallback<NonNullable<ModalProps["onClose"]>>(
(_, reason) => {
if (reason !== "backdropClick") {
onClose();
setModalOpen(false)
}
},
[onClose],
[],
);

return (
@@ -45,7 +52,7 @@ const QrCodeScannerModal: React.FC<QrCodeScannerModalProps> = ({
marginBlock: 4,
}}
>
<QrCodeScanner onScanSuccess={onScanSuccess} onScanError={onScanError} isOpen={isOpen}/>
<QrCodeScanner title={title} onScanSuccess={onScanSuccess} onScanError={onScanError} isOpen={modalOpen} onClose={onClose}/>
</Box>
</CardContent>
</Card>


Loading…
Cancel
Save