diff --git a/src/components/send-request-btns.js b/src/components/send-request-btns.js
index c794d6e..413a3ed 100644
--- a/src/components/send-request-btns.js
+++ b/src/components/send-request-btns.js
@@ -3,7 +3,7 @@ import { lit as html } from '../helpers/lit.js'
const initialState = {
rendered: null,
content: state => html`
-
diff --git a/src/helpers/constants.js b/src/helpers/constants.js
index fe748e1..8b37025 100644
--- a/src/helpers/constants.js
+++ b/src/helpers/constants.js
@@ -123,6 +123,10 @@ export const TIMEAGO_LOCALE_EN = {
export const PHRASE_REGEX = new RegExp(
/^([a-zA-Z]+\s){11,}([a-zA-Z]+)$/
)
+export const AMOUNT_REGEX = new RegExp(
+ // /^[0-9]{1,5}?$/
+ /^[0-9]+(\.[0-9]{1,8})?%?$/
+)
export const ALIAS_REGEX = new RegExp(
/^[a-zA-Z0-9]{1,}([a-zA-Z0-9_.\-]+[a-zA-Z0-9])?$/
)
diff --git a/src/helpers/utils.js b/src/helpers/utils.js
index 7fd4786..4b1bfe5 100644
--- a/src/helpers/utils.js
+++ b/src/helpers/utils.js
@@ -423,12 +423,56 @@ export function sortContactsByName(a, b) {
return 0;
}
+export function DashURLSearchParams(params) {
+ let searchParams
+ let qry = {}
+
+ Object.defineProperty(this, "entries", {
+ enumerable: false,
+ configurable: false,
+ writable: false,
+ value: () => Object.entries(qry),
+ });
+ Object.defineProperty(this, "toString", {
+ enumerable: false,
+ configurable: false,
+ writable: false,
+ value: () => this.entries().map(p => p.join('=')).join('&'),
+ });
+ Object.defineProperty(this, "size", {
+ get() { return this.entries().length },
+ enumerable: false,
+ configurable: false,
+ });
+
+ if (typeof params === 'string' && params !== '') {
+ searchParams = params.split('&')
+ searchParams.forEach(q => {
+ let [prop,val] = q.split('=')
+ qry[prop] = val
+ })
+ }
+
+ if(Array.isArray(params) && params.length > 0) {
+ params.forEach(q => {
+ let [prop,val] = q
+ qry[prop] = val
+ })
+ }
+
+ // console.log('DashURLSearchParams', {
+ // params, searchParams, qry,
+ // qryStr: this.toString(),
+ // })
+}
+
export function parseDashURI(uri) {
let result = {}
let parsedUri = [
...uri.matchAll(DASH_URI_REGEX)
]?.[0]?.groups || {}
- let searchParams = new URLSearchParams(parsedUri?.params || '')
+ // let searchParams = new URLSearchParams(parsedUri?.params || '')
+ let searchParams = new DashURLSearchParams(parsedUri?.params || '')
console.log(
'parseDashURI',
@@ -534,7 +578,11 @@ export function generateContactPairingURI(
}
let scope = claims.map(p => p[0]).join(',')
- let searchParams = new URLSearchParams([
+ // let searchParams = new URLSearchParams([
+ // ...claims,
+ // ['scope', scope]
+ // ])
+ let searchParams = new DashURLSearchParams([
...claims,
['scope', scope]
])
@@ -542,6 +590,8 @@ export function generateContactPairingURI(
console.log(
'Generate Dash URI claims',
claims, scope, searchParams,
+ searchParams.size,
+ searchParams.entries(),
)
let res = `${protocol}${joiner}${addr}`
@@ -601,7 +651,10 @@ export function generatePaymentRequestURI(
)
}
- let searchParams = new URLSearchParams([
+ // let searchParams = new URLSearchParams([
+ // ...claims,
+ // ])
+ let searchParams = new DashURLSearchParams([
...claims,
])
@@ -813,9 +866,50 @@ export function timeago(ms, locale = TIMEAGO_LOCALE_EN) {
return locale.never;
}
-export function getAvatar(c) {
+export async function sha256(str) {
+ const buf = await crypto.subtle.digest(
+ "SHA-256", new TextEncoder().encode(str)
+ );
+ return Array.prototype.map.call(
+ new Uint8Array(buf),
+ x => (('00' + x.toString(16)).slice(-2))
+ ).join('');
+}
+
+// https://stackoverflow.com/a/66494926
+export function getBackgroundColor(stringInput) {
+ let stringUniqueHash = [...stringInput].reduce((acc, char) => {
+ return char.charCodeAt(0) + ((acc << 5) - acc);
+ }, 0);
+ return `hsl(${stringUniqueHash % 360}, 100%, 67%)`;
+}
+
+export async function getAvatarUrl(
+ email,
+ size = 48,
+ rating = 'pg',
+ srv = 'gravatar',
+) {
+ let emailSHA = await sha256(email || '')
+
+ if (srv === 'gravatar') {
+ return `https://gravatar.com/avatar/${
+ emailSHA
+ }?s=${size}&r=${rating}&d=retro`
+ }
+ if (srv === 'libravatar') {
+ return `https://seccdn.libravatar.org/avatar/${
+ emailSHA
+ }?s=${size}&r=${rating}&d=retro`
+ }
+
+ return ''
+}
+
+export async function getAvatar(c) {
let initials = c?.info?.name?.
- split(' ').map(n => n[0]).join('') || ''
+ split(' ').map(n => n[0]).slice(0,3).join('') || ''
+ let nameOrAlias = c?.info?.name || c?.alias || c?.info?.preferred_username
if (!initials) {
initials = (c?.alias || c?.info?.preferred_username)?.[0] || ''
@@ -823,14 +917,22 @@ export function getAvatar(c) {
let avStr = `${initials}
`
}
@@ -860,6 +962,10 @@ export async function getRandomWords(len = 32) {
return await DashPhrase.generate(len)
}
+export async function verifyPhrase(phrase) {
+ return await DashPhrase.verify(phrase).catch(_ => false)
+}
+
export function isUniqueAlias(aliases, preferredAlias) {
return !aliases[preferredAlias]
}
diff --git a/src/helpers/wallet.js b/src/helpers/wallet.js
index 4b29cd8..9fcd132 100644
--- a/src/helpers/wallet.js
+++ b/src/helpers/wallet.js
@@ -1,4 +1,5 @@
import {
+ DashWallet,
DashTx,
DashHd,
DashSight,
@@ -543,7 +544,7 @@ export async function initWallet(
store.addresses.setItem(
a.address,
{
- updatedAt: (new Date()).toISOString(),
+ updatedAt: Date.now(),
walletId: wallet.id,
accountIndex: a.accountIndex,
addressIndex: a.addressIndex,
@@ -555,7 +556,7 @@ export async function initWallet(
`${id}`,
{
id,
- updatedAt: (new Date()).toISOString(),
+ updatedAt: Date.now(),
accountIndex,
addressIndex: addrs?.finalAddressIndex || addressIndex,
keystore: keystore || await encryptKeystore(
@@ -599,7 +600,7 @@ export async function checkWalletFunds(addr, wallet = {}) {
accountIndex,
addressIndex,
} = addr
- let updated_at = (new Date()).getTime()
+ let updatedAt = Date.now()
let $addr = await store.addresses.getItem(address) || {}
$addr = {
@@ -612,8 +613,8 @@ export async function checkWalletFunds(addr, wallet = {}) {
let walletFunds = $addr?.insight
if (
- !walletFunds?.updated_at ||
- updated_at - walletFunds?.updated_at > HOUR
+ !walletFunds?.updatedAt ||
+ updatedAt - walletFunds?.updatedAt > HOUR
) {
// console.info('check insight api for addr', addr)
@@ -625,7 +626,7 @@ export async function checkWalletFunds(addr, wallet = {}) {
$addr.insight = {
...walletFunds,
- updated_at,
+ updatedAt,
}
store.addresses.setItem(
@@ -640,6 +641,49 @@ export async function checkWalletFunds(addr, wallet = {}) {
return $addr
}
+export async function updateAddrFunds(
+ wallet, insightRes,
+) {
+ let updatedAt = Date.now()
+ let { addrStr, ...res } = insightRes
+ let $addr = await store.addresses.getItem(addrStr) || {}
+ let {
+ walletId,
+ accountIndex,
+ addressIndex,
+ } = $addr
+ // console.log(
+ // 'checkWalletFunds $addr',
+ // $addr,
+ // walletId,
+ // wallet?.id,
+ // walletId === wallet?.id
+ // )
+
+ if (walletId === wallet?.id) {
+ $addr = {
+ walletId: wallet.id,
+ accountIndex,
+ addressIndex,
+ ...$addr,
+ }
+
+ $addr.insight = {
+ ...res,
+ updatedAt,
+ }
+
+ store.addresses.setItem(
+ addrStr,
+ $addr,
+ )
+
+ // funds += res?.balance || 0
+ // walletFunds.balance = funds
+ return res
+ }
+}
+
export async function updateAllFunds(wallet, walletFunds) {
let funds = 0
let addrKeys = await store.addresses.keys()
@@ -654,7 +698,6 @@ export async function updateAllFunds(wallet, walletFunds) {
addrKeys.length,
)
let balances = await dashsight.getInstantBalances(addrKeys)
- let updated_at = (new Date()).getTime()
console.log('updateAllFunds balances', balances)
@@ -663,42 +706,22 @@ export async function updateAllFunds(wallet, walletFunds) {
}
for (const insightRes of balances) {
- let { addrStr, ...res } = insightRes
- let $addr = await store.addresses.getItem(addrStr) || {}
- let {
- walletId,
- accountIndex,
- addressIndex,
- } = $addr
- // console.log(
- // 'checkWalletFunds $addr',
- // $addr,
- // walletId,
- // wallet?.id,
- // walletId === wallet?.id
- // )
+ let { addrStr } = insightRes
+ let addrIdx = addrKeys.indexOf(addrStr)
+ if (addrIdx > -1) {
+ addrKeys.splice(addrIdx, 1)
+ }
+ funds += (await updateAddrFunds(wallet, insightRes))?.balance || 0
+ walletFunds.balance = funds
+ }
- if (walletId === wallet?.id) {
- $addr = {
- walletId: wallet.id,
- accountIndex,
- addressIndex,
- ...$addr,
- }
+ for (const addr of addrKeys) {
+ let { insight, ...$addr } = await store.addresses.getItem(addr) || {}
- $addr.insight = {
- ...res,
- updated_at,
- }
-
- store.addresses.setItem(
- addrStr,
- $addr,
- )
-
- funds += res?.balance || 0
- walletFunds.balance = funds
- }
+ store.addresses.setItem(
+ addr,
+ $addr,
+ )
}
console.log('updateAllFunds funds', funds)
@@ -779,7 +802,7 @@ export async function batchAddressGenerate(
address,
{
...$addr,
- updatedAt: (new Date()).toISOString(),
+ updatedAt: Date.now(),
walletId: wallet.id,
accountIndex,
addressIndex: addrIdx,
@@ -862,7 +885,7 @@ export async function forceInsightUpdateForAddress(addr) {
...currentAddr,
insight: {
...currentAddr.insight,
- updated_at: 0
+ updatedAt: 0
}
}
)
@@ -880,7 +903,7 @@ export function sortAddrs(a, b) {
// Ascending Vout (Numerical)
let indexDiff = a.addressIndex - b.addressIndex;
return indexDiff;
-};
+}
export function getBalance(utxos) {
return utxos.reduce(function (total, utxo) {
@@ -955,12 +978,18 @@ export async function createTx(
fundAddrs,
recipient,
amount,
+ fullTransfer = false,
) {
const MIN_FEE = 191;
const DUST = 2000;
const AMOUNT_SATS = DashTx.toSats(amount)
+ const COIN_TYPE = 5
+ const usage = 0
+
+ let cachedAddrs = {}
+
+ // console.log(DashTx, fromWallet)
- console.log(DashTx, fromWallet)
let dashTx = DashTx.create({
// @ts-ignore
version: 3,
@@ -975,9 +1004,11 @@ export async function createTx(
))?.address
let tmpWallet
+ let recipientAddr = recipient?.address || recipient
+
// console.log('createTx fundAddrs', [...fundAddrs])
- if (Array.isArray(fundAddrs)) {
+ if (Array.isArray(fundAddrs) && fundAddrs.length > 0) {
fundAddrs.sort(sortAddrs)
changeAddr = changeAddr || fundAddrs[0].address
// console.log('createTx fundAddrs sorted', fundAddrs)
@@ -989,6 +1020,14 @@ export async function createTx(
w.addressIndex,
)
privateKeys[tmpWallet.address] = tmpWallet.addressKey.privateKey
+ cachedAddrs[w.address] = {
+ checked_at: w.updatedAt,
+ hdpath: `m/44'/${COIN_TYPE}'/${w.accountIndex}'/${usage}`,
+ index: w.addressIndex,
+ wallet: w.walletId, // maybe `selectedAlias`?
+ txs: [],
+ utxos: [],
+ }
}
// console.log('createTx privateKeys', Object.keys(privateKeys), privateKeys)
coreUtxos = await dashsight.getMultiCoreUtxos(
@@ -1005,84 +1044,116 @@ export async function createTx(
tmpWallet.address
)
changeAddr = changeAddr || tmpWallet.address
+ cachedAddrs[fundAddrs.address] = {
+ checked_at: fundAddrs.updatedAt,
+ hdpath: `m/44'/${COIN_TYPE}'/${fundAddrs.accountIndex}'/${usage}`,
+ index: fundAddrs.addressIndex,
+ wallet: fundAddrs.walletId, // maybe `selectedAlias`?
+ txs: [],
+ utxos: [],
+ }
}
- let optimalUtxos = selectOptimalUtxos(
- coreUtxos,
- AMOUNT_SATS,
- )
-
- console.log('coreUtxos', coreUtxos);
- // console.log(
- // 'coreUtxos amounts',
- // coreUtxos.map(({ address, satoshis }) => ({ address, satoshis }))
- // );
- // console.log(
- // 'optimalUtxos',
- // amount,
- // AMOUNT_SATS,
- // optimalUtxos
- // );
+ // console.log('fundAddrs', {fundAddrs, cachedAddrs})
- let payments = [
- {
- address: recipient?.address || recipient,
- satoshis: AMOUNT_SATS,
+ // @ts-ignore
+ let dashwallet = await DashWallet.create({
+ safe: {
+ cache: {
+ addresses: cachedAddrs
+ }
},
- ];
+ store: {
+ save: data => console.log('dashwallet.store.save', {data})
+ },
+ dashsight,
+ });
- let spendableDuffs = optimalUtxos.reduce(function (total, utxo) {
- return total + utxo.satoshis;
- }, 0);
- let spentDuffs = payments.reduce(function (total, output) {
- return total + output.satoshis;
- }, 0);
- let unspentDuffs = spendableDuffs - spentDuffs;
+ console.log('output', {
+ amount,
+ AMOUNT_SATS,
+ });
- let txInfo = {
- inputs: optimalUtxos,
- outputs: payments,
- };
+ let selection
+ let receiverOutput
+ if (fullTransfer) {
+ selection = dashwallet.useAllCoins({
+ utxos: coreUtxos,
+ breakChange: false,
+ })
+ receiverOutput = selection.output
+ } else {
+ receiverOutput = DashWallet._parseSendInfo(dashwallet, AMOUNT_SATS);
- let sizes = DashTx.appraise(txInfo);
- let midFee = sizes.mid;
+ console.log('_parseSendInfo receiverOutput', {
+ receiverOutput,
+ });
- if (unspentDuffs < MIN_FEE) {
- throw new Error(
- `overspend: inputs total '${spendableDuffs}', but outputs total '${spentDuffs}', which leaves no way to pay the fee of '${sizes.mid}'`,
- );
+ selection = dashwallet.useMatchingCoins({
+ output: receiverOutput,
+ utxos: coreUtxos,
+ breakChange: false,
+ })
+ console.log('sendMatchingUtxos', {
+ selection,
+ });
}
- txInfo.inputs.sort(DashTx.sortInputs)
+ console.log('coreUtxos', {
+ coreUtxos,
+ selection,
+ amount,
+ AMOUNT_SATS,
+ fullTransfer,
+ });
- let outputs = txInfo.outputs.slice(0);
- let change;
+ let outputs = [];
+ let stampVal = dashwallet.__STAMP__ * selection.output.stampsPerCoin;
+ let receiverDenoms = receiverOutput?.denoms.slice(0);
+
+ for (let denom of selection.output.denoms) {
+ let address = '';
+ let matchingDenomIndex = receiverDenoms.indexOf(denom);
+ if (matchingDenomIndex >= 0) {
+ void receiverDenoms.splice(matchingDenomIndex, 1);
+ address = recipientAddr;
+ } else {
+ address = changeAddr;
+ }
- change = unspentDuffs - (midFee + DashTx.OUTPUT_SIZE);
- if (change < DUST) {
- change = 0;
- }
- if (change) {
- txInfo.outputs = outputs.slice(0);
- txInfo.outputs.push({
- address: changeAddr,
- satoshis: change,
- });
+ let coreOutput = {
+ address,
+ // address: addrsInfo.addresses.pop(),
+ satoshis: denom + stampVal,
+ faceValue: denom,
+ stamps: selection.output.stampsPerCoin,
+ };
+
+ outputs.push(coreOutput);
}
+ let txInfo = {
+ inputs: selection.inputs,
+ outputs: outputs,
+ };
+
txInfo.outputs.sort(DashTx.sortOutputs)
+ txInfo.inputs.sort(DashTx.sortInputs)
+
+ console.log('txInfo', txInfo);
- let keys = optimalUtxos.map(
+ let keys = txInfo.inputs.map(
utxo => privateKeys[utxo.address]
);
- console.log('txInfo', txInfo);
-
let tx = await dashTx.hashAndSignAll(txInfo, keys);
console.log('tx', tx);
- return tx
+ return {
+ tx,
+ changeAddr,
+ }
}
export async function sendTx(
diff --git a/src/imports.js b/src/imports.js
index e699da7..9057285 100644
--- a/src/imports.js
+++ b/src/imports.js
@@ -12,7 +12,6 @@
* See https://github.com/jojobyte/browser-import-rabbit-hole
*/
-// import '../node_modules/dashwallet/dashwallet.js';
import '../node_modules/dashtx/dashtx.js';
import '../node_modules/dashkeys/dashkeys.js';
import '../node_modules/dashhd/dashhd.js';
@@ -23,9 +22,9 @@ import '../node_modules/@dashincubator/base58check/base58check.js';
import '../node_modules/@dashincubator/ripemd160/ripemd160.js';
import '../node_modules/@dashincubator/secp256k1/secp256k1.js';
import '../node_modules/crypticstorage/cryptic.js';
+import '../node_modules/dashwallet/dashwallet.js';
import '../node_modules/localforage/dist/localforage.js';
-// import * as DashWalletTypes from '../node_modules/dashwallet/dashwallet.js';
import * as DashTxTypes from '../node_modules/dashtx/dashtx.js';
import * as DashKeysTypes from '../node_modules/dashkeys/dashkeys.js';
import * as DashHDTypes from '../node_modules/dashhd/dashhd.js';
@@ -37,10 +36,9 @@ import * as RIPEMD160Types from '../node_modules/@dashincubator/ripemd160/ripemd
import * as Secp256k1Types from '../node_modules/@dashincubator/secp256k1/secp256k1.js';
import * as CrypticTypes from '../node_modules/crypticstorage/cryptic.js';
import * as CrypticStorageTypes from '../node_modules/crypticstorage/storage.js';
+import * as DashWalletTypes from '../node_modules/dashwallet/dashwallet.js';
// import * as LocalForageTypes from '../node_modules/localforage/dist/localforage.js';
-// /** @type {DashWalletTypes} */
-// export let DashWallet = window?.Wallet || globalThis?.Wallet;
/** @type {DashTxTypes} */
export let DashTx = window?.DashTx || globalThis?.DashTx;
/** @type {DashKeysTypes} */
@@ -65,12 +63,14 @@ export let Cryptic =
/** @type {CrypticStorageTypes} */
export let CrypticStorage =
window?.CrypticStorage || globalThis?.CrypticStorage;
+/** @type {DashWalletTypes} */
+export let DashWallet = window?.Wallet || globalThis?.Wallet;
export let localforage =
window?.localforage || globalThis?.localforage;
export default {
- // DashWallet,
+ DashWallet,
DashTx,
DashKeys,
DashHd,
diff --git a/src/index.html b/src/index.html
index eb82908..1ee863c 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,7 +2,7 @@
-
+
Incubator Wallet
diff --git a/src/main.js b/src/main.js
index 7eae9bb..b3cd697 100644
--- a/src/main.js
+++ b/src/main.js
@@ -57,10 +57,11 @@ import confirmActionRig from './rigs/confirm-action.js'
import confirmDeleteRig from './rigs/confirm-delete.js'
import editProfileRig from './rigs/edit-profile.js'
import scanContactRig from './rigs/scan.js'
-import sendOrRequestRig from './rigs/send-or-request.js'
+import sendOrReceiveRig from './rigs/send-or-request.js'
import sendConfirmRig from './rigs/send-confirm.js'
import requestQrRig from './rigs/request-qr.js'
import pairQrRig from './rigs/pair-qr.js'
+import txInfoRig from './rigs/tx-info.js'
// app/data state
let accounts
@@ -124,7 +125,7 @@ let appDialogs = envoy(
editContact: {},
editProfile: {},
scanContact: {},
- sendOrRequest: {},
+ sendOrReceive: {},
sendConfirm: {},
requestQr: {},
},
@@ -157,7 +158,7 @@ let contactsList = await setupContactsList(
// state,
// )
- let contactArticle = event.target?.closest('article')
+ let contactArticle = event.target?.closest('a, article')
if (
// event.target?.nodeName === 'ARTICLE'
@@ -185,7 +186,7 @@ let contactsList = await setupContactsList(
if (!contactData.outgoing) {
// Finish Pairing
let contactName = contactData?.info?.name || 'Contact'
- appDialogs.addContact.render(
+ await appDialogs.addContact.render(
{
name: `Finish Pairing with ${contactName}`,
wallet: shareAccount,
@@ -197,7 +198,7 @@ let contactsList = await setupContactsList(
appDialogs.addContact.showModal()
} else {
// Edit Contact
- appDialogs.editContact.render(
+ await appDialogs.editContact.render(
{
wallet,
account: appState.account,
@@ -301,7 +302,7 @@ let contactsList = await setupContactsList(
appState.contacts.push(newContact)
- contactsList.render(
+ await contactsList.render(
appState.contacts.sort(sortContactsByAlias)
)
@@ -311,7 +312,7 @@ let contactsList = await setupContactsList(
)
}
- appDialogs.addContact.render(
+ await appDialogs.addContact.render(
{
name: 'Add a New Contact',
wallet: shareAccount,
@@ -359,6 +360,54 @@ async function getUserInfo() {
}
}
+async function handlePasswordToggle(event) {
+ let {
+ // @ts-ignore
+ name: fieldName, form,
+ } = event?.target
+
+ // console.log('handlePasswordToggle', {
+ // fieldName,
+ // form,
+ // })
+
+ if (
+ fieldName === 'show_pass'
+ ) {
+ event.stopPropagation()
+ event.preventDefault()
+
+ let { pass, show_pass, } = form
+
+ if (show_pass?.checked) {
+ pass.type = 'text'
+ } else {
+ pass.type = 'password'
+ }
+ }
+}
+
+function getTarget(event, selector) {
+ let {
+ // @ts-ignore
+ id,
+ // @ts-ignore
+ parentElement,
+ } = event?.target
+
+ let target
+
+ if (id === selector) {
+ target = event?.target
+ }
+
+ if (parentElement.id === selector) {
+ target = parentElement
+ }
+
+ return target
+}
+
async function main() {
appState.encryptionPassword = window.atob(
sessionStorage.encryptionPassword || ''
@@ -394,173 +443,101 @@ async function main() {
}
)
- appDialogs.walletEncrypt = walletEncryptRig({
+ appDialogs.walletEncrypt = await walletEncryptRig({
setupDialog, appDialogs, appState, mainApp,
wallet, wallets, bodyNav, dashBalance,
})
- appDialogs.walletDecrypt = walletDecryptRig({
+ appDialogs.walletDecrypt = await walletDecryptRig({
setupDialog, appDialogs, appState, mainApp, importFromJson,
wallets, decryptKeystore, getUserInfo, store, deriveWalletData,
})
- appDialogs.walletBackup = walletBackupRig({
+ appDialogs.walletBackup = await walletBackupRig({
mainApp, wallet, wallets, setupDialog, appDialogs, appState, store,
exportWalletData, saveJsonToFile, localForageBaseCfg,
})
- appDialogs.phraseBackup = phraseBackupRig({
+ appDialogs.phraseBackup = await phraseBackupRig({
mainApp, wallets, setupDialog, appDialogs,
})
- appDialogs.phraseGenerate = phraseGenerateRig({
+ appDialogs.phraseGenerate = await phraseGenerateRig({
setupDialog, appDialogs, appState,
mainApp, wallet, wallets, store,
deriveWalletData, generateWalletData,
})
- appDialogs.phraseImport = phraseImportRig({
+ appDialogs.phraseImport = await phraseImportRig({
setupDialog, appDialogs, appState, store,
mainApp, wallet, wallets, deriveWalletData,
})
- appDialogs.onboard = onboardRig({
+ appDialogs.onboard = await onboardRig({
mainApp, setupDialog, appDialogs,
})
- appDialogs.addContact = addContactRig({
+ appDialogs.addContact = await addContactRig({
setupDialog, updateAllFunds,
appDialogs, appState, appTools, store, walletFunds,
mainApp, wallet, userInfo, contactsList,
})
- appDialogs.confirmAction = confirmActionRig({
+ appDialogs.confirmAction = await confirmActionRig({
mainApp, setupDialog,
appDialogs, appState, appTools,
})
- appDialogs.confirmDelete = confirmDeleteRig({
+ appDialogs.confirmDelete = await confirmDeleteRig({
mainApp, setupDialog, appDialogs, appState, appTools,
store, userInfo, contactsList,
})
- appDialogs.editContact = editContactRig({
+ appDialogs.editContact = await editContactRig({
setupDialog, updateAllFunds,
appDialogs, appState, appTools, store, walletFunds,
mainApp, wallet, userInfo, contactsList,
})
- appDialogs.editProfile = editProfileRig({
+ appDialogs.editProfile = await editProfileRig({
mainApp, setupDialog, store,
appState, appTools, bodyNav,
})
- appDialogs.scanContact = scanContactRig({
+ appDialogs.scanContact = await scanContactRig({
setupDialog, mainApp,
})
- appDialogs.sendOrRequest = sendOrRequestRig({
+ appDialogs.sendOrReceive = await sendOrReceiveRig({
mainApp, appDialogs, appState, appTools, store,
wallet, account: appState.account, walletFunds,
setupDialog, deriveWalletData, createTx,
getAddrsWithFunds, batchGenAcctAddrs,
})
- appDialogs.sendConfirm = sendConfirmRig({
+ appDialogs.txInfo = await txInfoRig({
+ setupDialog,
+ mainApp, wallet, userInfo,
+ })
+
+ appDialogs.sendConfirm = await sendConfirmRig({
mainApp, appDialogs, appState, appTools,
store, userInfo, contactsList, walletFunds,
setupDialog, deriveWalletData, getAddrsWithFunds,
- createTx, sendTx,
+ createTx, sendTx, updateAllFunds,
})
- appDialogs.requestQr = requestQrRig({
- mainApp, setupDialog,
+ appDialogs.requestQr = await requestQrRig({
+ mainApp, setupDialog, appDialogs, appState, userInfo,
})
- appDialogs.pairQr = pairQrRig({
+ appDialogs.pairQr = await pairQrRig({
setupDialog,
mainApp, wallet, userInfo,
})
svgSprite.render()
- document.addEventListener('submit', async event => {
- let {
- // @ts-ignore
- name: formName, parentElement, form,
- } = event?.target
-
- let fde = formDataEntries(event)
-
- if (formName === 'send_or_request') {
- event.preventDefault()
- event.stopPropagation()
- let name = 'Send Funds'
- if (fde.intent === 'request') {
- name = 'Request Funds'
- }
-
- appDialogs.sendOrRequest.render({
- action: fde.intent,
- wallet,
- account: appState.account,
- // accounts,
- userInfo,
- contacts: appState.contacts,
- to: null,
- })
- appDialogs.sendOrRequest.showModal()
- // .catch(console.error)
- }
- })
- document.addEventListener('input', async event => {
- let {
- // @ts-ignore
- name: fieldName, form,
- } = event?.target
-
- if (
- fieldName === 'show_pass'
- ) {
- event.stopPropagation()
- event.preventDefault()
-
- let { pass, show_pass, } = form
-
- if (show_pass?.checked) {
- pass.type = 'text'
- } else {
- pass.type = 'password'
- }
- }
- })
- document.addEventListener('change', async event => {
- let {
- // @ts-ignore
- name: fieldName, parentElement, form,
- } = event?.target
-
- // console.log('form change', {
- // fieldName,
- // form,
- // })
-
- if (
- fieldName === 'show_pass'
- ) {
- event.stopPropagation()
- event.preventDefault()
-
- let { pass, show_pass, } = form
-
- if (show_pass?.checked) {
- pass.type = 'text'
- } else {
- pass.type = 'password'
- }
- }
- })
-
let ks = wallets?.[appState.selectedWallet]
?.keystore
let ks_phrase = ks?.crypto?.ciphertext || ''
@@ -593,7 +570,7 @@ async function main() {
) {
sessionStorage.removeItem('encryptionPassword')
- appDialogs.walletDecrypt.render({ wallet })
+ await appDialogs.walletDecrypt.render({ wallet })
await appDialogs.walletDecrypt.showModal()
}
@@ -611,21 +588,114 @@ async function main() {
}
]
+ document.addEventListener('input', handlePasswordToggle)
+ document.addEventListener('change', handlePasswordToggle)
+
if (!appState.phrase) {
- appDialogs.onboard.render()
+ await appDialogs.onboard.render()
await appDialogs.onboard.show()
- } else {
- wallet = await deriveWalletData(appState.phrase)
}
- batchGenAcctsAddrs(wallet)
- // .then(data => console.warn('batchGenAcctsAddrs', { data }))
-
- // temp fix, should be handled already
if (appState.phrase && !wallet) {
wallet = await deriveWalletData(appState.phrase)
}
+ document.addEventListener('submit', async event => {
+ let {
+ // @ts-ignore
+ name: formName,
+ } = event?.target
+
+ let fde = formDataEntries(event)
+
+ if (formName === 'send_or_receive') {
+ event.preventDefault()
+ event.stopPropagation()
+
+ if (fde.intent === 'receive') {
+ let receiveWallet
+
+ if (wallet?.xpub) {
+ wallet.addressIndex = (
+ appState.selectedWallet?.addressIndex || 0
+ ) + 1
+ receiveWallet = await deriveWalletData(
+ appState.phrase,
+ wallet.accountIndex,
+ wallet.addressIndex,
+ )
+ }
+
+ if (receiveWallet?.xkeyId) {
+ let tmpWallet = await store.accounts.getItem(
+ receiveWallet.xkeyId,
+ )
+
+ // state.wallet =
+ let tmpAcct = await store.accounts.setItem(
+ receiveWallet.xkeyId,
+ {
+ ...tmpWallet,
+ updatedAt: (new Date()).toISOString(),
+ address: receiveWallet.address,
+ addressIndex: receiveWallet.addressIndex,
+ }
+ )
+
+ batchGenAcctAddrs(receiveWallet, tmpAcct)
+
+ console.log(
+ `${fde.intent} FROM CONTACT`,
+ {
+ stateWallet: wallet,
+ receiveWallet,
+ tmpAcct,
+ }
+ )
+
+ await appDialogs.requestQr.render(
+ {
+ name: 'Share to receive funds',
+ submitTxt: `Select a Contact`,
+ submitAlt: `Change the currently selected contact`,
+ footer: state => html`
+
+
+ ${state.submitTxt}
+
+
+ `,
+ wallet: receiveWallet,
+ contacts: appState.contacts,
+ },
+ 'afterend',
+ )
+
+ let showRequestQR = await appDialogs.requestQr.showModal()
+ }
+ } else {
+ await appDialogs.sendOrReceive.render({
+ action: fde.intent,
+ wallet,
+ account: appState.account,
+ userInfo,
+ contacts: appState.contacts,
+ to: null,
+ })
+ appDialogs.sendOrReceive.showModal()
+ }
+ }
+ })
+
+ batchGenAcctsAddrs(wallet)
+ // .then(data => console.warn('batchGenAcctsAddrs', { data }))
+
bodyNav.render({
data: {
alias: appState.selectedAlias
@@ -670,15 +740,21 @@ async function main() {
id,
// @ts-ignore
nextElementSibling,
+ // @ts-ignore
+ parentElement,
} = event?.target
- if (id === 'nav-alias') {
+ let aliasTarg = getTarget(event, 'nav-alias')
+
+ if (aliasTarg) {
event.preventDefault()
event.stopPropagation()
- console.log('click alias', [event.target])
+ console.log('click alias', [aliasTarg])
- nextElementSibling.classList.toggle('hidden')
+ aliasTarg?.nextElementSibling.classList.toggle('hidden')
+ // @ts-ignore
+ // targ?.closest?.('menu.user')?.classList?.toggle('hidden')
// event.target.next
}
@@ -691,14 +767,13 @@ async function main() {
await getUserInfo()
- appDialogs.editProfile.render(
+ await appDialogs.editProfile.render(
{
wallet,
userInfo,
},
'afterend',
)
-
appDialogs.editProfile.showModal()
}
if (id === 'nav-backup') {
@@ -708,7 +783,7 @@ async function main() {
// @ts-ignore
event.target?.closest?.('menu.user')?.classList?.toggle('hidden')
- appDialogs.walletBackup.render(
+ await appDialogs.walletBackup.render(
{
wallet,
wallets,
@@ -724,7 +799,7 @@ async function main() {
// @ts-ignore
event.target?.closest?.('menu.user')?.classList?.toggle('hidden')
- appDialogs.confirmAction.render({
+ await appDialogs.confirmAction.render({
name: 'Confirm Wallet Lock',
actionTxt: 'Lock it!',
actionAlt: 'Lock the wallet',
@@ -747,7 +822,7 @@ async function main() {
// @ts-ignore
event.target?.closest?.('menu.user')?.classList?.toggle('hidden')
- appDialogs.confirmAction.render({
+ await appDialogs.confirmAction.render({
name: 'Confirm Wallet Disconnect',
actionTxt: 'Disconnect',
actionAlt: 'Clear all wallet data stored in browser',
@@ -755,7 +830,7 @@ async function main() {
// target: '',
// targetFallback: 'this wallet',
actionType: 'dang',
- submitIcon: state => `💣`,
+ submitIcon: state => `🧹`, // `💣`,
alert: state => html`
@@ -781,7 +856,11 @@ async function main() {
// @ts-ignore
// event.target?.closest?.('menu menu:not(.hidden)')?.classList?.add('hidden')
- if (!id || !id.startsWith('nav-')) {
+ if ((
+ !id?.startsWith('nav-')
+ ) && (
+ !parentElement?.id?.startsWith('nav-')
+ )) {
document.querySelector('menu.user:not(.hidden)')?.classList?.add('hidden')
}
})
@@ -830,6 +909,15 @@ async function main() {
'===sentTransactions TXID===',
appState?.sentTransactions?.[data.txid]
)
+
+ setTimeout(() =>
+ updateAllFunds(wallet, walletFunds)
+ .then(funds => {
+ console.log('updateAllFunds then funds', funds)
+ })
+ .catch(err => console.error('catch updateAllFunds', err, wallet)),
+ 1000
+ )
}
let now = Date.now();
@@ -842,7 +930,7 @@ async function main() {
// console.log('init dash socket vout', data.vout)
- let result = data.vout.some(function (vout) {
+ let result = data.vout.filter(function (vout) {
let v = Object.keys(vout)
let addr = v[0]
let duffs = vout[addr];
@@ -869,10 +957,10 @@ async function main() {
txUpdates[data.txid] = true
store.addresses.getItem(addr)
.then(async storedAddr => {
- if (storedAddr?.insight?.updated_at) {
+ if (storedAddr?.insight?.updatedAt) {
storedAddr.insight.balance = (duffs / DUFFS)
storedAddr.insight.balanceSat = duffs
- storedAddr.insight.updated_at = 0
+ storedAddr.insight.updatedAt = 0
store.addresses.setItem(addr, storedAddr)
}
})
@@ -917,10 +1005,10 @@ async function main() {
updates[addr] = newTx
store.addresses.getItem(addr)
.then(async storedAddr => {
- if (storedAddr.insight?.updated_at) {
+ if (storedAddr.insight?.updatedAt) {
storedAddr.insight.balance += (duffs / DUFFS)
storedAddr.insight.balanceSat += duffs
- storedAddr.insight.updated_at = 0
+ storedAddr.insight.updatedAt = 0
store.addresses.setItem(addr, storedAddr)
}
})
@@ -928,7 +1016,7 @@ async function main() {
return newTx;
});
- if (result) {
+ if (result.length > 0) {
console.log(
'socket found address in store',
updates,
@@ -936,11 +1024,20 @@ async function main() {
)
if (appDialogs.requestQr.element.open) {
- if (appDialogs.sendOrRequest.element.open) {
- appDialogs.sendOrRequest.close()
+ if (appDialogs.sendOrReceive.element.open) {
+ appDialogs.sendOrReceive.close()
}
appDialogs.requestQr.close()
}
+
+ setTimeout(() =>
+ updateAllFunds(wallet, walletFunds)
+ .then(funds => {
+ console.log('updateAllFunds then funds', funds)
+ })
+ .catch(err => console.error('catch updateAllFunds', err, wallet)),
+ 1000
+ )
}
let txs = appState?.sentTransactions
diff --git a/src/rigs/add-contact.js b/src/rigs/add-contact.js
index 3e7f44f..26159ff 100644
--- a/src/rigs/add-contact.js
+++ b/src/rigs/add-contact.js
@@ -22,7 +22,7 @@ import {
ALIAS_REGEX,
} from '../helpers/constants.js'
-export let addContactRig = (function (globals) {
+export let addContactRig = (async function (globals) {
'use strict';
let {
@@ -89,7 +89,145 @@ export let addContactRig = (function (globals) {
)
}, 1000)
- let addContact = setupDialog(
+ async function processURI(state, target, value) {
+ let {
+ address,
+ xpub,
+ xprv,
+ name,
+ preferred_username,
+ sub,
+ } = parseAddressField(value)
+
+ let xkey = xprv || xpub
+
+ let xkeyOrAddr = xkey || address
+
+ let info = {
+ name: name || '',
+ sub,
+ preferred_username,
+ }
+
+ let preferredAlias = await getUniqueAlias(
+ aliases,
+ preferred_username
+ )
+
+ let outgoing = {}
+
+ let existingContacts
+
+ if (!xkey && address) {
+ existingContacts = appState.contacts.filter(
+ c => c.outgoing?.[address]
+ )
+
+ outgoing = {
+ ...(state.contact.outgoing || {}),
+ [address]: {
+ address,
+ },
+ }
+ }
+
+ if (xkey) {
+ let {
+ xkeyId,
+ addressKeyId,
+ addressIndex,
+ address: addr,
+ } = await deriveWalletData(
+ xkey,
+ )
+
+ existingContacts = appState.contacts.filter(
+ c => c.outgoing?.[xkeyId]
+ )
+
+ outgoing = {
+ ...(state.contact.outgoing || {}),
+ [xkeyId]: {
+ addressIndex,
+ addressKeyId,
+ address: address || addr,
+ xkeyId,
+ xprv,
+ xpub,
+ },
+ }
+
+ console.log(
+ 'add contact handleInput parsedAddr',
+ value,
+ xkey,
+ )
+ }
+
+ if (existingContacts?.length > 0) {
+ console.warn(
+ `You've already paired with this contact`,
+ {
+ existingContacts,
+ newContact: {
+ alias: preferredAlias,
+ outgoing,
+ }
+ }
+ )
+ }
+
+ let newContact = await appTools.storedData.encryptItem(
+ store.contacts,
+ state.wallet.xkeyId,
+ {
+ ...state.contact,
+ updatedAt: (new Date()).toISOString(),
+ info: {
+ ...OIDC_CLAIMS,
+ ...(state.contact.info || {}),
+ ...info,
+ },
+ outgoing,
+ alias: preferredAlias,
+ uri: value,
+ },
+ false,
+ )
+
+ getStoreData(
+ store.contacts,
+ res => {
+ if (res) {
+ appState.contacts = res
+
+ return contactsList.restate({
+ contacts: res?.sort(sortContactsByAlias),
+ userInfo,
+ })
+ }
+ },
+ res => async v => {
+ res.push(await appTools.storedData.decryptData(v))
+ }
+ )
+
+ state.contact = newContact
+
+ if (xkeyOrAddr) {
+ target.contactAddr.value = xkeyOrAddr
+ }
+ if (name) {
+ target.contactName.value = name
+ }
+ if (preferred_username) {
+ target.contactAlias.value = preferredAlias
+ }
+
+ return
+ }
+
+ let addContact = await setupDialog(
mainApp,
{
name: 'Add a New Contact',
@@ -201,6 +339,7 @@ export let addContactRig = (function (globals) {
placeholder="your_alias"
pattern="${ALIAS_REGEX.source}"
spellcheck="false"
+ autocomplete="off"
value="${state.contact?.alias || ''}"
/>
@@ -242,146 +381,11 @@ export let addContactRig = (function (globals) {
if (event.target?.name === 'contactAddr') {
if (event.target?.value) {
- let {
- address,
- xpub,
- xprv,
- name,
- preferred_username,
- sub,
- } = parseAddressField(event.target.value)
-
- let xkey = xprv || xpub
-
- let xkeyOrAddr = xkey || address
-
- let info = {
- name: name || '',
- sub,
- preferred_username,
- }
-
- let preferredAlias = await getUniqueAlias(
- aliases,
- preferred_username
+ processURI(
+ state,
+ event.target.form,
+ event.target.value,
)
-
- // console.log(
- // 'appState.contacts',
- // appState.contacts,
- // // aliases,
- // // preferred_username,
- // // preferredAlias,
- // )
- let outgoing = {}
-
- let existingContacts
-
- if (!xkey && address) {
- existingContacts = appState.contacts.filter(
- c => c.outgoing?.[address]
- )
-
- outgoing = {
- ...(state.contact.outgoing || {}),
- [address]: {
- address,
- },
- }
- }
-
- if (xkey) {
- let {
- xkeyId,
- addressKeyId,
- addressIndex,
- address: addr,
- } = await deriveWalletData(
- xkey,
- )
-
- existingContacts = appState.contacts.filter(
- c => c.outgoing?.[xkeyId]
- )
-
- outgoing = {
- ...(state.contact.outgoing || {}),
- [xkeyId]: {
- addressIndex,
- addressKeyId,
- address: address || addr,
- xkeyId,
- xprv,
- xpub,
- },
- }
-
- console.log(
- 'add contact handleInput parsedAddr',
- event.target.value,
- xkey,
- )
- }
-
- if (existingContacts?.length > 0) {
- console.warn(
- `You've already paired with this contact`,
- {
- existingContacts,
- newContact: {
- alias: preferredAlias,
- outgoing,
- }
- }
- )
- }
-
- let newContact = await appTools.storedData.encryptItem(
- store.contacts,
- state.wallet.xkeyId,
- {
- ...state.contact,
- updatedAt: (new Date()).toISOString(),
- info: {
- ...OIDC_CLAIMS,
- ...(state.contact.info || {}),
- ...info,
- },
- outgoing,
- alias: preferredAlias,
- uri: event.target.value,
- },
- false,
- )
-
- getStoreData(
- store.contacts,
- res => {
- if (res) {
- appState.contacts = res
-
- return contactsList.restate({
- contacts: res?.sort(sortContactsByAlias),
- userInfo,
- })
- }
- },
- res => async v => {
- res.push(await appTools.storedData.decryptData(v))
- }
- )
-
- state.contact = newContact
-
- if (xkeyOrAddr) {
- event.target.form.contactAddr.value = xkeyOrAddr
- }
- if (name) {
- event.target.form.contactName.value = name
- }
- if (preferred_username) {
- event.target.form.contactAlias.value = preferredAlias
- }
}
}
if (
@@ -490,7 +494,7 @@ export let addContactRig = (function (globals) {
console.log('scanContact', appDialogs.scanContact)
if (fde?.intent === 'scan_new_contact') {
- appDialogs.scanContact.render(
+ await appDialogs.scanContact.render(
{
wallet,
},
@@ -500,29 +504,11 @@ export let addContactRig = (function (globals) {
let showScan = await appDialogs.scanContact.showModal()
if (showScan !== 'cancel') {
- parsedAddr = parseAddressField(showScan)
- let {
- address,
- xpub,
- xprv,
- name,
- preferred_username,
- sub,
- } = parsedAddr
-
- let xkey = xprv || xpub
-
- let xkeyOrAddr = xkey || address
-
- if (xkeyOrAddr) {
- event.target.contactAddr.value = xkeyOrAddr
- }
- if (name) {
- event.target.contactName.value = name
- }
- if (preferred_username) {
- event.target.contactAlias.value = preferred_username
- }
+ processURI(
+ state,
+ event.target,
+ showScan,
+ )
}
return;
diff --git a/src/rigs/confirm-action.js b/src/rigs/confirm-action.js
index 9a0c2d9..a7996ad 100644
--- a/src/rigs/confirm-action.js
+++ b/src/rigs/confirm-action.js
@@ -3,7 +3,7 @@ import {
formDataEntries,
} from '../helpers/utils.js'
-export let confirmActionRig = (function (globals) {
+export let confirmActionRig = (async function (globals) {
'use strict';
let {
@@ -11,7 +11,7 @@ export let confirmActionRig = (function (globals) {
store, userInfo, contactsList,
} = globals
- let confirmAction = setupDialog(
+ let confirmAction = await setupDialog(
mainApp,
{
name: 'Confirm',
diff --git a/src/rigs/confirm-delete.js b/src/rigs/confirm-delete.js
index d2bad60..218a5be 100644
--- a/src/rigs/confirm-delete.js
+++ b/src/rigs/confirm-delete.js
@@ -5,7 +5,7 @@ import {
sortContactsByAlias,
} from '../helpers/utils.js'
-export let confirmDeleteRig = (function (globals) {
+export let confirmDeleteRig = (async function (globals) {
'use strict';
let {
@@ -13,7 +13,7 @@ export let confirmDeleteRig = (function (globals) {
store, userInfo, contactsList,
} = globals
- let confirmDelete = setupDialog(
+ let confirmDelete = await setupDialog(
mainApp,
{
name: 'Confirm Remove',
diff --git a/src/rigs/edit-contact.js b/src/rigs/edit-contact.js
index 6784002..2d4f284 100644
--- a/src/rigs/edit-contact.js
+++ b/src/rigs/edit-contact.js
@@ -22,7 +22,7 @@ import {
ALIAS_REGEX,
} from '../helpers/constants.js'
-export let editContactRig = (function (globals) {
+export let editContactRig = (async function (globals) {
'use strict';
let {
@@ -89,7 +89,7 @@ export let editContactRig = (function (globals) {
)
}, 1000)
- let editContact = setupDialog(
+ let editContact = await setupDialog(
mainApp,
{
name: 'Edit Contact',
@@ -100,8 +100,8 @@ export let editContactRig = (function (globals) {
trashAlt: 'Remove Contact',
sendTxt: 'Send',
sendAlt: 'Send Dash to Contact',
- requestTxt: 'Request',
- requestAlt: 'Request Dash from Contact',
+ requestTxt: 'Receive',
+ requestAlt: 'Receive Dash from Contact',
cancelTxt: 'Cancel',
cancelAlt: `Cancel`,
closeAlt: `Close`,
@@ -145,7 +145,7 @@ export let editContactRig = (function (globals) {
class="rounded"
type="submit"
name="intent"
- value="request"
+ value="receive"
title="${state.requestAlt}"
>
@@ -155,12 +155,12 @@ export let editContactRig = (function (globals) {
`,
- content: state => html`
+ content: async state => html`
${state.header(state)}
- ${getAvatar(state.contact)}
+ ${await getAvatar(state.contact)}
@${state.contact?.alias || state.contact?.info?.preferred_username}
@@ -192,6 +192,7 @@ export let editContactRig = (function (globals) {
placeholder="your_alias"
pattern="${ALIAS_REGEX.source}"
spellcheck="false"
+ autocomplete="off"
value="${state.contact?.alias}"
/>
@@ -376,7 +377,7 @@ export let editContactRig = (function (globals) {
event.target !== contactCard &&
contactCard?.contains(event.target)
) {
- appDialogs.pairQr.render(
+ await appDialogs.pairQr.render(
{
name: `Pairing info for @${storedContact.alias}`,
wallet: state.shareAccount,
@@ -422,10 +423,10 @@ export let editContactRig = (function (globals) {
storedContact,
)
- if (['send','request'].includes(String(fde?.intent))) {
+ if (['send','receive'].includes(String(fde?.intent))) {
editContact.close()
- appDialogs.sendOrRequest.render({
+ await appDialogs.sendOrReceive.render({
action: fde.intent,
wallet: state.wallet,
account: appState.account,
@@ -433,13 +434,13 @@ export let editContactRig = (function (globals) {
contacts: appState.contacts,
to: `@${storedContact.alias}`,
})
- appDialogs.sendOrRequest.showModal()
+ appDialogs.sendOrReceive.showModal()
return;
}
if (fde?.intent === 'delete_contact') {
- appDialogs.confirmDelete.render({
+ await appDialogs.confirmDelete.render({
shareAccount: state.shareAccount,
userInfo,
contacts: appState.contacts,
diff --git a/src/rigs/edit-profile.js b/src/rigs/edit-profile.js
index 418e16a..afe0b00 100644
--- a/src/rigs/edit-profile.js
+++ b/src/rigs/edit-profile.js
@@ -13,7 +13,7 @@ import {
ALIAS_REGEX,
} from '../helpers/constants.js'
-export let editProfileRig = (function (globals) {
+export let editProfileRig = (async function (globals) {
'use strict';
let {
@@ -21,7 +21,7 @@ export let editProfileRig = (function (globals) {
appState, appTools, bodyNav,
} = globals;
- let editProfile = setupDialog(
+ let editProfile = await setupDialog(
mainApp,
{
name: 'Edit Profile',
@@ -34,9 +34,9 @@ export let editProfileRig = (function (globals) {
cancelTxt: 'Cancel',
cancelAlt: `Cancel`,
placement: 'wide',
- closeTxt: html`
-
- `,
+ uriLabelPrefix: 'Dash Incubator Wallet Funding:',
+ uriLabel: state => encodeURI(`${state.uriLabelPrefix} ${appState.selectedAlias}`),
+ closeTxt: html` `,
closeAlt: `Close`,
footer: state => html`
@@ -51,7 +51,7 @@ export let editProfileRig = (function (globals) {
`,
- getLink: state => `dash:${state.wallet?.address || ''}?label=funding`,
+ getLink: state => `dash:${state.wallet?.address || ''}?label=${state.uriLabel(state)}`,
content: state => html`
${state.header(state)}
@@ -92,6 +92,8 @@ export let editProfileRig = (function (globals) {
pattern="${ALIAS_REGEX.source}"
required
spellcheck="false"
+ autocomplete="off"
+ autocapitalize="off"
/>
Alias others can call you (similar to a @username)
diff --git a/src/rigs/onboard.js b/src/rigs/onboard.js
index 1ffb5f5..899120f 100644
--- a/src/rigs/onboard.js
+++ b/src/rigs/onboard.js
@@ -3,14 +3,14 @@ import {
formDataEntries,
} from '../helpers/utils.js'
-export let onboardRig = (function (globals) {
+export let onboardRig = (async function (globals) {
'use strict';
let {
mainApp, setupDialog, appDialogs,
} = globals;
- let onboard = setupDialog(
+ let onboard = await setupDialog(
mainApp,
{
name: 'Onboarding Flow',
@@ -24,10 +24,10 @@ export let onboardRig = (function (globals) {
let fde = formDataEntries(event)
if (fde?.intent === 'generate') {
- appDialogs.phraseGenerate.render()
+ await appDialogs.phraseGenerate.render()
appDialogs.phraseGenerate.showModal()
} else if (fde?.intent === 'import') {
- appDialogs.phraseImport.render()
+ await appDialogs.phraseImport.render()
appDialogs.phraseImport.showModal()
}
},
diff --git a/src/rigs/pair-qr.js b/src/rigs/pair-qr.js
index 8922c9d..c69d1e0 100644
--- a/src/rigs/pair-qr.js
+++ b/src/rigs/pair-qr.js
@@ -7,14 +7,14 @@ import {
generateContactPairingURI,
} from '../helpers/utils.js'
-export let pairQrRig = (function (globals) {
+export let pairQrRig = (async function (globals) {
'use strict';
let {
mainApp, setupDialog,
} = globals;
- let pairQr = setupDialog(
+ let pairQr = await setupDialog(
mainApp,
{
name: 'Pairing info',
diff --git a/src/rigs/phrase-backup.js b/src/rigs/phrase-backup.js
index d495423..4a23603 100644
--- a/src/rigs/phrase-backup.js
+++ b/src/rigs/phrase-backup.js
@@ -5,17 +5,17 @@ import {
setClipboard,
} from '../helpers/utils.js'
-export let phraseBackupRig = (function (globals) {
+export let phraseBackupRig = (async function (globals) {
'use strict';
let {
mainApp, setupDialog, appDialogs,
} = globals;
- let phraseBackup = setupDialog(
+ let phraseBackup = await setupDialog(
mainApp,
{
- name: 'New Wallet',
+ name: 'Your New Seed Phrase',
submitTxt: 'I backed up this Seed Phrase',
submitAlt: 'Confirm Seed Phrase backup',
cancelTxt: 'Cancel',
@@ -40,7 +40,7 @@ export let phraseBackupRig = (function (globals) {
`,
- content: state => html`
+ content: async state => html`
${state.header(state)}
@@ -54,6 +54,7 @@ export let phraseBackupRig = (function (globals) {
${phraseToEl(
state.wallet?.recoveryPhrase
@@ -82,7 +83,7 @@ export let phraseBackupRig = (function (globals) {
phraseBackup.close()
if (runEncryption) {
- appDialogs.walletEncrypt.render(
+ await appDialogs.walletEncrypt.render(
{
wallet,
},
diff --git a/src/rigs/phrase-generate.js b/src/rigs/phrase-generate.js
index ee8355b..5a3fb4c 100644
--- a/src/rigs/phrase-generate.js
+++ b/src/rigs/phrase-generate.js
@@ -6,7 +6,7 @@ import {
ALIAS_REGEX,
} from '../helpers/constants.js'
-export let phraseGenerateRig = (function (globals) {
+export let phraseGenerateRig = (async function (globals) {
'use strict';
let {
@@ -15,7 +15,7 @@ export let phraseGenerateRig = (function (globals) {
deriveWalletData, generateWalletData,
} = globals;
- let phraseGenerate = setupDialog(
+ let phraseGenerate = await setupDialog(
mainApp,
{
name: 'New Wallet',
@@ -60,8 +60,12 @@ export let phraseGenerateRig = (function (globals) {
pattern="${ALIAS_REGEX.source}"
required
spellcheck="false"
+ autocomplete="off"
+ autocapitalize="off"
+ title="Enter a string with one or more characters, that starts & ends with a letter or number and may contain underscores (_), periods (.) & hyphens (-) in between. (E.g. john.doe, jane_doe, 1.dash_fan)"
/>
+ Name the wallet (similar to a username), shared when connecting with a contact.
@@ -112,7 +116,7 @@ export let phraseGenerateRig = (function (globals) {
// console.log('GENERATE wallet!', wallet)
- appDialogs.phraseBackup.render(
+ await appDialogs.phraseBackup.render(
{
wallet,
},
diff --git a/src/rigs/phrase-import.js b/src/rigs/phrase-import.js
index 3a1bbdc..042ce02 100644
--- a/src/rigs/phrase-import.js
+++ b/src/rigs/phrase-import.js
@@ -2,13 +2,14 @@ import { lit as html } from '../helpers/lit.js'
import {
formDataEntries,
readFile,
+ verifyPhrase,
} from '../helpers/utils.js'
import {
ALIAS_REGEX,
PHRASE_REGEX,
} from '../helpers/constants.js'
-export let phraseImportRig = (function (globals) {
+export let phraseImportRig = (async function (globals) {
'use strict';
let {
@@ -38,7 +39,22 @@ export let phraseImportRig = (function (globals) {
}
}
- let phraseImport = setupDialog(
+ function clearFile(state) {
+ let updrField = state.elements.form?.querySelector(
+ '.updrop input[type="file"]'
+ )
+ updrField.value = null
+ state.keystoreData = null
+ state.walletImportData = null
+ state.keystoreFile = ''
+ state.render(state)
+ }
+
+ function updropContainer(state) {
+ return state.keystoreFile ? `div` : `label`
+ }
+
+ let phraseImport = await setupDialog(
mainApp,
{
name: 'Existing Wallet',
@@ -46,6 +62,7 @@ export let phraseImportRig = (function (globals) {
submitAlt: 'Import Existing Wallet',
cancelTxt: 'Cancel',
cancelAlt: `Cancel Wallet Import`,
+ clearAlt: `Clear selected file`,
closeTxt: html`
`,
@@ -67,16 +84,16 @@ export let phraseImportRig = (function (globals) {
if (state.keystoreFile) {
return ''
}
- // Drag and drop a Keystore file or Incubator Wallet backup file or click to upload
+
return html`
Select a
-
- Keystore or Backup
-
+ Keystore
+ or
+ Backup
file
`
@@ -85,58 +102,86 @@ export let phraseImportRig = (function (globals) {
if (!state.keystoreFile) {
return ''
}
+
return html`
- ${state.keystoreFile}
+
+
+
+
+
+
+ ${state.keystoreFile}
+
+ `
+ },
+ updrop: state => {
+ let el = updropContainer(state)
+
+ return html`
+
+ Keystore or Backup
+
+ <${el} for="keystore" class="updrop">
+ ${state.upload(state)}
+ ${state.showFileName(state)}
+
+ ${el}>
`
},
- updrop: state => html`
-
-
- ${state.showFileName(state)}
- ${state.upload(state)}
-
- `,
content: state => html`
${state.header(state)}
-
- ${state.updrop(state)}
+
+
+ ${state.updrop(state)}
-
-
-
-
- Seed Phrase
-
-
- Import an existing wallet by pasting a 12 word seed phrase.
+
+
+
+
+
+
+
+
+
+
+
+
+ Import an existing wallet by pasting a 12 word seed phrase.
-
-
+
+
+
Alias
@@ -152,6 +197,9 @@ export let phraseImportRig = (function (globals) {
pattern="${ALIAS_REGEX.source}"
required
spellcheck="false"
+ autocomplete="off"
+ autocapitalize="off"
+ title="Enter a string with one or more characters, that starts & ends with a letter or number and may contain underscores (_), periods (.) & hyphens (-) in between. (E.g. john.doe, jane_doe, 1.dash_fan)"
/>
Name the wallet (similar to a username), shared when connecting with a contact.
@@ -164,52 +212,103 @@ export let phraseImportRig = (function (globals) {
`,
fields: html``,
events: {
- handleDragOver: state => async event => {
+ handleFocusOut: state => event => {
+ if (event.target.id === 'keystore') {
+ event.target.parentElement.classList.remove('focus')
+ }
+ },
+ handleFocusIn: state => event => {
+ if (event.target.id === 'keystore') {
+ event.target.parentElement.classList.add('focus')
+ }
+ },
+ handleClose: (
+ state,
+ resolve = res=>{},
+ reject = res=>{},
+ ) => async event => {
event.preventDefault()
- event.stopPropagation()
+ state.removeAllListeners()
+
+ if (state.elements.dialog.returnValue !== 'cancel') {
+ resolve(state.elements.dialog.returnValue)
+ } else {
+ clearFile(state)
+ resolve('cancel')
+ }
// console.log(
- // 'PHRASE IMPORT DRAG OVER',
- // state, event.target,
- // event?.dataTransfer?.items,
- // event.target.files
+ // 'DIALOG handleClose',
+ // state.modal.rendered[state.slugs.dialog],
// )
+
+ setTimeout(t => {
+ state.modal.rendered[state.slugs.dialog] = null
+ event?.target?.remove()
+ }, state.delay)
+ },
+ handleDragOver: state => async event => {
+ event.preventDefault()
+ event.stopPropagation()
+
+ if (
+ event.target.classList.contains('updrop') &&
+ !event.target.classList.contains('disabled')
+ ) {
+ event.target.classList.add('drag-over')
+ }
+ },
+ handleDragLeave: state => async event => {
+ event.preventDefault()
+ event.stopPropagation()
+
+ if (
+ event.target.classList.contains('updrop') &&
+ !event.target.classList.contains('disabled')
+ ) {
+ event.target.classList.remove('drag-over')
+ }
+ },
+ handleDragEnd: state => async event => {
+ event.preventDefault()
+ event.stopPropagation()
+
+ if (
+ event.target.classList.contains('updrop') &&
+ !event.target.classList.contains('disabled')
+ ) {
+ event.target.classList.add('dropped')
+ }
},
handleDrop: state => async event => {
event.preventDefault()
event.stopPropagation()
- console.log(
- 'PHRASE IMPORT DROP',
- state, event.target,
- event?.dataTransfer?.items,
- event.target.files
- )
- if (event.dataTransfer.items) {
- // Use DataTransferItemList interface to access the file(s)
- [...event.dataTransfer.items].forEach((item, i) => {
- // If dropped items aren't files, reject them
- if (item.kind === "file") {
- const file = item.getAsFile();
- // console.log(`ITEMS file[${i}].name = ${file.name}`, file);
+ if (
+ event.target.classList.contains('updrop') &&
+ !event.target.classList.contains('disabled')
+ ) {
+ if (event.dataTransfer.items) {
+ [...event.dataTransfer.items].forEach((item, i) => {
+ if (item.kind === "file") {
+ const file = item.getAsFile();
+ readFile(
+ file,
+ processFile(state, event),
+ )
+ state.keystoreFile = file.name
+ state.render(state)
+ }
+ });
+ } else {
+ [...event.dataTransfer.files].forEach((file, i) => {
readFile(
file,
processFile(state, event),
)
state.keystoreFile = file.name
state.render(state)
- }
- });
- } else {
- // Use DataTransfer interface to access the file(s)
- [...event.dataTransfer.files].forEach((file, i) => {
- // console.log(`FILES file[${i}].name = ${file.name}`, file);
- readFile(
- file,
- processFile(state, event),
- )
- state.keystoreFile = file.name
- state.render(state)
- });
+ });
+ }
}
},
handleChange: state => async event => {
@@ -225,14 +324,55 @@ export let phraseImportRig = (function (globals) {
state.render(state)
}
},
+ handleInput: state => async event => {
+ if (
+ event.target.name === 'pass'
+ ) {
+ event.preventDefault()
+ event.stopPropagation()
+
+ let testPhrase = PHRASE_REGEX.test(event.target.value)
+ let updr = state.elements.form.querySelector('.updrop')
+ let updrField = updr?.querySelector('input[type="file"]')
+
+ // console.log('phrase import handleInput', testPhrase)
+
+ if (testPhrase) {
+ let tmpPhrase = await verifyPhrase(
+ event.target.value
+ )
+
+ state.validPhrase = testPhrase && tmpPhrase
+
+ if (tmpPhrase) {
+ updr?.classList.add('disabled')
+ updrField.disabled = true
+ } else {
+ updr?.classList.remove('disabled')
+ updrField.disabled = false
+ }
+ }
+ if (state.validPhrase && !testPhrase) {
+ updr?.classList.remove('disabled')
+ updrField.disabled = false
+ state.validPhrase = testPhrase
+ }
+ }
+ },
+ // PHRASE_REGEX
handleSubmit: state => async event => {
event.preventDefault()
event.stopPropagation()
let fde = formDataEntries(event)
+ if (fde.intent === 'clear') {
+ clearFile(state)
+ return;
+ }
+
if (state.walletImportData) {
- appDialogs.walletDecrypt.render({
+ await appDialogs.walletDecrypt.render({
walletImportData: state.walletImportData
})
await appDialogs.walletDecrypt.showModal()
@@ -263,7 +403,7 @@ export let phraseImportRig = (function (globals) {
localStorage.selectedAlias = appState.selectedAlias
if (state.keystoreData) {
- appDialogs.walletDecrypt.render({
+ await appDialogs.walletDecrypt.render({
keystore: state.keystoreData
})
await appDialogs.walletDecrypt.showModal()
@@ -295,7 +435,7 @@ export let phraseImportRig = (function (globals) {
phraseImport.close()
- appDialogs.walletEncrypt.render(
+ await appDialogs.walletEncrypt.render(
{
wallet,
},
diff --git a/src/rigs/request-qr.js b/src/rigs/request-qr.js
index 711780c..cdda83f 100644
--- a/src/rigs/request-qr.js
+++ b/src/rigs/request-qr.js
@@ -1,6 +1,7 @@
import { lit as html } from '../helpers/lit.js'
import { qrSvg } from '../helpers/qr.js'
import {
+ formDataEntries,
setClipboard,
openBlobSVG,
generatePaymentRequestURI,
@@ -8,36 +9,26 @@ import {
roundUsing,
} from '../helpers/utils.js'
-export let requestQrRig = (function (globals) {
+export let requestQrRig = (async function (globals) {
'use strict';
let {
- mainApp, setupDialog,
+ mainApp, setupDialog, appDialogs, appState, userInfo,
} = globals;
- let requestQr = setupDialog(
+ let requestQr = await setupDialog(
mainApp,
{
name: 'Share to receive funds',
address: '',
- submitTxt: html` Request Payment`,
- submitAlt: 'Request Payment',
+ submitTxt: html` Receive Payment`,
+ submitAlt: 'Receive Payment',
cancelTxt: 'Cancel',
cancelAlt: `Cancel`,
placement: 'wide',
closeTxt: html` `,
closeAlt: `Close`,
amount: 0,
- getContact: state => {
- let to = state.contact?.info?.name
- if (!to && state.contact?.alias) {
- to = `@${state.contact?.alias}`
- }
- if (!to) {
- to = state.to
- }
- return to
- },
footer: state => html`
Share this QR code to receive funds
`,
@@ -60,19 +51,28 @@ export let requestQrRig = (function (globals) {
`
},
+ aliasSelector: state => {
+ return html`
+
+ `
+ },
content: state => html`
${state.header(state)}
-
${state.showAmount(state)}
- ${qrSvg(
+ ${qrSvg(
generatePaymentRequestURI(state),
{
indent: 0,
@@ -102,6 +102,9 @@ export let requestQrRig = (function (globals) {
let shareAside = state.elements?.dialog?.querySelector(
'fieldset.share > aside'
)
+ let qrWrapper = state.elements?.dialog?.querySelector(
+ 'fieldset.share > aside .qr'
+ )
if (
shareAside?.contains(event.target)
) {
@@ -124,7 +127,8 @@ export let requestQrRig = (function (globals) {
setClipboard(event)
}
if (
- event.target?.nodeName.toLowerCase() === 'svg'
+ event.target?.nodeName.toLowerCase() === 'svg' &&
+ qrWrapper?.contains(event.target)
) {
event.preventDefault()
event.stopPropagation()
@@ -132,7 +136,8 @@ export let requestQrRig = (function (globals) {
openBlobSVG(event.target)
}
if (
- event.target?.parentElement?.nodeName.toLowerCase() === 'svg'
+ event.target?.parentElement?.nodeName.toLowerCase() === 'svg' &&
+ qrWrapper?.contains(event.target)
) {
event.preventDefault()
event.stopPropagation()
@@ -146,7 +151,27 @@ export let requestQrRig = (function (globals) {
event.preventDefault()
event.stopPropagation()
- console.log('Request Payment', state, event)
+ let fde = formDataEntries(event)
+
+ if (fde?.intent === 'select_address') {
+ // console.log('SELECT AN ADDRESS', {state, event, fde})
+
+ await appDialogs.sendOrReceive.render({
+ action: 'receive',
+ wallet: state.wallet,
+ account: appState.account,
+ contacts: appState.contacts,
+ userInfo,
+ to: null,
+ })
+ appDialogs.sendOrReceive.showModal()
+
+ return;
+ }
+ // else {
+ // console.log('Receive Payment', {state, event})
+ // }
+
requestQr.close()
},
diff --git a/src/rigs/scan.js b/src/rigs/scan.js
index 754ff39..0c481e1 100644
--- a/src/rigs/scan.js
+++ b/src/rigs/scan.js
@@ -3,14 +3,14 @@ import { lit as html } from '../helpers/lit.js'
// formDataEntries,
// } from '../helpers/utils.js'
-export let scanContactRig = (function (globals) {
+export let scanContactRig = (async function (globals) {
'use strict';
let {
setupDialog, mainApp,
} = globals;
- let scanContact = setupDialog(
+ let scanContact = await setupDialog(
mainApp,
{
name: 'Scan a Contact',
@@ -100,7 +100,7 @@ export let scanContactRig = (function (globals) {
}
setTimeout(t => {
- state.rendered = null
+ state.modal.rendered[state.slugs.dialog] = null
event?.target?.remove()
}, state.delay)
},
diff --git a/src/rigs/send-confirm.js b/src/rigs/send-confirm.js
index fac8d56..deeef91 100644
--- a/src/rigs/send-confirm.js
+++ b/src/rigs/send-confirm.js
@@ -5,7 +5,7 @@ import {
sortContactsByAlias,
} from '../helpers/utils.js'
-export let sendConfirmRig = (function (globals) {
+export let sendConfirmRig = (async function (globals) {
'use strict';
let {
@@ -13,7 +13,7 @@ export let sendConfirmRig = (function (globals) {
sendTx, store, userInfo, contactsList,
} = globals
- let sendConfirm = setupDialog(
+ let sendConfirm = await setupDialog(
mainApp,
{
name: 'Confirm Send',
@@ -95,21 +95,6 @@ export let sendConfirmRig = (function (globals) {
${state.footer(state)}
`,
events: {
- handleClose: (
- state,
- resolve = res=>{},
- reject = res=>{},
- ) => async event => {
- event.preventDefault()
- // event.stopPropagation()
- state.removeAllListeners()
-
- if (state.elements.dialog.returnValue !== 'cancel') {
- resolve(state.elements.dialog.returnValue)
- } else {
- resolve('cancel')
- }
- },
handleSubmit: state => async event => {
event.preventDefault()
event.stopPropagation()
@@ -186,15 +171,26 @@ export let sendConfirmRig = (function (globals) {
)
console.log(
- `${fde.intent} TO ${address}`,
+ `${fde.intent} TO ${state.to}`,
`Ð ${state.amount || 0}`,
state.contact,
txRes,
)
+
+ await appDialogs.txInfo.render(
+ {
+ contact: state.contact,
+ amount: state.amount,
+ txRes,
+ },
+ 'afterend',
+ )
+
+ let showTxInfo = appDialogs.txInfo.showModal()
}
sendConfirm.close(fde.intent)
- appDialogs.sendOrRequest.close(fde.intent)
+ appDialogs.sendOrReceive.close(fde.intent)
},
},
}
diff --git a/src/rigs/send-or-request.js b/src/rigs/send-or-request.js
index ff9d817..9e41cf6 100644
--- a/src/rigs/send-or-request.js
+++ b/src/rigs/send-or-request.js
@@ -1,13 +1,15 @@
import { lit as html } from '../helpers/lit.js'
+import { AMOUNT_REGEX } from '../helpers/constants.js'
import {
formDataEntries,
parseAddressField,
fixedDash,
+ toDASH,
+ toDash,
roundUsing,
} from '../helpers/utils.js'
-import setupInputAmount from '../components/input-amount.js'
-export let sendOrRequestRig = (function (globals) {
+export let sendOrReceiveRig = (async function (globals) {
'use strict';
let {
@@ -16,26 +18,22 @@ export let sendOrRequestRig = (function (globals) {
wallet, wallets, accounts, walletFunds,
} = globals
- let inputAmount = setupInputAmount(mainApp)
-
- let sendOrRequest = setupDialog(
+ let sendOrReceive = await setupDialog(
mainApp,
{
- name: 'Send or Request',
+ name: 'Send or Receive',
sendName: 'Send Funds',
sendTxt: 'Send',
sendAlt: 'Send Dash',
scanAlt: 'Scan a QR Code',
- requestName: 'Request Funds',
- requestTxt: 'Request',
- requestAlt: 'Request Dash',
+ receiveName: 'Receive Funds',
+ receiveTxt: 'Receive',
+ receiveAlt: 'Receive Dash',
actionTxt: 'Send',
actionAlt: 'Send Dash',
cancelTxt: 'Cancel',
cancelAlt: `Cancel Form`,
- closeTxt: html`
-
- `,
+ closeTxt: html` `,
closeAlt: `Close`,
action: 'send',
submitIcon: state => {
@@ -45,7 +43,7 @@ export let sendOrRequestRig = (function (globals) {
`,
- request: html`
+ receive: html`
@@ -58,9 +56,9 @@ export let sendOrRequestRig = (function (globals) {
state.actionTxt = state.sendTxt
state.actionAlt = state.sendAlt
}
- if (state.action === 'request') {
- state.actionTxt = state.requestTxt
- state.actionAlt = state.requestAlt
+ if (state.action === 'receive') {
+ state.actionTxt = state.receiveTxt
+ state.actionAlt = state.receiveAlt
}
return html`
@@ -94,8 +92,8 @@ export let sendOrRequestRig = (function (globals) {
if (state.action === 'send') {
state.name = state.sendName
}
- if (state.action === 'request') {
- state.name = state.requestName
+ if (state.action === 'receive') {
+ state.name = state.receiveName
}
return html`
@@ -107,14 +105,84 @@ export let sendOrRequestRig = (function (globals) {
`
},
+ qrScanBtn: state => {
+ if (state.action !== 'send') {
+ return ''
+ }
+
+ return html`
+
+
+
+
+
+
+
+ `
+ },
+ fundAmountBtns: state => {
+ if (state.action !== 'send') {
+ return ''
+ }
+
+ return html`
+
+
+
+ HALF
+
+
+
+
+
+ FULL
+
+
+ `
+ },
content: state => html`
${state.header(state)}