diff --git a/components/Alert.tsx b/components/Alert.tsx index 7ee7465..4fffdfb 100644 --- a/components/Alert.tsx +++ b/components/Alert.tsx @@ -10,7 +10,7 @@ function Alert(props: AlertProps): JSX.Element { return (
- {props.errorMessage} + {props.errorMessage} diff --git a/components/Form.tsx b/components/Form.tsx index fee3978..e053b42 100644 --- a/components/Form.tsx +++ b/components/Form.tsx @@ -29,6 +29,11 @@ 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); }; @@ -68,7 +73,20 @@ function Form(): JSX.Element { const codeReader = new BrowserQRCodeReader(); // Needs to be called before any camera can be accessed - await BrowserQRCodeReader.listVideoInputDevices(); + let deviceList: MediaDeviceInfo[]; + + try { + deviceList = await BrowserQRCodeReader.listVideoInputDevices(); + } catch (e) { + setErrorMessage('noCameraAccess'); + return; + } + + // Check if camera device is present + if (deviceList.length == 0) { + setErrorMessage("noCameraFound"); + return; + } // Get preview Element to show camera stream const previewElem: HTMLVideoElement = document.querySelector('#cameraPreview'); @@ -89,6 +107,9 @@ function Form(): JSX.Element { setGlobalControls(undefined); setIsCameraOpen(false); } + if (error !== undefined) { + setErrorMessage(error.message); + } } ) ); @@ -101,6 +122,12 @@ function Form(): JSX.Element { event.preventDefault(); setLoading(true); + if(navigator.userAgent.match('CriOS')) { + setErrorMessage('safariSupportOnly'); + setLoading(false); + return; + } + if (!file && !qrCode) { setErrorMessage('noFileOrQrCode') setLoading(false); @@ -228,7 +255,7 @@ function Form(): JSX.Element { {t('index:addToWallet')}
- + Safari > Kamera. +noCameraFound: Keine Kamera gefunden. +safariSupportOnly: Bitte verwende unter iOS den Safari Browser. \ No newline at end of file diff --git a/public/locales/en/errors.yml b/public/locales/en/errors.yml index 828ec38..0dcc155 100644 --- a/public/locales/en/errors.yml +++ b/public/locales/en/errors.yml @@ -14,4 +14,7 @@ couldNotFindQrCode: Could not find QR Code in provided file invalidQrCode: Invalid QR code certificateType: No valid certificate type found invalidTestResult: Invalid test result -invalidTestType: Invalid test type \ No newline at end of file +invalidTestType: Invalid test type +noCameraAccess: Could not access camera. Check permissions under Settings > Safari > Camera. +noCameraFound: Could not find camera. +safariSupportOnly: On iOS, please use the Safari Browser. \ No newline at end of file diff --git a/public/locales/fi/errors.yml b/public/locales/fi/errors.yml index 8e7bbe2..35ab1fd 100644 --- a/public/locales/fi/errors.yml +++ b/public/locales/fi/errors.yml @@ -14,4 +14,7 @@ couldNotFindQrCode: QR-koodia ei löytynyt annetusta tiedostosta invalidQrCode: Virheellinen QR-koodi certificateType: Kelvollista varmennetyyppiä ei löytynyt invalidTestResult: Virheellinen testitulos -invalidTestType: Virheellinen testityyppi \ No newline at end of file +invalidTestType: Virheellinen testityyppi +noCameraAccess: Kameraan ei päässyt käsiksi. Tarkista käyttöoikeudet kohdasta Asetukset > Safari > Kamera. +noCameraFound: En löytänyt kameraa. +safariSupportOnly: Käytä iOS:ssä Safari-selainta. \ No newline at end of file diff --git a/public/locales/fr/errors.yml b/public/locales/fr/errors.yml index 680e039..dc3119e 100644 --- a/public/locales/fr/errors.yml +++ b/public/locales/fr/errors.yml @@ -14,4 +14,7 @@ couldNotFindQrCode: Impossible de trouver le QR Code dans le fichier fourni invalidQrCode: QR Code non valide certificateType: Aucun type de certificat valide n'a été trouvé invalidTestResult: Résultat du test non valide -invalidTestType: Type de test non valide \ No newline at end of file +invalidTestType: Type de test non valide +noCameraAccess: Impossible d'accéder à la caméra. Vérifiez les autorisations sous Paramètres > Safari > Appareil photo. +noCameraFound: Impossible de trouver la caméra. +safariSupportOnly: Sur iOS, veuillez utiliser le navigateur Safari. \ No newline at end of file diff --git a/public/locales/nl/errors.yml b/public/locales/nl/errors.yml index 1614c80..1677032 100644 --- a/public/locales/nl/errors.yml +++ b/public/locales/nl/errors.yml @@ -14,4 +14,7 @@ couldNotFindQrCode: Kon de QR-code niet vinden in het verstrekte bestand invalidQrCode: Ongeldige QR-code certificateType: Geen geldig certificaattype gevonden invalidTestResult: Ongeldig testresultaat -invalidTestType: Ongeldig testtype \ No newline at end of file +invalidTestType: Ongeldig testtype +noCameraAccess: Kon geen toegang krijgen tot de camera. Controleer de machtigingen onder Instellingen > Safari > Camera. +noCameraFound: Ik kon de camera niet vinden. +safariSupportOnly: Gebruik op iOS de Safari-browser. \ No newline at end of file diff --git a/src/payload.ts b/src/payload.ts index e45d882..38f651a 100644 --- a/src/payload.ts +++ b/src/payload.ts @@ -2,7 +2,7 @@ import {ValueSets} from "./value_sets"; import {Constants} from "./constants"; enum CertificateType { - Vaccination = 'Vaccination', + Vaccine = 'Vaccine', Test = 'Test', Recovery = 'Recovery', } @@ -63,25 +63,35 @@ export class Payload { throw new Error('certificateData'); } - // Get name and date of birth information + // Get name information const nameInformation = covidCertificate['nam']; - const dateOfBirthInformation = covidCertificate['dob']; if (nameInformation == undefined) { throw new Error('nameMissing'); } - if (dateOfBirthInformation == undefined) { - throw new Error('dobMissing'); - } - const name = `${nameInformation['fn']}, ${nameInformation['gn']}`; - const dateOfBirth = dateOfBirthInformation; + 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)) { + name = `${firstName} ${lastName}`; + } else { + name = `${transliteratedFirstName} ${transliteratedLastName}`; + } let properties: object; // Set certificate type and properties if (covidCertificate['v'] !== undefined) { - this.certificateType = CertificateType.Vaccination; + this.certificateType = CertificateType.Vaccine; properties = covidCertificate['v'][0]; } if (covidCertificate['t'] !== undefined) { @@ -96,23 +106,16 @@ export class Payload { throw new Error('certificateType') } - // Get country code, identifier and issuer - const countryCode = properties['co']; + // Get identifier and issuer const uvci = properties['ci']; const certificateIssuer = properties['is']; - if (!(countryCode in valueSets.countryCodes)) { - throw new Error('invalidCountryCode'); - } - - const country = valueSets.countryCodes[countryCode].display; - const generic: PassDictionary = { headerFields: [ { key: "type", - label: "Certificate Type", - value: this.certificateType + label: "EU Digital COVID", + value: this.certificateType + " Certificate" } ], primaryFields: [ @@ -123,14 +126,7 @@ export class Payload { } ], secondaryFields: [], - auxiliaryFields: [ - { - key: "dob", - label: "Date of Birth", - value: dateOfBirth, - textAlignment: TextAlignment.right - } - ], + auxiliaryFields: [], backFields: [ { key: "uvci", @@ -141,11 +137,6 @@ export class Payload { key: "issuer", label: "Certificate Issuer", value: certificateIssuer - }, - { - key: "country", - label: "Country", - value: country } ] } @@ -164,8 +155,17 @@ export class Payload { } static fillPassData(type: CertificateType, data: PassDictionary, properties: Object, valueSets: ValueSets): PassDictionary { + // Get country name + const countryCode = properties['co']; + + if (!(countryCode in valueSets.countryCodes)) { + throw new Error('invalidCountryCode'); + } + + const country = valueSets.countryCodes[countryCode].display; + switch (type) { - case CertificateType.Vaccination: + case CertificateType.Vaccine: const dose = `${properties['dn']}/${properties['sd']}`; const dateOfVaccination = properties['dt']; const medialProductKey = properties['mp']; @@ -178,7 +178,7 @@ export class Payload { throw new Error('invalidManufacturer') } - const vaccineName = valueSets.medicalProducts[medialProductKey].display; + const vaccineName = valueSets.medicalProducts[medialProductKey].display.replace(/\s*\([^)]*\)\s*/g, ""); const manufacturer = valueSets.manufacturers[manufacturerKey].display; data.secondaryFields.push(...[ @@ -194,11 +194,19 @@ export class Payload { textAlignment: TextAlignment.right } ]); - data.auxiliaryFields.splice(0, 0, { - key: "vaccine", - label: "Vaccine", - value: vaccineName - }); + data.auxiliaryFields.push(...[ + { + key: "vaccine", + label: "Vaccine", + value: vaccineName + }, + { + key: "cov", + label: "Country of Vaccination", + value: country, + textAlignment: TextAlignment.right + } + ]); data.backFields.push(...[ { key: "manufacturer", @@ -234,7 +242,7 @@ export class Payload { data.secondaryFields.push(...[ { key: "result", - label: "Result", + label: "Test Result", value: testResult }, { @@ -244,7 +252,6 @@ export class Payload { textAlignment: TextAlignment.right } ]); - data.auxiliaryFields.pop(); data.auxiliaryFields.push(...[ { key: "test", @@ -258,6 +265,11 @@ export class Payload { textAlignment: TextAlignment.right }, ]); + data.backFields.push({ + key: "cot", + label: "Country of Test", + value: country + }); if (testingCentre !== undefined) data.backFields.push({ key: "centre", @@ -276,29 +288,28 @@ export class Payload { const validUntil = properties['du']; data.secondaryFields.push(...[ - { - key: "result", - label: "Test Result", - value: "Detected" - }, { key: "from", label: "Valid From", value: validFrom, + }, + { + key: "dov", + label: "Date of positive Test", + value: firstPositiveTestDate, textAlignment: TextAlignment.right } ]); - data.auxiliaryFields.pop(); data.auxiliaryFields.push(...[ - { - key: "testdate", - label: "Test Date", - value: firstPositiveTestDate - }, { key: "until", label: "Valid Until", value: validUntil, + }, + { + key: "cov", + label: "Country of Test", + value: country, textAlignment: TextAlignment.right }, ]);