FPSMS-frontend
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

QrCodeScanner.tsx 7.5 KiB

11 miesięcy temu
11 miesięcy temu
1 rok temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
10 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
10 miesięcy temu
11 miesięcy temu
1 rok temu
11 miesięcy temu
10 miesięcy temu
11 miesięcy temu
1 rok temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import {
  2. Autocomplete,
  3. Box,
  4. Button,
  5. Card,
  6. CardContent,
  7. Grid,
  8. Modal,
  9. ModalProps,
  10. Stack,
  11. SxProps,
  12. TextField,
  13. Typography,
  14. } from "@mui/material";
  15. import {
  16. CameraDevice,
  17. Html5Qrcode,
  18. Html5QrcodeCameraScanConfig,
  19. Html5QrcodeFullConfig,
  20. Html5QrcodeResult,
  21. Html5QrcodeScanner,
  22. Html5QrcodeScannerState,
  23. QrcodeErrorCallback,
  24. QrcodeSuccessCallback,
  25. } from "html5-qrcode";
  26. import { Html5QrcodeError } from "html5-qrcode/esm/core";
  27. import { Html5QrcodeScannerConfig } from "html5-qrcode/esm/html5-qrcode-scanner";
  28. import React, {
  29. RefObject,
  30. useCallback,
  31. useEffect,
  32. useMemo,
  33. useRef,
  34. useState,
  35. } from "react";
  36. import { useTranslation } from "react-i18next";
  37. import QrCodeScannerIcon from "@mui/icons-material/QrCodeScanner";
  38. import StopCircleOutlinedIcon from "@mui/icons-material/StopCircleOutlined";
  39. import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined";
  40. import { QrCodeInfo } from "@/app/api/qrcode";
  41. const scannerSx: React.CSSProperties = {
  42. position: "absolute",
  43. // top: "50%",
  44. // left: "50%",
  45. // transform: "translate(-50%, -50%)",
  46. width: "90%",
  47. maxHeight: "10%",
  48. maxWidth: 1400,
  49. };
  50. type QrCodeScannerProps = {
  51. cameras: CameraDevice[];
  52. title?: string;
  53. contents?: string[];
  54. onScanSuccess: (qrCodeInfo: QrCodeInfo) => void;
  55. onScanError?: (error: string) => void;
  56. isOpen: boolean;
  57. onClose: () => void;
  58. };
  59. const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
  60. title,
  61. contents,
  62. onScanSuccess,
  63. onScanError,
  64. isOpen,
  65. onClose,
  66. }) => {
  67. const { t } = useTranslation();
  68. const [isScanned, setIsScanned] = useState<boolean>(false);
  69. const [scanner, setScanner] = useState<Html5Qrcode | null>(null);
  70. const [cameraList, setCameraList] = useState<CameraDevice[]>([]);
  71. const [selectedCameraId, setSelectedCameraId] = useState<string | null>(null);
  72. const stringList = [
  73. "ABC: abc",
  74. "123:123",
  75. "ABC: abc",
  76. "123:123",
  77. "ABC: abc",
  78. "123:123",
  79. ];
  80. const scannerConfig: Html5QrcodeFullConfig = {
  81. verbose: false,
  82. };
  83. const cameraConfig: Html5QrcodeCameraScanConfig = {
  84. fps: 10,
  85. qrbox: { width: 250, height: 250 },
  86. // aspectRatio: cardRef.current ? (cardRef.current.offsetWidth / cardRef.current.offsetHeight) : 1.78,
  87. aspectRatio: (window.innerWidth / window.innerHeight) * 1.5, // can be better
  88. };
  89. // MediaTrackConstraintSet
  90. const mediaTrackConstraintSet = {
  91. facingMode: "environment",
  92. };
  93. const handleScanStart = useCallback(() => {
  94. if (scanner && selectedCameraId) {
  95. if (scanner.getState() === Html5QrcodeScannerState.SCANNING) {
  96. console.log("first");
  97. scanner.stop();
  98. }
  99. scanner.start(
  100. selectedCameraId,
  101. cameraConfig,
  102. handleScanSuccess,
  103. handleScanError,
  104. );
  105. }
  106. }, [selectedCameraId, scanner]);
  107. const handleCameraList = useCallback(async () => {
  108. const cameras = await Html5Qrcode.getCameras();
  109. setCameraList(cameras);
  110. if (cameras.length > 0) {
  111. handleCameraChange(cameras[cameras.length - 1].id);
  112. }
  113. }, []);
  114. const handleCameraChange = useCallback((id: string) => {
  115. setSelectedCameraId(id);
  116. }, []);
  117. const switchScanStatus = useCallback(() => {
  118. if (scanner) {
  119. console.log(isScanned);
  120. switch (isScanned) {
  121. case true:
  122. setIsScanned(false);
  123. scanner.resume();
  124. break;
  125. case false:
  126. setIsScanned(true);
  127. scanner.pause(true);
  128. break;
  129. }
  130. }
  131. }, [scanner, isScanned]);
  132. const handleScanSuccess = useCallback<QrcodeSuccessCallback>(
  133. (decodedText, result) => {
  134. if (scanner) {
  135. console.log(`Decoded text: ${decodedText}`);
  136. const parseData: QrCodeInfo = JSON.parse(decodedText);
  137. console.log(parseData);
  138. // Handle the decoded text as needed
  139. switchScanStatus();
  140. onScanSuccess(parseData);
  141. }
  142. },
  143. [scanner, onScanSuccess],
  144. );
  145. const handleScanError = useCallback<QrcodeErrorCallback>(
  146. (errorMessage, error) => {
  147. // console.log(`Error: ${errorMessage}`);
  148. if (onScanError) {
  149. onScanError(errorMessage);
  150. }
  151. },
  152. [scanner, onScanError],
  153. );
  154. const handleScanCloseButton = useCallback(async () => {
  155. if (scanner) {
  156. console.log("Cleaning up scanner...");
  157. await scanner.stop();
  158. scanner.clear();
  159. onClose();
  160. }
  161. }, [scanner]);
  162. // close modal without using Cancel Button
  163. const handleScanClose = useCallback(async () => {
  164. if (scanner && !isOpen) {
  165. handleScanCloseButton();
  166. }
  167. }, [scanner, isOpen, handleScanCloseButton]);
  168. // -------------------------------------------------------//
  169. useEffect(() => {
  170. setScanner(new Html5Qrcode("qr-reader", scannerConfig));
  171. handleCameraList();
  172. }, []);
  173. useEffect(() => {
  174. handleScanStart();
  175. }, [scanner, selectedCameraId]);
  176. useEffect(() => {
  177. handleScanClose();
  178. }, [isOpen]);
  179. return (
  180. <>
  181. <Stack spacing={2}>
  182. {title && (
  183. <Typography
  184. variant="overline"
  185. display="block"
  186. marginBlockEnd={1}
  187. paddingLeft={2}
  188. >
  189. {"Title"}
  190. </Typography>
  191. )}
  192. <Grid
  193. container
  194. alignItems="center"
  195. justifyContent="center"
  196. rowSpacing={2}
  197. columns={{ xs: 6, sm: 12 }}
  198. >
  199. <Grid item xs={12}>
  200. <div
  201. style={{
  202. textAlign: "center",
  203. margin: "auto",
  204. justifyContent: "center",
  205. }}
  206. id="qr-reader"
  207. hidden={isScanned}
  208. />
  209. </Grid>
  210. {/* {cameraList.length > 0 && <Grid item xs={6} >
  211. <Autocomplete
  212. disableClearable
  213. noOptionsText={t("No Options")}
  214. options={cameraList}
  215. value={cameraList.find((camera) => camera.id === selectedCameraId)}
  216. onChange={((event, value) => {
  217. setSelectedCameraId(value.id)
  218. })}
  219. renderInput={(params) => (
  220. <TextField
  221. {...params}
  222. variant="outlined"
  223. label={t("Selected Camera")}
  224. />
  225. )}
  226. />
  227. </Grid>} */}
  228. {contents &&
  229. contents.map((string) => {
  230. return (
  231. <Grid item xs={8}>
  232. {string}
  233. </Grid>
  234. );
  235. })}
  236. </Grid>
  237. <Stack
  238. direction="row"
  239. justifyContent={"flex-end"}
  240. spacing={2}
  241. sx={{ margin: 2 }}
  242. >
  243. <Button
  244. size="small"
  245. onClick={switchScanStatus}
  246. variant="contained"
  247. // sx={{ margin: 2 }}
  248. startIcon={
  249. isScanned ? <QrCodeScannerIcon /> : <StopCircleOutlinedIcon />
  250. }
  251. >
  252. {isScanned ? t("Start Scanning") : t("Stop Scanning")}
  253. </Button>
  254. <Button
  255. size="small"
  256. onClick={handleScanCloseButton}
  257. variant="outlined"
  258. // color="error"
  259. // sx={{ margin: 2 }}
  260. startIcon={<CloseOutlinedIcon />}
  261. >
  262. {t("Cancel")}
  263. </Button>
  264. </Stack>
  265. </Stack>
  266. </>
  267. );
  268. };
  269. export default QrCodeScanner;