Skip to content

Commit

Permalink
[INJIVER-127] Update QR code scanning and upload libraries (#136)
Browse files Browse the repository at this point in the history
* [DSD-5765] Updated pom.xml

Signed-off-by: Rakshith B <[email protected]>
Signed-off-by: srikanth716 <[email protected]>

* [DSD-5765] added browserstack-web.yml

Signed-off-by: Rakshith B <[email protected]>
Signed-off-by: srikanth716 <[email protected]>

* [DSD-5765] Updated browserstack-web.yml

Signed-off-by: Rakshith B <[email protected]>
Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-127]: integrate HTML5 QRcode to scan upload qr

Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-127]: integrate HTML5 library for both upload and scan

Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-127]: injiver-durian-integration-testing

Signed-off-by: srikanth716 <[email protected]>

* revert ovp qr header config

Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-444]: update readme file and added fictious assets (#130)

* [INJIVER-444]: update readme file and added fictious assets

Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-444]: Organized fictitious assets by placing them in the  directory

Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-444]: updated readme with available tags,release branches and active branches

Signed-off-by: srikanth716 <[email protected]>

---------

Signed-off-by: srikanth716 <[email protected]>

* Update browserstack-web.yml

Signed-off-by: Mohan E <[email protected]>
Signed-off-by: srikanth716 <[email protected]>

* [DSD-6004] Updated browserstack-web.yml to push artifacts to s3 bucket.

Signed-off-by: Mohan E <[email protected]>
Signed-off-by: srikanth716 <[email protected]>

* [DSD-6004] Updated browserstack-web.yml

Signed-off-by: Mohan E <[email protected]>
Signed-off-by: srikanth716 <[email protected]>

* [DSD-6004] Updated browserstack-web.yml

Signed-off-by: Mohan E <[email protected]>
Signed-off-by: srikanth716 <[email protected]>

* handeling durian changes from inji web (#135)

Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-127] chore: Update QR code scanning and upload libraries

Signed-off-by: srikanth716 <[email protected]>

* [INJIVER-127] remove old libraries

Signed-off-by: srikanth716 <[email protected]>

---------

Signed-off-by: Rakshith B <[email protected]>
Signed-off-by: srikanth716 <[email protected]>
Signed-off-by: Mohan E <[email protected]>
Co-authored-by: Rakshith B <[email protected]>
Co-authored-by: Mohan E <[email protected]>
  • Loading branch information
3 people authored Aug 27, 2024
1 parent 3d6a465 commit eb27f0b
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 158 deletions.
2 changes: 1 addition & 1 deletion inji-verify/.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
INTERNET_CONNECTIVITY_CHECK_ENDPOINT=https://dns.google/
INTERNET_CONNECTIVITY_CHECK_TIMEOUT=10000
OVP_CLIENT_ID=https://injiverify.dev1.mosip.net
OVP_QR_HEADER=INJI_OVP://payload=
OVP_QR_HEADER=INJI_OVP://
5 changes: 3 additions & 2 deletions inji-verify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"private": true,
"dependencies": {
"@mosip/pixelpass": "0.1.6",
"@openhealthnz-credentials/pdf-image-qr-scanner": "1.0.2",
"@reduxjs/toolkit": "^2.2.3",
"@sunbird-rc/verification-sdk": "0.1.0",
"@testing-library/jest-dom": "^5.17.0",
Expand All @@ -15,8 +14,10 @@
"@types/react-dom": "^18.2.23",
"@types/react-redux": "^7.1.33",
"@types/redux-thunk": "^2.1.0",
"@yudiel/react-qr-scanner": "2.0.0-beta.3",
"html5-qrcode": "^2.3.8",
"jsqr": "^1.4.0",
"patch-package": "^8.0.0",
"pdfjs-dist": "^4.5.136",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.2.1",
Expand Down
163 changes: 72 additions & 91 deletions inji-verify/src/components/Home/VerificationSection/QrScanner.tsx
Original file line number Diff line number Diff line change
@@ -1,105 +1,86 @@
import React, {useEffect, useRef, useState} from 'react';
import {Scanner} from '@yudiel/react-qr-scanner';
import React, { useEffect, useRef, useState } from "react";
import CameraAccessDenied from "./CameraAccessDenied";
import {ScanSessionExpiryTime} from "../../../utils/config";
import {useAppDispatch} from "../../../redux/hooks";
import {goHomeScreen, verificationInit} from "../../../redux/features/verification/verification.slice";
import {raiseAlert} from "../../../redux/features/alerts/alerts.slice";
import { ScanSessionExpiryTime } from "../../../utils/config";
import { useAppDispatch } from "../../../redux/hooks";
import {
goHomeScreen,
verificationInit,
} from "../../../redux/features/verification/verification.slice";
import { raiseAlert } from "../../../redux/features/alerts/alerts.slice";
import "./ScanningLine.css";
import { initiateQrScanning, terminateScanning } from "../../../utils/qr-utils";

let timer: NodeJS.Timeout;

function QrScanner() {
const dispatch = useAppDispatch();
const [isCameraBlocked, setIsCameraBlocked] = useState(false);
const dispatch = useAppDispatch();
const [isCameraBlocked, setIsCameraBlocked] = useState(false);

const scannerRef = useRef<HTMLDivElement>(null);
const scannerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
timer = setTimeout(() => {
dispatch(goHomeScreen({}));
dispatch(raiseAlert({
open: true,
message: "The scan session has expired due to inactivity. Please initiate a new scan.",
severity: "error"
}))
}, ScanSessionExpiryTime);
return () => {
console.log('Clearing timeout');
clearTimeout(timer)
};
}, [dispatch]);
const onSuccess = (decodedText: any) => {
dispatch(
verificationInit({
qrReadResult: { qrData: decodedText, status: "SUCCESS" },
flow: "SCAN",
})
);
clearTimeout(timer);
};

