import {saveAs} from 'file-saver'; import React, {FormEvent, useEffect, useRef, useState} from "react"; import {BrowserQRCodeReader, IScannerControls} from "@zxing/browser"; import {Result} from "@zxing/library"; import {useTranslation} from 'next-i18next'; import Link from 'next/link'; import Card from "./Card"; import Alert from "./Alert"; import Check from './Check'; import {PayloadBody} from "../src/payload"; import {getPayloadBodyFromFile} from "../src/process"; import {PassData} from "../src/pass"; import {Photo} from "../src/photo"; import {COLORS} from "../src/colors"; import Colors from './Colors'; import {isChrome, isIOS, isIPad13, isMacOs, isSafari, deviceDetect, osName, osVersion} from 'react-device-detect'; import * as Sentry from '@sentry/react'; import { counterReset } from 'html2canvas/dist/types/css/property-descriptors/counter-reset'; import { color } from 'html2canvas/dist/types/css/types/color'; import Bullet from './Bullet'; function Form(): JSX.Element { const {t} = useTranslation(['index', 'errors', 'common']); // Whether camera is open or not const [isCameraOpen, setIsCameraOpen] = useState(false); // Currently selected color const [selectedColor, setSelectedColor] = useState(COLORS.WHITE); // Currently selected dose const [selectedDose, setSelectedDose] = useState(2); // Global camera controls const [globalControls, setGlobalControls] = useState(undefined); // Currently selected QR Code / File. Only one of them is set. const [qrCode, setQrCode] = useState(undefined); const [file, setFile] = useState(undefined); const [payloadBody, setPayloadBody] = useState(undefined); const [saveLoading, setSaveLoading] = useState(false); const [fileLoading, setFileLoading] = useState(false); const [generated, setGenerated] = useState(false); // this flag represents the file has been used to generate a pass const [isDisabledAppleWallet, setIsDisabledAppleWallet] = useState(false); const [addErrorMessages, _setAddErrorMessages] = useState>([]); const [fileErrorMessages, _setFileErrorMessages] = useState>([]); const [showDoseOption, setShowDoseOption] = useState(false); // const [warningMessages, _setWarningMessages] = useState>([]); const hitcountHost = 'https://stats.vaccine-ontario.ca'; // Check if there is a translation and replace message accordingly const setAddErrorMessage = (message: string) => { if (!message) { return; } const translation = t('errors:'.concat(message)); _setAddErrorMessages(Array.from(new Set([...addErrorMessages, translation !== message ? translation : message]))); }; const setFileErrorMessage = (message: string) => { if (!message) { return; } const translation = t('errors:'.concat(message)); _setFileErrorMessages(Array.from(new Set([...addErrorMessages, translation !== message ? translation : message]))); }; // const setWarningMessage = (message: string) => { // if (!message) { // return; // } // const translation = t('errors:'.concat(message)); // _setWarningMessages(Array.from(new Set([...warningMessages, translation !== message ? translation : message]))); // } const deleteAddErrorMessage = (message: string) =>{ _setAddErrorMessages(addErrorMessages.filter(item => item !== message)) } const deleteFileErrorMessage = (message: string) =>{ _setFileErrorMessages(addErrorMessages.filter(item => item !== message)) } // File Input ref const inputFile = useRef(undefined) // Add event listener to listen for file change events useEffect(() => { if (inputFile && inputFile.current) { inputFile.current.addEventListener('change', () => { let selectedFile = inputFile.current.files[0]; if (selectedFile !== undefined) { setFileLoading(true); setQrCode(undefined); setPayloadBody(undefined); setFile(undefined); setShowDoseOption(false); setGenerated(false); deleteAddErrorMessage(t('errors:'.concat('noFileOrQrCode'))); _setFileErrorMessages([]); checkBrowserType(); getPayload(selectedFile); } }); } checkBrowserType(); }, [inputFile]) async function getPayload(file){ try { const payload = await getPayloadBodyFromFile(file); setPayloadBody(payload); setFileLoading(false); setFile(file); if (payload.rawData.length == 0) { if (Object.keys(payload.receipts).length === 1) { setSelectedDose(parseInt(Object.keys(payload.receipts)[0])); } else { setShowDoseOption(true); } } } catch (e) { setFile(file); setFileLoading(false); if (e != undefined) { console.error(e); // Don't report known errors to Sentry if (!e.message.includes('invalidFileType') && !e.message.includes('not digitally signed') && !e.message.includes('No valid ON proof-of-vaccination')) { Sentry.captureException(e); } if (e.message != undefined) { setFileErrorMessage(e.message); } else { setFileErrorMessage("Unable to continue."); } } else { setFileErrorMessage("Unexpected error. Sorry."); } } } // Show file Dialog async function showFileDialog() { inputFile.current.click(); } async function gotoOntarioHealth(e) { e.preventDefault(); window.open('https://covid19.ontariohealth.ca','_blank'); } async function goToFAQ(e) { e.preventDefault(); window.location.href = '/faq'; } // Hide camera view async function hideCameraView() { if (globalControls !== undefined) { globalControls.stop(); } 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 let deviceList: MediaDeviceInfo[]; try { deviceList = await BrowserQRCodeReader.listVideoInputDevices(); } catch (e) { setAddErrorMessage('noCameraAccess'); return; } // Check if camera device is present if (deviceList.length == 0) { setAddErrorMessage("noCameraFound"); return; } // Get preview Element to show camera stream const previewElem: HTMLVideoElement = document.querySelector('#cameraPreview'); // Set Global controls setGlobalControls( // Start decoding from video device await codeReader.decodeFromVideoDevice(undefined, previewElem, (result, error, controls) => { if (result !== undefined) { setQrCode(result); setFile(undefined); controls.stop(); // Reset setGlobalControls(undefined); setIsCameraOpen(false); } if (error !== undefined) { setAddErrorMessage(error.message); } } ) ); setIsCameraOpen(true); } async function incrementCount() { try { if (typeof generated == undefined || !generated) { const request = `${hitcountHost}/count?url=pass.vaccine-ontario.ca`; //console.log(request); let response = await fetch(request); //console.log(response); setGenerated(true); } } catch (e) { // Fail silently - we shouldn't blow up receipt processing because we couldn't increment our counter console.error(e); //return Promise.reject(e); } } // Add Pass to wallet async function addToWallet(event: FormEvent) { event.preventDefault(); setSaveLoading(true); if (!file && !qrCode) { setAddErrorMessage('noFileOrQrCode') setSaveLoading(false); return; } try { if (payloadBody) { let selectedReceipt; let filenameDetails = ''; if (payloadBody.rawData.length > 0) { // This is an SHC receipt, so do our SHC thing selectedReceipt = payloadBody.shcReceipt; filenameDetails = selectedReceipt.cardOrigin.replace(' ', '-'); } else { selectedReceipt = payloadBody.receipts[selectedDose]; const vaxName = selectedReceipt.vaccineName.replace(' ', '-'); const passDose = selectedReceipt.numDoses; filenameDetails = `${vaxName}-${passDose}`; } const passName = selectedReceipt.name.replace(' ', '-'); const covidPassFilename = `grassroots-receipt-${passName}-${filenameDetails}.pkpass`; await incrementCount(); const pass = await PassData.generatePass(payloadBody, selectedDose); const passBlob = new Blob([pass], {type: "application/vnd.apple.pkpass"}); saveAs(passBlob, covidPassFilename); setSaveLoading(false); } } catch (e) { if (e) { console.error(e); Sentry.captureException(e); if (e.message) { setAddErrorMessage(e.message); } else { setAddErrorMessage("Unable to continue."); } } else { setAddErrorMessage("Unexpected error. Sorry."); } setSaveLoading(false); } } //TODO: merge with addToWallet for common flow async function saveAsPhoto() { setSaveLoading(true); if (!file && !qrCode) { setAddErrorMessage('noFileOrQrCode'); setSaveLoading(false); return; } try { let selectedReceipt; let photoBlob: Blob; let filenameDetails = ''; if (payloadBody.rawData.length > 0) { // This is an SHC receipt, so do our SHC thing selectedReceipt = payloadBody.shcReceipt; photoBlob = await Photo.generateSHCPass(payloadBody); filenameDetails = selectedReceipt.cardOrigin.replace(' ', '-'); } else { // This is an old-style ON custom QR code Receipt selectedReceipt = payloadBody.receipts[selectedDose]; const vaxName = selectedReceipt.vaccineName.replace(' ', '-'); const passDose = selectedReceipt.numDoses; photoBlob = await Photo.generatePass(payloadBody, passDose); filenameDetails = `${vaxName}-${passDose}`; } const passName = selectedReceipt.name.replace(' ', '-'); const covidPassFilename = `grassroots-receipt-${passName}-${filenameDetails}.png`; await incrementCount(); saveAs(photoBlob, covidPassFilename); // need to clean up if (document.getElementById('qrcode').hasChildNodes()) { document.getElementById('qrcode').firstChild.remove(); } if (document.getElementById('shc-qrcode').hasChildNodes()) { document.getElementById('shc-qrcode').firstChild.remove(); } // Hide both our possible passes document.getElementById('pass-image').hidden = true; document.getElementById('shc-pass-image').hidden = true; setSaveLoading(false); } catch (e) { Sentry.captureException(e); setAddErrorMessage(e.message); setSaveLoading(false); } } const verifierLink = () =>
  • {t('verifierLink')}  verifier.vaccine-ontario.ca

  • const setDose = (e) => { setSelectedDose(e.target.value); } function checkBrowserType() { // if (isIPad13) { // setAddErrorMessage('Sorry. Apple does not support the use of Wallet on iPad. Please use iPhone/Safari.'); // setIsDisabledAppleWallet(true); // } // if (!isSafari && !isChrome) { // setAddErrorMessage('Sorry. Apple Wallet pass can be added using Safari or Chrome only.'); // setIsDisabledAppleWallet(true); // } // if (isIOS && (!osVersion.includes('13') && !osVersion.includes('14') && !osVersion.includes('15'))) { // setAddErrorMessage('Sorry, iOS 13+ is needed for the Apple Wallet functionality to work') // setIsDisabledAppleWallet(true); // } if (isIOS && !isSafari) { // setAddErrorMessage('Sorry, only Safari can be used to add a Wallet Pass on iOS'); setAddErrorMessage('Sorry, only Safari can be used to add a Wallet Pass on iOS'); setIsDisabledAppleWallet(true); console.log('not safari') } // } else if (!isIOS) { // setWarningMessage('Only Safari on iOS is officially supported for Wallet import at the moment - ' + // 'for other platforms, please ensure you have an application which can open Apple Wallet .pkpass files'); // setIsDisabledAppleWallet(false); // } } return (

    {t('index:visit')}  {t('index:ontarioHealth')}   {t('index:downloadSignedPDF')}

    {t('index:reminderNotToRepeat')}

    }/>

    {t('index:selectCertificateDescription')}

    {(qrCode || file) &&
    { qrCode && t('index:foundQrCode') } { file && file.name }
    } {fileErrorMessages.map((message, i) => )} }/> {showDoseOption &&

    {t('index:formatChange')}

    {t('index:saveMultiple')}

    {payloadBody && Object.keys(payloadBody.receipts).map(key =>
    )}
    } />} {/*

    {t('index:dataPrivacyDescription')} {t('index:privacyPolicy')} .

    */}
      {verifierLink()}
        
    {addErrorMessages.map((message, i) => )} {/* {warningMessages.map((message, i) => )} */} }/>

    Do you want to use this tool but...

        
    }/> ) } export default Form;