Merge pull request #70 from covidpass-org/dev

Color selector and translation fixes
This commit is contained in:
Marvin Sextro 2021-07-29 00:34:55 +02:00 committed by GitHub
commit 7a9197be85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 161 additions and 106 deletions

45
components/Colors.tsx Normal file
View File

@ -0,0 +1,45 @@
import React, {useState} from 'react'
import {RadioGroup} from '@headlessui/react'
import {COLORS, rgbToHex} from "../src/colors";
interface ColorsProps {
initialValue: COLORS,
onChange(value: COLORS): void;
}
function Colors(props: ColorsProps): JSX.Element {
let [color, setColor] = useState(props.initialValue)
return (
<RadioGroup<"div", COLORS>
className="flex flex-wrap" value={color}
onChange={function (value) {
setColor(value);
props.onChange(value);
}
}>
{Object.values(COLORS).map((color) => {
return (
<RadioGroup.Option value={color} key={color} className="outline-none">
{({checked}) => (
<div
key={color}
className={`${color !== COLORS.WHITE ? 'ring-white' : 'ring-black'} ring-2 shadow-xl cursor-pointer rounded-md w-10 h-10 flex items-center justify-center m-2`}
style={{backgroundColor: rgbToHex(color), WebkitAppearance: 'none'}}
>
{checked &&
<svg className={`${color !== COLORS.WHITE ? 'text-white' : 'text-black'} h-6 w-6`} fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M5 13l4 4L19 7"/>
</svg>
}
</div>
)}
</RadioGroup.Option>
)
})}
</RadioGroup>
)
}
export default Colors;

View File

