diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..de15042
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/favicon.png b/public/favicon.png
index 007d62b..c0f50e8 100644
Binary files a/public/favicon.png and b/public/favicon.png differ
diff --git a/src/components/balance.js b/src/components/balance.js
index daf3f43..9077705 100644
--- a/src/components/balance.js
+++ b/src/components/balance.js
@@ -1,5 +1,5 @@
import { lit as html } from '../helpers/lit.js'
-import { fixedDash, envoy, } from '../helpers/utils.js'
+import { envoy, formatDash, } from '../helpers/utils.js'
import { updateAllFunds, } from '../helpers/wallet.js'
const initialState = {
@@ -14,6 +14,7 @@ const initialState = {
addr: null,
maxlen: 10,
fract: 8,
+ sigsplit: 3,
render(
renderState = {},
position = 'afterbegin',
@@ -23,41 +24,19 @@ const initialState = {
${state.name} ${state.id}
`,
content: state => {
- let funds = 0
- let balance = `${funds}`
-
- if (state.walletFunds.balance) {
- funds += state.walletFunds.balance
- balance = fixedDash(funds, state.fract)
- // TODO FIX: does not support large balances
-
- // console.log('balance fixedDash', balance, balance.length)
-
- let [fundsInt,fundsFract] = balance.split('.')
- state.maxlen -= fundsInt.length
-
- let fundsFraction = fundsFract?.substring(
- 0, Math.min(Math.max(0, state.maxlen), 3)
- )
-
- let fundsRemainder = fundsFract?.substring(
- fundsFraction.length,
- Math.max(0, state.maxlen)
- )
-
- balance = html`${
- fundsInt
- }.${
- fundsFraction
- }${
- fundsRemainder
- }`
- }
+ let balance = formatDash(
+ state.walletFunds.balance,
+ {
+ fract: state.fract,
+ maxlen: state.maxlen,
+ sigsplit: state.sigsplit,
+ }
+ )
return html`
${state.header(state)}
-
+
diff --git a/src/components/dialog.js b/src/components/dialog.js
index f232feb..b133a09 100644
--- a/src/components/dialog.js
+++ b/src/components/dialog.js
@@ -267,7 +267,7 @@ const initialState = {
// )
state.elements.dialog.close('cancel')
}
- }
+ },
},
}
diff --git a/src/helpers/utils.js b/src/helpers/utils.js
index 6ed6abc..57ca83b 100644
--- a/src/helpers/utils.js
+++ b/src/helpers/utils.js
@@ -270,6 +270,50 @@ export function toDuff(dash) {
return Math.round(parseFloat(dash) * DUFFS);
}
+export function formatDash(
+ unformattedBalance,
+ options = {},
+) {
+ let opts = {
+ maxlen: 10,
+ fract: 8,
+ sigsplit: 3,
+ ...options,
+ }
+ let funds = 0
+ let balance = `${funds}`
+
+ if (unformattedBalance) {
+ funds += unformattedBalance
+ balance = fixedDash(funds, opts.fract)
+ // TODO FIX: does not support large balances
+
+ // console.log('balance fixedDash', balance, balance.length)
+
+ let [fundsInt,fundsFract] = balance.split('.')
+ opts.maxlen -= fundsInt.length
+
+ let fundsFraction = fundsFract?.substring(
+ 0, Math.min(Math.max(0, opts.maxlen), opts.sigsplit)
+ )
+
+ let fundsRemainder = fundsFract?.substring(
+ fundsFraction.length,
+ Math.max(0, opts.maxlen)
+ )
+
+ balance = `${
+ fundsInt
+ }.${
+ fundsFraction
+ }${
+ fundsRemainder
+ }`
+ }
+
+ return balance
+}
+
export function formDataEntries(event) {
let fd = new FormData(
event.target,
@@ -984,20 +1028,90 @@ export async function getAvatar(c) {
return `${avStr}">${initials}
`
}
-export function readFile(file, callback) {
+export function fileIsSubType(file, type) {
+ const fileType = file?.type?.split('/')?.[1]
+
+ if (!fileType) {
+ return false
+ }
+
+ return fileType === type
+}
+
+// fileInTypes({type:'application/json'}, ['image/png'])
+export function fileInMIMETypes(file, types = []) {
+ const fileType = file?.type
+
+ if (!fileType) {
+ return false
+ }
+
+ return types.includes(fileType)
+}
+
+export function fileTypeInTypes(file, types = []) {
+ const fileType = file?.type?.split('/')?.[0]
+
+ if (!fileType) {
+ return false
+ }
+
+ return types.includes(fileType)
+}
+
+export function fileTypeInSubtype(file, subtypes = []) {
+ const fileSubType = file?.type?.split('/')?.[1]
+
+ if (!fileSubType) {
+ return false
+ }
+
+ return subtypes.includes(fileSubType)
+}
+
+export function readFile(file, options) {
+ let opts = {
+ expectedFileType: 'json',
+ denyFileTypes: ['audio','video','image','font','model'],
+ denyFileSubTypes: ['msword','xml'],
+ callback: () => {},
+ errorCallback: () => {},
+ ...options,
+ }
let reader = new FileReader();
let result
reader.addEventListener('load', () => {
+ if (
+ fileTypeInTypes(
+ file,
+ opts.denyFileTypes,
+ ) || fileTypeInSubtype(
+ file,
+ opts.denyFileSubTypes,
+ )
+ ) {
+ return opts.errorCallback?.({
+ err: `Wrong file type: ${file.type}. Expected: ${opts.expectedFileType}.`,
+ file,
+ })
+ }
+
try {
// @ts-ignore
result = JSON.parse(reader?.result || '{}');
- console.log('parse loaded json', result);
- callback?.(result)
+ // console.log('parse loaded json', result);
+
+ opts.callback?.(result, file)
// state[key] = result
} catch(err) {
+ opts.errorCallback?.({
+ err,
+ file,
+ })
+
throw new Error(`failed to parse JSON data from ${file.name}`)
}
});
@@ -1054,11 +1168,26 @@ export function getPartialHDPath(wallet) {
].join('/')
}
-export function getAddressIndexFromUsage(wallet, account, usage) {
- let usageIndex = usage ?? wallet.usageIndex
+export function getAddressIndexFromUsage(wallet, account, usageIdx) {
+ let usageIndex = usageIdx ?? wallet?.usageIndex ?? 0
+ let addressIndex = account.usage?.[usageIndex] ?? account.addressIndex ?? 0
+ let usage = account.usage ?? [
+ account.addressIndex ?? 0,
+ 0
+ ]
+
+ // console.log(
+ // 'getAddressIndexFromUsage',
+ // usageIndex,
+ // addressIndex,
+ // account,
+ // usage,
+ // )
+
return {
...account,
+ usage,
usageIndex,
- addressIndex: account.usage[usageIndex],
+ addressIndex,
}
}
diff --git a/src/index.css b/src/index.css
index 7439fb5..7e69e4d 100644
--- a/src/index.css
+++ b/src/index.css
@@ -72,6 +72,7 @@ main header figure figcaption + div svg {
main header figure figcaption + div sub {
align-self: self-end;
font-size: 2.75rem;
+ font-size: 74%;
}
main footer {
text-align: center;
diff --git a/src/main.js b/src/main.js
index b3f0a79..89b417d 100644
--- a/src/main.js
+++ b/src/main.js
@@ -377,6 +377,13 @@ async function getUserInfo() {
([k,v]) => userInfo[k] = v
)
})
+ .catch(err => {
+ showErrorDialog({
+ title: 'Unable to decrypt seed phrase',
+ msg: err,
+ showActBtn: false,
+ })
+ })
}
}
@@ -428,6 +435,89 @@ function getTarget(event, selector) {
return target
}
+async function showNotification({
+ type = '',
+ title = '',
+ msg = '',
+ sticky = false,
+}) {
+ console.log('notification', {type, title, msg, sticky})
+}
+
+async function showErrorDialog(options) {
+ let opts = {
+ type: 'warn',
+ title: '⚠️ Error',
+ msg: '',
+ showCancelBtn: true,
+ showActBtn: true,
+ cancelCallback: () => {},
+ // timeout: null,
+ ...options,
+ }
+
+ opts.callback = opts.callback || (() => {
+ let firstLineFromError = ''
+ let { msg } = opts
+
+ if (typeof msg !== 'string' && msg.toString) {
+ msg = msg.toString()
+ }
+ if (typeof msg === 'string') {
+ firstLineFromError = msg.match(/[^\r\n]+/g)?.[0]
+ }
+
+ // console.log('firstLineFromError', firstLineFromError)
+
+ window.open(
+ `https://github.com/dashhive/wallet-ui/issues?q=${firstLineFromError}`,
+ '_blank',
+ )
+ })
+
+ if (opts.type === 'dang') {
+ console.error('showErrorDialog', opts.msg)
+ } else {
+ console.log('showErrorDialog', opts)
+ }
+
+ await appDialogs.confirmAction?.render({
+ name: opts.title,
+ actionTxt: 'Report Issue',
+ actionAlt: 'Report the error at GitHub',
+ action: 'lock',
+ cancelTxt: 'Close',
+ cancelAlt: `Close`,
+ // target: '',
+ // targetFallback: 'this wallet',
+ actionType: opts.type,
+ // action: 'disconnect',
+ // target: '',
+ // targetFallback: 'this wallet',
+ // actionType: 'dang',
+ showCancelBtn: opts.showCancelBtn,
+ showActBtn: opts.showActBtn,
+ submitIcon: state => `⚠️`,
+ alert: state => html``,
+ content: state => html`
+ ${state.header(state)}
+
+
+
+ Looks like we encountered an error.
+
+ ${opts.msg}
+
+
+ ${state.footer(state)}
+ `,
+ cancelCallback: opts.cancelCallback,
+ callback: opts.callback,
+ })
+
+ return appDialogs.confirmAction?.showModal()
+}
+
async function main() {
appState.encryptionPassword = window.atob(
sessionStorage.encryptionPassword || ''
@@ -463,6 +553,11 @@ async function main() {
}
)
+ appDialogs.confirmAction = await confirmActionRig({
+ mainApp, setupDialog,
+ appDialogs, appState, appTools,
+ })
+
appDialogs.walletEncrypt = await walletEncryptRig({
setupDialog, appDialogs, appState, mainApp,
wallet, wallets, bodyNav, dashBalance,
@@ -471,6 +566,7 @@ async function main() {
appDialogs.walletDecrypt = await walletDecryptRig({
setupDialog, appDialogs, appState, mainApp, importFromJson,
wallets, decryptKeystore, getUserInfo, store, deriveWalletData,
+ showErrorDialog,
})
appDialogs.walletBackup = await walletBackupRig({
@@ -491,6 +587,7 @@ async function main() {
appDialogs.phraseImport = await phraseImportRig({
setupDialog, appDialogs, appState, store,
mainApp, wallet, wallets, deriveWalletData,
+ showErrorDialog,
})
appDialogs.onboard = await onboardRig({
@@ -503,11 +600,6 @@ async function main() {
mainApp, wallet, userInfo, contactsList,
})
- appDialogs.confirmAction = await confirmActionRig({
- mainApp, setupDialog,
- appDialogs, appState, appTools,
- })
-
appDialogs.confirmDelete = await confirmDeleteRig({
mainApp, setupDialog, appDialogs, appState, appTools,
store, userInfo, contactsList,
@@ -532,7 +624,7 @@ async function main() {
mainApp, appDialogs, appState, appTools, store,
wallet, account: appState.account, walletFunds,
setupDialog, deriveWalletData, createTx,
- getAddrsWithFunds, batchGenAcctAddrs, getUnusedChangeAddress, getAccountWallet,
+ getAddrsWithFunds, batchGenAcctAddrs, getUnusedChangeAddress, getAccountWallet, showErrorDialog,
})
appDialogs.txInfo = await txInfoRig({
@@ -577,10 +669,15 @@ async function main() {
ks,
)
} catch(err) {
- console.error(
- '[fail] unable to decrypt seed phrase',
- err
- )
+ // console.error(
+ // '[fail] unable to decrypt seed phrase',
+ // err
+ // )
+ await showErrorDialog({
+ title: 'Unable to decrypt seed phrase',
+ msg: err,
+ showActBtn: false,
+ })
sessionStorage.removeItem('encryptionPassword')
}
}
@@ -693,7 +790,7 @@ async function main() {
await appDialogs.requestQr.render(
{
name: 'Share to receive funds',
- submitTxt: `Select a Contact`,
+ submitTxt: `Edit Amount or Contact`,
submitAlt: `Change the currently selected contact`,
footer: state => html`
@@ -85,16 +90,22 @@ export let sendConfirmRig = (async function (globals) {
if (!state.fee?.dash || !state.fullAmount) {
return ''
}
+ let dashFee = formatDash(
+ state.fee.dash,
+ )
+ let totalAmount = formatDash(
+ Number(state.fullAmount) + Number(state.fee?.dash),
+ )
return html`