mirror of
https://github.com/basicswap/basicswap.git
synced 2025-01-22 10:34:34 +00:00
Merge pull request #219 from gerlofvanek/memory
Some checks are pending
ci / ci (3.12) (push) Waiting to run
Some checks are pending
ci / ci (3.12) (push) Waiting to run
Fix potential sources of mem leaks + Various related fixes.
This commit is contained in:
commit
e548cf2b3b
3 changed files with 1215 additions and 718 deletions
File diff suppressed because it is too large
Load diff
|
@ -487,6 +487,13 @@ const chartModule = {
|
|||
chart: null,
|
||||
currentCoin: 'BTC',
|
||||
loadStartTime: 0,
|
||||
|
||||
cleanup: () => {
|
||||
if (chartModule.chart) {
|
||||
chartModule.chart.destroy();
|
||||
chartModule.chart = null;
|
||||
}
|
||||
},
|
||||
verticalLinePlugin: {
|
||||
id: 'verticalLine',
|
||||
beforeDraw: (chart, args, options) => {
|
||||
|
@ -509,7 +516,7 @@ const chartModule = {
|
|||
},
|
||||
|
||||
initChart: () => {
|
||||
const ctx = document.getElementById('coin-chart').getContext('2d');
|
||||
const ctx = document.getElementById('coin-chart')?.getContext('2d');
|
||||
if (!ctx) {
|
||||
logger.error('Failed to get chart context. Make sure the canvas element exists.');
|
||||
return;
|
||||
|
@ -568,7 +575,6 @@ const chartModule = {
|
|||
callback: function(value) {
|
||||
const date = new Date(value);
|
||||
if (config.currentResolution === 'day') {
|
||||
// Convert to AM/PM format
|
||||
return date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
|
@ -668,13 +674,10 @@ const chartModule = {
|
|||
},
|
||||
plugins: [chartModule.verticalLinePlugin]
|
||||
});
|
||||
|
||||
//console.log('Chart initialized:', chartModule.chart);
|
||||
},
|
||||
|
||||
prepareChartData: (coinSymbol, data) => {
|
||||
if (!data) {
|
||||
//console.error(`No data received for ${coinSymbol}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -733,7 +736,6 @@ const chartModule = {
|
|||
y: price
|
||||
}));
|
||||
} else {
|
||||
//console.error(`Unexpected data structure for ${coinSymbol}:`, data);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -742,7 +744,6 @@ const chartModule = {
|
|||
y: point.y
|
||||
}));
|
||||
} catch (error) {
|
||||
//console.error(`Error preparing chart data for ${coinSymbol}:`, error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
@ -780,21 +781,17 @@ const chartModule = {
|
|||
|
||||
if (cachedData && Object.keys(cachedData.value).length > 0) {
|
||||
data = cachedData.value;
|
||||
//console.log(`Using cached data for ${coinSymbol} (${config.currentResolution})`);
|
||||
} else {
|
||||
//console.log(`Fetching fresh data for ${coinSymbol} (${config.currentResolution})`);
|
||||
const allData = await api.fetchHistoricalDataXHR([coinSymbol]);
|
||||
data = allData[coinSymbol];
|
||||
if (!data || Object.keys(data).length === 0) {
|
||||
throw new Error(`No data returned for ${coinSymbol}`);
|
||||
}
|
||||
//console.log(`Caching new data for ${cacheKey}`);
|
||||
cache.set(cacheKey, data, config.cacheTTL);
|
||||
cachedData = null;
|
||||
}
|
||||
|
||||
const chartData = chartModule.prepareChartData(coinSymbol, data);
|
||||
//console.log(`Prepared chart data for ${coinSymbol}:`, chartData.slice(0, 5));
|
||||
|
||||
if (chartData.length === 0) {
|
||||
throw new Error(`No valid chart data for ${coinSymbol}`);
|
||||
|
@ -826,7 +823,6 @@ const chartModule = {
|
|||
|
||||
chartModule.chart.update('active');
|
||||
} else {
|
||||
//console.error('Chart object not initialized');
|
||||
throw new Error('Chart object not initialized');
|
||||
}
|
||||
|
||||
|
@ -835,7 +831,6 @@ const chartModule = {
|
|||
ui.updateLoadTimeAndCache(loadTime, cachedData);
|
||||
|
||||
} catch (error) {
|
||||
//console.error(`Error updating chart for ${coinSymbol}:`, error);
|
||||
ui.displayErrorMessage(`Failed to update chart for ${coinSymbol}: ${error.message}`);
|
||||
} finally {
|
||||
chartModule.hideChartLoader();
|
||||
|
@ -847,7 +842,6 @@ const chartModule = {
|
|||
const chart = document.getElementById('coin-chart');
|
||||
|
||||
if (!loader || !chart) {
|
||||
//console.warn('Chart loader or chart container elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -860,49 +854,61 @@ const chartModule = {
|
|||
const chart = document.getElementById('coin-chart');
|
||||
|
||||
if (!loader || !chart) {
|
||||
//console.warn('Chart loader or chart container elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
loader.classList.add('hidden');
|
||||
chart.classList.remove('hidden');
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
Chart.register(chartModule.verticalLinePlugin);
|
||||
|
||||
const volumeToggle = {
|
||||
const volumeToggle = {
|
||||
isVisible: localStorage.getItem('volumeToggleState') === 'true',
|
||||
|
||||
cleanup: () => {
|
||||
const toggleButton = document.getElementById('toggle-volume');
|
||||
if (toggleButton) {
|
||||
toggleButton.removeEventListener('click', volumeToggle.toggle);
|
||||
}
|
||||
},
|
||||
|
||||
init: () => {
|
||||
volumeToggle.cleanup();
|
||||
|
||||
const toggleButton = document.getElementById('toggle-volume');
|
||||
if (toggleButton) {
|
||||
toggleButton.addEventListener('click', volumeToggle.toggle);
|
||||
volumeToggle.updateVolumeDisplay();
|
||||
}
|
||||
},
|
||||
|
||||
toggle: () => {
|
||||
volumeToggle.isVisible = !volumeToggle.isVisible;
|
||||
localStorage.setItem('volumeToggleState', volumeToggle.isVisible.toString());
|
||||
volumeToggle.updateVolumeDisplay();
|
||||
},
|
||||
|
||||
updateVolumeDisplay: () => {
|
||||
const volumeDivs = document.querySelectorAll('[id$="-volume-div"]');
|
||||
volumeDivs.forEach(div => {
|
||||
div.style.display = volumeToggle.isVisible ? 'flex' : 'none';
|
||||
});
|
||||
|
||||
const toggleButton = document.getElementById('toggle-volume');
|
||||
if (toggleButton) {
|
||||
updateButtonStyles(toggleButton, volumeToggle.isVisible, 'green');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function updateButtonStyles(button, isActive, color) {
|
||||
function updateButtonStyles(button, isActive, color) {
|
||||
button.classList.toggle('text-' + color + '-500', isActive);
|
||||
button.classList.toggle('text-gray-600', !isActive);
|
||||
button.classList.toggle('dark:text-' + color + '-400', isActive);
|
||||
button.classList.toggle('dark:text-gray-400', !isActive);
|
||||
}
|
||||
}
|
||||
|
||||
const app = {
|
||||
btcPriceUSD: 0,
|
||||
|
@ -918,12 +924,109 @@ const app = {
|
|||
},
|
||||
cacheTTL: 5 * 60 * 1000, // 5 minutes
|
||||
minimumRefreshInterval: 60 * 1000, // 1 minute
|
||||
eventListeners: new Map(),
|
||||
visibilityCleanup: null,
|
||||
|
||||
cleanup: () => {
|
||||
if (app.autoRefreshInterval) {
|
||||
clearTimeout(app.autoRefreshInterval);
|
||||
app.autoRefreshInterval = null;
|
||||
}
|
||||
|
||||
if (app.updateNextRefreshTimeRAF) {
|
||||
cancelAnimationFrame(app.updateNextRefreshTimeRAF);
|
||||
app.updateNextRefreshTimeRAF = null;
|
||||
}
|
||||
|
||||
if (typeof app.visibilityCleanup === 'function') {
|
||||
app.visibilityCleanup();
|
||||
app.visibilityCleanup = null;
|
||||
}
|
||||
|
||||
volumeToggle.cleanup();
|
||||
|
||||
app.removeEventListeners();
|
||||
|
||||
if (chartModule.chart) {
|
||||
chartModule.chart.destroy();
|
||||
chartModule.chart = null;
|
||||
}
|
||||
|
||||
cache.clear();
|
||||
},
|
||||
|
||||
removeEventListeners: () => {
|
||||
app.eventListeners.forEach((listener, element) => {
|
||||
if (element && typeof element.removeEventListener === 'function') {
|
||||
element.removeEventListener(listener.type, listener.fn);
|
||||
}
|
||||
});
|
||||
app.eventListeners.clear();
|
||||
},
|
||||
|
||||
addEventListenerWithCleanup: (element, type, fn) => {
|
||||
if (element && typeof element.addEventListener === 'function') {
|
||||
element.addEventListener(type, fn);
|
||||
app.eventListeners.set(element, { type, fn });
|
||||
}
|
||||
},
|
||||
|
||||
initResolutionButtons: () => {
|
||||
const resolutionButtons = document.querySelectorAll('.resolution-button');
|
||||
resolutionButtons.forEach(button => {
|
||||
// Remove existing listeners first
|
||||
const oldListener = button.getAttribute('data-resolution-listener');
|
||||
if (oldListener && window[oldListener]) {
|
||||
button.removeEventListener('click', window[oldListener]);
|
||||
delete window[oldListener];
|
||||
}
|
||||
|
||||
const listener = () => {
|
||||
const resolution = button.id.split('-')[1];
|
||||
const currentCoin = chartModule.currentCoin;
|
||||
|
||||
if (currentCoin !== 'WOW' || resolution === 'day') {
|
||||
config.currentResolution = resolution;
|
||||
chartModule.updateChart(currentCoin, true);
|
||||
app.updateResolutionButtons(currentCoin);
|
||||
}
|
||||
};
|
||||
|
||||
const listenerName = `resolutionListener_${button.id}`;
|
||||
window[listenerName] = listener;
|
||||
button.setAttribute('data-resolution-listener', listenerName);
|
||||
button.addEventListener('click', listener);
|
||||
});
|
||||
},
|
||||
|
||||
setupVisibilityHandler: () => {
|
||||
const cleanup = () => {
|
||||
if (window.visibilityHandler) {
|
||||
document.removeEventListener('visibilitychange', window.visibilityHandler);
|
||||
delete window.visibilityHandler;
|
||||
}
|
||||
};
|
||||
|
||||
cleanup();
|
||||
|
||||
window.visibilityHandler = () => {
|
||||
if (!document.hidden && chartModule.chart) {
|
||||
chartModule.updateChart(chartModule.currentCoin, true);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', window.visibilityHandler);
|
||||
return cleanup;
|
||||
},
|
||||
|
||||
init: () => {
|
||||
console.log('Initializing app...');
|
||||
app.cleanup();
|
||||
window.addEventListener('load', app.onLoad);
|
||||
app.loadLastRefreshedTime();
|
||||
app.updateAutoRefreshButton();
|
||||
app.initResolutionButtons();
|
||||
app.setupVisibilityHandler();
|
||||
console.log('App initialized');
|
||||
},
|
||||
|
||||
|
@ -937,8 +1040,6 @@ const app = {
|
|||
if (chartContainer) {
|
||||
chartModule.initChart();
|
||||
chartModule.showChartLoader();
|
||||
} else {
|
||||
//console.warn('Chart container not found, skipping chart initialization');
|
||||
}
|
||||
|
||||
console.log('Loading all coin data...');
|
||||
|
@ -951,13 +1052,11 @@ const app = {
|
|||
}
|
||||
ui.setActiveContainer('btc-container');
|
||||
|
||||
//console.log('Setting up event listeners and initializations...');
|
||||
app.setupEventListeners();
|
||||
app.initializeSelectImages();
|
||||
app.initAutoRefresh();
|
||||
|
||||
} catch (error) {
|
||||
//console.error('Error during initialization:', error);
|
||||
ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.');
|
||||
} finally {
|
||||
ui.hideLoader();
|
||||
|
@ -966,10 +1065,9 @@ const app = {
|
|||
}
|
||||
console.log('App onLoad completed');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
loadAllCoinData: async () => {
|
||||
//console.log('Loading data for all coins...');
|
||||
try {
|
||||
const allCoinData = await api.fetchCoinGeckoDataXHR();
|
||||
if (allCoinData.error) {
|
||||
|
@ -983,25 +1081,18 @@ const app = {
|
|||
ui.displayCoinData(coin.symbol, coinData);
|
||||
const cacheKey = `coinData_${coin.symbol}`;
|
||||
cache.set(cacheKey, coinData);
|
||||
} else {
|
||||
//console.warn(`No data found for ${coin.symbol}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//console.error('Error loading all coin data:', error);
|
||||
ui.displayErrorMessage('Failed to load coin data. Please try refreshing the page.');
|
||||
} finally {
|
||||
//console.log('All coin data loaded');
|
||||
}
|
||||
},
|
||||
|
||||
loadCoinData: async (coin) => {
|
||||
//console.log(`Loading data for ${coin.symbol}...`);
|
||||
const cacheKey = `coinData_${coin.symbol}`;
|
||||
let cachedData = cache.get(cacheKey);
|
||||
let data;
|
||||
if (cachedData) {
|
||||
//console.log(`Using cached data for ${coin.symbol}`);
|
||||
data = cachedData.value;
|
||||
} else {
|
||||
try {
|
||||
|
@ -1014,11 +1105,9 @@ const app = {
|
|||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
//console.log(`Caching new data for ${coin.symbol}`);
|
||||
cache.set(cacheKey, data);
|
||||
cachedData = null;
|
||||
} catch (error) {
|
||||
//console.error(`Error fetching ${coin.symbol} data:`, error.message);
|
||||
data = {
|
||||
error: error.message
|
||||
};
|
||||
|
@ -1028,16 +1117,13 @@ const app = {
|
|||
}
|
||||
ui.displayCoinData(coin.symbol, data);
|
||||
ui.updateLoadTimeAndCache(0, cachedData);
|
||||
//console.log(`Data loaded for ${coin.symbol}`);
|
||||
},
|
||||
|
||||
setupEventListeners: () => {
|
||||
//console.log('Setting up event listeners...');
|
||||
config.coins.forEach(coin => {
|
||||
const container = document.getElementById(`${coin.symbol.toLowerCase()}-container`);
|
||||
if (container) {
|
||||
container.addEventListener('click', () => {
|
||||
//console.log(`${coin.symbol} container clicked`);
|
||||
app.addEventListenerWithCleanup(container, 'click', () => {
|
||||
ui.setActiveContainer(`${coin.symbol.toLowerCase()}-container`);
|
||||
if (chartModule.chart) {
|
||||
if (coin.symbol === 'WOW') {
|
||||
|
@ -1052,26 +1138,27 @@ const app = {
|
|||
|
||||
const refreshAllButton = document.getElementById('refresh-all');
|
||||
if (refreshAllButton) {
|
||||
refreshAllButton.addEventListener('click', app.refreshAllData);
|
||||
app.addEventListenerWithCleanup(refreshAllButton, 'click', app.refreshAllData);
|
||||
}
|
||||
|
||||
const headers = document.querySelectorAll('th');
|
||||
headers.forEach((header, index) => {
|
||||
header.addEventListener('click', () => app.sortTable(index, header.classList.contains('disabled')));
|
||||
app.addEventListenerWithCleanup(header, 'click', () =>
|
||||
app.sortTable(index, header.classList.contains('disabled'))
|
||||
);
|
||||
});
|
||||
|
||||
const closeErrorButton = document.getElementById('close-error');
|
||||
if (closeErrorButton) {
|
||||
closeErrorButton.addEventListener('click', ui.hideErrorMessage);
|
||||
app.addEventListenerWithCleanup(closeErrorButton, 'click', ui.hideErrorMessage);
|
||||
}
|
||||
//console.log('Event listeners set up');
|
||||
},
|
||||
|
||||
initAutoRefresh: () => {
|
||||
console.log('Initializing auto-refresh...');
|
||||
const toggleAutoRefreshButton = document.getElementById('toggle-auto-refresh');
|
||||
if (toggleAutoRefreshButton) {
|
||||
toggleAutoRefreshButton.addEventListener('click', app.toggleAutoRefresh);
|
||||
app.addEventListenerWithCleanup(toggleAutoRefreshButton, 'click', app.toggleAutoRefresh);
|
||||
app.updateAutoRefreshButton();
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1187,6 @@ const app = {
|
|||
earliestExpiration = Math.min(earliestExpiration, cachedItem.expiresAt);
|
||||
}
|
||||
} catch (error) {
|
||||
//console.error(`Error parsing cached item ${key}:`, error);
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
@ -1138,9 +1224,7 @@ const app = {
|
|||
chartModule.showChartLoader();
|
||||
|
||||
try {
|
||||
|
||||
cache.clear();
|
||||
|
||||
await app.updateBTCPrice();
|
||||
|
||||
const allCoinData = await api.fetchCoinGeckoDataXHR();
|
||||
|
@ -1153,13 +1237,9 @@ const app = {
|
|||
const coinData = allCoinData[symbol];
|
||||
if (coinData) {
|
||||
coinData.displayName = coin.displayName || coin.symbol;
|
||||
|
||||
ui.displayCoinData(coin.symbol, coinData);
|
||||
|
||||
const cacheKey = `coinData_${coin.symbol}`;
|
||||
cache.set(cacheKey, coinData);
|
||||
} else {
|
||||
//console.error(`No data found for ${coin.symbol}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1171,10 +1251,7 @@ const app = {
|
|||
localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString());
|
||||
ui.updateLastRefreshedTime();
|
||||
|
||||
console.log('All data refreshed successfully');
|
||||
|
||||
} catch (error) {
|
||||
//console.error('Error refreshing all data:', error);
|
||||
ui.displayErrorMessage('Failed to refresh all data. Please try again.');
|
||||
} finally {
|
||||
ui.hideLoader();
|
||||
|
@ -1215,6 +1292,7 @@ const app = {
|
|||
app.updateNextRefreshTimeRAF = requestAnimationFrame(updateDisplay);
|
||||
}
|
||||
};
|
||||
|
||||
updateDisplay();
|
||||
} else {
|
||||
labelElement.textContent = '';
|
||||
|
@ -1241,7 +1319,6 @@ const app = {
|
|||
},
|
||||
|
||||
startSpinAnimation: () => {
|
||||
//console.log('Starting spin animation on auto-refresh button');
|
||||
const svg = document.querySelector('#toggle-auto-refresh svg');
|
||||
if (svg) {
|
||||
svg.classList.add('animate-spin');
|
||||
|
@ -1252,7 +1329,6 @@ const app = {
|
|||
},
|
||||
|
||||
stopSpinAnimation: () => {
|
||||
//console.log('Stopping spin animation on auto-refresh button');
|
||||
const svg = document.querySelector('#toggle-auto-refresh svg');
|
||||
if (svg) {
|
||||
svg.classList.remove('animate-spin');
|
||||
|
@ -1260,7 +1336,6 @@ const app = {
|
|||
},
|
||||
|
||||
updateLastRefreshedTime: () => {
|
||||
//console.log('Updating last refreshed time');
|
||||
const lastRefreshedElement = document.getElementById('last-refreshed-time');
|
||||
if (lastRefreshedElement && app.lastRefreshedTime) {
|
||||
const formattedTime = app.lastRefreshedTime.toLocaleTimeString();
|
||||
|
@ -1278,45 +1353,37 @@ const app = {
|
|||
},
|
||||
|
||||
updateBTCPrice: async () => {
|
||||
//console.log('Updating BTC price...');
|
||||
try {
|
||||
const priceData = await api.fetchCoinGeckoDataXHR();
|
||||
if (priceData.error) {
|
||||
//console.error('Error fetching BTC price:', priceData.error);
|
||||
app.btcPriceUSD = 0;
|
||||
} else if (priceData.btc && priceData.btc.current_price) {
|
||||
|
||||
app.btcPriceUSD = priceData.btc.current_price;
|
||||
} else {
|
||||
//console.error('Unexpected BTC data structure:', priceData);
|
||||
app.btcPriceUSD = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
//console.error('Error fetching BTC price:', error);
|
||||
app.btcPriceUSD = 0;
|
||||
}
|
||||
//console.log('Current BTC price:', app.btcPriceUSD);
|
||||
},
|
||||
|
||||
sortTable: (columnIndex) => {
|
||||
//console.log(`Sorting column: ${columnIndex}`);
|
||||
sortTable: (columnIndex) => {
|
||||
const sortableColumns = [0, 5, 6, 7]; // 0: Time, 5: Rate, 6: Market +/-, 7: Trade
|
||||
if (!sortableColumns.includes(columnIndex)) {
|
||||
//console.log(`Column ${columnIndex} is not sortable`);
|
||||
return;
|
||||
}
|
||||
|
||||
const table = document.querySelector('table');
|
||||
if (!table) {
|
||||
//console.error("Table not found for sorting.");
|
||||
return;
|
||||
}
|
||||
|
||||
const rows = Array.from(table.querySelectorAll('tbody tr'));
|
||||
console.log(`Found ${rows.length} rows to sort`);
|
||||
const sortIcon = document.getElementById(`sort-icon-${columnIndex}`);
|
||||
if (!sortIcon) {
|
||||
//console.error("Sort icon not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
const sortOrder = sortIcon.textContent === '↓' ? 1 : -1;
|
||||
sortIcon.textContent = sortOrder === 1 ? '↑' : '↓';
|
||||
|
||||
|
@ -1328,7 +1395,6 @@ sortTable: (columnIndex) => {
|
|||
case 1: // Time column
|
||||
aValue = getSafeTextContent(a.querySelector('td:first-child .text-xs:first-child'));
|
||||
bValue = getSafeTextContent(b.querySelector('td:first-child .text-xs:first-child'));
|
||||
//console.log(`Comparing times: "${aValue}" vs "${bValue}"`);
|
||||
|
||||
const parseTime = (timeStr) => {
|
||||
const [value, unit] = timeStr.split(' ');
|
||||
|
@ -1347,7 +1413,6 @@ sortTable: (columnIndex) => {
|
|||
case 6: // Market +/-
|
||||
aValue = getSafeTextContent(a.cells[columnIndex]);
|
||||
bValue = getSafeTextContent(b.cells[columnIndex]);
|
||||
//console.log(`Comparing values: "${aValue}" vs "${bValue}"`);
|
||||
|
||||
aValue = parseFloat(aValue.replace(/[^\d.-]/g, '') || '0');
|
||||
bValue = parseFloat(bValue.replace(/[^\d.-]/g, '') || '0');
|
||||
|
@ -1356,8 +1421,6 @@ sortTable: (columnIndex) => {
|
|||
case 7: // Trade
|
||||
const aCell = a.cells[columnIndex];
|
||||
const bCell = b.cells[columnIndex];
|
||||
//console.log('aCell:', aCell ? aCell.outerHTML : 'null');
|
||||
//console.log('bCell:', bCell ? bCell.outerHTML : 'null');
|
||||
|
||||
aValue = getSafeTextContent(aCell.querySelector('a')) ||
|
||||
getSafeTextContent(aCell.querySelector('button')) ||
|
||||
|
@ -1369,8 +1432,6 @@ sortTable: (columnIndex) => {
|
|||
aValue = aValue.toLowerCase();
|
||||
bValue = bValue.toLowerCase();
|
||||
|
||||
//console.log(`Comparing trade actions: "${aValue}" vs "${bValue}"`);
|
||||
|
||||
if (aValue === bValue) return 0;
|
||||
if (aValue === "swap") return -1 * sortOrder;
|
||||
if (bValue === "swap") return 1 * sortOrder;
|
||||
|
@ -1379,7 +1440,6 @@ sortTable: (columnIndex) => {
|
|||
default:
|
||||
aValue = getSafeTextContent(a.cells[columnIndex]);
|
||||
bValue = getSafeTextContent(b.cells[columnIndex]);
|
||||
//console.log(`Comparing default values: "${aValue}" vs "${bValue}"`);
|
||||
return aValue.localeCompare(bValue, undefined, {
|
||||
numeric: true,
|
||||
sensitivity: 'base'
|
||||
|
@ -1389,23 +1449,29 @@ sortTable: (columnIndex) => {
|
|||
|
||||
const tbody = table.querySelector('tbody');
|
||||
if (tbody) {
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
} else {
|
||||
//console.error("Table body not found.");
|
||||
const fragment = document.createDocumentFragment();
|
||||
rows.forEach(row => fragment.appendChild(row));
|
||||
tbody.appendChild(fragment);
|
||||
}
|
||||
//console.log('Sorting completed');
|
||||
},
|
||||
},
|
||||
|
||||
initializeSelectImages: () => {
|
||||
const updateSelectedImage = (selectId) => {
|
||||
const select = document.getElementById(selectId);
|
||||
const button = document.getElementById(`${selectId}_button`);
|
||||
if (!select || !button) {
|
||||
//console.error(`Elements not found for ${selectId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const oldListener = select.getAttribute('data-change-listener');
|
||||
if (oldListener && window[oldListener]) {
|
||||
select.removeEventListener('change', window[oldListener]);
|
||||
delete window[oldListener];
|
||||
}
|
||||
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
const imageURL = selectedOption?.getAttribute('data-image');
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (imageURL) {
|
||||
button.style.backgroundImage = `url('${imageURL}')`;
|
||||
|
@ -1419,21 +1485,24 @@ sortTable: (columnIndex) => {
|
|||
button.style.minHeight = '25px';
|
||||
});
|
||||
};
|
||||
const handleSelectChange = (event) => {
|
||||
updateSelectedImage(event.target.id);
|
||||
};
|
||||
|
||||
['coin_to', 'coin_from'].forEach(selectId => {
|
||||
const select = document.getElementById(selectId);
|
||||
if (select) {
|
||||
select.addEventListener('change', handleSelectChange);
|
||||
|
||||
const listenerName = `selectChangeListener_${selectId}`;
|
||||
window[listenerName] = () => updateSelectedImage(selectId);
|
||||
|
||||
select.setAttribute('data-change-listener', listenerName);
|
||||
|
||||
select.addEventListener('change', window[listenerName]);
|
||||
|
||||
updateSelectedImage(selectId);
|
||||
} else {
|
||||
//console.error(`Select element not found for ${selectId}`);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateResolutionButtons: (coinSymbol) => {
|
||||
updateResolutionButtons: (coinSymbol) => {
|
||||
const resolutionButtons = document.querySelectorAll('.resolution-button');
|
||||
resolutionButtons.forEach(button => {
|
||||
const resolution = button.id.split('-')[1];
|
||||
|
@ -1453,12 +1522,13 @@ updateResolutionButtons: (coinSymbol) => {
|
|||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
toggleAutoRefresh: () => {
|
||||
console.log('Toggling auto-refresh');
|
||||
app.isAutoRefreshEnabled = !app.isAutoRefreshEnabled;
|
||||
localStorage.setItem('autoRefreshEnabled', app.isAutoRefreshEnabled.toString());
|
||||
|
||||
if (app.isAutoRefreshEnabled) {
|
||||
console.log('Auto-refresh enabled, scheduling next refresh');
|
||||
app.scheduleNextRefresh();
|
||||
|
@ -1471,31 +1541,18 @@ updateResolutionButtons: (coinSymbol) => {
|
|||
app.nextRefreshTime = null;
|
||||
localStorage.removeItem('nextRefreshTime');
|
||||
}
|
||||
|
||||
app.updateAutoRefreshButton();
|
||||
app.updateNextRefreshTime();
|
||||
}
|
||||
};
|
||||
|
||||
const resolutionButtons = document.querySelectorAll('.resolution-button');
|
||||
resolutionButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const resolution = button.id.split('-')[1];
|
||||
const currentCoin = chartModule.currentCoin;
|
||||
|
||||
if (currentCoin !== 'WOW' || resolution === 'day') {
|
||||
config.currentResolution = resolution;
|
||||
chartModule.updateChart(currentCoin, true);
|
||||
app.updateResolutionButtons(currentCoin);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// LOAD
|
||||
app.init();
|
||||
app.visibilityCleanup = app.setupVisibilityHandler();
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden && chartModule.chart) {
|
||||
console.log('Page became visible, reinitializing chart');
|
||||
chartModule.updateChart(chartModule.currentCoin, true);
|
||||
}
|
||||
window.addEventListener('beforeunload', () => {
|
||||
console.log('Page unloading, cleaning up...');
|
||||
app.cleanup();
|
||||
});
|
||||
|
|
|
@ -147,7 +147,7 @@ function getWebSocketConfig() {
|
|||
</button>
|
||||
<p class="text-red-600 font-semibold text-xl mb-4">Error</p>
|
||||
<p id="error-message" class="text-gray-700 dark:text-gray-300 text-lg mb-6"></p>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">To review or update your Chart API Key(s), navigate to<a href="/settings" class="text-blue-500 hover:underline">Settings & Tools > Settings > General (TAB)</a>.
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">To review or update your Chart API Key(s), navigate to <a href="/settings" class="text-blue-500 hover:underline">Settings & Tools > Settings > General (TAB)</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -337,7 +337,7 @@ function getWebSocketConfig() {
|
|||
<div class="container mt-5 mx-auto px-4">
|
||||
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-0">
|
||||
<div class="w-auto mt-6 overflow-x-auto">
|
||||
<div class="w-auto mt-6 overflow-hidden md:overflow-hidden sm:overflow-auto">
|
||||
<table class="w-full min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr>
|
||||
|
|
Loading…
Reference in a new issue