This commit is contained in:
Billy Lo 2021-09-23 19:15:26 -04:00
commit a61ab43551
5 changed files with 99 additions and 59 deletions

View File

@ -1,24 +1,39 @@
import {useTranslation} from 'next-i18next';
import { useTranslation } from 'next-i18next';
interface AlertProps {
onClose: () => void;
errorMessage: string;
onClose?: () => void;
type: 'error' | 'warning';
message: string;
}
function Alert(props: AlertProps): JSX.Element {
const { t } = useTranslation(['index', 'errors']);
let color = 'red';
let icon;
switch (props.type) {
case 'error':
color = 'red'
// icon = () =>
// <svg className="w-4 h-4 fill-current" viewBox="0 0 20 20"><path d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
break;
case 'warning':
color = 'yellow'
break;
}
return (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 mt-5 rounded relative" role="alert">
<span className="block sm:inline pr-6" id="message">{props.errorMessage}</span>
<span className="absolute top-0 bottom-0 right-0 px-4 py-3" onClick={props.onClose}>
<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>{t('index:errorClose')}</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>
</span>
<div className={`flex items-center bg-red-100 border border-red-400 text-red-700 px-4 py-3 mt-5 rounded relative`} role="alert">
{icon && icon()}
<span className="block sm:inline pr-6" id="message">{props.message}</span>
{props.onClose && <span className="absolute right-0 px-4 py-3" onClick={props.onClose}>
<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>{t('index:errorClose')}</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>
</span>}
</div>
)
}

View File