useEffect(() => {
timer = setTimeout(() => {
dispatch(goHomeScreen({}));
dispatch(
raiseAlert({
open: true,
message:
"The scan session has expired due to inactivity. Please initiate a new scan.",
severity: "error",
})
);
terminateScanning();
}, ScanSessionExpiryTime);
initiateQrScanning(timer, onSuccess);
return () => {
console.log("Clearing timeout");
clearTimeout(timer);
};
}, [dispatch]);

useEffect(() => {
// Disable inbuilt border around the video
if (scannerRef?.current) {
let svgElements = scannerRef?.current?.getElementsByTagName('svg');
if (svgElements.length === 1) {
svgElements[0].style.display = 'none';
}
}
}, [scannerRef]);
useEffect(() => {
// Disable inbuilt border around the video
if (scannerRef?.current) {
let svgElements = scannerRef?.current?.getElementsByTagName("svg");
if (svgElements.length === 1) {
svgElements[0].style.display = "none";
}
}
}, [scannerRef]);

return (
<div ref={scannerRef} className="relative">
{
!isCameraBlocked && (
<div
className="absolute top-[-15px] left-[-15px] h-[280px] w-[280px] lg:top-[-12px] lg:left-[-12px] lg:h-[340px] lg:w-[340px] flex items-center justify-center">
<div id="scanning-line" className="scanning-line"></div>
</div>
)
}
<Scanner
onResult={(text, result) => {
console.log(text, result);
dispatch(verificationInit({qrReadResult: {qrData: text, status: "SUCCESS"}, flow: "SCAN"}));
}}
onError={(error) => {
console.log('Clearing timeout - camera blocked');
clearTimeout(timer);
setIsCameraBlocked(true);
}}
components={{
torch: false
}}
options={{
constraints: {
"width": {
"min": 640,
"ideal": 720,
"max": 1920
},
"height": {
"min": 640,
"ideal": 720,
"max": 1080
},
facingMode: "environment"
},
delayBetweenScanSuccess: 1000000 // Scan once
}}
styles={{
container: {
width: window.innerWidth < 1024 ? "250px" : "316px",
placeContent: "center",
display: "grid",
placeItems: "center",
borderRadius: "12px"
},
video: {
objectFit: "cover",
objectPosition: "center"
}
}}
/>
<CameraAccessDenied open={isCameraBlocked} handleClose={() => {
console.log("closing camera");
dispatch(goHomeScreen({}));
setIsCameraBlocked(false)
}}/>
return (
<div ref={scannerRef} className="relative">
{!isCameraBlocked && (
<div className="absolute top-[-15px] left-[-15px] h-[280px] w-[280px] lg:top-[-12px] lg:left-[-12px] lg:h-[340px] lg:w-[340px] flex items-center justify-center">
<div id="scanning-line" className="scanning-line"></div>
</div>
);
)}

<div
className="none absolute h-[250px] w-[250px] lg:h-[316px] lg:w-[316px] rounded-lg overflow-hidden flex items-center justify-center"
id="reader"
/>

<CameraAccessDenied
open={isCameraBlocked}
handleClose={() => {
console.log("closing camera");
dispatch(goHomeScreen({}));
setIsCameraBlocked(false);
}}
/>
</div>
);
}

