functional now.  could use more user testing
This commit is contained in:
Billy Lo 2021-10-29 09:21:04 -04:00
parent 865d3a7442
commit 00bab2a5bf
4 changed files with 176 additions and 43 deletions

View File

@ -69,10 +69,13 @@ function Form(): JSX.Element {
// Currently selected QR Code / File. Only one of them is set.
const [qrCode, setQrCode] = useState<Result>(undefined);
const [file, setFile] = useState<File>(undefined);
const [file2, setFile2] = useState<File>(undefined);
const [payloadBody, setPayloadBody] = useState<PayloadBody>(undefined);
const [saveLoading, setSaveLoading] = useState<boolean>(false);
const [fileLoading, setFileLoading] = useState<boolean>(false);
const [file2Loading, setFile2Loading] = useState<boolean>(false);
const [generated, setGenerated] = useState<boolean>(false); // this flag represents the file has been used to generate a pass
@ -112,8 +115,14 @@ function Form(): JSX.Element {
// File Input ref
const inputFile = useRef<HTMLInputElement>(undefined)
const inputFile2 = useRef<HTMLInputElement>(undefined)
// Add event listener to listen for file change events
useEffect(() => {
let payloadBody;
if (inputFile && inputFile.current) {
inputFile.current.addEventListener('change', async () => {
let selectedFile = inputFile.current.files[0];
@ -127,19 +136,28 @@ function Form(): JSX.Element {
deleteAddErrorMessage(t('errors:'.concat('noFileOrQrCode')));
_setFileErrorMessages([]);
checkBrowserType();
const payloadBody = await getPayload(selectedFile);
//TODO: feature flagging
await createDataUrlForDisplay(selectedFile);
payloadBody = await getPayload(selectedFile);
await storeFileToLocalStorageBase64('receipt', selectedFile);
await renderPhoto(payloadBody);
}
});
}
if (inputFile2 && inputFile2.current) {
inputFile2.current.addEventListener('change', async () => {
let selectedFile2 = inputFile2.current.files[0];
if (selectedFile2) {
setFile2Loading(true);
setFile2(selectedFile2);
await storeFileToLocalStorageBase64('extra', selectedFile2);
payloadBody.extraUrl = 'extra';
setFile2Loading(false);
}
});
}
checkBrowserType();
}, [inputFile])
}, [inputFile, inputFile2])
async function createDataUrlForDisplay(file: File) {
async function storeFileToLocalStorageBase64(key: string, file: File) {
if (window.localStorage) {
@ -147,36 +165,21 @@ function Form(): JSX.Element {
const buffer = Buffer.from(await new Response(file).arrayBuffer());
let dataUrl = `data:${file.type};base64,${buffer.toString("base64")}`;
localStorage.setItem('pdfDataUrl', dataUrl);
localStorage.setItem(key, dataUrl);
}
}
async function showPDF() {
let dataUrl = localStorage.getItem('pdfDataUrl');
// console.log(dataUrl)
let script = `function debugBase64(base64URL){
var win = window.open();
win.document.write('<iframe src="' + base64URL + '" frameborder="0" style="border:0; top:0px; left:0px; bottom:0px; right:0px; width:100%; height:100%;" allowfullscreen></iframe>');
}
debugBase64('${dataUrl}');
`;
let hrefValue = `javascript:${script};`
document.getElementById('mylink').setAttribute('href', hrefValue);
}
async function getPayload(file) : Promise<PayloadBody> {
try {
const payload = await getPayloadBodyFromFile(file);
//TODO: feature flagging
// const buffer = Buffer.from(await new Response(file).arrayBuffer());
// let dataUrl = `data:${file.type};base64,${buffer.toString("base64")}`;
payload.dataUrl = "receipt";
console.log(file2);
// payload.dataUrl = dataUrl;
if (file2) {
payload.extraUrl = file2.name;
}
setPayloadBody(payload);
setFileLoading(false);
@ -229,6 +232,11 @@ function Form(): JSX.Element {
inputFile.current.click();
}
async function showFile2Dialog() {
inputFile2.current.click();
}
async function goToFAQ(e : any) {
e.preventDefault();
window.location.href = '/faq';
@ -487,7 +495,7 @@ function Form(): JSX.Element {
// setAddErrorMessage('Sorry. Apple Wallet pass can be added using Safari or Chrome only.');
// setIsDisabledAppleWallet(true);
// }
const uaIsiOS15 = getUA.includes('15_');
if (isIOS && ((!osVersion.startsWith('15')) && !uaIsiOS15)) {
const message = `Not iOS15 error: osVersion=${osVersion} UA=${getUA}`;
@ -592,6 +600,52 @@ function Form(): JSX.Element {
</div>
}/>
<Card step="3" heading='(Optional) Link a picture to your wallet pass for fast access' content={
<div className="space-y-5">
<p>The picture will be saved in your browser (in local storage, private). You can view it as extra info on your Wallet Pass' details pane.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 items-center justify-start">
<button
type="button"
onClick={showFile2Dialog}
className="h-20 bg-green-600 hover:bg-gray-700 text-white font-semibold rounded-md">
{t('index:openFile')}
</button>
<div id="spin2" className={fileLoading ? undefined : "hidden"}>
<svg className="animate-spin h-5 w-5 ml-4" viewBox="0 0 24 24">
<circle className="opacity-0" cx="12" cy="12" r="10" stroke="currentColor"
strokeWidth="4"/>
<path className="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
</svg>
</div>
</div>
<input type='file'
id='file2'
accept="application/pdf,.png,.jpg,.jpeg,.gif,.webp"
ref={inputFile2}
style={{display: 'none'}}
/>
{(file2) &&
<div className="flex items-center space-x-1">
<svg className="h-4 w-4 text-green-600" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7"/>
</svg>
<span className="w-full truncate">
{
file2 && file2.name
}
</span>
</div>
}
{fileErrorMessages.map((message, i) =>
<Alert message={message} key={'error-' + i} type="error" />
)}
</div>
}/>
{showDoseOption && <Card step="3" heading={'Choose dose number'} content={
<div className="space-y-5">
<p>
@ -615,7 +669,7 @@ function Form(): JSX.Element {
</div>
} />}
<Card step={showDoseOption ? '4' : '3'} heading={t('index:addToWalletHeader')} content={
<Card step={showDoseOption ? '5' : '4'} heading={t('index:addToWalletHeader')} content={
<div className="space-y-5">
<div>
<ul className="list-none">

View File

@ -0,0 +1,64 @@
<html>
<head>
<script>
function loadItem() {
if (navigator.userAgent.indexOf('Safari') < 0) {
document.getElementById('msg').textContent = 'Sorry. For fastlink to work, your iPhone needs to be using Safari as default browser. To change it, you can go to Settings - Chrome/Firefox';
return;
}
const itemKey = location.href.split('=')[1];
console.log(itemKey);
let dataUrl = localStorage.getItem(itemKey);
if (dataUrl) {
const imageElement = document.getElementById('image');
imageElement.setAttribute('src', dataUrl);
document.getElementById('clearButton').hidden = false;
} else {
document.getElementById('msg').innerHTML = `Sorry, ${itemKey} not found in local storage.<p />Creating your wallet pass again would likely fix this issue.`;
}
}
function clearLocalStorage() {
localStorage.clear();
document.getElementById('msg').innerHTML = 'Local storage cleared.';
document.getElementById('clearButton').hidden = true;
}
</script>
<style>
body {
font-family: sans-serif;
}
.center {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.big-text {
font-size: 48;
}
</style>
</head>
<body>
<img id='image' width="100%" />
<br />
<input id='clearButton' type="button" hidden value='Clear Local Storage' onclick="clearLocalStorage()"></input>
<div id='msg' class='center big-text' width="100%" />
<script>
loadItem();
</script>
</body>
</html>

View File

@ -44,6 +44,7 @@ export interface PayloadBody {
receipts: HashTable<Receipt>;
shcReceipt: SHCReceipt;
dataUrl?: string;
extraUrl?: string;
}
export class Payload {
@ -52,6 +53,7 @@ export class Payload {
shcReceipt: SHCReceipt;
rawData: string;
dataUrl?: string;
extraUrl?: string;
backgroundColor: string;
labelColor: string;
foregroundColor: string;
@ -67,6 +69,8 @@ export class Payload {
this.shcReceipt = body.shcReceipt;
this.rawData = body.rawData;
this.dataUrl = body.dataUrl;
this.extraUrl = body.extraUrl;
this.generic = {
headerFields: [],
primaryFields: [],
@ -83,19 +87,27 @@ export class Payload {
this.img1x = Constants.img1xBlack;
this.img2x = Constants.img2xBlack;
let displayLocallyStoredPDFUrl = window.location.href + "displayLocallyStoredPDF.html";
console.log(displayLocallyStoredPDFUrl)
const attributedValue = `<a href="${displayLocallyStoredPDFUrl}">View</a>`;
console.log('*** attributedValue ***');
console.log(attributedValue);
if (this.dataUrl) {
let displayLocallyStoredPDFUrl = window.location.href + "displayLocallyStoredItem.html?item=receipt" ;
const attributedValue = `<a href="${displayLocallyStoredPDFUrl}">View Receipt</a>`;
this.generic.backFields.push({
key: "original",
label: "Original receipt (saved locally in Safari)",
attributedValue: attributedValue
});
}
if (this.extraUrl) {
let extraUrl = window.location.href + "displayLocallyStoredItem.html?item=extra" ;
const attributedValue = `<a href="${extraUrl}">View Extra Info</a>`;
this.generic.backFields.push({
key: "extra",
label: "Extra image",
attributedValue: attributedValue
});
}
//TODO: feature flagging
this.generic.backFields.push({
key: "original",
label: "Original receipt (saved locally in Safari)",
attributedValue: attributedValue
});
}
}
}

View File

@ -6,12 +6,15 @@ import * as Decode from './decode';
import {getScannedJWS, verifyJWS, decodeJWS} from "./shc";
import { PDFPageProxy } from 'pdfjs-dist/types/src/display/api';
import pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";
// import {PNG} from 'pngjs'
// import {decodeData} from "./decode";
// import {Result} from "@zxing/library";
PdfJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PdfJS.version}/pdf.worker.js`
PdfJS.GlobalWorkerOptions.workerSrc = pdfjsWorker;
// PdfJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PdfJS.version}/pdf.worker.js`
export async function getPayloadBodyFromFile(file: File): Promise<PayloadBody> {
// Read file