diff --git a/.gitignore b/.gitignore index 886ab5b..ea5d8c7 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ yarn-error.log* .env.development.local .env.test.local .env.production.local + +# Idea files +.idea diff --git a/components/Alert.js b/components/Alert.js deleted file mode 100644 index a7bfad3..0000000 --- a/components/Alert.js +++ /dev/null @@ -1,20 +0,0 @@ - -export default Alert - -export function Alert() { - - const close = function() { - const alert = document.getElementById('alert') - alert.setAttribute('style', 'display: none;') - } - - return( - - ) -} \ No newline at end of file diff --git a/components/Alert.tsx b/components/Alert.tsx new file mode 100644 index 0000000..c1e57a2 --- /dev/null +++ b/components/Alert.tsx @@ -0,0 +1,24 @@ +interface AlertProps { + onClose: () => void; + errorMessage: string; +} + +function Alert(props: AlertProps): JSX.Element { + + return ( +
+ Error + {props.errorMessage} + + + Close + + + +
+ ) +} + +export default Alert; \ No newline at end of file diff --git a/components/Card.js b/components/Card.tsx similarity index 66% rename from components/Card.js rename to components/Card.tsx index 059f250..b6c1eaf 100644 --- a/components/Card.js +++ b/components/Card.tsx @@ -1,24 +1,30 @@ -export default Card +interface CardProps { + heading?: string, + step?: string, + content: JSX.Element, +} -function Card({heading, content, step}) { +function Card(props: CardProps): JSX.Element { return (
{ - step && + props.step &&

- {step} + {props.step}

- {heading} + {props.heading}
}
- {content} + {props.content}
) -} \ No newline at end of file +} + +export default Card; \ No newline at end of file diff --git a/components/Form.js b/components/Form.tsx similarity index 60% rename from components/Form.js rename to components/Form.tsx index 2ae7992..16d959d 100644 --- a/components/Form.js +++ b/components/Form.tsx @@ -1,143 +1,31 @@ +import Card from "./Card"; import {saveAs} from 'file-saver' -import {BrowserQRCodeReader} from '@zxing/browser' -import React, {useEffect, useRef, useState} from "react" -import {decodeData} from "../src/decode" -import {processPdf, processPng} from "../src/process" -import {createPass} from "../src/pass" -import Card from "../components/Card" -import Alert from "../components/Alert" -import jsQR from "jsqr"; +import React, {FormEvent, useEffect, useRef, useState} from "react"; +import {BrowserQRCodeReader} from "@zxing/browser"; +import {Result} from "@zxing/library"; +import {PayloadBody} from "../src/payload"; +import {getPayloadBodyFromFile, getPayloadBodyFromQR} from "../src/process"; +import {PassData} from "../src/pass"; +import Alert from "./Alert"; -export default Form +function Form(): JSX.Element { -function Form() { + // Whether camera is open or not + const [isCameraOpen, setIsCameraOpen] = useState(false); - function readFileAsync(file) { - return new Promise((resolve, reject) => { - let reader = new FileReader(); - - reader.onload = () => { - resolve(reader.result); - }; - - reader.onerror = reject; - - reader.readAsArrayBuffer(file); - }) - } - - const error = function (heading, message) { - const alert = document.getElementById('alert') - alert.setAttribute('style', null) - - document.getElementById('heading').innerHTML = heading - document.getElementById('message').innerHTML = message - - document.getElementById('spin').style.display = 'none' - } - - const processFile = async function () { - console.log(qrCode) - console.log(file) - if (!qrCode && !file) { - error("Error", "Please capture a QR Code or select a file to scan"); - return; - } - - document.getElementById('spin').style.display = 'block' - - let rawData; - - if (file) { - let imageData - const fileBuffer = await readFileAsync(file) - - switch (file.type) { - case 'application/pdf': - console.log('pdf') - imageData = await processPdf(fileBuffer) - break - case 'image/png': - console.log('png') - imageData = await processPng(fileBuffer) - break - default: - error('Error', 'Invalid file type') - return - } - - let code = jsQR(imageData.data, imageData.width, imageData.height, { - inversionAttempts: 'dontInvert', - }) - - rawData = code.data; - - } else { - rawData = qrCode.getText() - } - - if (rawData) { - let decoded - - try { - decoded = decodeData(rawData) - } catch (e) { - error('Invalid QR code found', 'Try another method to select your certificate') - return; - } - - return {decoded: decoded, raw: rawData} - } else { - error('No QR code found', 'Try another method to select your certificate') - } - } - - const addToWallet = async function (event) { - event.preventDefault() - - let result - - try { - result = await processFile() - } catch (e) { - error('Error:', 'Could not extract QR code data from certificate') - } - - if (typeof result === 'undefined') { - return - } - - const color = document.getElementById('color').value - - try { - const pass = await createPass( - { - decoded: result.decoded, - raw: result.raw, - color: color - } - ) - - if (!pass) { - error('Error:', "Something went wrong.") - } else { - const passBlob = new Blob([pass], {type: "application/vnd.apple.pkpass"}); - saveAs(passBlob, 'covid.pkpass') - } - } catch (e) { - error('Error:', e.message) - } finally { - document.getElementById('spin').style.display = 'none' - } - } - - const [isCameraOpen, setIsCameraOpen] = useState(false); + // Global camera controls const [globalControls, setGlobalControls] = useState(undefined); - const [qrCode, setQrCode] = useState(undefined); - const [file, setFile] = useState(undefined); - const inputFile = useRef(undefined) + // Currently selected QR Code / File. Only one of them is set. + const [qrCode, setQrCode] = useState(undefined); + const [file, setFile] = useState(undefined); + const [errorMessage, setErrorMessage] = useState(undefined); + + // File Input ref + const inputFile = useRef(undefined) + + // Add event listener to listen for file change events useEffect(() => { if (inputFile && inputFile.current) { inputFile.current.addEventListener('input', () => { @@ -150,12 +38,12 @@ function Form() { } }, [inputFile]) + // Show file Dialog async function showFileDialog() { inputFile.current.click(); - } - + // Hide camera view async function hideCameraView() { if (globalControls !== undefined) { globalControls.stop(); @@ -163,36 +51,72 @@ function Form() { setIsCameraOpen(false); } + // Show camera view async function showCameraView() { + // Create new QR Code Reader const codeReader = new BrowserQRCodeReader(); // Needs to be called before any camera can be accessed await BrowserQRCodeReader.listVideoInputDevices(); // Get preview Element to show camera stream - const previewElem = document.querySelector('#cameraPreview'); + const previewElem: HTMLVideoElement = document.querySelector('#cameraPreview'); - setGlobalControls(await codeReader.decodeFromVideoDevice(undefined, previewElem, (result, error, controls) => { + // Set Global controls + setGlobalControls( + // Start decoding from video device + await codeReader.decodeFromVideoDevice(undefined, + previewElem, + (result, error, controls) => { + if (result !== undefined) { + setQrCode(result); + setFile(undefined); - if (result !== undefined) { - setQrCode(result); - setFile(undefined); + controls.stop(); - controls.stop(); - - // Reset - setGlobalControls(undefined); - setIsCameraOpen(false); - } - })); + // Reset + setGlobalControls(undefined); + setIsCameraOpen(false); + } + } + ) + ); setIsCameraOpen(true); } + // Add Pass to wallet + async function addToWallet(event: FormEvent) { + event.preventDefault(); + + if (!file && !qrCode) { + setErrorMessage("Please scan a QR Code, or select a file to scan") + return; + } + + const color = (document.getElementById('color') as HTMLSelectElement).value; + let payloadBody: PayloadBody; + + try { + if (file) { + payloadBody = await getPayloadBodyFromFile(file, color); + } else { + payloadBody = await getPayloadBodyFromQR(qrCode, color); + } + + let pass = await PassData.generatePass(payloadBody); + + const passBlob = new Blob([pass], {type: "application/vnd.apple.pkpass"}); + saveAs(passBlob, 'covid.pkpass'); + } catch (e) { + setErrorMessage(e.toString()); + } + } + return (
-
addToWallet(e)}> - +

Please select the certificate screenshot or (scanned) PDF page, which you received from your @@ -214,7 +138,8 @@ function Form() {

-