Improve form and data processing
This commit is contained in:
parent
7fce889eb1
commit
cf34e31eb4
|
@ -10,7 +10,7 @@ export function Alert() {
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div id="alert" style={{"display": "none"}} className="bg-red-100 border border-red-400 text-red-700 px-4 mx-4 py-3 rounded relative" role="alert">
|
<div id="alert" style={{"display": "none"}} className="bg-red-100 border border-red-400 text-red-700 px-4 mx-4 py-3 rounded relative" role="alert">
|
||||||
<strong className="font-bold" id="heading"></strong>
|
<strong className="font-bold pr-2" id="heading"></strong>
|
||||||
<span className="block sm:inline" id="message"></span>
|
<span className="block sm:inline" id="message"></span>
|
||||||
<span className="absolute top-0 bottom-0 right-0 px-4 py-3" onClick={close}>
|
<span className="absolute top-0 bottom-0 right-0 px-4 py-3" onClick={close}>
|
||||||
<svg className="fill-current h-6 w-6 text-red-500" role="button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>Close</title><path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/></svg>
|
<svg className="fill-current h-6 w-6 text-red-500" role="button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>Close</title><path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/></svg>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
const PDFJS = require('pdfjs-dist')
|
const PDFJS = require('pdfjs-dist')
|
||||||
PDFJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDFJS.version}/pdf.worker.js`
|
PDFJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDFJS.version}/pdf.worker.js`
|
||||||
|
|
||||||
import jsQR from "jsqr";
|
import jsQR from "jsqr"
|
||||||
|
import { saveAs } from 'file-saver'
|
||||||
|
|
||||||
import { decodeData } from "../src/decode"
|
import { decodeData } from "../src/decode"
|
||||||
import Card from "../components/Card"
|
import Card from "../components/Card"
|
||||||
|
@ -11,79 +12,125 @@ export default Form
|
||||||
|
|
||||||
function Form() {
|
function Form() {
|
||||||
|
|
||||||
|
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 error = function(heading, message) {
|
||||||
const alert = document.getElementById('alert')
|
const alert = document.getElementById('alert')
|
||||||
alert.setAttribute('style', null)
|
alert.setAttribute('style', null)
|
||||||
|
|
||||||
document.getElementById('heading').innerHTML = heading
|
document.getElementById('heading').innerHTML = heading
|
||||||
document.getElementById('message').innerHTML = message
|
document.getElementById('message').innerHTML = message
|
||||||
|
|
||||||
|
document.getElementById('spin').style.display = 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
const processPdf = async function() {
|
const processPdf = async function() {
|
||||||
if (!document.getElementById('privacy').checked) {
|
|
||||||
document.getElementById('download').disabled = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('spin').style.display = 'block'
|
document.getElementById('spin').style.display = 'block'
|
||||||
|
|
||||||
const file = document.getElementById('pdf').files[0]
|
const file = document.getElementById('pdf').files[0]
|
||||||
|
|
||||||
var fileReader = new FileReader()
|
const result = await readFileAsync(file)
|
||||||
|
var typedarray = new Uint8Array(result)
|
||||||
|
|
||||||
fileReader.onload = async function() {
|
const canvas = document.getElementById('canvas')
|
||||||
var typedarray = new Uint8Array(this.result)
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
const canvas = document.getElementById('canvas')
|
var loadingTask = PDFJS.getDocument(typedarray)
|
||||||
const ctx = canvas.getContext('2d')
|
await loadingTask.promise.then(async function (pdfDocument) {
|
||||||
|
const pdfPage = await pdfDocument.getPage(1)
|
||||||
|
const viewport = pdfPage.getViewport({ scale: 1 })
|
||||||
|
canvas.width = viewport.width
|
||||||
|
canvas.height = viewport.height
|
||||||
|
|
||||||
var loadingTask = PDFJS.getDocument(typedarray)
|
const renderTask = pdfPage.render({
|
||||||
await loadingTask.promise.then(async function (pdfDocument) {
|
canvasContext: ctx,
|
||||||
const pdfPage = await pdfDocument.getPage(1)
|
viewport,
|
||||||
const viewport = pdfPage.getViewport({ scale: 1 })
|
|
||||||
canvas.width = viewport.width
|
|
||||||
canvas.height = viewport.height
|
|
||||||
|
|
||||||
const renderTask = pdfPage.render({
|
|
||||||
canvasContext: ctx,
|
|
||||||
viewport,
|
|
||||||
})
|
|
||||||
|
|
||||||
return await renderTask.promise
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
return await renderTask.promise
|
||||||
var code = jsQR(imageData.data, imageData.width, imageData.height, {
|
})
|
||||||
inversionAttempts: 'dontInvert',
|
|
||||||
})
|
|
||||||
|
|
||||||
if (code) {
|
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
||||||
const rawData = code.data
|
var code = jsQR(imageData.data, imageData.width, imageData.height, {
|
||||||
let decoded
|
inversionAttempts: 'dontInvert',
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
if (code) {
|
||||||
decoded = decodeData(rawData)
|
const rawData = code.data
|
||||||
} catch (error) {
|
let decoded
|
||||||
error('Invalid QR code found', 'Make sure that you picked the correct PDF')
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = JSON.stringify({ decoded: decoded, raw: rawData })
|
try {
|
||||||
|
decoded = decodeData(rawData)
|
||||||
document.getElementById('payload').setAttribute('value', result)
|
} catch (error) {
|
||||||
document.getElementById('download').disabled = false
|
error('Invalid QR code found', 'Make sure that you picked the correct PDF')
|
||||||
} else {
|
|
||||||
error('No QR code found', 'Try scanning the PDF again')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('spin').style.display = 'none'
|
const result = { decoded: decoded, raw: rawData }
|
||||||
|
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
error('No QR code found', 'Try scanning the PDF again')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addToWallet = async function(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
let result
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = await processPdf()
|
||||||
|
} catch {
|
||||||
|
error('Error:', 'Could not extract QR code data from PDF')
|
||||||
}
|
}
|
||||||
|
|
||||||
fileReader.readAsArrayBuffer(file)
|
if (typeof result === 'undefined') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const color = document.getElementById('color').value
|
||||||
|
|
||||||
|
fetch(event.target.action, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/vnd.apple.pkpass',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
decoded: result.decoded,
|
||||||
|
raw: result.raw,
|
||||||
|
color: color
|
||||||
|
})
|
||||||
|
}).then( async (resp) => {
|
||||||
|
if (!resp.ok) {
|
||||||
|
error('Error:', await resp.text())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pass = await resp.blob()
|
||||||
|
saveAs(pass, 'covid.pkpass')
|
||||||
|
}).catch((error) => {
|
||||||
|
error('Error:', error.message)
|
||||||
|
}).finally(() => {
|
||||||
|
document.getElementById('spin').style.display = 'none'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<form id="form">
|
<form id="form" action="https://api.covidpass.marvinsextro.de/covid.pkpass" method="POST" onSubmit={(e) => addToWallet(e)}>
|
||||||
<Card step={1} heading="Select Certificate" content={
|
<Card step={1} heading="Select Certificate" content={
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<p>
|
<p>
|
||||||
|
@ -102,8 +149,6 @@ function Form() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}/>
|
}/>
|
||||||
</form>
|
|
||||||
<form id="hidden" action="https://api.covidpass.marvinsextro.de/covid.pkpass" method="POST">
|
|
||||||
<Card step={2} heading="Pick a Color" content={
|
<Card step={2} heading="Pick a Color" content={
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<select name="color" id="color">
|
<select name="color" id="color">
|
||||||
|
@ -125,14 +170,13 @@ function Form() {
|
||||||
In order for you to make an informed decision, please read the <a href="/privacy">Privacy Policy</a>.
|
In order for you to make an informed decision, please read the <a href="/privacy">Privacy Policy</a>.
|
||||||
</p>
|
</p>
|
||||||
<label htmlFor="privacy" className="flex flex-row space-x-4 items-center">
|
<label htmlFor="privacy" className="flex flex-row space-x-4 items-center">
|
||||||
<input type="checkbox" id="privacy" value="privacy" required onChange={processPdf} />
|
<input type="checkbox" id="privacy" value="privacy" required />
|
||||||
<p>
|
<p>
|
||||||
I accept the <a href="/privacy" className="underline">Privacy Policy</a>
|
I accept the <a href="/privacy" className="underline">Privacy Policy</a>
|
||||||
</p>
|
</p>
|
||||||
</label>
|
</label>
|
||||||
<input type="hidden" id="payload" name="payload" />
|
|
||||||
<div className="flex flex-row items-center justify-start">
|
<div className="flex flex-row items-center justify-start">
|
||||||
<button id="download" type="download" disabled className="shadow-inner focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
|
<button id="download" type="download" className="shadow-inner focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
|
||||||
Add to Wallet
|
Add to Wallet
|
||||||
</button>
|
</button>
|
||||||
<div id="spin" style={{"display": "none"}}>
|
<div id="spin" style={{"display": "none"}}>
|
||||||
|
|
Loading…
Reference in New Issue