qrcode: move tally to a new element (#86853)
This commit is contained in:
parent
6fc97253ff
commit
7330fce543
|
@ -91,7 +91,9 @@ qrcode-reader {
|
|||
}
|
||||
|
||||
&.invalid-qrcode {
|
||||
qrcode-reader-validity, qrcode-reader-data {
|
||||
qrcode-reader-validity,
|
||||
qrcode-reader-data,
|
||||
qrcode-reader-tally {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -112,35 +114,11 @@ qrcode-reader {
|
|||
}
|
||||
}
|
||||
|
||||
&--popup-content {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
&--close-popup-button {
|
||||
margin: 5px 10px 10px 10px;
|
||||
padding: 5px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
&--tally-status {
|
||||
display: grid;
|
||||
margin-top: 5px;
|
||||
padding-top: 5px;
|
||||
border-top: 1px solid var(--gray-light);
|
||||
&.error {
|
||||
color: #{$red};
|
||||
}
|
||||
}
|
||||
|
||||
&--spinner {
|
||||
grid-area: 1 / 1 / 2 / 3;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
&--spinner-animation {
|
||||
transform-origin:center;
|
||||
animation:spinner-keyframes .75s infinite linear;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spinner-keyframes {
|
||||
|
|
|
@ -48,24 +48,11 @@ const readerTemplate = template(`
|
|||
<div class="qrcode-reader--popup-errors"></div>
|
||||
<qrcode-reader-validity></qrcode-reader-validity>
|
||||
<qrcode-reader-data></qrcode-reader-data>
|
||||
<div class="qrcode-reader--popup-content"></div>
|
||||
<qrcode-reader-tally></qrcode-reader-tally>
|
||||
<button class="qrcode-reader--close-popup-button">${translate('close')}</button>
|
||||
</div>
|
||||
`)
|
||||
|
||||
const dataTemplate = template(`
|
||||
<span class="qrcode-reader--tally-status"></span>
|
||||
`)
|
||||
|
||||
const spinnerTemplate = template(`
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="qrcode-reader--spinner">
|
||||
<path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/>
|
||||
<path
|
||||
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
|
||||
class="qrcode-reader--spinner-animation"/>
|
||||
</svg>
|
||||
`)
|
||||
|
||||
function decodeMimeLike (value) {
|
||||
const chunks = value.split('\n')
|
||||
const data = {}
|
||||
|
@ -136,11 +123,11 @@ function decodeBase45 (str) {
|
|||
|
||||
class QRCodeReader extends window.HTMLElement {
|
||||
#popup
|
||||
#popupContent
|
||||
#errors
|
||||
#currentCertificate
|
||||
#validity
|
||||
#data
|
||||
#tally
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
|
@ -153,14 +140,13 @@ class QRCodeReader extends window.HTMLElement {
|
|||
this.appendChild(readerTemplate.content.cloneNode(true))
|
||||
|
||||
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')
|
||||
this.#data = this.querySelector('qrcode-reader-data')
|
||||
this.#tally = this.querySelector('qrcode-reader-tally')
|
||||
|
||||
const closePopupButton = this.querySelector('.qrcode-reader--close-popup-button')
|
||||
closePopupButton.addEventListener('click', () => {
|
||||
this.#popupContent.innerHTML = ''
|
||||
this.#currentCertificate = undefined
|
||||
this.#popup.classList.add('closed')
|
||||
})
|
||||
|
@ -180,6 +166,10 @@ class QRCodeReader extends window.HTMLElement {
|
|||
return
|
||||
}
|
||||
|
||||
if(this.getAttribute('tally-url') === null) {
|
||||
this.#tally.setAttribute('hidden', true)
|
||||
}
|
||||
|
||||
this.#startScan()
|
||||
}
|
||||
|
||||
|
@ -234,8 +224,6 @@ class QRCodeReader extends window.HTMLElement {
|
|||
}
|
||||
|
||||
this.#currentCertificate = certificateUUID
|
||||
this.#popupContent.innerHTML = ''
|
||||
|
||||
|
||||
delete data.uuid
|
||||
|
||||
|
@ -247,16 +235,9 @@ class QRCodeReader extends window.HTMLElement {
|
|||
|
||||
this.#validity.refresh(validityStart, validityEnd)
|
||||
|
||||
|
||||
const dataElement = dataTemplate.cloneNode(true)
|
||||
const tallyStatus = dataElement.content.querySelector('.qrcode-reader--tally-status')
|
||||
|
||||
|
||||
this.#popupContent.append(dataElement.content)
|
||||
|
||||
await Promise.all([
|
||||
this.#data.refresh(data, this.getAttribute('metadata-url'), certificateUUID),
|
||||
this.#showTally(tallyStatus, certificateUUID)
|
||||
this.#tally.refresh(this.getAttribute('tally-url'), certificateUUID)
|
||||
])
|
||||
|
||||
if(this.querySelector('.qrcode-reader-section.error')) {
|
||||
|
@ -264,58 +245,6 @@ class QRCodeReader extends window.HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
async #showTally(statusElement, certificateUUID) {
|
||||
if(this.getAttribute('tally-url') === null) {
|
||||
return
|
||||
}
|
||||
const spinner = spinnerTemplate.cloneNode(true)
|
||||
statusElement.appendChild(spinner.content)
|
||||
|
||||
const tallyUrl = this.getAttribute('tally-url')
|
||||
try {
|
||||
const response = await fetch(
|
||||
tallyUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
events: [{
|
||||
certificate: certificateUUID,
|
||||
timestamp: Math.floor(Date.now() / 1000)}
|
||||
]}
|
||||
),
|
||||
})
|
||||
|
||||
statusElement.innerHTML = ''
|
||||
|
||||
if(!response.ok) {
|
||||
statusElement.classList.add('error')
|
||||
statusElement.innerHTML = `<p>${translate('tally_api_error')}</p>`
|
||||
}
|
||||
else {
|
||||
const json = await response.json()
|
||||
|
||||
if(json.data.stamps[certificateUUID] == 'duplicate') {
|
||||
statusElement.classList.add('error')
|
||||
statusElement.innerHTML = `<p>${translate('tally_status_already_seen')}</p>`
|
||||
}
|
||||
else {
|
||||
statusElement.innerHTML = `<p>${translate('tally_status_ok')}</p>`
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
if(err.name == 'NetworkError') {
|
||||
statusElement.classList.add('error')
|
||||
statusElement.innerHTML = `<p>${translate('tally_network_error')}</p>`
|
||||
}
|
||||
else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#showError (message) {
|
||||
this.#popup.classList.remove('closed')
|
||||
this.#popup.classList.add('error')
|
||||
|
@ -549,3 +478,61 @@ class QRCodeReaderData extends QRCodeReaderSection {
|
|||
}
|
||||
|
||||
window.customElements.define('qrcode-reader-data', QRCodeReaderData)
|
||||
|
||||
class QRCodeReaderTally extends QRCodeReaderSection {
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.setTitle(translate('tally_section_title'))
|
||||
}
|
||||
|
||||
async refresh(tallyUrl, certificateUUID) {
|
||||
if(tallyUrl === null) {
|
||||
return
|
||||
}
|
||||
|
||||
this.reset()
|
||||
|
||||
await this.load(async () => await this.#showTally(tallyUrl, certificateUUID))
|
||||
}
|
||||
|
||||
async #showTally(tallyUrl, certificateUUID) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
tallyUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
events: [{
|
||||
certificate: certificateUUID,
|
||||
timestamp: Math.floor(Date.now() / 1000)}
|
||||
]}
|
||||
),
|
||||
})
|
||||
|
||||
if(!response.ok) {
|
||||
this.showError(translate('tally_api_error'))
|
||||
}
|
||||
else {
|
||||
const json = await response.json()
|
||||
|
||||
if(json.data.stamps[certificateUUID] == 'duplicate') {
|
||||
this.showError(translate('tally_status_already_seen'))
|
||||
}
|
||||
else {
|
||||
this.contentElement.innerText = translate('tally_status_ok')
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
if(err.name == 'NetworkError') {
|
||||
this.showError(translate('tally_network_error'))
|
||||
}
|
||||
else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('qrcode-reader-tally', QRCodeReaderTally)
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
"to": "{% trans 'To' %}",
|
||||
"valid": "{% trans 'Valid QR code' %}",
|
||||
"validity_section_title": "{% trans 'Validity' %}",
|
||||
"data_section_title": "{% trans 'Data' %}"
|
||||
"data_section_title": "{% trans 'Data' %}",
|
||||
"tally_section_title": "{% trans 'Tallying' %}"
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="{% static 'qrcode/css/style.css' %}">
|
||||
|
|
|
@ -421,12 +421,11 @@ qrcodeReaderTallyTest('qrcode reader show tally status for unstamped certificate
|
|||
})
|
||||
Date.now = nowBackup
|
||||
|
||||
const spinner = reader.querySelector('.qrcode-reader--spinner')
|
||||
expect(spinner).toBe(null)
|
||||
const section = reader.querySelector('qrcode-reader-tally .qrcode-reader-section')
|
||||
expect(section.classList.contains('error')).toBe(false)
|
||||
|
||||
const items = reader.querySelector('.qrcode-reader--tally-status')
|
||||
expect(items.classList.contains('error')).toBe(false)
|
||||
expect(items.innerText.trim()).toBe('tally_status_ok')
|
||||
const content = reader.querySelector('qrcode-reader-tally .qrcode-reader-section--content')
|
||||
expect(content.innerText.trim()).toBe('tally_status_ok')
|
||||
})
|
||||
|
||||
qrcodeReaderTallyTest('qrcode reader show error for stamped certificates', async ({mock, loadTally}) => {
|
||||
|
@ -439,12 +438,11 @@ qrcodeReaderTallyTest('qrcode reader show error for stamped certificates', async
|
|||
}
|
||||
})
|
||||
|
||||
const spinner = reader.querySelector('.qrcode-reader--spinner')
|
||||
expect(spinner).toBe(null)
|
||||
const section = reader.querySelector('qrcode-reader-tally .qrcode-reader-section')
|
||||
expect(section.classList.contains('error')).toBe(true)
|
||||
|
||||
const items = reader.querySelector('.qrcode-reader--tally-status')
|
||||
expect(items.classList.contains('error')).toBe(true)
|
||||
expect(items.innerText.trim()).toBe('tally_status_already_seen')
|
||||
const errors = reader.querySelector('qrcode-reader-tally .qrcode-reader-section--errors')
|
||||
expect(errors.innerText.trim()).toBe('tally_status_already_seen')
|
||||
})
|
||||
|
||||
qrcodeReaderTallyTest('qrcode reader show tally when request fails ', async ({mock, loadTally}) => {
|
||||
|
@ -454,12 +452,11 @@ qrcodeReaderTallyTest('qrcode reader show tally when request fails ', async ({mo
|
|||
throw { name: 'NetworkError' }
|
||||
})
|
||||
|
||||
const spinner = reader.querySelector('.qrcode-reader--spinner')
|
||||
expect(spinner).toBe(null)
|
||||
const section = reader.querySelector('qrcode-reader-tally .qrcode-reader-section')
|
||||
expect(section.classList.contains('error')).toBe(true)
|
||||
|
||||
let items = reader.querySelector('.qrcode-reader--tally-status')
|
||||
expect(items.classList.contains('error')).toBe(true)
|
||||
expect(items.innerText.trim()).toBe('tally_network_error')
|
||||
const errors = reader.querySelector('qrcode-reader-tally .qrcode-reader-section--errors')
|
||||
expect(errors.innerText.trim()).toBe('tally_network_error')
|
||||
|
||||
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
|
||||
closeButton.dispatchEvent(new Event('click'))
|
||||
|
@ -470,9 +467,8 @@ qrcodeReaderTallyTest('qrcode reader show tally when request fails ', async ({mo
|
|||
}
|
||||
})
|
||||
|
||||
items = reader.querySelector('.qrcode-reader--tally-status')
|
||||
expect(items.classList.contains('error')).toBe(true)
|
||||
expect(items.innerText.trim()).toBe('tally_api_error')
|
||||
expect(section.classList.contains('error')).toBe(true)
|
||||
expect(errors.innerText.trim()).toBe('tally_api_error')
|
||||
})
|
||||
|
||||
qrcodeReaderTallyTest("qrcode reader doesn't refetch tally for the same certificate if popup is opened", async ({mock, loadTally}) => {
|
||||
|
@ -485,15 +481,11 @@ qrcodeReaderTallyTest("qrcode reader doesn't refetch tally for the same certific
|
|||
}
|
||||
})
|
||||
|
||||
let tallyStatus = reader.querySelector('.qrcode-reader--tally-status')
|
||||
|
||||
// loading the same certificate doesn't refeches metadata
|
||||
await loadTally(okCodeData, async () => {
|
||||
expect.unreachable()
|
||||
})
|
||||
|
||||
expect(reader.querySelector('.qrcode-reader--tally-status')).toBe(tallyStatus)
|
||||
|
||||
// loading another certificate refetches tally status
|
||||
await loadTally(certificateWithoutValidity, async (url, body) => {
|
||||
return {
|
||||
|
@ -502,9 +494,6 @@ qrcodeReaderTallyTest("qrcode reader doesn't refetch tally for the same certific
|
|||
}
|
||||
})
|
||||
|
||||
expect(reader.querySelector('.qrcode-reader--tally-status')).not.toBe(tallyStatus)
|
||||
tallyStatus = reader.querySelector('.qrcode-reader--tally-status')
|
||||
|
||||
const closeButton = reader.querySelector('.qrcode-reader--close-popup-button')
|
||||
closeButton.dispatchEvent(new Event('click'))
|
||||
|
||||
|
@ -515,7 +504,4 @@ qrcodeReaderTallyTest("qrcode reader doesn't refetch tally for the same certific
|
|||
json: async () => ({'err': 0, 'data': {'stamps': {[body.events[0].certificate]: 'ok'}}})
|
||||
}
|
||||
})
|
||||
|
||||
expect(reader.querySelector('.qrcode-reader--tally-status')).not.toBe(tallyStatus)
|
||||
tallyStatus = reader.querySelector('.qrcode-reader--tally-status')
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue