diff --git a/config.json b/config.json index b62f96e..50ca3b1 100644 --- a/config.json +++ b/config.json @@ -48,5 +48,7 @@ ], "description_md": "https://raw.githubusercontent.com/lnbits/tpos/main/description.md", "terms_and_conditions_md": "https://raw.githubusercontent.com/lnbits/tpos/main/toc.md", - "license": "MIT" + "license": "MIT", + "paid": "free/paid", + "paid_free_tooltip": "Free to use all the PoS features, apart from 0.5% charge for ATM withdraws to help maintain development." } diff --git a/static/js/tpos.js b/static/js/tpos.js index 060ea2b..755b2b7 100644 --- a/static/js/tpos.js +++ b/static/js/tpos.js @@ -121,6 +121,7 @@ window.app = Vue.createApp({ receiptData: null, currency_choice: false, _currencyResolver: null, + _withdrawing: false, headerElement: null } }, @@ -440,6 +441,9 @@ window.app = Vue.createApp({ LNbits.utils.notifyApiError(errorMessage) }) }, + isLNURL(link) { + return link.substring(0, 5) == 'LNURL' + }, atmGetWithdraw() { if (this.sat > this.withdrawMaximum) { Quasar.Notify.create({ @@ -464,7 +468,11 @@ window.app = Vue.createApp({ const url = `${window.location.origin}/tpos/api/v1/lnurl/${this.atmToken}/${this.sat}` const bytes = new TextEncoder().encode(url) const bech32 = NostrTools.nip19.encodeBytes('lnurl', bytes) - this.invoiceDialog.data = {payment_request: bech32.toUpperCase()} + this.invoiceDialog.data = { + payment_request: bech32.toUpperCase(), + fallback: + window.location.hostname + '?lightning=' + bech32.toUpperCase() + } this.invoiceDialog.show = true this.readNfcTag() this.invoiceDialog.dismissMsg = Quasar.Notify.create({ @@ -695,45 +703,47 @@ window.app = Vue.createApp({ return } - const ndef = new NDEFReader() + // Don’t start a new scan if one is active + if (this.nfcTagReading) { + console.debug( + 'NFC scan already in progress; ignoring duplicate call.' + ) + return + } + const ndef = new NDEFReader() const readerAbortController = new AbortController() - readerAbortController.signal.onabort = event => { + readerAbortController.signal.onabort = () => { console.debug('All NFC Read operations have been aborted.') } this.nfcTagReading = true Quasar.Notify.create({ message: this.atmMode - ? 'Tap your NFC tag to withdraw with LNURLp.' - : 'Tap your NFC tag to pay this invoice with LNURLw.' + ? 'Tap your NFC tag to withdraw with LNURLw.' + : 'Tap your NFC tag to pay this invoice with LNURLp.' }) return ndef.scan({signal: readerAbortController.signal}).then(() => { ndef.onreadingerror = event => { this.nfcTagReading = false - Quasar.Notify.create({ type: 'negative', message: 'There was an error reading this NFC tag.', caption: event.message || 'Please try again.' }) - readerAbortController.abort() } ndef.onreading = ({message}) => { - // Abort scan immediately to prevent duplicate reads + // stop scanning immediately to avoid duplicate reads readerAbortController.abort() - this.nfcTagReading = false - //Decode NDEF data from tag const textDecoder = new TextDecoder('utf-8') - const record = message.records.find(el => { const payload = textDecoder.decode(el.data) - return payload.toUpperCase().indexOf('LNURL') !== -1 + return payload.toUpperCase().includes('LNURL') }) if (!record) { @@ -743,19 +753,15 @@ window.app = Vue.createApp({ }) return } + const lnurl = textDecoder.decode(record.data) - //User feedback, show loader icon if (this.atmMode) { const url = lnurl.replace(/^lnurl[wp]:\/\//, 'https://') LNbits.api .request('GET', url) - .then(res => { - this.makeWithdraw(res.data.payLink) - }) - .catch(e => { - LNbits.utils.notifyApiError(e) - }) + .then(res => this.makeWithdraw(res.data.payLink)) + .catch(e => LNbits.utils.notifyApiError(e)) } else { this.payInvoice(lnurl) } @@ -774,13 +780,7 @@ window.app = Vue.createApp({ ? error.toString() : 'An unexpected error has occurred.', timeout: 0, - actions: [ - { - icon: 'close', - color: 'white', - round: true - } - ] + actions: [{icon: 'close', color: 'white', round: true}] }) } }, @@ -792,6 +792,11 @@ window.app = Vue.createApp({ }) return } + if (this._withdrawing) { + console.debug('Withdraw already in progress; ignoring duplicate.') + return + } + this._withdrawing = true LNbits.api .request( 'POST', @@ -812,13 +817,16 @@ window.app = Vue.createApp({ this.total = 0.0 Quasar.Notify.create({ type: 'positive', - message: 'Topup successful!' + message: 'Withdraw successful!' }) } }) .catch(e => { LNbits.utils.notifyApiError(e) }) + .finally(() => { + this._withdrawing = false + }) }, payInvoice(lnurl) { const payment_request = this.invoiceDialog.data.payment_request diff --git a/templates/tpos/dialogs.html b/templates/tpos/dialogs.html index fe33b35..9bef6ae 100644 --- a/templates/tpos/dialogs.html +++ b/templates/tpos/dialogs.html @@ -21,7 +21,10 @@