]> zoso.dev Git - nemo-wallet.git/commitdiff
Begin overhaul of wallet and account selection system.
authorChris Duncan <chris@zoso.dev>
Fri, 18 Oct 2024 07:27:24 +0000 (00:27 -0700)
committerChris Duncan <chris@zoso.dev>
Fri, 18 Oct 2024 07:27:24 +0000 (00:27 -0700)
app/pages/nemo-wallet.html

index 4b83da03c710efa9726fbb7c3f770a2a3deb906d..0b2d2eb6a1aae231bb0034d2e7561e8bc2e354df 100644 (file)
         }
       }
     }
-    async function createWallet (elm, type) {
-      const label = elm.firstElementChild
+
+    async function updateWallet () {
+      const wallet = document.getElementById('wallet').value
+      if (wallet !== '_new' && wallet !== '_import' && wallet !== '_ledger') {
+        return await updateAccount()
+      }
+      if (wallet === '_ledger') {
+        return await LedgerWallet.create()
+      }
+      const form = document.getElementById('wallet-form')
+      const walletType = document.getElementById('wallet-form-wallet-type')
+      const importType = document.getElementById('wallet-form-import-type')
+      const importValue = document.getElementById('wallet-form-import-value')
+      const salt = document.getElementById('wallet-form-salt')
+      walletType.querySelector('x-radios').value = null
+      importType.querySelector('x-select').value = null
+      importValue.querySelector('x-input').value = null
+      salt.querySelector('x-input').value = null
+      if (wallet.value === '_new') {
+        importType.classList.add('hide')
+        importValue.classList.add('hide')
+        salt.classList.remove('hide')
+      }
+      if (wallet.value === '_import') {
+        importType.classList.remove('hide')
+        importValue.classList.remove('hide')
+        salt.classList.add('hide')
+      }
+      return await form.showModal()
+    }
+
+    function spinner (btn) {
+      const label = btn.firstElementChild
       const spinner = document.createElement('x-throbber')
       spinner.size = 'small'
       spinner.style.width = `${label.scrollWidth}px`
-      elm.replaceChildren(spinner)
+      btn.replaceChildren(spinner)
+      // at some point then...
+      // btn.replaceChildren(label)
+    }
+
+    async function createWallet () {
+      const walletSelect = document.getElementById('wallet')
+      const form = document.getElementById('wallet-form')
+      const name = form.querySelector('#wallet-form-name x-input').value
+      if (name === '_new' || name === '_import' || name === '_ledger') {
+        walletSelect.value = null
+        notify.warn(`Invalid wallet name "${name}".`)
+        return
+      }
+      const type = form.querySelector('#wallet-form-wallet-type x-radios').value
       const walletType = getWalletType(type)
+      const salt = form.querySelector('#wallet-form-salt x-input')?.value
+      const password = form.querySelector('#wallet-form-password x-input')?.value
+      if (!password) {
+        walletSelect.value = null
+        notify.error('Password is required')
+        return
+      }
       let wallet
       try {
-        wallet = await walletType.create('password')
+        wallet = await walletType.create(password, salt)
         const walletData = {
           id: wallet.id,
+          name: name || wallet.id,
           type
         }
-        addToStorage(`wallets`, JSON.stringify(walletData))
+        await addToStorage(`wallets`, JSON.stringify(walletData))
+
+        const menu = document.querySelector('#wallet > x-menu')
+        const menuitem = document.createElement('x-menuitem')
+        menuitem.value = walletData.id
+        if (menu.childElementCount >= 0) {
+          menu.appendChild(menuitem)
+        }
+        const label = document.createElement('x-label')
+        label.innerText = walletData.name
+        menuitem.appendChild(label)
+        walletSelect.value = walletData.id
       } catch (err) {
+        label?.remove()
+        menuitem?.remove()
+        walletSelect.value = null
         notify.error(err.msg)
       } finally {
-        elm.replaceChildren(label)
+        await form.close()
         return wallet
       }
     }
+
     function addToStorage (key, value) {
       if (typeof value !== 'string') {
         throw new TypeError(`Cannot add ${typeof value} to storage`)
         throw new TypeError(`Storage item is not an array`)
       }
       item.push(value)
-      sessionStorage.setItem(key, JSON.stringify(item))
+      return new Promise((resolve, reject) => {
+        try {
+          sessionStorage.setItem(key, JSON.stringify(item))
+          resolve()
+        } catch (err) {
+          console.error(err)
+          reject(err)
+        }
+      })
+    }
+    function updateWalletForm () {
+      const form = document.getElementById('wallet-form')
+      const walletTypeContainer = document.getElementById('wallet-form-wallet-type')
+      const walletType = walletTypeContainer.firstChild
+      const importTypeContainer = document.getElementById('wallet-form-import-type')
+      const importType = importTypeContainer.firstChild
+      const saltContainer = document.getElementById('wallet-form-salt')
+      if (walletType.value === 'bip44' && importType.value === 'mnemonic') {
+        saltContainer.classList.remove('hide')
+      } else {
+        saltContainer.classList.add('hide')
+      }
+      document.querySelector('#wallet-form-import-value x-label').innerText = importType.value
     }
     async function showWallets () {
       const card = document.getElementById('walletCard')
       for (const walletData of storedWalletData) {
         const w = JSON.parse(walletData)
         console.dir(w)
-        if (w.id && w.type) {
+        if (w.id && w.name && w.type) {
           const walletType = getWalletType(w.type)
           wallets.push(await walletType.restore(w.id))
         }
       for (const wallet of wallets) {
         const walletElement = document.createElement('x-label')
         await wallet.unlock('password')
-        walletElement.innerText = wallet.mnemonic
+        walletElement.innerText = `${wallet.name}: ${wallet.mnemonic}`
         await wallet.lock('password')
         walletElements.push(walletElement)
       }
       card.replaceChildren(...walletElements)
     }
+
+    async function deriveAccounts () {
+      const wallet = document.querySelector('#wallet').value
+      const form = btn.querySelector('#account-form')
+      await console.log('deriveAccounts')
+    }
     async function clearStorage () {
       sessionStorage.clear()
       notify.info(`Session storage cleared`)
   <meta name="xel-theme" content="https://unpkg.com/xel@latest/themes/adwaita-dark.css" />
   <meta name="xel-accent-color" content="blue" />
   <meta name="xel-icons" content="https://unpkg.com/bootstrap-icons@latest/bootstrap-icons.svg" />
-  <link rel="preload" as="image" href="https://unpkg.com/bootstrap-icons@latest/bootstrap-icons.svg" />
-
   <style>
     body {
       background-color: slategrey;
     .page.show {
       display: initial;
     }
+    h1 {
+      text-align: center;
+    }
+    dialog {
+      padding: 1rem !important;
+    }
+    dialog .hide {
+      display: none;
+    }
     nav {
       align-items: flex-end;
       bottom: 0;
   <header>
     <x-box id="notifications"></x-box>
     <x-box id="wallets">
-      <x-select>
-        <x-icon href="#wallet2"></x-icon>
+      <x-select id="wallet" style="width:100%;" onchange="updateWallet()">
         <x-menu>
-          <x-menuitem value="new">
-            <x-button>
-              <x-label>New Wallet</x-label>
-              <dialog>
-                <x-input type="text">Name</x-input>
-                <x-radios>
-                  <x-label>Type</x-label>
-                  <x-radio value="bip44" toggled><x-label>BIP-44</x-label></x-radio>
-                  <x-radio value="blake2b"><x-label>BLAKE2b</x-label></x-radio>
-                </x-radios>
-                <x-input type="password" required="true">Password</x-input>
-                <x-button
-                  onclick="createWallet(this, this.closest('dialog').querySelector('x-radios').value).then(w => { console.log(w);showWallets() })"></x-button>
-              </dialog>
-            </x-button>
+          <x-menuitem value="_new">
+            <x-label>New</x-label>
+          </x-menuitem>
+          <x-menuitem value="_import">
+            <x-label>Import</x-label>
           </x-menuitem>
-          <x-menuitem value="import">
-            <x-label>Import Wallet</x-label>
-            <dialog>
-            </dialog>
+          <hr />
+          <x-menuitem value="_ledger">
+            <x-label>Ledger</x-label>
           </x-menuitem>
-          <hr>
+          <hr />
         </x-menu>
       </x-select>
+      <dialog id="wallet-form">
+        <x-box vertical>
+          <x-box id="wallet-form-name">
+            <x-label>Name</x-label>
+            <x-input type="text"></x-input>
+          </x-box>
+          <x-box id="wallet-form-wallet-type">
+            <x-radios required onchange="updateWalletForm()">
+              <x-radio value="bip44"><x-label>BIP-44</x-label></x-radio>
+              <x-radio value="blake2b"><x-label>BLAKE2b</x-label></x-radio>
+            </x-radios>
+          </x-box>
+          <x-box id="wallet-form-import-type" class="hide">
+            <x-select onchange="updateWalletForm()">
+              <x-menu>
+                <x-menuitem value="mnemonic"><x-label>Import mnemonic</x-label></x-menuitem>
+                <x-menuitem value="seed"><x-label>Import seed</x-label></x-menuitem>
+              </x-menu>
+            </x-select>
+          </x-box>
+          <x-box id="wallet-form-import-value" class="hide">
+            <x-label style="text-transform:capitalize"></x-label>
+            <x-input type="password"></x-input>
+          </x-box>
+          <x-box id="wallet-form-salt" class="hide">
+            <x-label>Salt</x-label>
+            <x-input type="password">Optional</x-input>
+          </x-box>
+          <x-box id="wallet-form-password">
+            <x-label>Lock</x-label>
+            <x-input type="password" required><x-label>Password</x-label></x-input>
+          </x-box>
+          <x-box>
+            <x-buttons>
+              <x-button onclick="document.getElementById('wallet-form').close()">
+                Cancel
+              </x-button>
+              <x-button onclick="createWallet().then(w => { console.log(w);showWallets() })">
+                OK
+              </x-button>
+            </x-buttons>
+          </x-box>
+        </x-box>
+      </dialog>
     </x-box><!-- end #wallets-->
     <x-box id="accounts">
-      <x-select>
-        <x-icon href="#cash-stack"></x-icon>
+      <x-select id="account" style="width:100%;" onchange="updateAccount()">
         <x-menu>
-          <x-menuitem value="new">
-            <x-label>New Account</x-label>
+          <x-menuitem value="_new">
+            <x-label>New</x-label>
           </x-menuitem>
-          <x-menuitem value="import">
-            <x-label>Derive</x-label>
-            <dialog>
-            </dialog>
+          <x-menuitem value="_import">
+            <x-label>Import</x-label>
           </x-menuitem>
-          <hr>
+          <hr />
         </x-menu>
       </x-select>
+      <x-button>
+        <x-icon href="#cash-stack"></x-icon>
+        <x-label>New Account</x-label>
+        <dialog id="account-form">
+          <x-box vertical>
+            <x-box>
+              <x-label>Name</x-label>
+              <x-input type="text"></x-input>
+            </x-box>
+            <x-box>
+              <x-label>Index (optional)</x-label>
+              <x-numberinput min="0" max="2147483647"></x-numberinput>
+            </x-box>
+            <x-buttons>
+              <x-button onclick="document.getElementById('account-form').close()">
+                Cancel
+              </x-button>
+              <x-button onclick="deriveAccounts(this).then(w => { console.log(w);showWallets() })">
+                OK
+              </x-button>
+            </x-buttons>
+          </x-box>
+        </dialog>
+      </x-button>
     </x-box><!-- end #accounts -->
   </header>
   <main>
     <x-box id="exchange" class="page">
-      Exchange
+      <h1>Exchange</h1>
     </x-box><!-- end #exchange -->
     <x-box id="history" class="page">
-      History
+      <h1>History</h1>
     </x-box><!-- end #history -->
     <x-box id="transact" class="page">
-      Transact
+      <h1>Transact</h1>
       <x-card id="walletCard">
       </x-card>
     </x-box><!-- end #transact -->
     <x-box id="rolodex" class="page">
-      Rolodex
+      <h1>Rolodex</h1>
     </x-box><!-- end #rolodex -->
     <x-box id="settings" class="page">
       <h1>Settings</h1>