export default QrScanner;
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ export const UploadQrCode = ({displayMessage, className}: { displayMessage: stri
return;
}

dispatch(qrReadInit({method: "UPLOAD"}));
scanFilesForQr(file)
.then(scanResult => {
if (scanResult.error) console.error(scanResult.error);
if (!!scanResult.data) {
dispatch(qrReadInit({method: "UPLOAD"}));
dispatch(raiseAlert({...AlertMessages.qrUploadSuccess, open: true}));
dispatch(verificationInit({qrReadResult: {qrData: scanResult.data, status: "SUCCESS"}}));
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,49 @@
import React from 'react';
import React from "react";
import scanQr from "../../../assets/scanner-ouline.svg";
import Loader from "../../commons/Loader";
import QrScanner from "./QrScanner";
import StyledButton from "./commons/StyledButton";
import {useAppDispatch} from "../../../redux/hooks";
import {goHomeScreen} from "../../../redux/features/verification/verification.slice";
import {VerificationSteps} from "../../../utils/config";
import {useVerificationFlowSelector} from "../../../redux/features/verification/verification.selector";
import { useAppDispatch } from "../../../redux/hooks";
import { goHomeScreen } from "../../../redux/features/verification/verification.slice";
import { VerificationSteps } from "../../../utils/config";
import { useVerificationFlowSelector } from "../../../redux/features/verification/verification.selector";
import { terminateScanning } from "../../../utils/qr-utils";

const Verification = () => {
const dispatch = useAppDispatch();
const {activeScreen, method} = useVerificationFlowSelector(state => ({activeScreen: state.activeScreen, method: state.method}));
console.log({activeScreen});
return (
<div
className="grid grid-cols-12 mx-auto pt-1 pb-[100px] px-[16px] lg:py-[42px] lg:px-[104px] text-center content-center justify-center">
<div
className="col-start-1 col-end-13 grid w-[100%] lg:w-[350px] aspect-square max-w-[280px] lg:max-w-none bg-cover content-center justify-center m-auto"
style={{
backgroundImage: `url(${scanQr})`
}}>
{
activeScreen === VerificationSteps[method].Verifying
? (<Loader/>)
: (<QrScanner/>)
}
</div>
<div className="col-span-12">
<StyledButton
id="verification-back-button"
className="w-[100%] lg:w-[350px] max-w-[280px] lg:max-w-none mt-[18px]"
onClick={() => {
dispatch(goHomeScreen({}))
}}>
Back
</StyledButton>
</div>
</div>
);
}
const dispatch = useAppDispatch();
const { activeScreen, method } = useVerificationFlowSelector((state) => ({
activeScreen: state.activeScreen,
method: state.method,
}));
console.log({ activeScreen });
return (
<div className="grid grid-cols-12 mx-auto pt-1 pb-[100px] px-[16px] lg:py-[42px] lg:px-[104px] text-center content-center justify-center">
<div
className="col-start-1 col-end-13 grid w-[100%] lg:w-[350px] aspect-square max-w-[280px] lg:max-w-none bg-cover content-center justify-center m-auto"
style={{
backgroundImage: `url(${scanQr})`,
}}
>
{activeScreen === VerificationSteps[method].Verifying ? (
<Loader />
) : (
<QrScanner />
)}
</div>
<div className="col-span-12">
<StyledButton
id="verification-back-button"
className="w-[100%] lg:w-[350px] max-w-[280px] lg:max-w-none mt-[18px]"
onClick={() => {
terminateScanning();
dispatch(goHomeScreen({}));
}}
>
Back
</StyledButton>
</div>
</div>
);
};

export default Verification;
57 changes: 57 additions & 0 deletions inji-verify/src/utils/pdfToQrData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as pdfjsLib from "pdfjs-dist/webpack";
import jsQR from "jsqr";

const decodeQrCode = (imageDataUrl) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const decoded = jsQR(imageData.data, canvas.width, canvas.height);
if (decoded) {
resolve(decoded.data);
} else {
resolve(null);
}
};
img.onerror = () => reject("Error loading image");
img.src = imageDataUrl;
});
};

export const pdfToQrData = async (file) => {
try {
const pdfData = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise;
let qrData;

for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const viewport = page.getViewport({ scale: 2.0 });
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport,
};
await page.render(renderContext).promise;
const dataURL = canvas.toDataURL("image/png");
qrData = await decodeQrCode(dataURL);

if (qrData) {
break; // Exit loop if QR code is found
}
}

return qrData;
} catch (err) {
console.error("Error processing PDF:", err);
throw new Error("Failed to process PDF file.");
}
};
Loading

0 comments on commit eb27f0b

Please sign in to comment.