]> zoso.dev Git - nemo-wallet.git/commitdiff
Switch to zxing-wasm from barcode-detector so we can generate them as well as detect...
authorChris Duncan <chris@zoso.dev>
Sun, 3 Nov 2024 07:47:02 +0000 (00:47 -0700)
committerChris Duncan <chris@zoso.dev>
Sun, 3 Nov 2024 07:47:02 +0000 (00:47 -0700)
nemo-wallet.html

index 6f295c60d8fa938e9385eae08066f152f4040e3b..961003eab7b7666eb324ebc7a99ba898ea6ec928 100644 (file)
@@ -4,7 +4,7 @@
        <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')
@@ -55,6 +44,7 @@
                        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>