qrcode: move validity code to a new element (#86853)
This commit is contained in:
parent
99662694e6
commit
8e7056f4de
|
@ -7,6 +7,7 @@ $gray-light: #CECECE;
|
|||
qrcode-reader {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
display: grid;
|
||||
}
|
||||
|
@ -88,6 +89,12 @@ qrcode-reader {
|
|||
&.closed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.invalid-qrcode {
|
||||
qrcode-reader-validity {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--popup-errors {
|
||||
|
@ -109,18 +116,6 @@ qrcode-reader {
|
|||
margin: 10px;
|
||||
}
|
||||
|
||||
&--validity {
|
||||
font-weight: bold;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
&--validity-label {
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
&--data-items, &--metadata-items {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
|
@ -173,3 +168,78 @@ qrcode-reader {
|
|||
@keyframes spinner-keyframes {
|
||||
100% { transform:rotate(360deg); }
|
||||
}
|
||||
|
||||
.qrcode-reader-section {
|
||||
--color: #{$green};
|
||||
--background-color: #{$green-light};
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
|
||||
&--header {
|
||||
padding: 0 5px;
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
background: var(--background-color);
|
||||
font-size: 1.1rem;
|
||||
color: var(--color);
|
||||
fill: var(--color);
|
||||
}
|
||||
|
||||
&--ok-icon, &--error-icon {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
}
|
||||
|
||||
&--error-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--title {
|
||||
font-size: 1.2rem;
|
||||
grid-area: 1 / 2 / 2 / 3;
|
||||
}
|
||||
|
||||
&--errors {
|
||||
display: none;
|
||||
color: $red;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
&--content {
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
&.error {
|
||||
--color: #{$red};
|
||||
--background-color: #{$red-light};
|
||||
|
||||
.qrcode-reader-section--errors {
|
||||
display: block;
|
||||
}
|
||||
.qrcode-reader-section--ok-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.qrcode-reader-section--error-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qrcode-reader-validity {
|
||||
.qrcode-reader-section--content {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0 5px;
|
||||
}
|
||||
|
||||
.qrcode-reader-validity--label {
|
||||
align-self: end;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,20 +46,12 @@ const readerTemplate = template(`
|
|||
</div>
|
||||
<div class="qrcode-reader--popup closed">
|
||||
<div class="qrcode-reader--popup-errors"></div>
|
||||
<qrcode-reader-validity></qrcode-reader-validity>
|
||||
<div class="qrcode-reader--popup-content"></div>
|
||||
<button class="qrcode-reader--close-popup-button">${translate('close')}</button>
|
||||
</div>
|
||||
`)
|
||||
|
||||
const validityTemplate = template(`
|
||||
<div class="qrcode-reader--validity">
|
||||
<div class="qrcode-reader--validity-label">${translate('from')} :</div>
|
||||
<div>{validityStart}</div>
|
||||
<div>${translate('to')} :</div>
|
||||
<div>{validityEnd}</div>
|
||||
</div>
|
||||
`)
|
||||
|
||||
const dataTemplate = template(`
|
||||
<div class="qrcode-reader--data-items"></div>
|
||||
<span class="qrcode-reader--metadata-items"></span>
|
||||
|
@ -153,6 +145,7 @@ class QRCodeReader extends window.HTMLElement {
|
|||
#popupContent
|
||||
#errors
|
||||
#currentCertificate
|
||||
#validity
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
|
@ -167,6 +160,7 @@ class QRCodeReader extends window.HTMLElement {
|
|||
this.#popup = this.querySelector('.qrcode-reader--popup')
|
||||
this.#popupContent = this.querySelector('.qrcode-reader--popup-content')
|
||||
this.#errors = this.querySelector('.qrcode-reader--popup-errors')
|
||||
this.#validity = this.querySelector('qrcode-reader-validity')
|
||||
|
||||
const closePopupButton = this.querySelector('.qrcode-reader--close-popup-button')
|
||||
closePopupButton.addEventListener('click', () => {
|
||||
|
@ -215,18 +209,22 @@ class QRCodeReader extends window.HTMLElement {
|
|||
async #showResult (qrCodeContent) {
|
||||
this.#popup.classList.remove('error')
|
||||
this.#popup.classList.remove('closed')
|
||||
this.#popup.classList.remove('invalid-qrcode')
|
||||
this.#errors.innerHTML = ''
|
||||
|
||||
let signed
|
||||
try {
|
||||
signed = decodeBase45(qrCodeContent)
|
||||
} catch (error) {
|
||||
this.#showError(translate('invalid_qrcode'))
|
||||
this.#popup.classList.add('invalid-qrcode')
|
||||
return
|
||||
}
|
||||
|
||||
const opened = window.nacl.sign.open(signed, this.#verifyKey)
|
||||
if (opened == null) {
|
||||
this.#showError(translate('invalid_signature'))
|
||||
this.#popup.classList.add('invalid-qrcode')
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -245,36 +243,13 @@ class QRCodeReader extends window.HTMLElement {
|
|||
|
||||
delete data.uuid
|
||||
|
||||
const validityStart = data.validity_start && new Date(parseFloat(data.validity_start) * 1000)
|
||||
const validityStart = data.validity_start
|
||||
delete data.validity_start
|
||||
|
||||
const validityEnd = data.validity_end && new Date(parseFloat(data.validity_end) * 1000)
|
||||
const validityEnd = data.validity_end
|
||||
delete data.validity_end
|
||||
|
||||
const now = new Date()
|
||||
|
||||
if (validityStart && now.getTime() < validityStart.getTime()) {
|
||||
this.#errors.innerText = translate('not_yet_valid')
|
||||
this.#popup.classList.add('error')
|
||||
} else if (validityEnd && now.getTime() > validityEnd.getTime()) {
|
||||
this.#errors.innerText = translate('expired')
|
||||
this.#popup.classList.add('error')
|
||||
}
|
||||
|
||||
const validityElement = validityTemplate.cloneNode(true)
|
||||
if (validityStart) {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityStart}', validityStart.toLocaleString())
|
||||
} else {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityStart}', translate('always'))
|
||||
}
|
||||
|
||||
if (validityStart) {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityEnd}', validityEnd.toLocaleString())
|
||||
} else {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityEnd}', translate('never'))
|
||||
}
|
||||
|
||||
this.#popupContent.append(validityElement.content)
|
||||
this.#validity.refresh(validityStart, validityEnd)
|
||||
|
||||
const dataElement = dataTemplate.cloneNode(true)
|
||||
|
||||
|
@ -294,6 +269,10 @@ class QRCodeReader extends window.HTMLElement {
|
|||
this.#showMetadata(metadataItems, certificateUUID),
|
||||
this.#showTally(tallyStatus, certificateUUID)
|
||||
])
|
||||
|
||||
if(this.querySelector('.qrcode-reader-section.error')) {
|
||||
this.#popup.classList.add('error')
|
||||
}
|
||||
}
|
||||
|
||||
async #showMetadata(itemsElement, certificateUUID) {
|
||||
|
@ -422,3 +401,113 @@ class QRCodeReader extends window.HTMLElement {
|
|||
}
|
||||
|
||||
window.customElements.define('qrcode-reader', QRCodeReader)
|
||||
|
||||
const sectionTemplate = template(`
|
||||
<div class="qrcode-reader-section">
|
||||
<div class="qrcode-reader-section--header">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
height="16"
|
||||
class="qrcode-reader-section--ok-icon">
|
||||
>
|
||||
<path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11.0026 16L6.75999 11.7574L8.17421 10.3431L11.0026 13.1716L16.6595 7.51472L18.0737 8.92893L11.0026 16Z"></path>
|
||||
</svg>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
class="qrcode-reader-section--error-icon">
|
||||
>
|
||||
<path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 15H13V17H11V15ZM11 7H13V13H11V7Z"></path>
|
||||
</svg>
|
||||
<div class="qrcode-reader-section--title"></div>
|
||||
</div>
|
||||
<div class="qrcode-reader-section--errors">
|
||||
</div>
|
||||
<div class="qrcode-reader-section--content">
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
|
||||
|
||||
class QRCodeReaderSection extends HTMLElement {
|
||||
#title
|
||||
#content
|
||||
#errors
|
||||
#wrapper
|
||||
|
||||
connectedCallback() {
|
||||
this.appendChild(sectionTemplate.content.cloneNode(true))
|
||||
|
||||
this.#wrapper = this.querySelector('.qrcode-reader-section')
|
||||
this.#title = this.querySelector('.qrcode-reader-section--title')
|
||||
this.#content = this.querySelector('.qrcode-reader-section--content')
|
||||
this.#errors = this.querySelector('.qrcode-reader-section--errors')
|
||||
}
|
||||
|
||||
setTitle(title) {
|
||||
this.#title.innerText = title
|
||||
}
|
||||
|
||||
setContent(element) {
|
||||
this.#content.innerHTML = ''
|
||||
this.#content.appendChild(element)
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
this.#wrapper.classList.add('error')
|
||||
this.#errors.innerHTML = message
|
||||
}
|
||||
reset() {
|
||||
this.#wrapper.classList.remove('error')
|
||||
this.#content.innerHTML = ''
|
||||
}
|
||||
}
|
||||
|
||||
const validityTemplate = template(`
|
||||
<div class="qrcode-reader-validity--label">${translate('from')} :</div>
|
||||
<div>{validityStart}</div>
|
||||
<div class="qrcode-reader-validity--label">${translate('to')} :</div>
|
||||
<div>{validityEnd}</div>
|
||||
`)
|
||||
|
||||
class QRCodeReaderValidity extends QRCodeReaderSection {
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.setTitle(translate('validity_section_title'))
|
||||
}
|
||||
|
||||
refresh(validityStart, validityEnd) {
|
||||
this.reset()
|
||||
validityStart = validityStart && new Date(parseFloat(validityStart) * 1000)
|
||||
validityEnd = validityEnd && new Date(parseFloat(validityEnd) * 1000)
|
||||
|
||||
const now = new Date()
|
||||
|
||||
if (validityStart && now.getTime() < validityStart.getTime()) {
|
||||
this.showError(translate('not_yet_valid'))
|
||||
} else if (validityEnd && now.getTime() > validityEnd.getTime()) {
|
||||
this.showError(translate('expired'))
|
||||
}
|
||||
|
||||
const validityElement = validityTemplate.cloneNode(true)
|
||||
if (validityStart) {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityStart}', validityStart.toLocaleString())
|
||||
} else {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityStart}', translate('always'))
|
||||
}
|
||||
|
||||
if (validityStart) {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityEnd}', validityEnd.toLocaleString())
|
||||
} else {
|
||||
validityElement.innerHTML = validityElement.innerHTML.replace('{validityEnd}', translate('never'))
|
||||
}
|
||||
|
||||
this.setContent(validityElement.content)
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('qrcode-reader-validity', QRCodeReaderValidity)
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
"tally_status_ok": "{% trans 'QR Code tallied.' %}",
|
||||
"metadata_network_error": "{% trans 'A network error occured while tallying the QR code.' %}",
|
||||
"to": "{% trans 'To' %}",
|
||||
"valid": "{% trans 'Valid QR code' %}"
|
||||
"valid": "{% trans 'Valid QR code' %}",
|
||||
"validity_section_title": "{% trans 'Validity' %}",
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="{% static 'qrcode/css/style.css' %}">
|
||||
|
|
|
@ -106,7 +106,7 @@ qrcodeReaderTest('qrcode reader shows valid qrcode informations', async ({mock})
|
|||
expect(popup.classList.contains('closed')).toBe(false)
|
||||
expect(popup.classList.contains('error')).toBe(false)
|
||||
|
||||
const validity = popup.querySelector('.qrcode-reader--validity')
|
||||
const validity = popup.querySelector('qrcode-reader-validity .qrcode-reader-section--content')
|
||||
|
||||
expect(validity.innerText).toMatch(/from :\s*10\/31\/2023, 11:00:00 PM\s*to :\s*12\/1\/2023, 10:59:59 PM/)
|
||||
|
||||
|
@ -137,7 +137,7 @@ qrcodeReaderTest('qrcode reader shows error on not yet valid or expired qrcodes'
|
|||
await scan(okCodeData)
|
||||
|
||||
const popup = reader.querySelector('.qrcode-reader--popup')
|
||||
const errors = popup.querySelector('.qrcode-reader--popup-errors')
|
||||
const errors = popup.querySelector('qrcode-reader-validity .qrcode-reader-section--errors')
|
||||
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
|
||||
|
||||
expect(popup.classList.contains('closed')).toBe(false)
|
||||
|
@ -227,7 +227,7 @@ qrcodeReaderTest('qrcode reader accepts certificate without validity dates', asy
|
|||
await scan(certificateWithoutValidity)
|
||||
|
||||
const popup = reader.querySelector('.qrcode-reader--popup')
|
||||
const validity = popup.querySelector('.qrcode-reader--validity')
|
||||
const validity = popup.querySelector('qrcode-reader-validity .qrcode-reader-section--content')
|
||||
|
||||
expect(popup.classList.contains('closed')).toBe(false)
|
||||
expect(popup.classList.contains('error')).toBe(false)
|
||||
|
|
Loading…
Reference in New Issue