<meta charset="utf-8" />
<meta lang="en" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
- <script type="module" src="https://cdn.jsdelivr.net/npm/barcode-detector@2.2.11/dist/es/side-effects.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/zxing-wasm@1.3.2/dist/iife/full/index.js"></script>
<script type="module">
import { Buffer } from 'https://esm.run/buffer-browser@6.0.4/index.js'
globalThis.Buffer = Buffer
window.addEventListener('load', async (event) => {
try {
- document.querySelector(location.hash).classList.add('show')
+ document.querySelector(location.hash).classList.remove('hide')
} catch (err) {
location.hash = '#transact'
}
- await loadQr()
await loadData()
await loadUi()
console.log('done')
})
- async function loadQr () {
- const barcodeDetector = new BarcodeDetector({
- formats: ["qr_code"],
- })
- const imageFile = await fetch(
- "https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=Hello%20world!",
- ).then(resp => resp.blob())
- barcodeDetector.detect(imageFile).then(console.log)
- }
-
async function loadData () {
console.log('loading data')
const walletSelect = document.querySelector('#wallet')
if (walletSelect.value === '' || accountSelect.value.substring(0, 1) === '_') {
accountSelect.setAttribute('disabled', '')
accountSelect.value = ''
+ resetQrCode()
}
console.log('loaded data')
}
function switchPage (event) {
const pages = document.getElementsByClassName('page')
- Array.from(pages).map(p => p.classList.remove('show'))
+ Array.from(pages).map(p => p.classList.add('hide'))
const oldHash = new URL(event.oldURL)?.hash
const newHash = new URL(event.newURL)?.hash
if (oldHash) {
- document.querySelector(oldHash)?.classList.remove('show')
+ document.querySelector(oldHash)?.classList.add('hide')
}
try {
if (newHash) {
- document.querySelector(newHash).classList.add('show')
+ document.querySelector(newHash).classList.remove('hide')
}
} catch (err) {
notify.error(`Page not found`)
}
case '_new': {
try {
- document.querySelector(location.hash).classList.remove('show')
- document.getElementById('wallet-new').classList.add('show')
+ document.querySelector(location.hash).classList.add('hide')
+ document.getElementById('wallet-new').classList.remove('hide')
} catch (err) {
walletSelect.value = ''
console.error(err)
}
case '_import': {
try {
- document.querySelector(location.hash).classList.remove('show')
- document.getElementById('wallet-import').classList.add('show')
+ document.querySelector(location.hash).classList.add('hide')
+ document.getElementById('wallet-import').classList.remove('hide')
} catch (err) {
walletSelect.value = ''
console.error(err)
if (walletId == null || walletId === '' || walletId === '_new' || walletId === '_import') {
accountSelect.value = ''
accountSelect.setAttribute('disabled', '')
+ resetQrCode()
return
}
accountSelect.removeAttribute('disabled')
switch (accountSelect.value) {
+ case undefined:
+ case null:
+ case '': {
+ resetQrCode()
+ break
+ }
case '_new': {
try {
- document.querySelector(location.hash).classList.remove('show')
- document.getElementById('account-new').classList.add('show')
+ document.querySelector(location.hash).classList.add('hide')
+ document.getElementById('account-new').classList.remove('hide')
} catch (err) {
accountSelect.value = ''
console.error(err)
notify.error(`Error creating account`)
- return
+ } finally {
+ break
}
}
default: {
- await loadHistory()
+ try {
+ const account = await loadAccount()
+ createQrCode(account.address)
+ } catch (err) {
+ accountSelect.value = ''
+ console.error(err)
+ notify.error(`Error loading account`)
+ } finally {
+ break
+ }
}
}
}
async function cancelForm (id) {
await resetFormInputs()
- document.getElementById(id).classList.remove('show')
- document.querySelector(location.hash).classList.add('show')
+ document.getElementById(id).classList.add('hide')
+ document.querySelector(location.hash).classList.remove('hide')
}
async function resetFormInputs () {
themeCheckbox.value = 'light'
}
}
+ async function createQrCode (address, options) {
+ if (address == null || typeof address !== 'string' || address === '') {
+ notify.error(`Error creating QR code with invalid address`)
+ return
+ }
+ const nanoUrlOptions = []
+ if (options?.amount != null) {
+ try {
+ const bigAmount = BigInt(options.amount)
+ nanoUrlOptions.push(`amount=${bigAmount.toString()}`)
+ } catch (err) {
+ notify.error(`Error creating QR code with invalid amount`)
+ return
+ }
+ }
+ if (typeof options?.label === 'string') {
+ nanoUrlOptions.push(`label=${encodeURI(options.label)}`)
+ }
+ if (typeof options?.message === 'string') {
+ nanoUrlOptions.push(`message=${encodeURI(options.message)}`)
+ }
+ const nanoUrl = `nano:${encodeURIComponent(address)}?${nanoUrlOptions.join('&')}`
+ const config = {
+ width: 1080,
+ height: 1080,
+ margin: 10,
+ }
+ const { image } = await ZXingWASM.writeBarcodeToImageFile(nanoUrl, config)
+ const dataUrl = URL.createObjectURL(image)
+ const img = document.getElementById('qr-code')
+ URL.revokeObjectURL(img.src)
+ img.src = dataUrl
+ }
+
+ function resetQrCode () {
+ const qrCode = document.getElementById('qr-code')
+ qrCode.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4AQMAAADW3v7MAAAABlBMVEUAAAD///+l2Z/dAAAC0klEQVR42u3cPQ6CMBiAYYWFjStwE6+mN+Mq3oCVxKQO4k8N6ABNWvK8G1+HPuuXNBzDIYuOHBwcHBwcHBwcHBwcHMU6rt3cyanf/raxmZu2AwcHBwcHBwcHBwcHR3GOdoiPLudUjvoWj6bLOTg4ODg4ODg4ODg4ynO8d62Q2BGqj52Rg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4Oj986Rg4ODg4ODg4ODg4NjlWMm///g4ODg4ODg4ODg4OBYcuQQBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHB8e+Hdcu/g4cHBwcHBwcHBwcHBz5Otrhaag4ODg4ODg4ODg4ODjKdnzvWo9O/faOsTnUNw4ODg4ODg4ODg4Ojj04XrvW1OWcxPF3n+Pg4ODg4ODg4ODg4CjIMTbPWUjjiOLg4ODg4ODg4ODg4OBYFwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHB8feHVEcHBwcHBwcHBwcHBwcvx0zcXBwcHBwcHBwcHBwcCw5coiDg4ODg4ODg4ODg4ODY013wW3C00//4ZIAAAAASUVORK5CYII='
+ }
</script>
<style>
*{border:0;box-sizing:border-box;font:normal normal normal calc(16px + 0.1dvh) 'sans-serif';margin:0;}
width: fit-content;
}
.page {
- display: none;
height: 100%;
}
- .page.show {
- display: initial;
- }
.page.form {
z-index: 10;
}
left: 1.2em;
transition: all 0.2s;
}
+ #qr-code {
+ display: block;
+ height: auto;
+ margin: 0 auto;
+ max-height: 50dvh;
+ max-width: 100%;
+ width: auto;
+ }
</style>
</head>
<!-- New Wallet Form -->
- <div id="wallet-new" class="form page">
+ <div id="wallet-new" class="page form hide">
<h1>New Wallet</h1>
<div class="flex-y stretch">
<div id="wallet-new-name" class="flex-x left mid">
<!-- Import Wallet Form -->
- <div id="wallet-import" class="form page">
+ <div id="wallet-import" class="page form hide">
<h1>Import Wallet</h1>
<div class="flex-y stretch">
<div id="wallet-import-name" class="flex-x left mid">
<!-- New Account Form -->
- <div id="account-new" class="form page">
+ <div id="account-new" class="page form hide">
<h1>New Account</h1>
<div class="flex-y stretch">
<div id="account-new-name" class="flex-x left mid">
<!-- Exchange Page -->
- <div id="exchange" class="page">
+ <div id="exchange" class="page hide">
<h1>Exchange</h1>
</div>
<!-- History Page -->
- <div id="history" class="page">
+ <div id="history" class="page hide">
<h1>History</h1>
<iframe id="history-iframe" src="" loading="eager"></iframe>
<!--
<!-- Transact Page (default) -->
- <div id="transact" class="page">
- <h1>Transact</h1>
+ <div id="transact" class="page flex-y center mid hide">
+ <img id="qr-code" alt="QR code" src="" />
+ <canvas id="scanner" class="hide">Scan</canvas>
+ <button>Request</button>
</div>
<!-- Rolodex Page -->
- <div id="rolodex" class="page">
+ <div id="rolodex" class="page hide">
<h1>Rolodex</h1>
</div>
<!-- Settings Page -->
- <div id="settings" class="page">
+ <div id="settings" class="page hide">
<h1>Settings</h1>
<div class="flex-y left mid">
<label for="theme-checkbox">Theme</label>