|
- 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";
- import { QrCodeInfo } from "@/app/api/qrcode";
-
- const scannerSx: React.CSSProperties = {
- position: "absolute",
- // top: "50%",
- // left: "50%",
- // transform: "translate(-50%, -50%)",
- width: "90%",
- maxHeight: "10%",
- maxWidth: 1400,
- };
-
- type QrCodeScannerProps = {
- cameras: CameraDevice[];
- title?: string;
- contents?: string[];
- onScanSuccess: (qrCodeInfo: QrCodeInfo) => void;
- onScanError?: (error: string) => void;
- isOpen: boolean;
- onClose: () => void;
- };
-
- const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
- title,
- contents,
- onScanSuccess,
- onScanError,
- 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: 250, height: 250 },
- // aspectRatio: cardRef.current ? (cardRef.current.offsetWidth / cardRef.current.offsetHeight) : 1.78,
- aspectRatio: (window.innerWidth / window.innerHeight) * 1.5, // can be better
- };
-
- // MediaTrackConstraintSet
- const mediaTrackConstraintSet = {
- facingMode: "environment",
- };
-
- 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[cameras.length - 1].id);
- }
- }, []);
-
- const handleCameraChange = useCallback((id: string) => {
- setSelectedCameraId(id);
- }, []);
-
- const switchScanStatus = useCallback(() => {
- if (scanner) {
- console.log(isScanned);
- switch (isScanned) {
- case true:
- setIsScanned(false);
- scanner.resume();
- break;
- case false:
- setIsScanned(true);
- scanner.pause(true);
- break;
- }
- }
- }, [scanner, isScanned]);
-
- const handleScanSuccess = useCallback<QrcodeSuccessCallback>(
- (decodedText, result) => {
- if (scanner) {
- console.log(`Decoded text: ${decodedText}`);
- const parseData: QrCodeInfo = JSON.parse(decodedText);
- console.log(parseData);
- // Handle the decoded text as needed
- switchScanStatus();
- onScanSuccess(parseData);
- }
- },
- [scanner, onScanSuccess]
- );
-
- const handleScanError = useCallback<QrcodeErrorCallback>(
- (errorMessage, error) => {
- // console.log(`Error: ${errorMessage}`);
-
- if (onScanError) {
- onScanError(errorMessage);
- }
- },
- [scanner, onScanError]
- );
-
- const handleScanCloseButton = useCallback(async () => {
- if (scanner) {
- console.log("Cleaning up scanner...");
- await scanner.stop();
- scanner.clear();
- onClose();
- }
- }, [scanner]);
-
- // close modal without using Cancel Button
- const handleScanClose = useCallback(async () => {
- if (scanner && !isOpen) {
- handleScanCloseButton();
- }
- }, [scanner, isOpen, handleScanCloseButton]);
-
- // -------------------------------------------------------//
- useEffect(() => {
- setScanner(new Html5Qrcode("qr-reader", scannerConfig));
-
- handleCameraList();
- }, []);
-
- useEffect(() => {
- handleScanStart();
- }, [scanner, selectedCameraId]);
-
- useEffect(() => {
- handleScanClose();
- }, [isOpen]);
-
- return (
- <>
- <Stack spacing={2}>
- {title && (
- <Typography
- variant="overline"
- display="block"
- marginBlockEnd={1}
- paddingLeft={2}
- >
- {"Title"}
- </Typography>
- )}
- <Grid
- container
- alignItems="center"
- justifyContent="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
- 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>} */}
- {contents &&
- contents.map((string) => {
- return (
- <Grid item xs={8}>
- {string}
- </Grid>
- );
- })}
- </Grid>
- <Stack
- direction="row"
- justifyContent={"flex-end"}
- spacing={2}
- sx={{ margin: 2 }}
- >
- <Button
- size="small"
- 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="outlined"
- // color="error"
- // sx={{ margin: 2 }}
- startIcon={<CloseOutlinedIcon />}
- >
- {t("Cancel")}
- </Button>
- </Stack>
- </Stack>
- </>
- );
- };
-
- export default QrCodeScanner;
|