@ -1,6 +1,6 @@
import {saveAs} from 'file-saver';
import React, {FormEvent, useEffect, useRef, useState} from "react";
import {BrowserQRCodeReader} from "@zxing/browser";
import {BrowserQRCodeReader, IScannerControls} from "@zxing/browser";
import {Result} from "@zxing/library";
import {useTranslation} from 'next-i18next';
import Link from 'next/link';
@ -11,15 +11,20 @@ import Check from './Check';
import {PayloadBody} from "../src/payload";
import {getPayloadBodyFromFile, getPayloadBodyFromQR} from "../src/process";
import {PassData} from "../src/pass";
import {COLORS} from "../src/colors";
import Colors from './Colors';
function Form(): JSX.Element {
const { t } = useTranslation(['index', 'errors', 'common']);
const {t} = useTranslation(['index', 'errors', 'common']);
// Whether camera is open or not
const [isCameraOpen, setIsCameraOpen] = useState<boolean>(false);
// Currently selected color
const [selectedColor, setSelectedColor] = useState<COLORS>(COLORS.WHITE);
// Global camera controls
const [globalControls, setGlobalControls] = useState(undefined);
const [globalControls, setGlobalControls] = useState<IScannerControls>(undefined);
// Currently selected QR Code / File. Only one of them is set.
const [qrCode, setQrCode] = useState<Result>(undefined);
@ -82,7 +87,7 @@ function Form(): JSX.Element {
setErrorMessage('noCameraAccess');
return;
}
// Check if camera device is present
if (deviceList.length == 0) {
setErrorMessage("noCameraFound");
@ -123,7 +128,7 @@ function Form(): JSX.Element {
event.preventDefault();
setLoading(true);
if(navigator.userAgent.match('CriOS')) {
if (navigator.userAgent.match('CriOS')) {
setErrorMessage('safariSupportOnly');
setLoading(false);
return;
@ -135,7 +140,7 @@ function Form(): JSX.Element {
return;
}
const color = (document.getElementById('color') as HTMLSelectElement).value;
const color = selectedColor;
let payloadBody: PayloadBody;
try {
@ -208,24 +213,7 @@ function Form(): JSX.Element {
<div className="space-y-5">
<p>{t('index:pickColorDescription')}</p>
<div className="relative inline-block w-full">
<select name="color" id="color"
className="bg-gray-200 dark:bg-gray-900 focus:outline-none w-full h-10 pl-3 pr-6 text-base rounded-md appearance-none cursor-pointer">
<option value="white">{t('index:colorWhite')}</option>
<option value="black">{t('index:colorBlack')}</option>
<option value="grey">{t('index:colorGrey')}</option>
<option value="green">{t('index:colorGreen')}</option>
<option value="indigo">{t('index:colorIndigo')}</option>
<option value="blue">{t('index:colorBlue')}</option>
<option value="purple">{t('index:colorPurple')}</option>
<option value="teal">{t('index:colorTeal')}</option>
</select>
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
<svg className="w-5 h-5 fill-current" viewBox="0 0 20 20">
<path
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clipRule="evenodd" fillRule="evenodd"/>
</svg>
</div>
<Colors onChange={setSelectedColor} initialValue={selectedColor}/>
</div>
</div>
}/>
@ -241,13 +229,13 @@ function Form(): JSX.Element {
</p>
<div>
<ul className="list-none">
<Check text={t('createdOnDevice')}></Check>
<Check text={t('openSourceTransparent')}></Check>
<Check text={t('hostedInEU')}></Check>
<Check text={t('createdOnDevice')}/>
<Check text={t('openSourceTransparent')}/>
<Check text={t('hostedInEU')}/>
</ul>
</div>
<label htmlFor="privacy" className="flex flex-row space-x-4 items-center">
<input type="checkbox" id="privacy" value="privacy" required className="h-4 w-4"/>
<input type="checkbox" id="privacy" value="privacy" required className="h-5 w-5 outline-none"/>
<p>
{t('index:iAcceptThe')}&nbsp;
<Link href="/privacy">
@ -259,11 +247,11 @@ function Form(): JSX.Element {
</label>
<div className="flex flex-row items-center justify-start">
<button id="download" type="submit"
className="focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
className="focus:outline-none bg-green-600 py-2 px-3 mt-2 text-white font-semibold rounded-md disabled:bg-gray-400">
{t('index:addToWallet')}
</button>
<div id="spin" className={loading ? undefined : "hidden"}>
<svg className="animate-spin h-5 w-5 ml-3" viewBox="0 0 24 24">
<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"

View File

@ -9,6 +9,7 @@
"start": "next start"
},
"dependencies": {
"@headlessui/react": "^1.3.0",
"@zxing/browser": "^0.0.9",
"@zxing/library": "^0.18.6",
"base45": "^3.0.0",

View File

@ -1,5 +1,5 @@
title: CovidPass
subtitle: Προσθέστε το Ευρωπαϊκό ψηφιακό πιστοποιητικό COVID-19 στην αγαπημένη σας Wallet εφαρμογή.
subtitle: Προσθέστε το Ευρωπαϊκό ψηφιακό πιστοποιητικό COVID-19 στην αγαπημένη σας εφαρμογή Wallet.
privacyPolicy: Πολιτική Απορρήτου
donate: Χορηγία
gitHub: GitHub

View File

@ -17,4 +17,4 @@ invalidTestResult: Μη έγκυρο αποτέλεσμα εξέτασης
invalidTestType: Μη έγκυρος τύπος εξέτασης
noCameraAccess: Δεν ήταν δυνατή η πρόσβαση στην κάμερα. Ελέγξτε τα δικαιώματα στις Ρυθμίσεις > Safari > Κάμερα.
noCameraFound: Δεν μπόρεσε να βρει τη φωτογραφική μηχανή.
safariSupportOnly: Στο iOS, χρησιμοποιήστε το πρόγραμμα περιήγησης Safari.
safariSupportOnly: Στο iOS, χρησιμοποιήστε το πρόγραμμα περιήγησης Safari.

View File

@ -1,19 +1,19 @@
heading: Πληροφορίες σχετικά με την § 5 του Γερμανικού Telemediengesetz (TMG)
contact: Επικοινωνία
euDisputeResolution: Επίλυση Διαφορών στην ΕΕ
euDisputeResolution: Επίλυση διαφορών στην ΕΕ
euDisputeResolutionParagraph: |
Η Ευρωπαϊκή Επιτροπή παρέχει μια πλατφόρμα για διαδικτυακή επίλυση διαφορών (OS): https://ec.europa.eu/consumers/odr
Μπορείτε να βρείτε την διεύθυνση e-mail μας στον παραπάνω σύνδεσμο.
consumerDisputeResolution: Επίλυση διαφορών καταναλωτών / Γενική επιτροπή διαιτησίας
consumerDisputeResolutionParagraph: Δεν είμαστε πρόθυμοι ή υποχρεωμένοι να συμμετάσχουμε σε διαδικασίες επίλυσης διαφορών ενώπιον κάποιας επιτροπής διαιτησίας για τον καταναλωτή.
liabilityForContents: Ευθύνη για το Περιεχόμενο
liabilityForContents: Ευθύνη για το περιεχόμενο
liabilityForContentsParagraph: |
Ως πάροχος μιας υπηρεσίας, είμαστε υπεύθυνοι για το δικό μας περιεχόμενο σε αυτές τις σελίδες, βάσει της § 7 παράγραφο 1 του TMG και σύμφωνα με τους γενικούς νόμους.
Σύμφωνα με τις §§ 8 έως 10 του TMG, δεν είμαστε υποχρεωμένοι να παρακολουθούμε μεταδιδόμενες ή αποθηκευμένες πληροφορίες ή να διερευνούμε περιπτώσεις που υποδηλώνουν παράνομη δραστηριότητα.
Οι υποχρεώσεις για κατάργηση ή αποκλεισμό της χρήσης πληροφοριών, σύμφωνα με τους γενικούς νόμους, παραμένουν ανεπηρέαστες.
Ωστόσο, η ευθύνη στο θέμα αυτό είναι δυνατή μόνο από τη στιγμή κατά την οποία γίνεται γνωστή κάποια παγειωμένη παράβαση του νόμου.
Αν αντιληφθούμε τέτοιες παραβάσεις, θα καταργήσουμε αμέσως το σχετικό περιεχόμενο.
liabilityForLinks: Ευθύνη για Συνδέσμους
liabilityForLinks: Ευθύνη για τους συνδέσμους
liabilityForLinksParagraph: |
Η υπηρεσία που παρέχουμε περιέχει συνδέσμους προς εξωτερικούς ιστότοπους τρίτων, στα περιεχόμενα των οποίων δεν έχουμε καμία επιρροή.
Επομένως, δεν μπορούμε να αναλάβουμε καμία ευθύνη για αυτά τα περιεχόμενα τρίτων.

View File

@ -14,9 +14,9 @@ colorWhite: λευκό
colorBlack: μαύρο
colorGrey: γκρι
colorGreen: πράσινο
colorIndigo: σκούρο μωβ
colorBlue: μπλε
colorPurple: ανοικτό μωβ
colorIndigo: μπλε
colorBlue: γαλάζιο
colorPurple: μωβ
colorTeal: τιρκουάζ
addToWallet: Προσθήκη στο Wallet
dataPrivacyDescription: |
@ -24,6 +24,6 @@ dataPrivacyDescription: |
Για να μπορέσετε να λάβετε μια τεκμηριωμένη απόφαση, παρακαλώ διαβάστε πρώτα την
iAcceptThe: Αποδέχομαι την
privacyPolicy: Πολιτική Απορρήτου
createdOnDevice: Δημιουργήθηκε στη συσκευή σας
OpenSourceTransparent: Ανοιχτού κώδικα και διαφανής
hostedInEU: φιλοξενείται στην ΕΕ
createdOnDevice: Δημιουργείται στη συσκευή σας
openSourceTransparent: Ανοιχτού κώδικα και διαφανής
hostedInEU: Φιλοξενείται στην ΕΕ

View File

@ -21,7 +21,7 @@ dataPrivacyFaq: συχνές ερωτήσεις (FAQ) για το απόρρητ
contact: Επικοινωνία
email: E-mail
website: Ιστότοπος
process: Απλοποιημένη εξήγηση της διαδικασίας
process: Απλοποιημένη επεξήγηση της διαδικασίας
processFirst: Αρχικά, τα ακόλουθα βήματα λαμβάνουν χώρα τοπικά στο πρόγραμμα περιήγησής σας
processSecond: Δεύτερον, τα ακόλουθα βήματα λαμβάνουν χώρα στον διακομιστή μας
processThird: Τέλος, τα ακόλουθα βήματα λαμβάνουν χώρα τοπικά στο πρόγραμμα περιήγησής σας
@ -54,4 +54,4 @@ rightsErasure: Δικαίωμα διαγραφής - έχετε το δικαί
rightsRectification: Δικαίωμα διόρθωσης - έχετε το δικαίωμα να διορθώσετε ανακριβή δεδομένα.
rightsPortability: Δικαίωμα φορητότητας δεδομένων - έχετε το δικαίωμα να μεταφέρετε τα δεδομένα σας από ένα σύστημα επεξεργασίας σε άλλο.
thirdParties: Συνδεδεμένοι πάροχοι τρίτων
appleSync: Η Apple δύναται να συγχρονίσει το πάσο σας μέσω του iCloud
appleSync: Η Apple ενδέχεται να συγχρονίσει το πάσο σας μέσω του iCloud

View File

@ -1,6 +1,6 @@
title: CovidPass
subtitle: Aggiungi i tuoi certificati digitali europei COVID alle tue app Wallet preferite.
privacyPolicy: Politica sulla Riservatezza
subtitle: Aggiungi i tuoi Certificati COVID Digitali UE alle tue wallet app preferite.
privacyPolicy: Privacy Policy
donate: Donazione
gitHub: GitHub
imprint: Menzioni legali
imprint: Menzioni legali

View File

@ -1,20 +1,20 @@
noFileOrQrCode: Perfavore scannerizza un Codice QR, o seleziona un file
signatureFailed: Errore durante la firma del pass sul server
decodingFailed: Impossibile decodificare il payload del Codice QR
decodingFailed: Impossibile decodificare il contenuto del codice QR
invalidColor: Colore non valido
certificateData: Impossibile leggere i dati del certificato
nameMissing: Impossibile leggere il nome
dobMissing: Impossibile leggere la data di nascita
invalidMedicalProduct: Prodotto medico non valido
invalidCountryCode: Codice paese non valido
invalidCountryCode: Codice del paese non valido
invalidManufacturer: Produttore non valido
invalidFileType: Tipo di file non valido
couldNotDecode: Impossibile decodificare il Codice QR dal file
couldNotFindQrCode: Impossibile trovare il Codice QR nel Codice QR fornito dal file
couldNotFindQrCode: Impossibile trovare il Codice QR nel file
invalidQrCode: Codice QR non valido
certificateType: Nessun tipo di certificato valido trovato
invalidTestResult: Risultato del test non valido
invalidTestType: Tipo di test non valido
noCameraAccess: Impossibile accedere alla fotocamera. Controlla le autorizzazioni in Impostazioni > Safari > Fotocamera.
noCameraFound: Impossibile trovare la fotocamera.
safariSupportOnly: Su iOS, usa il browser Safari.
safariSupportOnly: Su iOS, usa il browser Safari.

View File

@ -24,4 +24,4 @@ liabilityForLinksParagraph: |
Se veniamo a conoscenza di eventuali violazioni, rimuoveremo immediatamente tali collegamenti.
credits: Crediti
creditsSource: Con estratti da https://www.e-recht24.de/impressum-generator.html
creditsTranslation: Tradotto con https://www.DeepL.com/Translator (versione gratuita)
creditsTranslation: Tradotto con https://www.DeepL.com/Translator (versione gratuita)

View File

@ -2,11 +2,11 @@ iosHint: Su iOS, si prega di utilizzare il browser Safari.
errorClose: Chiudi
selectCertificate: Seleziona il certificato
selectCertificateDescription: |
Scannerizza il codice QR sul tuo certificato o seleziona uno screenshot o una pagina PDF con il codice QR.
Scannerizza il codice QR sul tuo certificato o seleziona uno screenshot o una pagina PDF con il codice QR.
Nota che la selezione di un file direttamente dalla fotocamera non è supportata.
stopCamera: Blocca Fotocamera
startCamera: Avvia Fotocamera
openFile: Selezionare File
openFile: Seleziona un File
foundQrCode: Codice QR trovato!
pickColor: Seleziona un colore
pickColorDescription: Seleziona un colore di sfondo per il certificato.
@ -22,8 +22,8 @@ addToWallet: Aggiungi a Wallet
dataPrivacyDescription: |
La riservatezza dei dati è di particolare importanza quando si elaborano dati relativi alla salute.
Per poter prendere una decisione informata, si prega di leggere il
iAcceptThe: Accetto il
privacyPolicy: Politica sulla Riservatezza
iAcceptThe: Accetto la
privacyPolicy: Privacy Policy
createdOnDevice: Creato sul tuo dispositivo
openSourceTransparent: Open source e trasparente
hostedInEU: Ospitato nell'UE
hostedInEU: Server nell'UE

View File

@ -1,5 +1,5 @@
gdprNotice: |
La nostra Politica sulla Riservatezza si basa sui termini utilizzati dal legislatore europeo
La nostra Privacy Policy si basa sui termini utilizzati dal legislatore europeo
per l'adozione del Regolamento generale sulla protezione dei dati (GDPR).
generalInfo: Informazione Generale
generalInfoProcess: |
@ -14,8 +14,8 @@ generalInfoLockScreen: Per impostazione predefinita, gli abbonamenti Apple Walle
settings: impostazioni
generalInfoProvider: |
Il provider del server elabora i dati per fornire questo sito.
Per capire meglio quali misure adottano per proteggere i tuoi dati, leggi anche il loro
privacyPolicy: politica sulla riservatezza
Per capire meglio quali misure adottano per proteggere i tuoi dati, leggi anche la loro
privacyPolicy: privacy policy
andThe: e il
dataPrivacyFaq: domande frequenti sulla privacy dei dati
contact: Contatti
@ -54,4 +54,4 @@ rightsErasure: Diritto all'oblio; Cancellazione dei tuoi dati personali.
rightsRectification: Diritto di rettifica; Hai il diritto di correggere i dati inesatti.
rightsPortability: Diritto alla portabilità dei dati; Hai il diritto di trasferire i tuoi dati da un sistema di elaborazione a un altro.
thirdParties: Terze parti collegate
appleSync: Apple potrebbe sincronizzare i tuoi pass tramite iCloud
appleSync: Apple potrebbe sincronizzare i tuoi pass tramite iCloud

31
src/colors.ts Normal file
View File

@ -0,0 +1,31 @@
// Accessible colors from https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/color
export enum COLORS {
WHITE = 'rgb(255, 255, 255)',
BLACK = 'rgb(0, 0, 0)',
GREY = 'rgb(36, 36, 38)',
BLUE = 'rgb(0, 64, 221)',
BROWN = 'rgb(127, 101, 69)',
CYAN = 'rgb(0, 113, 164)',
GREEN = 'rgb(36, 138, 61)',
INDIGO = 'rgb(54, 52, 163)',
MINT = 'rgb(12, 129, 123)',
ORANGE = 'rgb(201, 52, 0)',
PINK = 'rgb(211, 15, 69)',
PURPLE = 'rgb(137, 68, 171)',
RED = 'rgb(215, 0, 21)',
TEAL = 'rgb(0, 130, 153)',
YELLOW = 'rgb(178, 80, 0)'
}
export function rgbToHex(rgbString: string) {
let a = rgbString.split("(")[1].split(")")[0];
let values = a.split(",");
let b = values.map(function (value) {
let x = parseInt(value).toString(16);
return (x.length == 1) ? "0" + x : x;
})
return "#" + b.join("");
}

View File

@ -3,17 +3,6 @@ export class Constants {
public static PASS_IDENTIFIER = 'pass.de.marvinsextro.covidpass'
public static TEAM_IDENTIFIER = 'X8Q7Q2RLTD'
public static COLORS = {
white: 'rgb(255, 255, 255)',
black: 'rgb(0, 0, 0)',
grey: 'rgb(33, 33, 33)',
green: 'rgb(27, 94, 32)',
indigo: 'rgb(26, 35, 126)',
blue: 'rgb(1, 87, 155)',
purple: 'rgb(74, 20, 140)',
teal: 'rgb(0, 77, 64)',
}
public static img1xBlack: Buffer = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAABU0lEQVR4AWIYaWAUiAExoB06gGggDOMwripJthAgGwBoCGYDBIDIgAFBwDAABoJjhBgEAEChBBBaAA0JyeKAqR0hmnWx3p5o8MHdvfd9Z7SHH8Dr723iCpdoYBOZtoJ9XOALYghxjj0sw1k7OEEAiekVxyjBShto4h6SUg8N5KGqhCHEshdsI3FdiCM3SNwnxA1uKxKXZm3QfJCPQ3RmYVAfW5j2YH+QfkweQ1uDviEmdNHBR8SYddxCDOC2ojeI4RlL+K2Kd8UYcFvRE8TQxyKmVdFLOAbcVnQNMeEUCzCKPQbcVnQEiRilGQNuK9qFRI1SjAG3Fa0iiDh8hgPcQWIKwG1dHsQyD+qKCCGWhCgiVZ7T7yhagw9JyQe37FTGCKI0QhlWq2GiGDNBDU6qYwyJaYw6nFbBABJhgAoyKYc2QoghRBs5ZF4BLTz+aaGAef+nHwt5/579e2c2AAAAAElFTkSuQmCC', 'base64');
public static img2xBlack: Buffer = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAACZ0lEQVR4Ae3TA8xcURRF4dq2ozqqbRtxUjeq7Zi127C2bXvSsLZtu/PP6ardjs577765K/li7mQnizGbzWaz2Wx50BXjMRYdkR0JXRq0xVq8g/ziDZaiGVIiYSqLybgPCdMtjEZpP1+oP45CYnQYPZDd7xeKnPMX1L+QAoULmnUhX12wVrwupHjBKnC8tFgEMcRcpIFjrYQYZhkcqQXEUM2h3haIoTZDvRsQQ92AeiGIoUJQTwxnB7ID2YHsQHYgO5B7zmMlztiBfhbCCKQAJUuOYbADIYRe+FP7TB1IfxyiUaYOpD8O0TJzB9IfpyqCZg6kP05ZPIBESL0gJAIBVENONMRJF8cJQr1nkDBdRWb8WBYEHB8HeAb1bkPCNB5E/xlJfRzgNtQ7CQnTWNB/R9IfBzgJ9TZCwnQJGcMYSX8cYCPUmw6JwCqkwt9K5cg4wHSo1x0SoZX/GUJ/HKA71KsAURhJdxygAtRLg1cKI2mP8wpp4EibIQoj6YwDbIZj9YIojKQzDtALjlUESZAYrEN2fK2u4jhJKAJH2wmJ0UOsRQBJECU74XjtIYZoD8dLi1sQj7uFtHClIRCPGwLXyox7EI+6h8xwtR4Qj+oB10uFExCPOYFU8ERVEIR4RBBV4KlGQTxiFDxXWgQgLgsgLTxZQdyBuOQOCsLTVcELiMNeoAqMqBHeQhzyFo1gVC3wCqLsFVrAyGrgMUTJY9SA0RXDMYVxjqEYfFEGzITEyUxkgO9qhEuQKF1CI/i69BiCB5AwPcAQpEfClBUDcR7yF+cxEFmR0NXDVFz5YirqwWaz2Ww2W9R9AE/cBAw+cEeMAAAAAElFTkSuQmCC', 'base64')
public static img1xWhite: Buffer = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAQAAABLCVATAAABUUlEQVRIx+XWP0tCURjH8cf+IBLaELSEvoIcWkRHX0AQbm4tbQ7uTi4XhIbAxfdQQ9HqkK05RINIgZtUgoOQ6A3026Sc9D73jzblb73P/XAOnPM8R2RTwyFF7rmjwMGqxC5n3PLNLDY3nLITDDnhih5O+eCSpB9inyLPeKVJgZgbk+QTv3nnWIcaBMmDDo0DQWMdCpj/A3W4oLo+9MqRiAgv60EzJmaeNB2aGr82qPK1wOzxaFRMdag/L3pjW4QMA5WBvg61ja1siYiQoakw0NahulFWI2R8WWagrkPlX4VzypGBsg5lF0prhFQGsjoUXmpn15zz5Mj0CLt1JMv3/bDcO2QC2xdjk/BqttYfrEdEhAgdT6ZDxM8ASDF0ZYak/I6jHBOVmZALMtnyjByZEfmgkzZNd4npkl5laEepGIfBpkJ09UdEnBItWpSIb+xL6gcK3j+JspcAUAAAAABJRU5ErkJggg==', 'base64')

View File

@ -1,10 +1,11 @@
import {ValueSets} from "./value_sets";
import {Constants} from "./constants";
import {COLORS} from "./colors";
enum CertificateType {
Vaccine = 'Vaccine',
Test = 'Test',
Recovery = 'Recovery',
Vaccination = 'Vaccination Card',
Test = 'Test Certificate',
Recovery = 'Recovery Certificate',
}
enum TextAlignment {
@ -27,7 +28,7 @@ export interface PassDictionary {
}
export interface PayloadBody {
color: string;
color: COLORS;
rawData: string;
decodedData: Uint8Array;
}
@ -48,13 +49,7 @@ export class Payload {
constructor(body: PayloadBody, valueSets: ValueSets) {
let colors = Constants.COLORS;
if (!(body.color in colors)) {
throw new Error('invalidColor');
}
const dark = body.color != 'white'
const dark = body.color != COLORS.WHITE;
const healthCertificate = body.decodedData['-260'];
const covidCertificate = healthCertificate['1']; // Version number subject to change
@ -76,13 +71,13 @@ export class Payload {
const firstName = nameInformation['gn'];
const lastName = nameInformation['fn'];
const transliteratedFirstName = nameInformation['gnt'].replaceAll('<', ' ');
const transliteratedLastName = nameInformation['fnt'].replaceAll('<', ' ');
// Check if name contains non-latin characters
const nameRegex = new RegExp('^[\\p{Script=Latin}\\p{P}\\p{M}\\p{Z}]+$', 'u');
let name: string;
if (nameRegex.test(firstName) && nameRegex.test(lastName)) {
@ -95,7 +90,7 @@ export class Payload {
// Set certificate type and properties
if (covidCertificate['v'] !== undefined) {
this.certificateType = CertificateType.Vaccine;
this.certificateType = CertificateType.Vaccination;
properties = covidCertificate['v'][0];
}
if (covidCertificate['t'] !== undefined) {
@ -126,7 +121,7 @@ export class Payload {
{
key: "type",
label: "EU Digital COVID",
value: this.certificateType + " Certificate"
value: this.certificateType
}
],
primaryFields: [
@ -155,9 +150,9 @@ export class Payload {
// Set Values
this.rawData = body.rawData;
this.backgroundColor = dark ? colors[body.color] : colors.white
this.labelColor = dark ? colors.white : colors.grey
this.foregroundColor = dark ? colors.white : colors.black
this.backgroundColor = dark ? body.color : COLORS.WHITE
this.labelColor = dark ? COLORS.WHITE : COLORS.GREY
this.foregroundColor = dark ? COLORS.WHITE : COLORS.BLACK
this.img1x = dark ? Constants.img1xWhite : Constants.img1xBlack
this.img2x = dark ? Constants.img2xWhite : Constants.img2xBlack
this.dark = dark;
@ -167,7 +162,7 @@ export class Payload {
static fillPassData(type: CertificateType, data: PassDictionary, properties: Object, valueSets: ValueSets, country: string, dateOfBirth: string): PassDictionary {
switch (type) {
case CertificateType.Vaccine:
case CertificateType.Vaccination:
const dose = `${properties['dn']}/${properties['sd']}`;
const dateOfVaccination = properties['dt'];
const medialProductKey = properties['mp'];
@ -188,7 +183,7 @@ export class Payload {
key: "dose",
label: "Dose",
value: dose
},
},
{
key: "dov",
label: "Date of Vaccination",
@ -232,7 +227,7 @@ export class Payload {
const testDateTimeString = properties['sc'];
const testResultKey = properties['tr'];
const testingCentre = properties['tc'];
if (!(testResultKey in valueSets.testResults)) {
throw new Error('invalidTestResult');
}
@ -244,7 +239,7 @@ export class Payload {
const testType = valueSets.testTypes[testTypeKey].display;
const testTime = testDateTimeString.replace(/.*T/, '').replace('Z', ' ') + 'UTC';
const testDate = testDateTimeString.replace(/T.*/,'');
const testDate = testDateTimeString.replace(/T.*/, '');
data.secondaryFields.push(...[
{
@ -343,7 +338,7 @@ export class Payload {
default:
throw new Error('certificateType');
}
return data;
}
}

View File

@ -4,10 +4,11 @@ import * as PdfJS from 'pdfjs-dist'
import jsQR, {QRCode} from "jsqr";
import {decodeData} from "./decode";
import {Result} from "@zxing/library";
import {COLORS} from "./colors";
PdfJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PdfJS.version}/pdf.worker.js`
export async function getPayloadBodyFromFile(file: File, color: string): Promise<PayloadBody> {
export async function getPayloadBodyFromFile(file: File, color: COLORS): Promise<PayloadBody> {
// Read file
const fileBuffer = await file.arrayBuffer();
@ -59,7 +60,7 @@ export async function getPayloadBodyFromFile(file: File, color: string): Promise
}
}
export async function getPayloadBodyFromQR(qrCodeResult: Result, color: string): Promise<PayloadBody> {
export async function getPayloadBodyFromQR(qrCodeResult: Result, color: COLORS): Promise<PayloadBody> {
// Get raw data
let rawData = qrCodeResult.getText();

View File

@ -1,12 +1,12 @@
module.exports = {
mode: 'jit',
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
darkMode: 'media',
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
mode: 'jit',
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
darkMode: 'media',
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}

View File

@ -80,6 +80,11 @@
resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz"
integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==
"@headlessui/react@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.3.0.tgz#62287c92604923e5dfb394483e5ec2463e1baea6"
integrity sha512-2gqTO6BQ3Jr8vDX1B67n1gl6MGKTt6DBmR+H0qxwj0gTMnR2+Qpktj8alRWxsZBODyOiBb77QSQpE/6gG3MX4Q==
"@next/env@11.0.1":
version "11.0.1"
resolved "https://registry.npmjs.org/@next/env/-/env-11.0.1.tgz"