Compare commits

...

2 Commits

Author SHA1 Message Date
e47a4b0e27 Refactoring 2023-07-08 16:21:24 +02:00
de51b66968 Support NFC on modal 2023-07-08 16:21:24 +02:00
2 changed files with 79 additions and 13 deletions

View File

@ -21,8 +21,23 @@
</div>
</template>
</template>
<script type="text/javascript">
// https://developer.chrome.com/articles/nfc/
<script>
class NDEFReaderWrapper {
constructor() {
this.onreading = null;
this.onreadingerror = null;
}
async scan(opts) {
if (opts && opts.signal){
opts.signal.addEventListener('abort', () => {
window.parent.postMessage('nfc:abort', '*');
});
}
window.parent.postMessage('nfc:startScan', '*');
}
}
Vue.component("lnurl-withdraw-checkout", {
template: "#lnurl-withdraw-template",
props: {
@ -69,7 +84,7 @@ Vue.component("lnurl-withdraw-checkout", {
data () {
return {
url: @Safe.Json(Context.Request.GetAbsoluteUri(Url.Action("SubmitLNURLWithdrawForInvoice", "NFC"))),
supported: 'NDEFReader' in window && window.self === window.top,
supported: 'NDEFReader' in window,
scanning: false,
submitting: false,
permissionGranted: false,
@ -135,7 +150,8 @@ Vue.component("lnurl-withdraw-checkout", {
this.submitting = false;
this.scanning = true;
try {
const ndef = new NDEFReader()
const inModal = window.self !== window.top;
const ndef = inModal ? new NDEFReaderWrapper() : new NDEFReader();
this.readerAbortController = new AbortController()
this.readerAbortController.signal.onabort = () => {
this.scanning = false;
@ -143,17 +159,33 @@ Vue.component("lnurl-withdraw-checkout", {
await ndef.scan({ signal: this.readerAbortController.signal })
ndef.onreadingerror = () => {
this.errorMessage = "Could not read NFC tag";
}
ndef.onreadingerror = this.reportNfcError
ndef.onreading = async ({ message, serialNumber }) => {
ndef.onreading = async ({ message }) => {
const record = message.records[0]
const textDecoder = new TextDecoder('utf-8')
const lnurl = textDecoder.decode(record.data)
await this.sendData(lnurl)
}
if (inModal) {
// receive messages from iframe
window.addEventListener('message', async event => {
// deny messages from other origins
if (event.origin !== window.location.origin) return
const { action, data } = event.data
switch (action) {
case 'nfc:data':
await this.sendData(data)
break;
case 'nfc:error':
this.reportNfcError()
break;
}
});
}
// we came here, so the user must have allowed NFC access
this.permissionGranted = true;
} catch (error) {
@ -182,6 +214,9 @@ Vue.component("lnurl-withdraw-checkout", {
this.errorMessage = error;
}
this.submitting = false;
},
reportNfcError() {
this.errorMessage = 'Could not read NFC tag';
}
}
});

View File

@ -48,7 +48,7 @@
var scriptMatch = thisScript.match(scriptSrcRegex)
if (scriptMatch) {
// We can't just take the domain as btcpay can run under a sub path with RootPath
origin = thisScript.substr(0, thisScript.length - scriptMatch[0].length);
origin = thisScript.slice(0, thisScript.length - scriptMatch[0].length);
}
// urlPrefix should be site root without trailing slash
function setApiUrlPrefix(urlPrefix) {
@ -88,10 +88,36 @@
function onModalReceiveMessage(customOnModalReceiveMessage) {
onModalReceiveMessageMethod = customOnModalReceiveMessage;
}
var readerAbortController = null;
function startNfcScan() {
const ndef = new NDEFReader();
readerAbortController = new AbortController()
readerAbortController.signal.onabort = () => {
this.scanning = false;
};
ndef.scan({ signal:readerAbortController.signal }).then(() => {
ndef.onreading = event => {
const message = event.message;
const record = message.records[0];
const textDecoder = new TextDecoder('utf-8');
const data = textDecoder.decode(record.data);
// Send NFC data back to the iframe
if (iframe) {
iframe.contentWindow.postMessage({ action: 'nfc:data', data }, '*');
}
};
ndef.onreadingerror = () => {
// Send error message back to the iframe
if (iframe) {
iframe.contentWindow.postMessage({ action: 'nfc:error' }, '*');
}
};
}).catch(console.error);
}
function receiveMessage(event) {
var uri;
if (!origin.startsWith(event.origin) || !showingInvoice) {
return;
}
@ -99,8 +125,14 @@
hideFrame();
} else if (event.data === 'loaded') {
showFrame();
} else if (event.data === 'nfc:startScan') {
startNfcScan();
} else if (event.data === 'nfc:abort') {
if (readerAbortController) {
readerAbortController.abort()
}
} else if (event.data && event.data.open) {
uri = event.data.open;
const uri = event.data.open;
if (uri.indexOf('bitcoin:') === 0) {
window.location = uri;
}
@ -149,5 +181,4 @@
setApiUrlPrefix: setApiUrlPrefix,
onModalReceiveMessage: onModalReceiveMessage
};
})();