mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
670 lines
29 KiB
HTML
670 lines
29 KiB
HTML
{% include 'header.html' %}
|
|
{% from 'style.html' import breadcrumb_line_svg, circular_arrows_svg, withdraw_svg, utxo_groups_svg, create_utxo_svg, lock_svg, eye_show_svg %}
|
|
|
|
<div class="container mx-auto">
|
|
<section class="p-5 mt-5">
|
|
<div class="flex flex-wrap items-center -m-2">
|
|
<div class="w-full md:w-1/2 p-2">
|
|
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
|
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/"><p>Home</p></a></li>
|
|
<li>{{ breadcrumb_line_svg | safe }}</li>
|
|
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallets">Wallets</a></li>
|
|
<li>{{ breadcrumb_line_svg | safe }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="py-3">
|
|
<div class="container px-4 mx-auto">
|
|
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
|
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="dots-red">
|
|
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="dots-red">
|
|
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="wave">
|
|
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
|
<div class="w-full md:w-1/2 p-3 h-48">
|
|
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Wallets</h2>
|
|
<div class="flex items-center">
|
|
<h2 class="text-lg font-bold text-white tracking-tighter mr-2">Total Assets:</h2>
|
|
<button id="hide-usd-amount-toggle" class="flex items-center justify-center p-1 focus:ring-0 focus:outline-none">{{ eye_show_svg | safe }}</button>
|
|
</div>
|
|
<div class="flex items-baseline mt-2">
|
|
<div id="total-usd-value" class="text-5xl font-bold text-white"></div>
|
|
<div id="usd-text" class="text-sm text-white ml-1">USD</div>
|
|
</div>
|
|
<div id="total-btc-value" class="text-sm text-white mt-2"></div>
|
|
</div>
|
|
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto">
|
|
<a class="rounded-full mr-5 flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="refresh" href="/changepassword">{{ lock_svg | safe }}<span>Change/Set Password</span></a>
|
|
<a class="rounded-full flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="refresh" href="/wallets">{{ circular_arrows_svg | safe }}<span>Refresh</span></a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{% include 'inc_messages.html' %}
|
|
|
|
<section class="py-4">
|
|
<div class="container px-4 mx-auto">
|
|
<div class="flex flex-wrap -m-4">
|
|
{% for w in wallets %}
|
|
{% if w.havedata %}
|
|
{% if w.error %}<p>Error: {{ w.error }}</p>
|
|
{% else %}
|
|
<div class="w-full lg:w-1/3 p-4">
|
|
<div class="bg-gray-50 rounded overflow-hidden dark:bg-gray-500">
|
|
<div class="pt-6 px-6 mb-10 flex justify-between items-center">
|
|
<span class="inline-flex items-center justify-center w-9 h-10 bg-white-50 rounded">
|
|
<img class="h-9" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}">
|
|
</span>
|
|
<a class="py-2 px-3 bg-blue-500 text-xs text-white rounded-full hover:bg-blue-600" href="/wallet/{{ w.ticker }}">Manage Wallet</a>
|
|
</div>
|
|
<div class="px-6 mb-6">
|
|
<h4 class="text-xl font-bold dark:text-white">{{ w.name }}
|
|
<span class="inline-block font-medium text-xs text-gray-500 dark:text-white">({{ w.ticker }})</span>
|
|
</h4>
|
|
<p class="text-xs text-gray-500 dark:text-gray-200">Version: {{ w.version }} {% if w.updating %} <span class="inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-700 dark:hover:bg-gray-700">Updating..</span></p>
|
|
{% endif %}
|
|
</div>
|
|
<div class="p-6 bg-coolGray-100 dark:bg-gray-600">
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Balance:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.balance }} {{ w.ticker }}</div>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white usd-text">{{ w.ticker }} USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 usd-value" data-coinname="{{ w.name }}"></div>
|
|
</div>
|
|
{% if w.pending %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">Pending:</h4>
|
|
<span class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 coinname-value" data-coinname="{{ w.name }}">+{{ w.pending }} {{ w.ticker }}</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">Pending USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 usd-value"></div>
|
|
</div>
|
|
{% endif %}
|
|
{% if w.cid == '1' %} {# PART #}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Blind Balance:</h4>
|
|
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }}</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Blind USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 usd-value"></div>
|
|
</div>
|
|
{% if w.blind_unconfirmed %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">Blind Unconfirmed:</h4>
|
|
<span class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 coinname-value" data-coinname="{{ w.name }}" >+{{ w.blind_unconfirmed }} {{ w.ticker }}</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">Blind Unconfirmed USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 usd-value"></div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Anon Balance:</h4>
|
|
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }}</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Anon USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 usd-value"></div>
|
|
</div>
|
|
{% if w.anon_pending %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">Anon Pending:</h4>
|
|
<span class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 coinname-value" data-coinname="{{ w.name }}">
|
|
+{{ w.anon_pending }} {{ w.ticker }}</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">Anon Pending USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 usd-value"></div>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %} {# / PART #}
|
|
{% if w.cid == '3' %} {# LTC #}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">MWEB Balance:</h4>
|
|
<span class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 coinname-value" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }}</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">MWEB USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200 usd-value"></div>
|
|
</div>
|
|
{% if w.mweb_pending %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">MWEB Pending:</h4>
|
|
<span class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 coinname-value" data-coinname="{{ w.name }}">
|
|
+{{ w.mweb_pending }} {{ w.ticker }}</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-bold text-green-500 dark:text-green-500">MWEB Pending USD value:</h4>
|
|
<div class="bold inline-block py-1 px-2 rounded-full bg-green-100 text-xs text-green-500 dark:bg-gray-500 dark:text-green-500 usd-value"></div>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
{# / LTC #}
|
|
<hr class="border-t border-gray-100 dark:border-gray-500 my-5">
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Blocks:</h4>
|
|
<span class="inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200">{{ w.blocks }}{% if w.known_block_count %} / {{ w.known_block_count }}
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Last Updated:</h4>
|
|
<span class="inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200">{{ w.lastupdated }}</span>
|
|
</div>
|
|
{% if w.bootstrapping %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Bootstrapping:</h4>
|
|
<span class="inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200">{{ w.bootstrapping }}</span>
|
|
</div>
|
|
{% endif %}
|
|
{% if w.encrypted %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Locked:</h4>
|
|
<span class="inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200">{{ w.locked }}</span>
|
|
</div>
|
|
{% endif %}
|
|
<div class="flex mb-2 justify-between items-center">
|
|
<h4 class="text-xs font-medium dark:text-white">Expected Seed:</h4>
|
|
<span class="inline-block py-1 px-2 rounded-full bg-blue-100 text-xs text-black-500 dark:bg-gray-500 dark:text-gray-200">{{ w.expected_seed }}</span>
|
|
</div>
|
|
<div class="flex justify-between mb-1 mt-10">
|
|
<span class="text-xs font-medium dark:text-gray-200">Blockchain</span>
|
|
<span class="text-xs font-medium dark:text-gray-200">{{ w.synced }}%</span>
|
|
</div>
|
|
<div class="w-full bg-gray-200 rounded-full h-1 " data-tooltip-target="tooltip-blocks{{loop.index}}">
|
|
<div class="{% if w.synced | float < 100 %} bg-red-500 sync-bar-color-change {% else %} bg-blue-500 {% endif %} h-1 rounded-full" style="width: {{ w.synced }}%;"></div>
|
|
</div>
|
|
<div class="flex justify-between mb-1 mt-5">
|
|
<span class="text-xs font-medium dark:text-gray-200">
|
|
<script>
|
|
if ({{ w.synced }} !== 100) {
|
|
document.write("<p class='bg-gray-50 rounded overflow-hidden dark:bg-gray-500 p-2.5 dark:text-white'>The order book/blockchain is currently syncing, offers will only display once the network has fully <b>100%</b> synced. Please wait until the process completes.</p>");
|
|
}
|
|
</script>
|
|
<div id="tooltip-blocks{{loop.index}}" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-xs text-white {% if w.synced | float < 100 %} bg-red-500 sync-bar-color-change {% else %} bg-blue-500 {% endif %} rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
|
<div><span class="bold">Blocks: {{ w.blocks }}{% if w.known_block_count %} / {{ w.known_block_count }} {% endif %}</div>
|
|
<div class="tooltip-arrow pl-1" data-popper-arrow></div>
|
|
</div>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
{% include 'footer.html' %}
|
|
|
|
<script>
|
|
const MAX_RETRIES = 3;
|
|
const BASE_DELAY = 1000;
|
|
|
|
const api = {
|
|
cache: {
|
|
data: null,
|
|
timestamp: null,
|
|
expirationTime: 10 * 60 * 1000, // 10 minutes
|
|
|
|
isValid() {
|
|
console.log('Checking cache validity...');
|
|
const isValid = this.data && this.timestamp &&
|
|
(Date.now() - this.timestamp < this.expirationTime);
|
|
console.log('Cache is valid:', isValid);
|
|
return isValid;
|
|
},
|
|
|
|
set(data) {
|
|
console.log('Updating cache with new data...');
|
|
this.data = data;
|
|
this.timestamp = Date.now();
|
|
},
|
|
|
|
get() {
|
|
console.log('Retrieving data from cache...');
|
|
return this.isValid() ? this.data : null;
|
|
},
|
|
|
|
clear() {
|
|
console.log('Clearing cache...');
|
|
this.data = null;
|
|
this.timestamp = null;
|
|
}
|
|
},
|
|
|
|
makePostRequest: (url, headers = {}) => {
|
|
return new Promise((resolve, reject) => {
|
|
console.log('Making POST request to:', url);
|
|
const cachedData = api.cache.get();
|
|
if (cachedData) {
|
|
console.log('Using cached data');
|
|
return resolve(cachedData);
|
|
}
|
|
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open('POST', '/json/readurl');
|
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
xhr.timeout = 30000;
|
|
xhr.ontimeout = () => {
|
|
console.error('Request timed out');
|
|
reject(new Error('Request timed out'));
|
|
};
|
|
xhr.onload = () => {
|
|
if (xhr.status === 200) {
|
|
try {
|
|
const response = JSON.parse(xhr.responseText);
|
|
if (response.Error) {
|
|
console.error('Error in API response:', response.Error);
|
|
reject(new Error(response.Error));
|
|
} else {
|
|
console.log('Caching API response data');
|
|
api.cache.set(response);
|
|
resolve(response);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error parsing JSON response:', error.message);
|
|
reject(new Error(`Invalid JSON response: ${error.message}`));
|
|
}
|
|
} else {
|
|
console.error(`HTTP Error: ${xhr.status} ${xhr.statusText}`);
|
|
reject(new Error(`HTTP Error: ${xhr.status} ${xhr.statusText}`));
|
|
}
|
|
};
|
|
xhr.onerror = () => {
|
|
console.error('Network error occurred');
|
|
reject(new Error('Network error occurred'));
|
|
};
|
|
xhr.send(JSON.stringify({ url, headers }));
|
|
});
|
|
}
|
|
};
|
|
|
|
const coinNameToSymbol = {
|
|
'Bitcoin': 'bitcoin',
|
|
'Particl': 'particl',
|
|
'Particl Blind': 'particl',
|
|
'Particl Anon': 'particl',
|
|
'Monero': 'monero',
|
|
'Wownero': 'wownero',
|
|
'Litecoin': 'litecoin',
|
|
'Firo': 'zcoin',
|
|
'Dash': 'dash',
|
|
'PIVX': 'pivx',
|
|
'Decred': 'decred',
|
|
'Zano': 'zano',
|
|
'Bitcoin Cash': 'bitcoin-cash'
|
|
};
|
|
|
|
function initializePercentageTooltip() {
|
|
if (typeof Tooltip === 'undefined') {
|
|
console.warn('Tooltip is not defined. Make sure the required library is loaded.');
|
|
return;
|
|
}
|
|
|
|
console.log('Initializing percentage tooltip...');
|
|
const percentageEl = document.querySelector('[data-tooltip-target="tooltip-percentage"]');
|
|
const tooltipEl = document.getElementById('tooltip-percentage');
|
|
if (percentageEl && tooltipEl) {
|
|
console.log('Creating new tooltip instance');
|
|
new Tooltip(tooltipEl, percentageEl);
|
|
} else {
|
|
console.warn('Tooltip elements not found');
|
|
}
|
|
}
|
|
|
|
const PRICE_UPDATE_INTERVAL = 300000;
|
|
let isUpdating = false;
|
|
let previousTotalUsd = null;
|
|
let currentPercentageChangeColor = 'yellow';
|
|
let percentageChangeEl = null;
|
|
let currentPercentageChange = null;
|
|
|
|
async function fetchLatestPrices() {
|
|
let prices = null;
|
|
let retryAttempt = 0;
|
|
|
|
while (retryAttempt < MAX_RETRIES) {
|
|
try {
|
|
console.log(`Attempt ${retryAttempt + 1} of ${MAX_RETRIES} to fetch prices from API`);
|
|
prices = await api.makePostRequest(
|
|
'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zano,wownero,zcoin&vs_currencies=USD,BTC'
|
|
);
|
|
|
|
console.log('Caching fetched prices');
|
|
api.cache.set(prices);
|
|
return prices;
|
|
} catch (error) {
|
|
console.error('Error fetching prices:', error);
|
|
|
|
const cachedPrices = api.cache.get();
|
|
if (cachedPrices) {
|
|
console.log('Using cached prices');
|
|
return cachedPrices;
|
|
}
|
|
|
|
retryAttempt++;
|
|
const delay = Math.min(BASE_DELAY * Math.pow(2, retryAttempt), 10000);
|
|
console.log(`Retrying in ${delay / 1000} seconds...`);
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
}
|
|
}
|
|
|
|
console.log('All retries failed, returning cached prices if available');
|
|
return api.cache.get() || null;
|
|
}
|
|
|
|
async function updatePrices(forceUpdate = false) {
|
|
if (isUpdating) {
|
|
console.log('Price update already in progress, skipping...');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
console.log('Starting price update...');
|
|
isUpdating = true;
|
|
|
|
if (forceUpdate) {
|
|
console.log('Clearing price-related data from cache and localStorage');
|
|
api.cache.clear();
|
|
const keys = Object.keys(localStorage).filter(key => key.endsWith('-usd') || key === 'total-usd' || key === 'total-btc');
|
|
keys.forEach(key => localStorage.removeItem(key));
|
|
}
|
|
|
|
const response = await fetchLatestPrices();
|
|
|
|
if (localStorage.getItem('balancesVisible') !== 'true') {
|
|
console.log('Balances not visible, skipping update');
|
|
const existingPercentageChangeEl = document.querySelector('.percentage-change');
|
|
if (existingPercentageChangeEl) {
|
|
console.log('Removing existing percentage change element');
|
|
existingPercentageChangeEl.remove();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
let total = 0;
|
|
let hasMissingPrices = false;
|
|
const updates = [];
|
|
|
|
console.log('Updating individual coin values...');
|
|
document.querySelectorAll('.coinname-value').forEach(el => {
|
|
const coinName = el.getAttribute('data-coinname');
|
|
const amountStr = el.getAttribute('data-original-value');
|
|
if (!amountStr) return;
|
|
|
|
const amount = parseFloat(amountStr.replace(/[^0-9.-]+/g, ''));
|
|
const coinId = coinNameToSymbol[coinName];
|
|
const price = response?.[coinId]?.usd;
|
|
|
|
let usdValue;
|
|
if (price && !isNaN(amount)) {
|
|
usdValue = (amount * price).toFixed(2);
|
|
total += parseFloat(usdValue);
|
|
localStorage.setItem(`${coinId}-usd`, usdValue);
|
|
} else {
|
|
// Use cached price if available
|
|
const cachedPrice = api.cache.get()?.[coinId]?.usd || localStorage.getItem(`${coinId}-usd`);
|
|
usdValue = cachedPrice ? (amount * cachedPrice).toFixed(2) : '****';
|
|
hasMissingPrices = true;
|
|
console.log(`Could not find price for coin: ${coinName}`);
|
|
}
|
|
|
|
const usdEl = el.closest('.flex').nextElementSibling?.querySelector('.usd-value');
|
|
if (usdEl) {
|
|
updates.push([usdEl, usdValue]);
|
|
}
|
|
});
|
|
|
|
console.log('Updating total USD and BTC values...');
|
|
updates.forEach(([el, value]) => {
|
|
el.textContent = value;
|
|
el.setAttribute('data-original-value', value);
|
|
});
|
|
|
|
const totalUsdEl = document.getElementById('total-usd-value');
|
|
if (totalUsdEl) {
|
|
const totalText = `$${total.toFixed(2)}`;
|
|
totalUsdEl.textContent = totalText;
|
|
totalUsdEl.setAttribute('data-original-value', totalText);
|
|
localStorage.setItem('total-usd', total);
|
|
} else {
|
|
console.log('Total USD element not found, skipping total USD update');
|
|
}
|
|
|
|
const btcPrice = response?.bitcoin?.usd;
|
|
if (btcPrice) {
|
|
const btcTotal = total / btcPrice;
|
|
const totalBtcEl = document.getElementById('total-btc-value');
|
|
if (totalBtcEl) {
|
|
const btcText = `~ ${btcTotal.toFixed(8)} BTC`;
|
|
totalBtcEl.textContent = btcText;
|
|
totalBtcEl.setAttribute('data-original-value', btcText);
|
|
localStorage.setItem('total-btc', btcTotal);
|
|
} else {
|
|
console.log('Total BTC element not found, skipping total BTC update');
|
|
}
|
|
} else {
|
|
console.log('Could not find BTC price');
|
|
}
|
|
|
|
let percentageChangeEl = document.querySelector('.percentage-change');
|
|
if (!percentageChangeEl) {
|
|
console.log('Creating percentage change elements...');
|
|
const tooltipId = 'tooltip-percentage';
|
|
|
|
const tooltip = document.createElement('div');
|
|
tooltip.id = tooltipId;
|
|
tooltip.role = 'tooltip';
|
|
tooltip.className = 'inline-block absolute invisible z-50 py-2 px-3 text-sm font-medium text-white bg-gray-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip';
|
|
tooltip.innerHTML = `
|
|
Price change in the last 10 minutes<br>
|
|
<span style="color: rgb(34, 197, 94);">▲ Green:</span><span> Price increased</span><br>
|
|
<span style="color: rgb(239, 68, 68);">▼ Red:</span><span> Price decreased</span><br>
|
|
<span style="color: white;">→ White:</span><span> No change</span>
|
|
`;
|
|
document.body.appendChild(tooltip);
|
|
|
|
percentageChangeEl = document.createElement('span');
|
|
percentageChangeEl.setAttribute('data-tooltip-target', tooltipId);
|
|
percentageChangeEl.className = 'ml-2 text-base bg-gray-500 percentage-change px-2 py-1 rounded-full cursor-help';
|
|
totalUsdEl?.parentNode?.appendChild(percentageChangeEl);
|
|
|
|
console.log('Initializing tooltip...');
|
|
initializePercentageTooltip();
|
|
}
|
|
|
|
let percentageChange = 0;
|
|
let percentageChangeIcon = '→';
|
|
let currentPercentageChangeColor = 'white';
|
|
|
|
percentageChangeEl.textContent = `${percentageChangeIcon} 0.00%`;
|
|
percentageChangeEl.style.color = currentPercentageChangeColor;
|
|
percentageChangeEl.style.display = localStorage.getItem('balancesVisible') === 'true' ? 'inline' : 'none';
|
|
console.log(`Displaying percentage change in total USD: ${percentageChangeEl.textContent}`);
|
|
|
|
console.log('Price update completed successfully');
|
|
return !hasMissingPrices;
|
|
} catch (error) {
|
|
console.error('Price update failed:', error);
|
|
// Only clear price-related data from localStorage
|
|
const keys = Object.keys(localStorage).filter(key => key.endsWith('-usd') || key === 'total-usd' || key === 'total-btc');
|
|
keys.forEach(key => localStorage.removeItem(key));
|
|
return false;
|
|
} finally {
|
|
isUpdating = false;
|
|
}
|
|
}
|
|
|
|
function storeOriginalValues() {
|
|
console.log('Storing original coin values...');
|
|
document.querySelectorAll('.coinname-value').forEach(el => {
|
|
if (!el.getAttribute('data-original-value')) {
|
|
el.setAttribute('data-original-value', el.textContent.trim());
|
|
}
|
|
});
|
|
}
|
|
|
|
const toggleIcon = (isVisible) => {
|
|
const eyeIcon = document.querySelector("#hide-usd-amount-toggle svg");
|
|
if (eyeIcon) {
|
|
console.log('Toggling eye icon visibility:', isVisible);
|
|
if (isVisible) {
|
|
eyeIcon.innerHTML = `
|
|
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062,.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z"></path>
|
|
`;
|
|
} else {
|
|
eyeIcon.innerHTML = `
|
|
<path d="M23.444,10.239a22.936,22.936,0,0,0-2.492-2.948l-4.021,4.021A5.026,5.026,0,0,1,17,12a5,5,0,0,1-5,5,5.026,5.026,0,0,1-.688-.069L8.055,20.188A10.286,10.286,0,0,0,12,21c5.708,0,9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239Z"></path>
|
|
<path d="M12,3C6.292,3,2.1,8.062,.555,10.24a3.058,3.058,0,0,0,0,3.52h0a21.272,21.272,0,0,0,4.784,4.9l3.124-3.124a5,5,0,0,1,7.071-7.072L8.464,15.536l10.2-10.2A11.484,11.484,0,0,0,12,3Z"></path>
|
|
<path data-color="color-2" d="M1,24a1,1,0,0,1-.707-1.707l22-22a1,1,0,0,1,1.414,1.414l-22,22A1,1,0,0,1,1,24Z"></path>
|
|
`;
|
|
}
|
|
}
|
|
};
|
|
|
|
let toggleInProgress = false;
|
|
let toggleBalancesDebounce;
|
|
|
|
const toggleBalances = async (isVisible) => {
|
|
console.log('Toggling balance visibility:', isVisible);
|
|
storeOriginalValues();
|
|
|
|
const usdText = document.getElementById('usd-text');
|
|
const totalUsdEl = document.getElementById('total-usd-value');
|
|
|
|
if (usdText) {
|
|
console.log('Updating USD text visibility:', isVisible);
|
|
usdText.style.display = isVisible ? 'inline' : 'none';
|
|
}
|
|
|
|
if (isVisible) {
|
|
console.log('Restoring coin amounts...');
|
|
document.querySelectorAll('.coinname-value').forEach(el => {
|
|
const originalValue = el.getAttribute('data-original-value');
|
|
if (originalValue) {
|
|
el.textContent = originalValue;
|
|
}
|
|
});
|
|
|
|
console.log('Updating prices...');
|
|
const success = await updatePrices(true);
|
|
|
|
if (!success) {
|
|
console.log('Price update failed, restoring previous USD values...');
|
|
document.querySelectorAll('.usd-value').forEach(el => {
|
|
const storedValue = el.getAttribute('data-original-value');
|
|
el.textContent = storedValue || '****';
|
|
});
|
|
|
|
['total-usd-value', 'total-btc-value'].forEach(id => {
|
|
const el = document.getElementById(id);
|
|
if (el) {
|
|
el.textContent = el.getAttribute('data-original-value') || '****';
|
|
}
|
|
});
|
|
}
|
|
|
|
if (totalUsdEl) {
|
|
totalUsdEl.classList.add('font-extrabold');
|
|
}
|
|
if (percentageChangeEl) {
|
|
percentageChangeEl.style.display = 'inline';
|
|
percentageChangeEl.style.color = currentPercentageChangeColor;
|
|
}
|
|
} else {
|
|
console.log('Hiding all balance values...');
|
|
['coinname-value', 'usd-value'].forEach(className => {
|
|
document.querySelectorAll('.' + className).forEach(el => {
|
|
el.textContent = '****';
|
|
});
|
|
});
|
|
|
|
['total-usd-value', 'total-btc-value'].forEach(id => {
|
|
const el = document.getElementById(id);
|
|
if (el) el.textContent = '****';
|
|
});
|
|
|
|
const percentageChangeEl = document.querySelector('.percentage-change');
|
|
if (percentageChangeEl) {
|
|
console.log('Hiding percentage change element');
|
|
percentageChangeEl.style.display = 'none';
|
|
}
|
|
|
|
if (totalUsdEl) {
|
|
totalUsdEl.classList.remove('font-extrabold');
|
|
}
|
|
}
|
|
};
|
|
|
|
const toggleBalancesDebounced = () => {
|
|
if (toggleBalancesDebounce) {
|
|
clearTimeout(toggleBalancesDebounce);
|
|
}
|
|
|
|
toggleBalancesDebounce = setTimeout(() => {
|
|
toggleInProgress = false;
|
|
toggleBalances(localStorage.getItem('balancesVisible') === 'true');
|
|
}, 500);
|
|
};
|
|
|
|
const loadBalanceVisibility = async () => {
|
|
console.log('Loading balance visibility...');
|
|
const balancesVisible = localStorage.getItem('balancesVisible') === 'true';
|
|
toggleIcon(balancesVisible);
|
|
await toggleBalancesDebounced();
|
|
};
|
|
|
|
window.onload = async () => {
|
|
console.log('Window loaded, initializing price visualization...');
|
|
storeOriginalValues();
|
|
|
|
if (localStorage.getItem('balancesVisible') === null) {
|
|
console.log('Balances visibility not set, setting to true');
|
|
localStorage.setItem('balancesVisible', 'true');
|
|
}
|
|
|
|
const hideBalancesToggle = document.getElementById('hide-usd-amount-toggle');
|
|
hideBalancesToggle?.addEventListener('click', async () => {
|
|
if (toggleInProgress) {
|
|
console.log('Toggle already in progress, skipping...');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
toggleInProgress = true;
|
|
console.log('Toggling balance visibility...');
|
|
const balancesVisible = localStorage.getItem('balancesVisible') === 'true';
|
|
const newVisibility = !balancesVisible;
|
|
|
|
localStorage.setItem('balancesVisible', newVisibility);
|
|
toggleIcon(newVisibility);
|
|
await toggleBalancesDebounced();
|
|
} finally {
|
|
toggleInProgress = false;
|
|
}
|
|
});
|
|
|
|
await loadBalanceVisibility();
|
|
|
|
console.log(`Setting up periodic price updates every ${PRICE_UPDATE_INTERVAL / 1000} seconds...`);
|
|
setInterval(async () => {
|
|
if (localStorage.getItem('balancesVisible') === 'true' && !toggleInProgress && !isUpdating) {
|
|
console.log('Running periodic price update...');
|
|
await updatePrices();
|
|
}
|
|
}, PRICE_UPDATE_INTERVAL);
|
|
};
|
|
</script>
|
|
</body>
|
|
</html>
|