@ -36,11 +36,13 @@ function Form(): JSX.Element {
const [qrCode, setQrCode] = useState<Result>(undefined);
const [file, setFile] = useState<File>(undefined);
const [errorMessage, _setErrorMessage] = useState<string>(undefined);
const [loading, setLoading] = useState<boolean>(false);
const [passCount, setPassCount] = useState<string>('');
const [generated, setGenerated] = useState<boolean>(false); // this flag represents the file has been used to generate a pass
const [isDisabledAppleWallet, setIsDisabledAppleWallet] = useState<boolean>(false);
const [errorMessages, _setErrorMessages] = useState<Array<string>>([]);
const hitcountHost = 'https://stats.vaccine-ontario.ca';
@ -75,14 +77,18 @@ function Form(): JSX.Element {
// Check if there is a translation and replace message accordingly
const setErrorMessage = (message: string) => {
if (message == undefined) {
_setErrorMessage(undefined);
return;
}
const translation = t('errors:'.concat(message));
_setErrorMessage(translation !== message ? translation : message);
_setErrorMessages(Array.from(new Set([...errorMessages, translation !== message ? translation : message])));
};
const deleteErrorMessage = (message: string) =>{
console.log(errorMessages)
_setErrorMessages(errorMessages.filter(item => item !== message))
}
// File Input ref
const inputFile = useRef<HTMLInputElement>(undefined)
@ -95,6 +101,8 @@ function Form(): JSX.Element {
setQrCode(undefined);
setFile(selectedFile);
setGenerated(false);
deleteErrorMessage(t('errors:'.concat('noFileOrQrCode')));
checkBrowserType();
}
});
}
@ -261,7 +269,7 @@ function Form(): JSX.Element {
setLoading(true);
if (!file && !qrCode) {
setErrorMessage('noFileOrQrCode')
setErrorMessage('noFileOrQrCode');
setLoading(false);
return;
}
@ -289,25 +297,38 @@ function Form(): JSX.Element {
setLoading(false);
}
}
const verifierLink = () => <li className="flex flex-row items-center">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mx-2 fill-current text-green-500" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
<p>
{t('verifierLink')}&nbsp;
<Link href="https://verifier.vaccine-ontario.ca/">
<a className="underline">verifier.vaccine-ontario.ca </a>
</Link>
</p>
</li>
async function checkBrowserType() {
function checkBrowserType() {
// if (isIPad13) {
// setErrorMessage('Sorry. Apple does not support the use of Wallet on iPad. Please use iPhone/Safari.');
// document.getElementById('download').setAttribute('disabled','true');
// setIsDisabledAppleWallet(true);
// }
// if (!isSafari && !isChrome) {
// setErrorMessage('Sorry. Apple Wallet pass can be added using Safari or Chrome only.');
// setIsDisabledAppleWallet(true);
// }
if (!isSafari && !isChrome) {
setErrorMessage('Sorry. Apple Wallet pass can be added using Safari or Chrome only.');
document.getElementById('download').setAttribute('disabled','true');
}
// if (isIOS && (!osVersion.includes('13') && !osVersion.includes('14') && !osVersion.includes('15'))) {
// setErrorMessage('Sorry, iOS 13+ is needed for the Apple Wallet functionality to work')
// document.getElementById('download').setAttribute('disabled','true')
// }
// if (isIOS && !isSafari) {
// setErrorMessage('Sorry, only Safari can be used to add a Wallet Pass on iOS')
// document.getElementById('download').setAttribute('disabled','true')
// setIsDisabledAppleWallet(true);
// }
if (isIOS && !isSafari) {
// setErrorMessage('Sorry, only Safari can be used to add a Wallet Pass on iOS');
setErrorMessage('Sorry, only Safari can be used to add a Wallet Pass on iOS');
setIsDisabledAppleWallet(true);
console.log('not safari')
}
}
return (
@ -374,20 +395,20 @@ function Form(): JSX.Element {
<Card step="3" heading={t('index:addToWalletHeader')} content={
<div className="space-y-5">
<p>
{/* <p>
{t('index:dataPrivacyDescription')}
{/* <Link href="/privacy">
<Link href="/privacy">
<a>
{t('index:privacyPolicy')}
</a>
</Link>. */}
</p>
</Link>.
</p> */}
<div>
<ul className="list-none">
<Check text={t('createdOnDevice')}/>
<Check text={t('qrCode')}/>
<Check text={t('openSourceTransparent')}/>
<Check text={t('verifierLink')}/>
{verifierLink()}
{passCount && <Check text={passCount + ' ' + t('numPasses')}/>}
{/* <Check text={t('hostedInEU')}/> */}
@ -395,8 +416,8 @@ function Form(): JSX.Element {
</div>
<div className="flex flex-row items-center justify-start">
<button id="download" type="submit" value='applewallet' name='action'
className="focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
<button disabled={isDisabledAppleWallet} id="download" type="submit" value='applewallet' name='action'
className="focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
{t('index:addToWallet')}
</button>
&nbsp;&nbsp;&nbsp;&nbsp;
@ -414,13 +435,13 @@ function Form(): JSX.Element {
</svg>
</div>
</div>
{errorMessages.map((message, i) =>
<Alert message={message} key={'error-' + i} type="error" />
)}
</div>
}/>
</form>
<canvas id="canvas" style={{display: "none"}}/>
{
errorMessage && <Alert errorMessage={errorMessage} onClose={() => setErrorMessage(undefined)}/>
}
<canvas id="canvas" style={{ display: "none" }} />
</div>
)
}

View File

@ -7,20 +7,21 @@ import Card from '../components/Card'
function Faq(): JSX.Element {
const { t } = useTranslation(['common', 'index', 'faq']);
const questionList = [
{description: 'Which version of iOS does this support?', answer: 'iOS 13.7 is the minimum at the moment. We are looking for adjustments for older iOSes, but it will take a bit of time.', key: 1},
{description: 'Why have we taken time to build this?', answer: 'Gives Ontarians/organizations something easy to use (volunteered-developed, unofficial) until the official provincial app comes out in October.', key: 2},
{description: 'Who made this?', answer: 'The same group of volunteers (Billy Lo, Ryan Slobojan, Evert Timberg, Jason Liu, Anujan Mathisekaran, Lisa Discepola, Samantha Finn, Madison Pearce) who created the all-in-one vaccine appointment finding tool at vaccine-ontario.ca.', key: 3},
{description: 'Should I use the official provincial apps when they come out on 22nd October?', answer: 'YES. Once the official QR code from the province is available, you will also be able to refresh what\'s in your Apple Wallet as well. More details will follow.', key: 4},
{description: 'How is the data on my vaccination receipt processed?', answer: 'It checks the receipt for an official signature from the province. If present, the receipt data is converted into Apple\'s format and then added into your iOS Wallet app.', key: 5},
{description: 'How can organizations validate this QR code?', answer: 'Just aim your standard camera app (iPhone/Android) at the code, and it will bring up a web page that shows the verification result.', key: 6},
{description: 'Is this free and private?', answer: 'Similar to VaxHuntersCanada, there are no commerical interests. Just volunteers trying to do our part to help the community.', key: 7},
{description: 'Do you have plans for Android support?', answer: 'Yes. We are working with Google to gain access to the APIs required. Meanwhile, you can also use this tool to download an Apple Wallet pass and import that into Google Pay Wallet using apps such as Pass2Pay.', key: 8},
{description: 'How about BC, Quebec and Alberta?', answer: 'We will be investigating BC shortly. If you are interested in contributing, email us at grassroots@vaccine-ontario.ca', key: 9},
{description: 'How can I stay up-to-date on your progress?', answer: 'We will post regular updates on Twitter @grassroots_team.', key: 10},
{description: 'I only have an emailed receipt (e.g. Red/White OHIP card users). Can I still use this tool?', answer: 'Not right now unfortunately. But we expect to be able to support that after the official app is released on 22 Oct.', key: 11},
{description: 'What does the colour of the Apple Wallet pass mean?', answer: 'Dose 1 is shown as Orange; dose 2+ in green for easy differentiation without reading the text.', key: 12},
{description: 'How about Apple Watch?', answer: 'If you have iCloud sync enabled, you will see the pass on the watch too.', key: 13},
{description: 'I have more questions. Can you please help me?', answer: 'Sure. Just email us at grassroots@vaccine-ontario.ca.', key: 14}
{description: 'Which version of iOS does this support?', answer: 'iOS 13.7 is the minimum at the moment. We are looking for adjustments for older iOSes, but it will take a bit of time.'},
{description: "What are the supported browsers?", answer: 'For iPhones, only Safari is supported for importing to your Apple Wallet. For any other devices, we recommend that you save it as photo using your browser of choice. Broswers built internally into mobile apps (e.g. Facebook, Twitter, Instagram) are known to have issues.'},
{description: 'Why have we taken time to build this?', answer: 'Gives Ontarians/organizations something easy to use (volunteered-developed, unofficial) until the official provincial app comes out in October.'},
{description: 'Who made this?', answer: 'The same group of volunteers (Billy Lo, Ryan Slobojan, Evert Timberg, Jason Liu, Anujan Mathisekaran, Lisa Discepola, Samantha Finn, Madison Pearce) who created the all-in-one vaccine appointment finding tool at vaccine-ontario.ca.'},
{description: 'Should I use the official provincial apps when they come out on 22nd October?', answer: 'YES. Once the official QR code from the province is available, you will also be able to refresh what\'s in your Apple Wallet as well. More details will follow.'},
{description: 'How is the data on my vaccination receipt processed?', answer: 'It checks the receipt for an official signature from the province. If present, the receipt data is converted into Apple\'s format and then added into your iOS Wallet app.'},
{description: 'How can organizations validate this QR code?', answer: 'Just aim your standard camera app (iPhone/Android) at the code, and it will bring up a web page that shows the verification result.'},
{description: 'Is this free and private?', answer: 'Similar to VaxHuntersCanada, there are no commerical interests. Just volunteers trying to do our part to help the community.'},
{description: 'Do you have plans for Android support?', answer: 'Yes. We are working with Google to gain access to the APIs required. Meanwhile, you can also use this tool to download an Apple Wallet pass and import that into Google Pay Wallet using apps such as Pass2Pay.'},
{description: 'How about BC, Quebec and Alberta?', answer: 'We will be investigating BC shortly. If you are interested in contributing, email us at grassroots@vaccine-ontario.ca'},
{description: 'How can I stay up-to-date on your progress?', answer: 'We will post regular updates on Twitter @grassroots_team.'},
{description: 'I only have an emailed receipt (e.g. Red/White OHIP card users). Can I still use this tool?', answer: 'Not right now unfortunately. But we expect to be able to support that after the official app is released on 22 Oct.'},
{description: 'What does the colour of the Apple Wallet pass mean?', answer: 'Dose 1 is shown as Orange; dose 2+ in green for easy differentiation without reading the text.'},
{description: 'How about Apple Watch?', answer: 'If you have iCloud sync enabled, you will see the pass on the watch too.'},
{description: 'I have more questions. Can you please help me?', answer: 'Sure. Just email us at grassroots@vaccine-ontario.ca.'}
];
@ -30,11 +31,11 @@ function Faq(): JSX.Element {
<div className="space-y-3">
<p className="font-bold">{t('faq:heading')}</p>
<ol>
{questionList.map(question => {
{questionList.map((question, i) => {
return (
<div>
<li key={question.key}><b>{question.key}. {question.description}</b></li>
<li key={question.key}>{question.answer}</li>
<li key={i}><b>{i+1}. {question.description}</b></li>
<li key={i}>{question.answer}</li>
<br></br>
</div>
);

View File

@ -5,11 +5,14 @@ import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
import Form from '../components/Form';
import Card from '../components/Card';
import Page from '../components/Page';
import Alert from '../components/Alert';
import { useEffect, useState } from 'react';
function Index(): JSX.Element {
const { t } = useTranslation(['common', 'index', 'errors']);
const [warning, setWarning] = useState("If you previously created a vaccination receipt before Sept. 23rd and need to add your date of birth on your vaccination receipt, please reimport your Ministry of Health official vaccination receipt again below and the date of birth will now be visible on the created receipt.")
const title = 'Grassroots - Ontario vaccination receipt to your Apple wallet';
const description = 'Stores it on iPhone with a QR code for others to validate in a privacy respecting way.';
@ -40,10 +43,10 @@ function Index(): JSX.Element {
/>
<Page content={
<div className="space-y-5">
{warning && <Alert type="error" onClose={() => setWarning(undefined)} message={warning} />}
<Card content={
<div><p>{t('common:subtitle')}</p><br /><p>{t('common:subtitle2')}</p><br /><p><b>{t('common:update1Date')}</b> - {t('common:update1')}</p><br /><p>{t('common:continueSpirit')}</p></div>
}/>
<Form/>
</div>
}/>

View File

@ -13,7 +13,7 @@ downloadReceipt: Download official receipt from Ontario Ministry of Health
visit: Visit
ontarioHealth: Ontario Ministry of Health
gotoOntarioHealth: Go to Ontario Ministry of Health
downloadSignedPDF: and enter your information to display your official vaccination receipt. Press the Share Icon at the bottom, "Save As Files" to store it onto your iPhone.
downloadSignedPDF: and enter your information to display your official vaccination receipt. Press the Share Icon at the bottom, "Save As Files" to store it onto your iPhone. If you have completed this step before, you can proceed to the next step to prevent downloading the same file multiple times.
pickColor: Pick a Color
pickColorDescription: Pick a background color for your pass.
colorWhite: white
@ -34,7 +34,7 @@ privacyPolicy: Privacy Policy
createdOnDevice: No personal data is sent to the Internet.
qrCode: QR code is for verification only, with no personal info.
openSourceTransparent: Source code is free and open for re-use/contributions on GitHub.
verifierLink: QR code verifier available at https://verifier.vaccine-ontario.ca
verifierLink: QR code verifier available at
numPasses: receipts processed since Sept 2, 2021
demo: Video Demo
whatsnew: What's New