mirror of
https://github.com/basicswap/basicswap.git
synced 2024-12-23 03:49:25 +00:00
ui: Fix cache wallets, Better hide/show (crypto/usd). Removed % on single val. Fixes.
This commit is contained in:
parent
31ead537c9
commit
1763dec981
1 changed files with 523 additions and 377 deletions
|
@ -207,93 +207,32 @@
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const MAX_RETRIES = 3;
|
// Config
|
||||||
const BASE_DELAY = 1000;
|
const CONFIG = {
|
||||||
|
MAX_RETRIES: 3,
|
||||||
const api = {
|
BASE_DELAY: 1000,
|
||||||
cache: {
|
CACHE_EXPIRATION: 5 * 60 * 1000, // 5 minutes
|
||||||
data: null,
|
PRICE_UPDATE_INTERVAL: 5 * 60 * 1000, // 5 minutes
|
||||||
timestamp: null,
|
API_TIMEOUT: 30000,
|
||||||
expirationTime: 10 * 60 * 1000, // 10 minutes
|
DEBOUNCE_DELAY: 500,
|
||||||
|
CACHE_MIN_INTERVAL: 60 * 1000, // Minimum 1 minute between API calls
|
||||||
isValid() {
|
MAX_PERCENTAGE_CHANGE: 50, // Max reasonable percentage change (50%)
|
||||||
console.log('Checking cache validity...');
|
MIN_VALUE_FOR_PERCENTAGE: 0.01 // Minimum value to calculate percentage changes
|
||||||
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 = {
|
const STATE_KEYS = {
|
||||||
|
PERCENTAGE_CHANGE: 'last-percentage-change',
|
||||||
|
PERCENTAGE_COLOR: 'percentage-change-color',
|
||||||
|
PERCENTAGE_ICON: 'percentage-change-icon',
|
||||||
|
LAST_UPDATE: 'last-update-time',
|
||||||
|
PREVIOUS_TOTAL: 'previous-total-usd',
|
||||||
|
CURRENT_TOTAL: 'current-total-usd',
|
||||||
|
BALANCES_VISIBLE: 'balancesVisible'
|
||||||
|
};
|
||||||
|
|
||||||
|
const COIN_SYMBOLS = {
|
||||||
'Bitcoin': 'bitcoin',
|
'Bitcoin': 'bitcoin',
|
||||||
'Particl': 'particl',
|
'Particl': 'particl',
|
||||||
'Particl Blind': 'particl',
|
|
||||||
'Particl Anon': 'particl',
|
|
||||||
'Monero': 'monero',
|
'Monero': 'monero',
|
||||||
'Wownero': 'wownero',
|
'Wownero': 'wownero',
|
||||||
'Litecoin': 'litecoin',
|
'Litecoin': 'litecoin',
|
||||||
|
@ -305,250 +244,415 @@ const coinNameToSymbol = {
|
||||||
'Bitcoin Cash': 'bitcoin-cash'
|
'Bitcoin Cash': 'bitcoin-cash'
|
||||||
};
|
};
|
||||||
|
|
||||||
function initializePercentageTooltip() {
|
const SHORT_NAMES = {
|
||||||
if (typeof Tooltip === 'undefined') {
|
'Bitcoin': 'BTC',
|
||||||
console.warn('Tooltip is not defined. Make sure the required library is loaded.');
|
'Particl': 'PART',
|
||||||
return;
|
'Monero': 'XMR',
|
||||||
|
'Wownero': 'WOW',
|
||||||
|
'Litecoin': 'LTC',
|
||||||
|
'Litecoin MWEB': 'LTC MWEB',
|
||||||
|
'Firo': 'FIRO',
|
||||||
|
'Dash': 'DASH',
|
||||||
|
'PIVX': 'PIVX',
|
||||||
|
'Decred': 'DCR',
|
||||||
|
'Zano': 'ZANO',
|
||||||
|
'Bitcoin Cash': 'BCH'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cache
|
||||||
|
class Cache {
|
||||||
|
constructor(expirationTime) {
|
||||||
|
console.log(`Initializing cache with ${expirationTime/1000} seconds expiration time`);
|
||||||
|
this.data = null;
|
||||||
|
this.timestamp = null;
|
||||||
|
this.expirationTime = expirationTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Initializing percentage tooltip...');
|
isValid() {
|
||||||
const percentageEl = document.querySelector('[data-tooltip-target="tooltip-percentage"]');
|
console.log('Checking cache validity...');
|
||||||
const tooltipEl = document.getElementById('tooltip-percentage');
|
const isValid = Boolean(
|
||||||
if (percentageEl && tooltipEl) {
|
this.data &&
|
||||||
console.log('Creating new tooltip instance');
|
this.timestamp &&
|
||||||
new Tooltip(tooltipEl, percentageEl);
|
(Date.now() - this.timestamp < this.expirationTime)
|
||||||
} else {
|
);
|
||||||
console.warn('Tooltip elements not found');
|
console.log(`Cache validity check result: ${isValid}`);
|
||||||
|
if (this.timestamp) {
|
||||||
|
const age = (Date.now() - this.timestamp) / 1000;
|
||||||
|
console.log(`Cache age: ${age.toFixed(1)} seconds`);
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(data) {
|
||||||
|
console.log('Updating cache with new data');
|
||||||
|
this.data = data;
|
||||||
|
this.timestamp = Date.now();
|
||||||
|
console.log('Cache updated successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
console.log('Attempting to retrieve data from cache');
|
||||||
|
if (this.isValid()) {
|
||||||
|
console.log('Returning valid cached data');
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
console.log('No valid cached data available');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
console.log('Clearing cache data');
|
||||||
|
this.data = null;
|
||||||
|
this.timestamp = null;
|
||||||
|
console.log('Cache cleared successfully');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PRICE_UPDATE_INTERVAL = 300000;
|
// API
|
||||||
let isUpdating = false;
|
class ApiClient {
|
||||||
let previousTotalUsd = null;
|
constructor() {
|
||||||
let currentPercentageChangeColor = 'yellow';
|
console.log('Initializing API client');
|
||||||
let percentageChangeEl = null;
|
this.cache = new Cache(CONFIG.CACHE_EXPIRATION);
|
||||||
let currentPercentageChange = null;
|
this.lastFetchTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchLatestPrices() {
|
makeRequest(url, headers = {}) {
|
||||||
let prices = null;
|
console.log(`Making API request to: ${url}`);
|
||||||
let retryAttempt = 0;
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', '/json/readurl');
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
xhr.timeout = CONFIG.API_TIMEOUT;
|
||||||
|
|
||||||
while (retryAttempt < MAX_RETRIES) {
|
xhr.ontimeout = () => {
|
||||||
|
console.error('API request timed out');
|
||||||
|
reject(new Error('Request timed out'));
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
console.log('Received 200 response from API');
|
||||||
try {
|
try {
|
||||||
console.log(`Attempt ${retryAttempt + 1} of ${MAX_RETRIES} to fetch prices from API`);
|
const response = JSON.parse(xhr.responseText);
|
||||||
prices = await api.makePostRequest(
|
if (response.Error) {
|
||||||
|
console.error('Error in API response:', response.Error);
|
||||||
|
reject(new Error(response.Error));
|
||||||
|
} else {
|
||||||
|
console.log('Successfully parsed API response');
|
||||||
|
resolve(response);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to parse API response:', error);
|
||||||
|
reject(new Error(`Invalid JSON response: ${error.message}`));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`API request failed with status: ${xhr.status}`);
|
||||||
|
reject(new Error(`HTTP Error: ${xhr.status} ${xhr.statusText}`));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = () => {
|
||||||
|
console.error('Network error occurred during API request');
|
||||||
|
reject(new Error('Network error occurred'));
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Sending API request...');
|
||||||
|
xhr.send(JSON.stringify({ url, headers }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchPrices(forceUpdate = false) {
|
||||||
|
const now = Date.now();
|
||||||
|
const timeSinceLastFetch = now - this.lastFetchTime;
|
||||||
|
|
||||||
|
if (!forceUpdate && timeSinceLastFetch < CONFIG.CACHE_MIN_INTERVAL) {
|
||||||
|
console.log(`Skipping API call - only ${(timeSinceLastFetch/1000).toFixed(1)}s since last fetch`);
|
||||||
|
const cachedData = this.cache.get();
|
||||||
|
if (cachedData) {
|
||||||
|
console.log('Using recent cached data');
|
||||||
|
return cachedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastError = null;
|
||||||
|
for (let attempt = 0; attempt < CONFIG.MAX_RETRIES; attempt++) {
|
||||||
|
try {
|
||||||
|
console.log(`Attempting to fetch fresh prices (attempt ${attempt + 1}/${CONFIG.MAX_RETRIES})`);
|
||||||
|
const prices = await this.makeRequest(
|
||||||
'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'
|
'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('Successfully fetched new prices, updating cache');
|
||||||
console.log('Caching fetched prices');
|
this.cache.set(prices);
|
||||||
api.cache.set(prices);
|
this.lastFetchTime = now;
|
||||||
return prices;
|
return prices;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching prices:', error);
|
lastError = error;
|
||||||
|
console.error(`Price fetch attempt ${attempt + 1} failed:`, error);
|
||||||
|
|
||||||
const cachedPrices = api.cache.get();
|
if (attempt < CONFIG.MAX_RETRIES - 1) {
|
||||||
if (cachedPrices) {
|
const delay = Math.min(CONFIG.BASE_DELAY * Math.pow(2, attempt), 10000);
|
||||||
console.log('Using cached prices');
|
console.log(`Waiting ${delay/1000} seconds before next retry...`);
|
||||||
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));
|
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) {
|
const cachedData = this.cache.get();
|
||||||
if (isUpdating) {
|
if (cachedData) {
|
||||||
console.log('Price update already in progress, skipping...');
|
console.log('Using cached data after all fetch attempts failed');
|
||||||
return;
|
return cachedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
console.error('All fetch attempts failed and no cache available');
|
||||||
console.log('Starting price update...');
|
throw lastError || new Error('Failed to fetch prices');
|
||||||
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();
|
class UiManager {
|
||||||
|
constructor() {
|
||||||
if (localStorage.getItem('balancesVisible') !== 'true') {
|
console.log('Initializing UI Manager');
|
||||||
console.log('Balances not visible, skipping update');
|
this.api = new ApiClient();
|
||||||
const existingPercentageChangeEl = document.querySelector('.percentage-change');
|
this.toggleInProgress = false;
|
||||||
if (existingPercentageChangeEl) {
|
this.toggleDebounceTimer = null;
|
||||||
console.log('Removing existing percentage change element');
|
this.priceUpdateInterval = null;
|
||||||
existingPercentageChangeEl.remove();
|
this.lastUpdateTime = parseInt(localStorage.getItem(STATE_KEYS.LAST_UPDATE) || '0');
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let total = 0;
|
getShortName(fullName) {
|
||||||
let hasMissingPrices = false;
|
return SHORT_NAMES[fullName] || fullName;
|
||||||
const updates = [];
|
}
|
||||||
|
|
||||||
console.log('Updating individual coin values...');
|
storeOriginalValues() {
|
||||||
|
console.log('Storing original coin values');
|
||||||
document.querySelectorAll('.coinname-value').forEach(el => {
|
document.querySelectorAll('.coinname-value').forEach(el => {
|
||||||
const coinName = el.getAttribute('data-coinname');
|
const coinName = el.getAttribute('data-coinname');
|
||||||
const amountStr = el.getAttribute('data-original-value');
|
const value = el.textContent?.trim() || '';
|
||||||
if (!amountStr) return;
|
|
||||||
|
|
||||||
const amount = parseFloat(amountStr.replace(/[^0-9.-]+/g, ''));
|
if (coinName) {
|
||||||
const coinId = coinNameToSymbol[coinName];
|
const amount = value ? parseFloat(value.replace(/[^0-9.-]+/g, '')) : 0;
|
||||||
const price = response?.[coinId]?.usd;
|
const coinId = COIN_SYMBOLS[coinName];
|
||||||
|
const shortName = this.getShortName(coinName);
|
||||||
|
|
||||||
let usdValue;
|
if (coinId) {
|
||||||
if (price && !isNaN(amount)) {
|
if (coinId === 'particl') {
|
||||||
usdValue = (amount * price).toFixed(2);
|
const isBlind = el.closest('.flex')?.querySelector('h4')?.textContent.includes('Blind');
|
||||||
total += parseFloat(usdValue);
|
const isAnon = el.closest('.flex')?.querySelector('h4')?.textContent.includes('Anon');
|
||||||
localStorage.setItem(`${coinId}-usd`, usdValue);
|
const balanceType = isBlind ? 'blind' : isAnon ? 'anon' : 'public';
|
||||||
|
localStorage.setItem(`particl-${balanceType}-amount`, amount.toString());
|
||||||
|
} else if (coinId === 'litecoin') {
|
||||||
|
const isMWEB = el.closest('.flex')?.querySelector('h4')?.textContent.includes('MWEB');
|
||||||
|
const balanceType = isMWEB ? 'mweb' : 'public';
|
||||||
|
localStorage.setItem(`litecoin-${balanceType}-amount`, amount.toString());
|
||||||
} else {
|
} else {
|
||||||
// Use cached price if available
|
localStorage.setItem(`${coinId}-amount`, amount.toString());
|
||||||
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');
|
el.setAttribute('data-original-value', `${amount} ${shortName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatePercentageChange(oldValue, newValue) {
|
||||||
|
if (!oldValue && !newValue) return 0;
|
||||||
|
|
||||||
|
oldValue = parseFloat(oldValue);
|
||||||
|
newValue = parseFloat(newValue);
|
||||||
|
|
||||||
|
if (isNaN(oldValue) || isNaN(newValue)) {
|
||||||
|
console.log('Invalid values for percentage calculation');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldValue === 0 && newValue === 0) return 0;
|
||||||
|
if (oldValue === 0) return 100;
|
||||||
|
|
||||||
|
const change = ((newValue - oldValue) / oldValue) * 100;
|
||||||
|
|
||||||
|
if (Math.abs(change) > CONFIG.MAX_PERCENTAGE_CHANGE) {
|
||||||
|
console.log(`Capping extreme percentage change: ${change}%`);
|
||||||
|
return change > 0 ? CONFIG.MAX_PERCENTAGE_CHANGE : -CONFIG.MAX_PERCENTAGE_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPercentageChangeDisplay(percentageChange) {
|
||||||
|
let icon, iconColor, text;
|
||||||
|
const textColor = 'white';
|
||||||
|
|
||||||
|
if (percentageChange > 0) {
|
||||||
|
icon = '▲';
|
||||||
|
iconColor = 'rgb(34, 197, 94)';
|
||||||
|
text = `${Math.abs(percentageChange).toFixed(2)}%`;
|
||||||
|
} else if (percentageChange < 0) {
|
||||||
|
icon = '▼';
|
||||||
|
iconColor = 'rgb(239, 68, 68)';
|
||||||
|
text = `${Math.abs(percentageChange).toFixed(2)}%`;
|
||||||
|
} else {
|
||||||
|
icon = '→';
|
||||||
|
iconColor = 'white';
|
||||||
|
text = `0.00%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
icon,
|
||||||
|
color: textColor,
|
||||||
|
text: `<span style="color: ${iconColor}">${icon}</span> ${text}`,
|
||||||
|
useHTML: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePrices(forceUpdate = false) {
|
||||||
|
console.log(`Starting price update (force update: ${forceUpdate})`);
|
||||||
|
try {
|
||||||
|
const prices = await this.api.fetchPrices(forceUpdate);
|
||||||
|
const previousTotal = parseFloat(localStorage.getItem(STATE_KEYS.PREVIOUS_TOTAL) || '0');
|
||||||
|
let newTotal = 0;
|
||||||
|
|
||||||
|
const currentTime = Date.now();
|
||||||
|
localStorage.setItem(STATE_KEYS.LAST_UPDATE, currentTime.toString());
|
||||||
|
this.lastUpdateTime = currentTime;
|
||||||
|
|
||||||
|
if (prices) {
|
||||||
|
Object.entries(COIN_SYMBOLS).forEach(([coinName, coinId]) => {
|
||||||
|
if (prices[coinId]?.usd) {
|
||||||
|
localStorage.setItem(`${coinId}-price`, prices[coinId].usd.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.coinname-value').forEach(el => {
|
||||||
|
const coinName = el.getAttribute('data-coinname');
|
||||||
|
const amountStr = el.getAttribute('data-original-value') || el.textContent?.trim() || '';
|
||||||
|
|
||||||
|
if (!coinName) return;
|
||||||
|
|
||||||
|
const amount = amountStr ? parseFloat(amountStr.replace(/[^0-9.-]+/g, '')) : 0;
|
||||||
|
const coinId = COIN_SYMBOLS[coinName];
|
||||||
|
if (!coinId) return;
|
||||||
|
|
||||||
|
const price = prices?.[coinId]?.usd || parseFloat(localStorage.getItem(`${coinId}-price`) || '0');
|
||||||
|
const usdValue = (amount * price).toFixed(2);
|
||||||
|
|
||||||
|
if (coinId === 'particl') {
|
||||||
|
const isBlind = el.closest('.flex')?.querySelector('h4')?.textContent.includes('Blind');
|
||||||
|
const isAnon = el.closest('.flex')?.querySelector('h4')?.textContent.includes('Anon');
|
||||||
|
const balanceType = isBlind ? 'blind' : isAnon ? 'anon' : 'public';
|
||||||
|
localStorage.setItem(`particl-${balanceType}-last-value`, usdValue);
|
||||||
|
localStorage.setItem(`particl-${balanceType}-amount`, amount.toString());
|
||||||
|
} else if (coinId === 'litecoin') {
|
||||||
|
const isMWEB = el.closest('.flex')?.querySelector('h4')?.textContent.includes('MWEB');
|
||||||
|
const balanceType = isMWEB ? 'mweb' : 'public';
|
||||||
|
localStorage.setItem(`litecoin-${balanceType}-last-value`, usdValue);
|
||||||
|
localStorage.setItem(`litecoin-${balanceType}-amount`, amount.toString());
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(`${coinId}-last-value`, usdValue);
|
||||||
|
localStorage.setItem(`${coinId}-amount`, amount.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
newTotal += parseFloat(usdValue);
|
||||||
|
|
||||||
|
const usdEl = el.closest('.flex')?.nextElementSibling?.querySelector('.usd-value');
|
||||||
if (usdEl) {
|
if (usdEl) {
|
||||||
updates.push([usdEl, usdValue]);
|
usdEl.textContent = `$${usdValue} USD`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Updating total USD and BTC values...');
|
const percentageChange = this.calculatePercentageChange(previousTotal, newTotal);
|
||||||
updates.forEach(([el, value]) => {
|
this.updateTotalValues(newTotal, prices?.bitcoin?.usd, percentageChange);
|
||||||
el.textContent = value;
|
|
||||||
el.setAttribute('data-original-value', value);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
localStorage.setItem(STATE_KEYS.PREVIOUS_TOTAL, localStorage.getItem(STATE_KEYS.CURRENT_TOTAL) || '0');
|
||||||
|
localStorage.setItem(STATE_KEYS.CURRENT_TOTAL, newTotal.toString());
|
||||||
|
localStorage.setItem(STATE_KEYS.PERCENTAGE_CHANGE, percentageChange.toString());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Price update failed:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTotalValues(totalUsd, btcPrice, percentageChange) {
|
||||||
const totalUsdEl = document.getElementById('total-usd-value');
|
const totalUsdEl = document.getElementById('total-usd-value');
|
||||||
if (totalUsdEl) {
|
if (totalUsdEl) {
|
||||||
const totalText = `$${total.toFixed(2)}`;
|
totalUsdEl.textContent = `$${totalUsd.toFixed(2)}`;
|
||||||
totalUsdEl.textContent = totalText;
|
totalUsdEl.setAttribute('data-original-value', totalUsd.toString());
|
||||||
totalUsdEl.setAttribute('data-original-value', totalText);
|
localStorage.setItem('total-usd', totalUsd.toString());
|
||||||
localStorage.setItem('total-usd', total);
|
|
||||||
} else {
|
|
||||||
console.log('Total USD element not found, skipping total USD update');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const btcPrice = response?.bitcoin?.usd;
|
|
||||||
if (btcPrice) {
|
if (btcPrice) {
|
||||||
const btcTotal = total / btcPrice;
|
const btcTotal = btcPrice ? totalUsd / btcPrice : 0;
|
||||||
const totalBtcEl = document.getElementById('total-btc-value');
|
const totalBtcEl = document.getElementById('total-btc-value');
|
||||||
if (totalBtcEl) {
|
if (totalBtcEl) {
|
||||||
const btcText = `~ ${btcTotal.toFixed(8)} BTC`;
|
totalBtcEl.textContent = `~ ${btcTotal.toFixed(8)} BTC`;
|
||||||
totalBtcEl.textContent = btcText;
|
totalBtcEl.setAttribute('data-original-value', btcTotal.toString());
|
||||||
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');
|
let percentageChangeEl = document.querySelector('.percentage-change');
|
||||||
if (!percentageChangeEl) {
|
if (!percentageChangeEl) {
|
||||||
console.log('Creating percentage change elements...');
|
percentageChangeEl = this.createPercentageChangeElement();
|
||||||
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;
|
if (percentageChangeEl) {
|
||||||
let percentageChangeIcon = '→';
|
const { color, text, useHTML } = this.getPercentageChangeDisplay(percentageChange);
|
||||||
let currentPercentageChangeColor = 'white';
|
if (useHTML) {
|
||||||
|
percentageChangeEl.innerHTML = text;
|
||||||
|
}
|
||||||
|
percentageChangeEl.style.color = color;
|
||||||
|
percentageChangeEl.style.display = 'inline';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
percentageChangeEl.textContent = `${percentageChangeIcon} 0.00%`;
|
async toggleBalances() {
|
||||||
percentageChangeEl.style.color = currentPercentageChangeColor;
|
if (this.toggleInProgress) return;
|
||||||
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');
|
try {
|
||||||
return !hasMissingPrices;
|
this.toggleInProgress = true;
|
||||||
|
const balancesVisible = localStorage.getItem('balancesVisible') === 'true';
|
||||||
|
const newVisibility = !balancesVisible;
|
||||||
|
|
||||||
|
localStorage.setItem('balancesVisible', newVisibility.toString());
|
||||||
|
this.updateVisibility(newVisibility);
|
||||||
|
|
||||||
|
if (this.toggleDebounceTimer) {
|
||||||
|
clearTimeout(this.toggleDebounceTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toggleDebounceTimer = window.setTimeout(async () => {
|
||||||
|
this.toggleInProgress = false;
|
||||||
|
if (newVisibility) {
|
||||||
|
await this.updatePrices(true);
|
||||||
|
}
|
||||||
|
}, CONFIG.DEBOUNCE_DELAY);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Price update failed:', error);
|
console.error('Failed to toggle balances:', error);
|
||||||
// Only clear price-related data from localStorage
|
this.toggleInProgress = false;
|
||||||
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() {
|
updateVisibility(isVisible) {
|
||||||
console.log('Storing original coin values...');
|
if (isVisible) {
|
||||||
document.querySelectorAll('.coinname-value').forEach(el => {
|
this.showBalances();
|
||||||
if (!el.getAttribute('data-original-value')) {
|
} else {
|
||||||
el.setAttribute('data-original-value', el.textContent.trim());
|
this.hideBalances();
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleIcon = (isVisible) => {
|
|
||||||
const eyeIcon = document.querySelector("#hide-usd-amount-toggle svg");
|
const eyeIcon = document.querySelector("#hide-usd-amount-toggle svg");
|
||||||
if (eyeIcon) {
|
if (eyeIcon) {
|
||||||
console.log('Toggling eye icon visibility:', isVisible);
|
eyeIcon.innerHTML = isVisible ?
|
||||||
if (isVisible) {
|
'<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>' :
|
||||||
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>';
|
||||||
<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();
|
|
||||||
|
|
||||||
|
showBalances() {
|
||||||
const usdText = document.getElementById('usd-text');
|
const usdText = document.getElementById('usd-text');
|
||||||
const totalUsdEl = document.getElementById('total-usd-value');
|
|
||||||
|
|
||||||
if (usdText) {
|
if (usdText) {
|
||||||
console.log('Updating USD text visibility:', isVisible);
|
usdText.style.display = 'inline';
|
||||||
usdText.style.display = isVisible ? 'inline' : 'none';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVisible) {
|
|
||||||
console.log('Restoring coin amounts...');
|
|
||||||
document.querySelectorAll('.coinname-value').forEach(el => {
|
document.querySelectorAll('.coinname-value').forEach(el => {
|
||||||
const originalValue = el.getAttribute('data-original-value');
|
const originalValue = el.getAttribute('data-original-value');
|
||||||
if (originalValue) {
|
if (originalValue) {
|
||||||
|
@ -556,114 +660,156 @@ const toggleBalances = async (isVisible) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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 => {
|
document.querySelectorAll('.usd-value').forEach(el => {
|
||||||
const storedValue = el.getAttribute('data-original-value');
|
const storedValue = el.getAttribute('data-original-value');
|
||||||
el.textContent = storedValue || '****';
|
if (storedValue) {
|
||||||
|
el.textContent = `($${parseFloat(storedValue).toFixed(2)} USD)`;
|
||||||
|
el.style.color = 'white';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
['total-usd-value', 'total-btc-value'].forEach(id => {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
const originalValue = el?.getAttribute('data-original-value');
|
||||||
|
if (el && originalValue) {
|
||||||
|
if (id === 'total-usd-value') {
|
||||||
|
el.textContent = `$${parseFloat(originalValue).toFixed(2)}`;
|
||||||
|
el.classList.add('font-extrabold');
|
||||||
|
} else {
|
||||||
|
el.textContent = `~ ${parseFloat(originalValue).toFixed(8)} BTC`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const percentageChangeEl = document.querySelector('.percentage-change');
|
||||||
|
if (percentageChangeEl) {
|
||||||
|
const storedPercentage = parseFloat(localStorage.getItem(STATE_KEYS.PERCENTAGE_CHANGE) || '0');
|
||||||
|
const { text } = this.getPercentageChangeDisplay(storedPercentage);
|
||||||
|
percentageChangeEl.innerHTML = text;
|
||||||
|
percentageChangeEl.style.display = 'inline';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hideBalances() {
|
||||||
|
console.log('Hiding balances');
|
||||||
|
const usdText = document.getElementById('usd-text');
|
||||||
|
if (usdText) {
|
||||||
|
usdText.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.coinname-value, .usd-value').forEach(el => {
|
||||||
|
el.textContent = '****';
|
||||||
});
|
});
|
||||||
|
|
||||||
['total-usd-value', 'total-btc-value'].forEach(id => {
|
['total-usd-value', 'total-btc-value'].forEach(id => {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) {
|
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 = '****';
|
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');
|
const percentageChangeEl = document.querySelector('.percentage-change');
|
||||||
if (percentageChangeEl) {
|
if (percentageChangeEl) {
|
||||||
console.log('Hiding percentage change element');
|
percentageChangeEl.textContent = '****';
|
||||||
percentageChangeEl.style.display = 'none';
|
percentageChangeEl.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const totalUsdEl = document.getElementById('total-usd-value');
|
||||||
if (totalUsdEl) {
|
if (totalUsdEl) {
|
||||||
totalUsdEl.classList.remove('font-extrabold');
|
totalUsdEl.classList.remove('font-extrabold');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const toggleBalancesDebounced = () => {
|
createPercentageChangeElement() {
|
||||||
if (toggleBalancesDebounce) {
|
const totalUsdEl = document.getElementById('total-usd-value');
|
||||||
clearTimeout(toggleBalancesDebounce);
|
if (totalUsdEl) {
|
||||||
|
const percentageChangeEl = document.createElement('span');
|
||||||
|
percentageChangeEl.className = 'ml-2 text-base percentage-change cursor-help';
|
||||||
|
totalUsdEl.parentNode.appendChild(percentageChangeEl);
|
||||||
|
|
||||||
|
const tooltipId = 'tooltip-percentage';
|
||||||
|
percentageChangeEl.setAttribute('data-tooltip-target', tooltipId);
|
||||||
|
|
||||||
|
if (!document.getElementById(tooltipId)) {
|
||||||
|
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 since last update<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);
|
||||||
|
|
||||||
|
if (typeof Tooltip !== 'undefined') {
|
||||||
|
new Tooltip(tooltip, percentageChangeEl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleBalancesDebounce = setTimeout(() => {
|
return percentageChangeEl;
|
||||||
toggleInProgress = false;
|
}
|
||||||
toggleBalances(localStorage.getItem('balancesVisible') === 'true');
|
return null;
|
||||||
}, 500);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const loadBalanceVisibility = async () => {
|
async initialize() {
|
||||||
console.log('Loading balance visibility...');
|
console.log('Initializing UI Manager...');
|
||||||
const balancesVisible = localStorage.getItem('balancesVisible') === 'true';
|
this.storeOriginalValues();
|
||||||
toggleIcon(balancesVisible);
|
|
||||||
await toggleBalancesDebounced();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onload = async () => {
|
|
||||||
console.log('Window loaded, initializing price visualization...');
|
|
||||||
storeOriginalValues();
|
|
||||||
|
|
||||||
if (localStorage.getItem('balancesVisible') === null) {
|
if (localStorage.getItem('balancesVisible') === null) {
|
||||||
console.log('Balances visibility not set, setting to true');
|
|
||||||
localStorage.setItem('balancesVisible', 'true');
|
localStorage.setItem('balancesVisible', 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideBalancesToggle = document.getElementById('hide-usd-amount-toggle');
|
const hideBalancesToggle = document.getElementById('hide-usd-amount-toggle');
|
||||||
hideBalancesToggle?.addEventListener('click', async () => {
|
if (hideBalancesToggle) {
|
||||||
if (toggleInProgress) {
|
hideBalancesToggle.addEventListener('click', () => this.toggleBalances());
|
||||||
console.log('Toggle already in progress, skipping...');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
await this.loadBalanceVisibility();
|
||||||
toggleInProgress = true;
|
|
||||||
console.log('Toggling balance visibility...');
|
|
||||||
const balancesVisible = localStorage.getItem('balancesVisible') === 'true';
|
|
||||||
const newVisibility = !balancesVisible;
|
|
||||||
|
|
||||||
localStorage.setItem('balancesVisible', newVisibility);
|
if (this.priceUpdateInterval) {
|
||||||
toggleIcon(newVisibility);
|
clearInterval(this.priceUpdateInterval);
|
||||||
await toggleBalancesDebounced();
|
}
|
||||||
} finally {
|
|
||||||
toggleInProgress = false;
|
this.priceUpdateInterval = setInterval(() => {
|
||||||
|
if (localStorage.getItem('balancesVisible') === 'true' && !this.toggleInProgress) {
|
||||||
|
this.updatePrices(false);
|
||||||
|
}
|
||||||
|
}, CONFIG.PRICE_UPDATE_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadBalanceVisibility() {
|
||||||
|
const balancesVisible = localStorage.getItem('balancesVisible') === 'true';
|
||||||
|
this.updateVisibility(balancesVisible);
|
||||||
|
|
||||||
|
if (balancesVisible) {
|
||||||
|
await this.updatePrices(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
console.log('Page unloading, cleaning up...');
|
||||||
|
const uiManager = window.uiManager;
|
||||||
|
if (uiManager?.priceUpdateInterval) {
|
||||||
|
clearInterval(uiManager.priceUpdateInterval);
|
||||||
|
console.log('Cleared price update interval');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await loadBalanceVisibility();
|
// Initialize
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
console.log('Page loaded, starting application initialization');
|
||||||
|
const uiManager = new UiManager();
|
||||||
|
window.uiManager = uiManager; // Store reference for cleanup
|
||||||
|
uiManager.initialize().catch(error => {
|
||||||
|
console.error('Failed to initialize application:', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
console.log(`Setting up periodic price updates every ${PRICE_UPDATE_INTERVAL / 1000} seconds...`);
|
console.log('Loaded..');
|
||||||
setInterval(async () => {
|
|
||||||
if (localStorage.getItem('balancesVisible') === 'true' && !toggleInProgress && !isUpdating) {
|
|
||||||
console.log('Running periodic price update...');
|
|
||||||
await updatePrices();
|
|
||||||
}
|
|
||||||
}, PRICE_UPDATE_INTERVAL);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue