Added balance checking history
This commit is contained in:
parent
6fa3d33e6e
commit
56e8e28d7a
73
css/main.css
73
css/main.css
@ -198,10 +198,16 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-only {
|
.icon-only {
|
||||||
padding: 0.5rem;
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
padding: 0.4rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
aspect-ratio: 1/1;
|
aspect-ratio: 1/1;
|
||||||
}
|
}
|
||||||
|
.icon-only .icon {
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
a:-webkit-any-link:focus-visible {
|
a:-webkit-any-link:focus-visible {
|
||||||
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
outline: rgba(var(--text-color), 1) 0.1rem solid;
|
||||||
@ -712,11 +718,14 @@ menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#main_header {
|
#main_header {
|
||||||
|
grid-area: header;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
border-bottom: solid thin rgba(var(--text-color), 0.3);
|
||||||
|
grid-column: 1/-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
@ -796,11 +805,53 @@ theme-toggle {
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
display: grid;
|
display: grid;
|
||||||
min-height: calc(100% - 5rem);
|
height: 100%;
|
||||||
|
grid-template-rows: auto auto 1fr;
|
||||||
|
grid-template-areas: "header" "main" "search-history";
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
grid-area: search-history;
|
||||||
|
view-transition-name: search-history;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
aside > * {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
aside h4 {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
border: solid thin rgba(var(--text-color), 0.3);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
.contact sm-chips {
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
padding: 0.2rem 0.3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
width: -webkit-fit-content;
|
||||||
|
width: -moz-fit-content;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
.contact sm-chip {
|
||||||
|
--padding: 0.3rem 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
--border-radius: 0.3rem;
|
||||||
|
}
|
||||||
|
.contact sm-copy {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main_section {
|
#main_section {
|
||||||
align-content: center;
|
grid-area: main;
|
||||||
|
margin-top: 3vw;
|
||||||
|
align-content: start;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
#main_section > * {
|
#main_section > * {
|
||||||
@ -853,6 +904,22 @@ main {
|
|||||||
.popup__header {
|
.popup__header {
|
||||||
padding: 1rem 1.5rem 0 1.5rem;
|
padding: 1rem 1.5rem 0 1.5rem;
|
||||||
}
|
}
|
||||||
|
main {
|
||||||
|
grid-template-columns: 22rem 1fr;
|
||||||
|
grid-template-areas: "header header" "search-history main";
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
border-right: solid thin rgba(var(--text-color), 0.3);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
aside h4 {
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
#input_wrapper {
|
#input_wrapper {
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
}
|
}
|
||||||
|
|||||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -185,9 +185,15 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-only {
|
.icon-only {
|
||||||
padding: 0.5rem;
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
padding: 0.4rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
aspect-ratio: 1/1;
|
aspect-ratio: 1/1;
|
||||||
|
.icon {
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a:any-link:focus-visible {
|
a:any-link:focus-visible {
|
||||||
@ -658,11 +664,14 @@ menu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#main_header {
|
#main_header {
|
||||||
|
grid-area: header;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
border-bottom: solid thin rgba(var(--text-color), 0.3);
|
||||||
|
grid-column: 1/-1;
|
||||||
}
|
}
|
||||||
#logo {
|
#logo {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@ -737,10 +746,48 @@ theme-toggle {
|
|||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
display: grid;
|
display: grid;
|
||||||
min-height: calc(100% - 5rem);
|
height: 100%;
|
||||||
|
grid-template-rows: auto auto 1fr;
|
||||||
|
grid-template-areas: "header" "main" "search-history";
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
grid-area: search-history;
|
||||||
|
view-transition-name: search-history;
|
||||||
|
& > * {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.contact {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
border: solid thin rgba(var(--text-color), 0.3);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
sm-chips {
|
||||||
|
background-color: rgba(var(--text-color), 0.06);
|
||||||
|
padding: 0.2rem 0.3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
sm-chip {
|
||||||
|
--padding: 0.3rem 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
--border-radius: 0.3rem;
|
||||||
|
}
|
||||||
|
sm-copy {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#main_section {
|
#main_section {
|
||||||
align-content: center;
|
grid-area: main;
|
||||||
|
margin-top: 3vw;
|
||||||
|
align-content: start;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
& > * {
|
& > * {
|
||||||
max-width: 36rem;
|
max-width: 36rem;
|
||||||
@ -791,6 +838,21 @@ main {
|
|||||||
.popup__header {
|
.popup__header {
|
||||||
padding: 1rem 1.5rem 0 1.5rem;
|
padding: 1rem 1.5rem 0 1.5rem;
|
||||||
}
|
}
|
||||||
|
main {
|
||||||
|
grid-template-columns: 22rem 1fr;
|
||||||
|
grid-template-areas: "header header" "search-history main";
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
border-right: solid thin rgba(var(--text-color), 0.3);
|
||||||
|
overflow-y: auto;
|
||||||
|
h4 {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
#input_wrapper {
|
#input_wrapper {
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
}
|
}
|
||||||
|
|||||||
185
index.html
185
index.html
@ -15,6 +15,15 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<sm-notifications id="notification_drawer"></sm-notifications>
|
<sm-notifications id="notification_drawer"></sm-notifications>
|
||||||
|
<sm-popup id="confirmation_popup">
|
||||||
|
<h4 id="confirm_title"></h4>
|
||||||
|
<p id="confirm_message" class="breakable"></p>
|
||||||
|
<div class="flex align-center gap-0-5 margin-left-auto">
|
||||||
|
<button class="button cancel-button">Cancel</button>
|
||||||
|
<button class="button button--primary confirm-button">OK</button>
|
||||||
|
</div>
|
||||||
|
</sm-popup>
|
||||||
|
<main>
|
||||||
<header id="main_header">
|
<header id="main_header">
|
||||||
<div id="logo" class="app-brand">
|
<div id="logo" class="app-brand">
|
||||||
<svg id="main_logo" class="icon" viewBox="0 0 27.25 32">
|
<svg id="main_logo" class="icon" viewBox="0 0 27.25 32">
|
||||||
@ -83,12 +92,17 @@
|
|||||||
</button>
|
</button>
|
||||||
<theme-toggle></theme-toggle>
|
<theme-toggle></theme-toggle>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<aside class="flex flex-direction-column">
|
||||||
|
<h4>
|
||||||
|
Searched addresses
|
||||||
|
</h4>
|
||||||
|
<ul id="searched_addresses_list" class="grid gap-0-5"></ul>
|
||||||
|
</aside>
|
||||||
<section id="main_section" class="grid gap-1-5">
|
<section id="main_section" class="grid gap-1-5">
|
||||||
<h2>
|
<h2>
|
||||||
Check USDC/USDT balance
|
Check USDC/USDT balance
|
||||||
</h2>
|
</h2>
|
||||||
<sm-form>
|
<sm-form oninvalid="handleInvalidSearch()">
|
||||||
<div id="input_wrapper">
|
<div id="input_wrapper">
|
||||||
<sm-input id="private_key_input" class="password-field flex-1" placeholder="FLO/BTC private key"
|
<sm-input id="private_key_input" class="password-field flex-1" placeholder="FLO/BTC private key"
|
||||||
type="password" data-private-key animate required>
|
type="password" data-private-key animate required>
|
||||||
@ -218,6 +232,7 @@
|
|||||||
<script src="scripts/tap_combined.js" type="text/javascript"></script>
|
<script src="scripts/tap_combined.js" type="text/javascript"></script>
|
||||||
<script src="scripts/keccak.js" type="text/javascript"></script>
|
<script src="scripts/keccak.js" type="text/javascript"></script>
|
||||||
<script src="scripts/floEthereum.js" type="text/javascript"></script>
|
<script src="scripts/floEthereum.js" type="text/javascript"></script>
|
||||||
|
<script src="scripts/compactIDB.js" type="text/javascript"></script>
|
||||||
<script src="https://cdn.ethers.io/lib/ethers-5.6.umd.min.js" type="text/javascript"> </script>
|
<script src="https://cdn.ethers.io/lib/ethers-5.6.umd.min.js" type="text/javascript"> </script>
|
||||||
<script src="scripts/usdc_balance.js" type="text/javascript"> </script>
|
<script src="scripts/usdc_balance.js" type="text/javascript"> </script>
|
||||||
<script>
|
<script>
|
||||||
@ -240,6 +255,48 @@
|
|||||||
function getRef(elementId) {
|
function getRef(elementId) {
|
||||||
return document.getElementById(elementId)
|
return document.getElementById(elementId)
|
||||||
}
|
}
|
||||||
|
let zIndex = 50
|
||||||
|
// function required for popups or modals to appear
|
||||||
|
function openPopup(popupId, pinned) {
|
||||||
|
zIndex++
|
||||||
|
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
|
||||||
|
getRef(popupId).show({ pinned })
|
||||||
|
return getRef(popupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hides the popup or modal
|
||||||
|
function closePopup() {
|
||||||
|
if (popupStack.peek() === undefined)
|
||||||
|
return;
|
||||||
|
popupStack.peek().popup.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('popupopened', async e => {
|
||||||
|
switch (e.target.id) {
|
||||||
|
case 'saved_ids_popup':
|
||||||
|
const allSavedIds = await getArrayOfSavedIds()
|
||||||
|
const renderedIds = renderContactPickerList(allSavedIds)
|
||||||
|
renderElem(getRef('saved_ids_picker_list'), html`${renderedIds}`)
|
||||||
|
getRef('search_saved_ids_picker').focusIn()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
document.addEventListener('popupclosed', e => {
|
||||||
|
zIndex--
|
||||||
|
switch (e.target.id) {
|
||||||
|
case 'saved_ids_popup':
|
||||||
|
renderElem(getRef('saved_ids_picker_list'), html``)
|
||||||
|
getRef('search_saved_ids_picker').value = ''
|
||||||
|
floGlobals.addressSelectorTarget = null
|
||||||
|
break;
|
||||||
|
case 'transaction_result_popup':
|
||||||
|
renderElem(getRef('transaction_result'), html``)
|
||||||
|
break;
|
||||||
|
case 'retrieve_flo_id_popup':
|
||||||
|
getRef('recovered_flo_id_wrapper').classList.add('hidden')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
|
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
|
||||||
function notify(message, mode, options = {}) {
|
function notify(message, mode, options = {}) {
|
||||||
let icon
|
let icon
|
||||||
@ -257,6 +314,31 @@
|
|||||||
}
|
}
|
||||||
return getRef("notification_drawer").push(message, { icon, ...options });
|
return getRef("notification_drawer").push(message, { icon, ...options });
|
||||||
}
|
}
|
||||||
|
// displays a popup for asking permission. Use this instead of JS confirm
|
||||||
|
const getConfirmation = (title, options = {}) => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const { message = '', cancelText = 'Cancel', confirmText = 'OK', danger = false } = options
|
||||||
|
openPopup('confirmation_popup', true)
|
||||||
|
getRef('confirm_title').innerText = title;
|
||||||
|
getRef('confirm_message').innerText = message;
|
||||||
|
const cancelButton = getRef('confirmation_popup').querySelector('.cancel-button');
|
||||||
|
const confirmButton = getRef('confirmation_popup').querySelector('.confirm-button')
|
||||||
|
confirmButton.textContent = confirmText
|
||||||
|
cancelButton.textContent = cancelText
|
||||||
|
if (danger)
|
||||||
|
confirmButton.classList.add('button--danger')
|
||||||
|
else
|
||||||
|
confirmButton.classList.remove('button--danger')
|
||||||
|
confirmButton.onclick = () => {
|
||||||
|
closePopup()
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
cancelButton.onclick = () => {
|
||||||
|
closePopup()
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
function createRipple(event, target) {
|
function createRipple(event, target) {
|
||||||
const circle = document.createElement("span");
|
const circle = document.createElement("span");
|
||||||
const diameter = Math.max(target.clientWidth, target.clientHeight);
|
const diameter = Math.max(target.clientWidth, target.clientHeight);
|
||||||
@ -290,13 +372,16 @@
|
|||||||
}
|
}
|
||||||
function buttonLoader(id, show) {
|
function buttonLoader(id, show) {
|
||||||
const button = typeof id === 'string' ? document.getElementById(id) : id;
|
const button = typeof id === 'string' ? document.getElementById(id) : id;
|
||||||
button.disabled = show;
|
if (!button) return
|
||||||
|
if (!button.dataset.hasOwnProperty('wasDisabled'))
|
||||||
|
button.dataset.wasDisabled = button.disabled
|
||||||
const animOptions = {
|
const animOptions = {
|
||||||
duration: 200,
|
duration: 200,
|
||||||
fill: 'forwards',
|
fill: 'forwards',
|
||||||
easing: 'ease'
|
easing: 'ease'
|
||||||
}
|
}
|
||||||
if (show) {
|
if (show) {
|
||||||
|
button.disabled = true
|
||||||
button.parentNode.append(document.createElement('sm-spinner'))
|
button.parentNode.append(document.createElement('sm-spinner'))
|
||||||
button.animate([
|
button.animate([
|
||||||
{
|
{
|
||||||
@ -307,7 +392,17 @@
|
|||||||
},
|
},
|
||||||
], animOptions)
|
], animOptions)
|
||||||
} else {
|
} else {
|
||||||
button.getAnimations().forEach(anim => anim.cancel())
|
button.disabled = button.dataset.wasDisabled === 'true';
|
||||||
|
button.animate([
|
||||||
|
{
|
||||||
|
clipPath: 'circle(0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
clipPath: 'circle(100%)',
|
||||||
|
},
|
||||||
|
], animOptions).onfinish = (e) => {
|
||||||
|
button.removeAttribute('data-original-state')
|
||||||
|
}
|
||||||
const potentialTarget = button.parentNode.querySelector('sm-spinner')
|
const potentialTarget = button.parentNode.querySelector('sm-spinner')
|
||||||
if (potentialTarget) potentialTarget.remove();
|
if (potentialTarget) potentialTarget.remove();
|
||||||
}
|
}
|
||||||
@ -332,7 +427,7 @@
|
|||||||
if (!value) return { isValid: false, errorText: 'Please enter a private key' }
|
if (!value) return { isValid: false, errorText: 'Please enter a private key' }
|
||||||
return {
|
return {
|
||||||
isValid: floCrypto.getPubKeyHex(value),
|
isValid: floCrypto.getPubKeyHex(value),
|
||||||
errorText: `Invalid private key.<br> It usually starts with "L".`
|
errorText: `Invalid private key.<br> It usually starts with "L" or "R".`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,9 +458,16 @@
|
|||||||
createRipple(e, e.target.closest("button, .interactive"));
|
createRipple(e, e.target.closest("button, .interactive"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
compactIDB.initDB('floEthereum', {
|
||||||
|
contacts: {}
|
||||||
|
}).then((result) => {
|
||||||
|
renderSearchedAddressList()
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
connectToMetaMask().then(() => {
|
connectToMetaMask().then(() => {
|
||||||
// setMetaMaskStatus(window.ethereum.isConnected())
|
// setMetaMaskStatus(window.ethereum.isConnected())
|
||||||
window.ethereum.on('networkChanged', (networkId) => {
|
window.ethereum.on('chainChanged', (networkId) => {
|
||||||
if (networkId !== '1') {
|
if (networkId !== '1') {
|
||||||
getRef('error__title').textContent = 'Please switch MetaMask to Ethereum Mainnet'
|
getRef('error__title').textContent = 'Please switch MetaMask to Ethereum Mainnet'
|
||||||
getRef('main_section').classList.add('hidden')
|
getRef('main_section').classList.add('hidden')
|
||||||
@ -414,18 +516,60 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
function checkBalance() {
|
function renderSearchedAddressList() {
|
||||||
|
compactIDB.readAllData('contacts').then(contacts => {
|
||||||
|
if (Object.keys(contacts).length === 0) {
|
||||||
|
renderElem(getRef('searched_addresses_list'), html`<li class="flex align-center justify-center">
|
||||||
|
<p>Your searched addresses will appear here for easier access in future.</p>
|
||||||
|
</li>`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const renderedContacts = []
|
||||||
|
for (const floAddress in contacts) {
|
||||||
|
const { ethAddress } = contacts[floAddress]
|
||||||
|
renderedContacts.push(html`
|
||||||
|
<li class="contact" .dataset=${{ floAddress, ethAddress }}>
|
||||||
|
<sm-chips onchange=${e => e.target.closest('.contact').querySelector('sm-copy').value = e.target.value}>
|
||||||
|
<sm-chip value=${floAddress} selected>FLO</sm-chip>
|
||||||
|
<sm-chip value=${ethAddress}>ETH</sm-chip>
|
||||||
|
</sm-chips>
|
||||||
|
<sm-copy value="${floAddress}"></sm-copy>
|
||||||
|
<div class="flex align-center space-between gap-0-5">
|
||||||
|
<button class="button button--small" onclick=${() => deleteContact(floAddress)}>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
<button class="button button--colored button--small" onclick=${() => checkBalance(ethAddress, floAddress)}>Check balance</button>
|
||||||
|
</div>
|
||||||
|
</li>`)
|
||||||
|
}
|
||||||
|
renderElem(getRef('searched_addresses_list'), html`${renderedContacts}`)
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function checkBalance(ethAddress, floAddress) {
|
||||||
|
if (!ethAddress) {
|
||||||
const keyToConvert = document.querySelector('[data-private-key]').value.trim()
|
const keyToConvert = document.querySelector('[data-private-key]').value.trim()
|
||||||
if (/^[0-9a-fA-F]{64}$/.test(keyToConvert)) {
|
if (/^[0-9a-fA-F]{64}$/.test(keyToConvert)) {
|
||||||
keyToConvert = coinjs.privkey2wif(keyToConvert)
|
keyToConvert = coinjs.privkey2wif(keyToConvert)
|
||||||
}
|
}
|
||||||
const ethPrivateKey = coinjs.wif2privkey(keyToConvert).privkey;
|
const ethPrivateKey = coinjs.wif2privkey(keyToConvert).privkey;
|
||||||
const ethAddress = floEthereum.ethAddressFromPrivateKey(ethPrivateKey)
|
ethAddress = floEthereum.ethAddressFromPrivateKey(ethPrivateKey)
|
||||||
const floAddress = floCrypto.getFloID(keyToConvert)
|
floAddress = floCrypto.getFloID(keyToConvert)
|
||||||
|
}
|
||||||
if (!ethAddress) return
|
if (!ethAddress) return
|
||||||
buttonLoader('check_balance_button', true)
|
buttonLoader('check_balance_button', true)
|
||||||
Promise.all([checkUSDCBalance(ethAddress), checkUSDTBalance(ethAddress)]).then(([usdcBalance, usdtBalance]) => {
|
Promise.all([checkUSDCBalance(ethAddress), checkUSDTBalance(ethAddress)]).then(([usdcBalance, usdtBalance]) => {
|
||||||
console.log(usdcBalance, usdtBalance)
|
compactIDB.readData('contacts', floAddress).then(result => {
|
||||||
|
if (result) return
|
||||||
|
compactIDB.addData('contacts', {
|
||||||
|
ethAddress,
|
||||||
|
}, floAddress).then(() => {
|
||||||
|
renderSearchedAddressList()
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
getRef('eth_address').value = ethAddress
|
getRef('eth_address').value = ethAddress
|
||||||
getRef('flo_address').value = floAddress
|
getRef('flo_address').value = floAddress
|
||||||
getRef('usdc_balance').textContent = `${ethers.utils.formatUnits(usdcBalance, 6)} USDC`
|
getRef('usdc_balance').textContent = `${ethers.utils.formatUnits(usdcBalance, 6)} USDC`
|
||||||
@ -451,6 +595,27 @@
|
|||||||
buttonLoader('check_balance_button', false)
|
buttonLoader('check_balance_button', false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
function handleInvalidSearch() {
|
||||||
|
if (document.startViewTransition)
|
||||||
|
document.startViewTransition(() => {
|
||||||
|
getRef('eth_balance_wrapper').classList.add('hidden')
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
getRef('eth_balance_wrapper').classList.add('hidden')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
async function deleteContact(floAddress) {
|
||||||
|
const confirmed = await getConfirmation('Delete contact', {
|
||||||
|
message: 'Are you sure you want to delete this contact?'
|
||||||
|
})
|
||||||
|
if (!confirmed) return
|
||||||
|
compactIDB.removeData('contacts', floAddress).then(() => {
|
||||||
|
renderSearchedAddressList()
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
257
scripts/compactIDB.js
Normal file
257
scripts/compactIDB.js
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
(function (EXPORTS) { //compactIDB v2.1.2
|
||||||
|
/* Compact IndexedDB operations */
|
||||||
|
'use strict';
|
||||||
|
const compactIDB = EXPORTS;
|
||||||
|
|
||||||
|
var defaultDB;
|
||||||
|
|
||||||
|
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
||||||
|
const IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
|
||||||
|
const IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
|
||||||
|
|
||||||
|
if (!indexedDB) {
|
||||||
|
console.error("Your browser doesn't support a stable version of IndexedDB.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.setDefaultDB = dbName => defaultDB = dbName;
|
||||||
|
|
||||||
|
Object.defineProperty(compactIDB, 'default', {
|
||||||
|
get: () => defaultDB,
|
||||||
|
set: dbName => defaultDB = dbName
|
||||||
|
});
|
||||||
|
|
||||||
|
function getDBversion(dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
resolve(db.version)
|
||||||
|
db.close()
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgradeDB(dbName, createList = null, deleteList = null) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
getDBversion(dbName).then(version => {
|
||||||
|
var idb = indexedDB.open(dbName, version + 1);
|
||||||
|
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
||||||
|
idb.onupgradeneeded = (event) => {
|
||||||
|
let db = event.target.result;
|
||||||
|
if (createList instanceof Object) {
|
||||||
|
if (Array.isArray(createList)) {
|
||||||
|
let tmp = {}
|
||||||
|
createList.forEach(o => tmp[o] = {})
|
||||||
|
createList = tmp
|
||||||
|
}
|
||||||
|
for (let o in createList) {
|
||||||
|
let obs = db.createObjectStore(o, createList[o].options || {});
|
||||||
|
if (createList[o].indexes instanceof Object)
|
||||||
|
for (let i in createList[o].indexes)
|
||||||
|
obs.createIndex(i, i, createList[o].indexes || {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Array.isArray(deleteList))
|
||||||
|
deleteList.forEach(o => db.deleteObjectStore(o));
|
||||||
|
resolve('Database upgraded')
|
||||||
|
}
|
||||||
|
idb.onsuccess = (event) => event.target.result.close();
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.initDB = function (dbName, objectStores = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!(objectStores instanceof Object))
|
||||||
|
return reject('ObjectStores must be an object or array')
|
||||||
|
defaultDB = defaultDB || dbName;
|
||||||
|
var idb = indexedDB.open(dbName);
|
||||||
|
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
||||||
|
idb.onsuccess = (event) => {
|
||||||
|
var db = event.target.result;
|
||||||
|
let cList = Object.values(db.objectStoreNames);
|
||||||
|
var obs = {},
|
||||||
|
a_obs = {},
|
||||||
|
d_obs = [];
|
||||||
|
if (!Array.isArray(objectStores))
|
||||||
|
var obs = objectStores
|
||||||
|
else
|
||||||
|
objectStores.forEach(o => obs[o] = {})
|
||||||
|
let nList = Object.keys(obs)
|
||||||
|
for (let o of nList)
|
||||||
|
if (!cList.includes(o))
|
||||||
|
a_obs[o] = obs[o]
|
||||||
|
for (let o of cList)
|
||||||
|
if (!nList.includes(o))
|
||||||
|
d_obs.push(o)
|
||||||
|
if (!Object.keys(a_obs).length && !d_obs.length)
|
||||||
|
resolve("Initiated IndexedDB");
|
||||||
|
else
|
||||||
|
upgradeDB(dbName, a_obs, d_obs)
|
||||||
|
.then(result => resolve(result))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDB = compactIDB.openDB = function (dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var idb = indexedDB.open(dbName);
|
||||||
|
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
||||||
|
idb.onupgradeneeded = (event) => {
|
||||||
|
event.target.result.close();
|
||||||
|
deleteDB(dbName).then(_ => null).catch(_ => null).finally(_ => reject("Datebase not found"))
|
||||||
|
}
|
||||||
|
idb.onsuccess = (event) => resolve(event.target.result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteDB = compactIDB.deleteDB = function (dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var deleteReq = indexedDB.deleteDatabase(dbName);;
|
||||||
|
deleteReq.onerror = (event) => reject("Error deleting database!");
|
||||||
|
deleteReq.onsuccess = (event) => resolve("Database deleted successfully");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.writeData = function (obsName, data, key = false, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let writeReq = (key ? obs.put(data, key) : obs.put(data));
|
||||||
|
writeReq.onsuccess = (evt) => resolve(`Write data Successful`);
|
||||||
|
writeReq.onerror = (evt) => reject(
|
||||||
|
`Write data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.addData = function (obsName, data, key = false, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let addReq = (key ? obs.add(data, key) : obs.add(data));
|
||||||
|
addReq.onsuccess = (evt) => resolve(`Add data successful`);
|
||||||
|
addReq.onerror = (evt) => reject(
|
||||||
|
`Add data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.removeData = function (obsName, key, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let delReq = obs.delete(key);
|
||||||
|
delReq.onsuccess = (evt) => resolve(`Removed Data ${key}`);
|
||||||
|
delReq.onerror = (evt) => reject(
|
||||||
|
`Remove data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.clearData = function (obsName, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
|
let clearReq = obs.clear();
|
||||||
|
clearReq.onsuccess = (evt) => resolve(`Clear data Successful`);
|
||||||
|
clearReq.onerror = (evt) => reject(`Clear data Unsuccessful`);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.readData = function (obsName, key, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
let getReq = obs.get(key);
|
||||||
|
getReq.onsuccess = (evt) => resolve(evt.target.result);
|
||||||
|
getReq.onerror = (evt) => reject(
|
||||||
|
`Read data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compactIDB.readAllData = function (obsName, dbName = defaultDB) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
var tmpResult = {}
|
||||||
|
let curReq = obs.openCursor();
|
||||||
|
curReq.onsuccess = (evt) => {
|
||||||
|
var cursor = evt.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
tmpResult[cursor.primaryKey] = cursor.value;
|
||||||
|
cursor.continue();
|
||||||
|
} else
|
||||||
|
resolve(tmpResult);
|
||||||
|
}
|
||||||
|
curReq.onerror = (evt) => reject(
|
||||||
|
`Read-All data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
||||||
|
);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
var filteredResult = {}
|
||||||
|
let keyRange;
|
||||||
|
if(options.lowerKey!==null && options.upperKey!==null)
|
||||||
|
keyRange = IDBKeyRange.bound(options.lowerKey, options.upperKey);
|
||||||
|
else if(options.lowerKey!==null)
|
||||||
|
keyRange = IDBKeyRange.lowerBound(options.lowerKey);
|
||||||
|
else if (options.upperKey!==null)
|
||||||
|
keyRange = IDBKeyRange.upperBound(options.upperBound);
|
||||||
|
else if (options.atKey)
|
||||||
|
let curReq = obs.openCursor(keyRange, )
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}*/
|
||||||
|
|
||||||
|
compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
|
||||||
|
options.lowerKey = options.atKey || options.lowerKey || 0
|
||||||
|
options.upperKey = options.atKey || options.upperKey || false
|
||||||
|
options.patternEval = options.patternEval || ((k, v) => true);
|
||||||
|
options.limit = options.limit || false;
|
||||||
|
options.reverse = options.reverse || false;
|
||||||
|
options.lastOnly = options.lastOnly || false
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openDB(dbName).then(db => {
|
||||||
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
|
var filteredResult = {}
|
||||||
|
let curReq = obs.openCursor(
|
||||||
|
options.upperKey ? IDBKeyRange.bound(options.lowerKey, options.upperKey) : IDBKeyRange.lowerBound(options.lowerKey),
|
||||||
|
options.lastOnly || options.reverse ? "prev" : "next");
|
||||||
|
curReq.onsuccess = (evt) => {
|
||||||
|
var cursor = evt.target.result;
|
||||||
|
if (!cursor || (options.limit && options.limit <= Object.keys(filteredResult).length))
|
||||||
|
return resolve(filteredResult); //reached end of key list or limit reached
|
||||||
|
else if (options.patternEval(cursor.primaryKey, cursor.value)) {
|
||||||
|
filteredResult[cursor.primaryKey] = cursor.value;
|
||||||
|
options.lastOnly ? resolve(filteredResult) : cursor.continue();
|
||||||
|
} else
|
||||||
|
cursor.continue();
|
||||||
|
}
|
||||||
|
curReq.onerror = (evt) => reject(`Search unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`);
|
||||||
|
db.close();
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})(window.compactIDB = {});
|
||||||
File diff suppressed because one or more lines are too long
2
scripts/components.min.js
vendored
2
scripts/components.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user