From eeade736a4516f40d0d524e803e77a0b3170fd26 Mon Sep 17 00:00:00 2001 From: nahuhh <50635951+nahuhh@users.noreply.github.com> Date: Wed, 26 Mar 2025 23:01:30 +0000 Subject: [PATCH] js: eslints --- basicswap/static/js/bids_sentreceived.js | 68 +++++++------- .../static/js/bids_sentreceived_export.js | 42 ++++----- basicswap/static/js/global.js | 8 +- basicswap/static/js/modules/api-manager.js | 34 +++---- basicswap/static/js/modules/cache-manager.js | 30 +++--- .../static/js/modules/cleanup-manager.js | 12 +-- basicswap/static/js/modules/config-manager.js | 26 +++--- .../static/js/modules/identity-manager.js | 10 +- basicswap/static/js/modules/memory-manager.js | 8 +- .../static/js/modules/network-manager.js | 20 ++-- .../static/js/modules/summary-manager.js | 56 +++++------ .../static/js/modules/tooltips-manager.js | 16 ++-- basicswap/static/js/modules/wallet-manager.js | 32 +++---- .../static/js/modules/websocket-manager.js | 48 +++++----- basicswap/static/js/new_offer.js | 38 ++++---- basicswap/static/js/pricechart.js | 92 +++++++++---------- basicswap/static/js/swaps_in_progress.js | 20 ++-- basicswap/static/js/ui/dropdown.js | 8 +- 18 files changed, 284 insertions(+), 284 deletions(-) diff --git a/basicswap/static/js/bids_sentreceived.js b/basicswap/static/js/bids_sentreceived.js index cdbebca..614c837 100644 --- a/basicswap/static/js/bids_sentreceived.js +++ b/basicswap/static/js/bids_sentreceived.js @@ -97,7 +97,7 @@ const EventManager = { add(element, type, handler, options = false) { if (!element) return null; - + if (!this.listeners.has(element)) { this.listeners.set(element, new Map()); } @@ -116,7 +116,7 @@ const EventManager = { remove(element, type, handler, options = false) { if (!element) return; - + const elementListeners = this.listeners.get(element); if (!elementListeners) return; @@ -140,7 +140,7 @@ const EventManager = { removeAll(element) { if (!element) return; - + const elementListeners = this.listeners.get(element); if (!elementListeners) return; @@ -167,7 +167,7 @@ const EventManager = { function cleanup() { //console.log('Starting comprehensive cleanup process for bids table'); - + try { if (searchTimeout) { clearTimeout(searchTimeout); @@ -184,7 +184,7 @@ function cleanup() { cleanupTooltips(); forceTooltipDOMCleanup(); - + if (window.TooltipManager) { window.TooltipManager.cleanup(); } @@ -237,12 +237,12 @@ function cleanup() { sent: [], received: [] }; - + state.currentPage = { sent: 1, received: 1 }; - + state.isLoading = false; state.isRefreshing = false; state.wsConnected = false; @@ -310,7 +310,7 @@ CleanupManager.addListener(document, 'visibilitychange', () => { if (window.TooltipManager && typeof window.TooltipManager.cleanup === 'function') { window.TooltipManager.cleanup(); } - + // Run memory optimization if (window.MemoryManager) { MemoryManager.forceCleanup(); @@ -326,7 +326,7 @@ CleanupManager.addListener(document, 'visibilitychange', () => { const lastUpdateTime = state.lastRefresh || 0; const now = Date.now(); const refreshInterval = 5 * 60 * 1000; // 5 minutes - + if (now - lastUpdateTime > refreshInterval) { setTimeout(() => { updateBidsTable(); @@ -366,7 +366,7 @@ function cleanupRow(row) { function optimizeMemoryUsage() { const MAX_BIDS_IN_MEMORY = 500; - + ['sent', 'received'].forEach(type => { if (state.data[type] && state.data[type].length > MAX_BIDS_IN_MEMORY) { console.log(`Trimming ${type} bids data from ${state.data[type].length} to ${MAX_BIDS_IN_MEMORY}`); @@ -581,7 +581,7 @@ function filterAndSortData(bids) { } catch (e) { console.warn('Error accessing identity for search:', e); } - + const matchesLabel = label.toLowerCase().includes(searchStr); let matchesDisplayedLabel = false; @@ -589,7 +589,7 @@ function filterAndSortData(bids) { try { const tableId = state.currentTab === 'sent' ? 'sent' : 'received'; const cells = document.querySelectorAll(`#${tableId} a[href^="/identity/"]`); - + for (const cell of cells) { const href = cell.getAttribute('href'); @@ -607,7 +607,7 @@ function filterAndSortData(bids) { console.warn('Error checking displayed labels:', e); } } - + if (!(matchesBidId || matchesIdentity || matchesLabel || matchesDisplayedLabel)) { return false; } @@ -627,7 +627,7 @@ async function preloadIdentitiesForSearch(bids) { if (!window.IdentityManager || typeof IdentityManager.getIdentityData !== 'function') { return; } - + try { const addresses = new Set(); bids.forEach(bid => { @@ -638,7 +638,7 @@ async function preloadIdentitiesForSearch(bids) { const BATCH_SIZE = 20; const addressArray = Array.from(addresses); - + for (let i = 0; i < addressArray.length; i += BATCH_SIZE) { const batch = addressArray.slice(i, i + BATCH_SIZE); await Promise.all(batch.map(addr => IdentityManager.getIdentityData(addr))); @@ -647,7 +647,7 @@ async function preloadIdentitiesForSearch(bids) { await new Promise(resolve => setTimeout(resolve, 10)); } } - + console.log(`Preloaded ${addressArray.length} identities for search`); } catch (error) { console.error('Error preloading identities:', error); @@ -847,7 +847,7 @@ const createIdentityTooltipContent = (identity) => { `; }; -let tooltipIdsToCleanup = new Set(); +const tooltipIdsToCleanup = new Set(); const cleanupTooltips = () => { if (window.TooltipManager) { @@ -869,7 +869,7 @@ const forceTooltipDOMCleanup = () => { foundCount += allTooltipElements.length; allTooltipElements.forEach(element => { - const isDetached = !document.body.contains(element) || + const isDetached = !document.body.contains(element) || element.classList.contains('hidden') || element.style.display === 'none'; @@ -877,7 +877,7 @@ const forceTooltipDOMCleanup = () => { const triggerId = element.id; const triggerElement = document.querySelector(`[data-tooltip-target="${triggerId}"]`); - if (!triggerElement || + if (!triggerElement || !document.body.contains(triggerElement) || triggerElement.classList.contains('hidden')) { element.remove(); @@ -899,7 +899,7 @@ const forceTooltipDOMCleanup = () => { const tippyRoots = document.querySelectorAll('[data-tippy-root]'); foundCount += tippyRoots.length; tippyRoots.forEach(element => { - const isOrphan = !element.children.length || + const isOrphan = !element.children.length || element.children[0].classList.contains('hidden') || !document.body.contains(element); @@ -926,7 +926,7 @@ const forceTooltipDOMCleanup = () => { } }); document.querySelectorAll('.tooltip').forEach(element => { - const isTrulyDetached = !element.parentElement || + const isTrulyDetached = !element.parentElement || !document.body.contains(element.parentElement) || element.classList.contains('hidden'); if (isTrulyDetached) { @@ -1108,7 +1108,7 @@ const updateTableContent = async (type) => { if (currentPageData.length > 0) { const BATCH_SIZE = 10; let allRows = []; - + for (let i = 0; i < currentPageData.length; i += BATCH_SIZE) { const batch = currentPageData.slice(i, i + BATCH_SIZE); const rowPromises = batch.map(bid => createTableRow(bid)); @@ -1166,7 +1166,7 @@ const initializeTooltips = () => { window.TooltipManager.cleanup(); - let selector = '#' + state.currentTab + ' [data-tooltip-target]'; + const selector = '#' + state.currentTab + ' [data-tooltip-target]'; const tooltipTriggers = document.querySelectorAll(selector); const tooltipCount = tooltipTriggers.length; if (tooltipCount > 50) { @@ -1187,7 +1187,7 @@ const initializeTooltips = () => { }); const offscreenTooltips = Array.from(tooltipTriggers).filter(t => !viewportTooltips.includes(t)); - + offscreenTooltips.forEach(trigger => { const createTooltipOnHover = () => { createTooltipForTrigger(trigger); @@ -1206,7 +1206,7 @@ const initializeTooltips = () => { const createTooltipForTrigger = (trigger) => { if (!trigger || !window.TooltipManager) return; - + const targetId = trigger.getAttribute('data-tooltip-target'); const tooltipContent = document.getElementById(targetId); @@ -1250,7 +1250,7 @@ function cleanupOffscreenTooltips() { const farOffscreenTriggers = Array.from(tooltipTriggers).filter(trigger => { const rect = trigger.getBoundingClientRect(); - return (rect.bottom < -window.innerHeight * 2 || + return (rect.bottom < -window.innerHeight * 2 || rect.top > window.innerHeight * 3); }); @@ -1312,7 +1312,7 @@ const fetchBids = async () => { activeFetchController.abort(); } }, 30000); - + const response = await fetch(endpoint, { method: 'POST', headers: { @@ -1328,14 +1328,14 @@ const fetchBids = async () => { }), signal: activeFetchController.signal }); - + clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } - let data = await response.json(); + const data = await response.json(); //console.log('Received raw data:', data.length, 'bids'); state.filters.with_expired = includeExpired; @@ -1351,7 +1351,7 @@ const fetchBids = async () => { } else { processedData = filterAndSortData(data); } - + return processedData; } catch (error) { if (error.name === 'AbortError') { @@ -1375,12 +1375,12 @@ const updateBidsTable = async () => { updateLoadingState(true); const bids = await fetchBids(); - + // Add identity preloading if we're searching if (state.filters.searchQuery && state.filters.searchQuery.length > 0) { await preloadIdentitiesForSearch(bids); } - + state.data[state.currentTab] = bids; state.currentPage[state.currentTab] = 1; @@ -1911,13 +1911,13 @@ function initialize() { WebSocketManager.initialize(); setupEventListeners(); }, 10); - + setTimeout(() => { setupRefreshButtons(); setupFilterEventListeners(); updateCoinFilterImages(); }, 50); - + setTimeout(() => { updateClearFiltersButton(); state.currentTab = 'sent'; diff --git a/basicswap/static/js/bids_sentreceived_export.js b/basicswap/static/js/bids_sentreceived_export.js index 823a1e4..acee851 100644 --- a/basicswap/static/js/bids_sentreceived_export.js +++ b/basicswap/static/js/bids_sentreceived_export.js @@ -5,7 +5,7 @@ const BidExporter = { } const isSent = type === 'sent'; - + const headers = [ 'Date/Time', 'Bid ID', @@ -19,9 +19,9 @@ const BidExporter = { 'Created At', 'Expires At' ]; - + let csvContent = headers.join(',') + '\n'; - + bids.forEach(bid => { const row = [ `"${formatTime(bid.created_at)}"`, @@ -36,17 +36,17 @@ const BidExporter = { bid.created_at, bid.expire_at ]; - + csvContent += row.join(',') + '\n'; }); - + return csvContent; }, - + download(content, filename) { try { const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' }); - + if (window.navigator && window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(blob, filename); return; @@ -54,48 +54,48 @@ const BidExporter = { const url = URL.createObjectURL(blob); const link = document.createElement('a'); - + link.href = url; link.download = filename; link.style.display = 'none'; - + document.body.appendChild(link); link.click(); document.body.removeChild(link); - + setTimeout(() => { URL.revokeObjectURL(url); }, 100); } catch (error) { console.error('Error downloading CSV:', error); - + const csvData = 'data:text/csv;charset=utf-8,' + encodeURIComponent(content); const link = document.createElement('a'); link.setAttribute('href', csvData); link.setAttribute('download', filename); link.style.display = 'none'; - + document.body.appendChild(link); link.click(); document.body.removeChild(link); } }, - + exportCurrentView() { const type = state.currentTab; const data = state.data[type]; - + if (!data || !data.length) { alert('No data to export'); return; } - + const csvContent = this.toCSV(data, type); - + const now = new Date(); const dateStr = now.toISOString().split('T')[0]; const filename = `bsx_${type}_bids_${dateStr}.csv`; - + this.download(csvContent, filename); } }; @@ -111,7 +111,7 @@ document.addEventListener('DOMContentLoaded', function() { BidExporter.exportCurrentView(); }); } - + const exportReceivedButton = document.getElementById('exportReceivedBids'); if (exportReceivedButton) { EventManager.add(exportReceivedButton, 'click', (e) => { @@ -127,14 +127,14 @@ document.addEventListener('DOMContentLoaded', function() { const originalCleanup = window.cleanup || function(){}; window.cleanup = function() { originalCleanup(); - + const exportSentButton = document.getElementById('exportSentBids'); const exportReceivedButton = document.getElementById('exportReceivedBids'); - + if (exportSentButton && typeof EventManager !== 'undefined') { EventManager.remove(exportSentButton, 'click'); } - + if (exportReceivedButton && typeof EventManager !== 'undefined') { EventManager.remove(exportReceivedButton, 'click'); } diff --git a/basicswap/static/js/global.js b/basicswap/static/js/global.js index 5892e98..f6c24e2 100644 --- a/basicswap/static/js/global.js +++ b/basicswap/static/js/global.js @@ -84,7 +84,7 @@ function setupShutdownModal() { function showShutdownModal() { closeAllDropdowns(); - + const activeSwaps = parseInt(shutdownButtons[0].getAttribute('data-active-swaps') || '0'); if (activeSwaps > 0) { shutdownWarning.classList.remove('hidden'); @@ -142,7 +142,7 @@ function setupDarkMode() { const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon'); if (themeToggleDarkIcon && themeToggleLightIcon) { - if (localStorage.getItem('color-theme') === 'dark' || + if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { themeToggleLightIcon.classList.remove('hidden'); } else { @@ -167,12 +167,12 @@ function setupDarkMode() { } else { setTheme('dark'); } - + if (themeToggleDarkIcon && themeToggleLightIcon) { themeToggleDarkIcon.classList.toggle('hidden'); themeToggleLightIcon.classList.toggle('hidden'); } - + toggleImages(); }); } diff --git a/basicswap/static/js/modules/api-manager.js b/basicswap/static/js/modules/api-manager.js index 2effc12..9f299e1 100644 --- a/basicswap/static/js/modules/api-manager.js +++ b/basicswap/static/js/modules/api-manager.js @@ -51,7 +51,7 @@ const ApiManager = (function() { try { await this.requestQueue[apiName]; - + const executeRequest = async () => { const waitTime = this.getWaitTime(apiName); if (waitTime > 0) { @@ -69,7 +69,7 @@ const ApiManager = (function() { return publicAPI.rateLimiter.queueRequest(apiName, requestFn, retryCount + 1); } - if ((error.message.includes('timeout') || error.name === 'NetworkError') && + if ((error.message.includes('timeout') || error.name === 'NetworkError') && retryCount < this.retryDelays.length) { const delay = this.retryDelays[retryCount]; console.warn(`Request failed, retrying in ${delay/1000} seconds...`, { @@ -87,10 +87,10 @@ const ApiManager = (function() { this.requestQueue[apiName] = executeRequest(); return await this.requestQueue[apiName]; - + } catch (error) { - if (error.message.includes('429') || - error.message.includes('timeout') || + if (error.message.includes('429') || + error.message.includes('timeout') || error.name === 'NetworkError') { const cacheKey = `coinData_${apiName}`; try { @@ -110,7 +110,7 @@ const ApiManager = (function() { const publicAPI = { config, rateLimiter, - + initialize: function(options = {}) { if (state.isInitialized) { console.warn('[ApiManager] Already initialized'); @@ -158,7 +158,7 @@ const ApiManager = (function() { } const response = await fetch(url, options); - + if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } @@ -215,18 +215,18 @@ const ApiManager = (function() { fetchCoinGeckoData: async function() { return this.rateLimiter.queueRequest('coingecko', async () => { try { - const coins = (window.config && window.config.coins) ? + const coins = (window.config && window.config.coins) ? window.config.coins .filter(coin => coin.usesCoinGecko) .map(coin => coin.name) - .join(',') : + .join(',') : 'bitcoin,monero,particl,bitcoincash,pivx,firo,dash,litecoin,dogecoin,decred'; //console.log('Fetching coin prices for:', coins); const response = await this.fetchCoinPrices(coins); - + //console.log('Full API response:', response); - + if (!response || typeof response !== 'object') { throw new Error('Invalid response type'); } @@ -249,11 +249,11 @@ const ApiManager = (function() { fetchVolumeData: async function() { return this.rateLimiter.queueRequest('coingecko', async () => { try { - const coins = (window.config && window.config.coins) ? + const coins = (window.config && window.config.coins) ? window.config.coins .filter(coin => coin.usesCoinGecko) .map(coin => getCoinBackendId ? getCoinBackendId(coin.name) : coin.name) - .join(',') : + .join(',') : 'bitcoin,monero,particl,bitcoin-cash,pivx,firo,dash,litecoin,dogecoin,decred'; const url = `https://api.coingecko.com/api/v3/simple/price?ids=${coins}&vs_currencies=usd&include_24hr_vol=true&include_24hr_change=true`; @@ -280,7 +280,7 @@ const ApiManager = (function() { } }); }, - + fetchCryptoCompareData: function(coin) { return this.rateLimiter.queueRequest('cryptocompare', async () => { try { @@ -290,7 +290,7 @@ const ApiManager = (function() { 'User-Agent': 'Mozilla/5.0', 'Accept': 'application/json' }; - + return await this.makePostRequest(url, headers); } catch (error) { console.error(`CryptoCompare request failed for ${coin}:`, error); @@ -324,7 +324,7 @@ const ApiManager = (function() { try { const apiKey = window.config?.apiKeys?.cryptoCompare || ''; let url; - + if (resolution === 'day') { url = `https://min-api.cryptocompare.com/data/v2/histohour?fsym=${coin}&tsym=USD&limit=24&api_key=${apiKey}`; } else if (resolution === 'year') { @@ -351,7 +351,7 @@ const ApiManager = (function() { await Promise.all(fetchPromises); return results; }, - + dispose: function() { // Clear any pending requests or resources rateLimiter.requestQueue = {}; diff --git a/basicswap/static/js/modules/cache-manager.js b/basicswap/static/js/modules/cache-manager.js index 1dec899..e8fba7c 100644 --- a/basicswap/static/js/modules/cache-manager.js +++ b/basicswap/static/js/modules/cache-manager.js @@ -19,7 +19,7 @@ const CacheManager = (function() { ]; const isCacheKey = (key) => { - return CACHE_KEY_PATTERNS.some(pattern => key.startsWith(pattern)) || + return CACHE_KEY_PATTERNS.some(pattern => key.startsWith(pattern)) || key === 'coinGeckoOneLiner' || key === PRICES_CACHE_KEY; }; @@ -48,7 +48,7 @@ const CacheManager = (function() { const ttlConfig = window.config?.cacheConfig?.ttlSettings || {}; return ttlConfig[resourceType] || window.config?.cacheConfig?.defaultTTL || defaults.defaultTTL; }, - + set: function(key, value, resourceTypeOrCustomTtl = null) { try { this.cleanup(); @@ -119,10 +119,10 @@ const CacheManager = (function() { .filter(k => isCacheKey(k)) .sort((a, b) => memoryCache.get(a).timestamp - memoryCache.get(b).timestamp) .slice(0, Math.floor(memoryCache.size * 0.2)); // Remove oldest 20% - + keysToDelete.forEach(k => memoryCache.delete(k)); } - + return true; } } catch (error) { @@ -194,7 +194,7 @@ const CacheManager = (function() { memoryCache.delete(key); } } - + return null; } catch (error) { console.error("Cache retrieval error:", error); @@ -286,7 +286,7 @@ const CacheManager = (function() { .filter(key => isCacheKey(key)) .sort((a, b) => memoryCache.get(a).timestamp - memoryCache.get(b).timestamp) .slice(0, Math.floor(memoryCache.size * 0.3)); // Remove oldest 30% during aggressive cleanup - + keysToDelete.forEach(key => memoryCache.delete(key)); } @@ -327,7 +327,7 @@ const CacheManager = (function() { Array.from(memoryCache.keys()) .filter(key => isCacheKey(key)) .forEach(key => memoryCache.delete(key)); - + console.log("Cache cleared successfully"); return true; }, @@ -368,7 +368,7 @@ const CacheManager = (function() { let memoryCacheSize = 0; let memoryCacheItems = 0; let memoryCacheExpired = 0; - + memoryCache.forEach((item, key) => { if (isCacheKey(key)) { memoryCacheItems++; @@ -381,7 +381,7 @@ const CacheManager = (function() { } } }); - + return { totalSizeMB: (totalSize / 1024 / 1024).toFixed(2), itemCount, @@ -415,10 +415,10 @@ const CacheManager = (function() { } } }); - + console.log(`Migrated ${migratedCount} items from memory cache to localStorage.`); } - + return { available: storageAvailable, type: storageAvailable ? 'localStorage' : 'memory' @@ -430,7 +430,7 @@ const CacheManager = (function() { ...cacheAPI, setPrices: function(priceData, customTtl = null) { - return this.set(PRICES_CACHE_KEY, priceData, + return this.set(PRICES_CACHE_KEY, priceData, customTtl || (typeof customTtl === 'undefined' ? 'prices' : null)); }, @@ -447,7 +447,7 @@ const CacheManager = (function() { const normalizedSymbol = symbol.toLowerCase(); return prices.value[normalizedSymbol] || null; }, - + getCompatiblePrices: function(format) { const prices = this.getPrices(); if (!prices || !prices.value) { @@ -464,7 +464,7 @@ const CacheManager = (function() { .join(' ') .toLowerCase() .replace(' ', '-'); - + ratesFormat[coinKey] = { usd: data.price || data.usd, btc: data.price_btc || data.btc @@ -493,7 +493,7 @@ const CacheManager = (function() { value: geckoFormat, remainingTime: prices.remainingTime }; - + default: return prices; } diff --git a/basicswap/static/js/modules/cleanup-manager.js b/basicswap/static/js/modules/cleanup-manager.js index 0615fca..d179ee9 100644 --- a/basicswap/static/js/modules/cleanup-manager.js +++ b/basicswap/static/js/modules/cleanup-manager.js @@ -111,8 +111,8 @@ const CleanupManager = (function() { } state.eventListeners = state.eventListeners.filter( - listener => !(listener.element === element && - listener.type === type && + listener => !(listener.element === element && + listener.type === type && listener.handler === handler) ); }, @@ -224,10 +224,10 @@ const CleanupManager = (function() { intervals: state.intervals.length, animationFrames: state.animationFrames.length, resources: state.resources.size, - total: state.eventListeners.length + - state.timeouts.length + - state.intervals.length + - state.animationFrames.length + + total: state.eventListeners.length + + state.timeouts.length + + state.intervals.length + + state.animationFrames.length + state.resources.size }; }, diff --git a/basicswap/static/js/modules/config-manager.js b/basicswap/static/js/modules/config-manager.js index e77c55e..1176cc3 100644 --- a/basicswap/static/js/modules/config-manager.js +++ b/basicswap/static/js/modules/config-manager.js @@ -4,9 +4,9 @@ const ConfigManager = (function() { }; function determineWebSocketPort() { - const wsPort = - window.ws_port || - (typeof getWebSocketConfig === 'function' ? getWebSocketConfig().port : null) || + const wsPort = + window.ws_port || + (typeof getWebSocketConfig === 'function' ? getWebSocketConfig().port : null) || '11700'; return wsPort; } @@ -17,10 +17,10 @@ const ConfigManager = (function() { cacheDuration: 10 * 60 * 1000, requestTimeout: 60000, wsPort: selectedWsPort, - + cacheConfig: { defaultTTL: 10 * 60 * 1000, - + ttlSettings: { prices: 5 * 60 * 1000, chart: 5 * 60 * 1000, @@ -34,7 +34,7 @@ const ConfigManager = (function() { maxSizeBytes: 10 * 1024 * 1024, maxItems: 200 }, - + fallbackTTL: 24 * 60 * 60 * 1000 }, @@ -92,7 +92,7 @@ const ConfigManager = (function() { 'Bitcoin Cash': 'BCH', 'Dogecoin': 'DOGE' }, - + nameToDisplayName: { 'Bitcoin': 'Bitcoin', 'Litecoin': 'Litecoin', @@ -164,13 +164,13 @@ const ConfigManager = (function() { if (options) { Object.assign(this, options); } - + if (window.CleanupManager) { window.CleanupManager.registerResource('configManager', this, (mgr) => mgr.dispose()); } this.utils = utils; - + state.isInitialized = true; console.log('ConfigManager initialized'); return this; @@ -205,7 +205,7 @@ const ConfigManager = (function() { const lowerCoinName = typeof coinName === 'string' ? coinName.toLowerCase() : ''; return nameMap[lowerCoinName] || lowerCoinName; }, - + coinMatches: function(offerCoin, filterCoin) { if (!offerCoin || !filterCoin) return false; @@ -254,7 +254,7 @@ const ConfigManager = (function() { get: function(path, defaultValue = null) { const parts = path.split('.'); let current = this; - + for (let i = 0; i < parts.length; i++) { if (current === undefined || current === null) { return defaultValue; @@ -376,12 +376,12 @@ const ConfigManager = (function() { 'firo': { usd: null, btc: null } }; }, - + getCoinSymbol: function(fullName) { return publicAPI.coinMappings?.nameToSymbol[fullName] || fullName; } }; - + return publicAPI; })(); diff --git a/basicswap/static/js/modules/identity-manager.js b/basicswap/static/js/modules/identity-manager.js index 9ac2859..68359f7 100644 --- a/basicswap/static/js/modules/identity-manager.js +++ b/basicswap/static/js/modules/identity-manager.js @@ -60,7 +60,7 @@ const IdentityManager = (function() { const oldestEntries = [...state.cache.entries()] .sort((a, b) => a[1].timestamp - b[1].timestamp) .slice(0, Math.floor(state.config.maxCacheSize * 0.2)); - + oldestEntries.forEach(([key]) => { state.cache.delete(key); log(`Pruned cache entry for ${key}`); @@ -88,10 +88,10 @@ const IdentityManager = (function() { const entriesToRemove = [...state.cache.entries()] .sort((a, b) => a[1].timestamp - b[1].timestamp) .slice(0, state.cache.size - maxSize); - + entriesToRemove.forEach(([key]) => state.cache.delete(key)); log(`Limited cache size, removed ${entriesToRemove.length} entries`); - + return entriesToRemove.length; }, @@ -138,11 +138,11 @@ const IdentityManager = (function() { if (options) { this.configure(options); } - + if (window.CleanupManager) { window.CleanupManager.registerResource('identityManager', this, (mgr) => mgr.dispose()); } - + log('IdentityManager initialized'); return this; }, diff --git a/basicswap/static/js/modules/memory-manager.js b/basicswap/static/js/modules/memory-manager.js index 4c3cffd..fd5489f 100644 --- a/basicswap/static/js/modules/memory-manager.js +++ b/basicswap/static/js/modules/memory-manager.js @@ -65,7 +65,7 @@ const MemoryManager = (function() { const nodeCount = document.querySelectorAll('*').length; console.log('DOM node count:', nodeCount); - + if (window.CleanupManager) { const counts = CleanupManager.getResourceCounts(); console.log('Managed resources:', counts); @@ -102,11 +102,11 @@ const MemoryManager = (function() { state.cleanupInterval = setInterval(() => { this.forceCleanup(); }, interval); - + log('Auto-cleanup enabled every', interval/1000, 'seconds'); return true; }, - + disableAutoCleanup: function() { if (state.cleanupInterval) { clearInterval(state.cleanupInterval); @@ -155,7 +155,7 @@ const MemoryManager = (function() { return true; }, - + setDebugMode: function(enabled) { config.debug = Boolean(enabled); return `Debug mode ${config.debug ? 'enabled' : 'disabled'}`; diff --git a/basicswap/static/js/modules/network-manager.js b/basicswap/static/js/modules/network-manager.js index c5a1dc4..6a8a932 100644 --- a/basicswap/static/js/modules/network-manager.js +++ b/basicswap/static/js/modules/network-manager.js @@ -30,13 +30,13 @@ const NetworkManager = (function() { const publicAPI = { initialize: function(options = {}) { Object.assign(config, options); - + window.addEventListener('online', this.handleOnlineStatus.bind(this)); window.addEventListener('offline', this.handleOfflineStatus.bind(this)); - + state.isOnline = navigator.onLine; log(`Network status initialized: ${state.isOnline ? 'online' : 'offline'}`); - + if (window.CleanupManager) { window.CleanupManager.registerResource('networkManager', this, (mgr) => mgr.dispose()); } @@ -61,7 +61,7 @@ const NetworkManager = (function() { log('Browser reports online status'); state.isOnline = true; this.notifyHandlers('online'); - + if (state.reconnectTimer) { this.scheduleReconnectRefresh(); } @@ -103,7 +103,7 @@ const NetworkManager = (function() { state.reconnectTimer = null; } - const delay = config.reconnectDelay * Math.pow(config.reconnectBackoff, + const delay = config.reconnectDelay * Math.pow(config.reconnectBackoff, Math.min(state.reconnectAttempts, 5)); log(`Scheduling reconnection attempt in ${delay/1000} seconds`); @@ -145,7 +145,7 @@ const NetworkManager = (function() { this.notifyHandlers('reconnected'); } else { log('Backend still unavailable'); - + if (state.reconnectAttempts < config.maxReconnectAttempts) { this.scheduleReconnectRefresh(); } else { @@ -157,7 +157,7 @@ const NetworkManager = (function() { .catch(error => { state.connectionTestInProgress = false; log('Error during connection test:', error); - + if (state.reconnectAttempts < config.maxReconnectAttempts) { this.scheduleReconnectRefresh(); } else { @@ -210,7 +210,7 @@ const NetworkManager = (function() { const handlerId = generateHandlerId(); state.eventHandlers[event][handlerId] = handler; - + return handlerId; }, @@ -256,9 +256,9 @@ const NetworkManager = (function() { window.removeEventListener('online', this.handleOnlineStatus); window.removeEventListener('offline', this.handleOfflineStatus); - + state.eventHandlers = {}; - + log('NetworkManager disposed'); } }; diff --git a/basicswap/static/js/modules/summary-manager.js b/basicswap/static/js/modules/summary-manager.js index d4f42b4..77317b7 100644 --- a/basicswap/static/js/modules/summary-manager.js +++ b/basicswap/static/js/modules/summary-manager.js @@ -15,9 +15,9 @@ const SummaryManager = (function() { function updateElement(elementId, value) { const element = document.getElementById(elementId); if (!element) return false; - - const safeValue = (value !== undefined && value !== null) - ? value + + const safeValue = (value !== undefined && value !== null) + ? value : (element.dataset.lastValue || 0); element.dataset.lastValue = safeValue; @@ -32,8 +32,8 @@ const SummaryManager = (function() { element.textContent = safeValue; } - if (['offers-counter', 'bid-requests-counter', 'sent-bids-counter', - 'recv-bids-counter', 'swaps-counter', 'network-offers-counter', + if (['offers-counter', 'bid-requests-counter', 'sent-bids-counter', + 'recv-bids-counter', 'swaps-counter', 'network-offers-counter', 'watched-outputs-counter'].includes(elementId)) { element.classList.remove('bg-blue-500', 'bg-gray-400'); element.classList.add(safeValue > 0 ? 'bg-blue-500' : 'bg-gray-400'); @@ -57,7 +57,7 @@ const SummaryManager = (function() { function updateUIFromData(data) { if (!data) return; - + updateElement('network-offers-counter', data.num_network_offers); updateElement('offers-counter', data.num_sent_active_offers); updateElement('sent-bids-counter', data.num_sent_active_bids); @@ -65,7 +65,7 @@ const SummaryManager = (function() { updateElement('bid-requests-counter', data.num_available_bids); updateElement('swaps-counter', data.num_swapping); updateElement('watched-outputs-counter', data.num_watched_outputs); - + const shutdownButtons = document.querySelectorAll('.shutdown-button'); shutdownButtons.forEach(button => { button.setAttribute('data-active-swaps', data.num_swapping); @@ -83,7 +83,7 @@ const SummaryManager = (function() { function cacheSummaryData(data) { if (!data) return; - + localStorage.setItem('summary_data_cache', JSON.stringify({ timestamp: Date.now(), data: data @@ -92,24 +92,24 @@ const SummaryManager = (function() { function getCachedSummaryData() { let cachedData = null; - + cachedData = localStorage.getItem('summary_data_cache'); if (!cachedData) return null; - + const parsedCache = JSON.parse(cachedData); const maxAge = 24 * 60 * 60 * 1000; - + if (Date.now() - parsedCache.timestamp < maxAge) { return parsedCache.data; } - + return null; } function fetchSummaryDataWithTimeout() { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), config.requestTimeout); - + return fetch(config.summaryEndpoint, { signal: controller.signal, headers: { @@ -120,11 +120,11 @@ const SummaryManager = (function() { }) .then(response => { clearTimeout(timeoutId); - + if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } - + return response.json(); }) .catch(error => { @@ -138,21 +138,21 @@ const SummaryManager = (function() { webSocket.close(); } - const wsPort = window.config?.wsPort || + const wsPort = window.config?.wsPort || (typeof determineWebSocketPort === 'function' ? determineWebSocketPort() : '11700'); - + const wsUrl = "ws://" + window.location.hostname + ":" + wsPort; webSocket = new WebSocket(wsUrl); - + webSocket.onopen = () => { publicAPI.fetchSummaryData() .then(() => {}) .catch(() => {}); }; - + webSocket.onmessage = (event) => { let data; - + try { data = JSON.parse(event.data); } catch (error) { @@ -161,18 +161,18 @@ const SummaryManager = (function() { } return; } - + if (data.event) { publicAPI.fetchSummaryData() .then(() => {}) .catch(() => {}); - + if (window.NotificationManager && typeof window.NotificationManager.handleWebSocketEvent === 'function') { window.NotificationManager.handleWebSocketEvent(data); } } }; - + webSocket.onclose = () => { setTimeout(setupWebSocket, 5000); }; @@ -185,8 +185,8 @@ const SummaryManager = (function() { template.innerHTML = document.querySelector('[id^="swapContainer"]')?.innerHTML || ''; document.body.appendChild(template); } - - if (!document.getElementById('swap-in-progress-green-template') && + + if (!document.getElementById('swap-in-progress-green-template') && document.querySelector('[id^="swapContainer"]')?.innerHTML) { const greenTemplate = document.createElement('template'); greenTemplate.id = 'swap-in-progress-green-template'; @@ -229,7 +229,7 @@ const SummaryManager = (function() { if (window.WebSocketManager && typeof window.WebSocketManager.initialize === 'function') { const wsManager = window.WebSocketManager; - + if (!wsManager.isConnected()) { wsManager.connect(); } @@ -239,7 +239,7 @@ const SummaryManager = (function() { this.fetchSummaryData() .then(() => {}) .catch(() => {}); - + if (window.NotificationManager && typeof window.NotificationManager.handleWebSocketEvent === 'function') { window.NotificationManager.handleWebSocketEvent(data); } @@ -302,7 +302,7 @@ const SummaryManager = (function() { } }); }, - + startRefreshTimer: function() { startRefreshTimer(); }, diff --git a/basicswap/static/js/modules/tooltips-manager.js b/basicswap/static/js/modules/tooltips-manager.js index cb8018f..6af1b6e 100644 --- a/basicswap/static/js/modules/tooltips-manager.js +++ b/basicswap/static/js/modules/tooltips-manager.js @@ -26,14 +26,14 @@ const TooltipManager = (function() { create(element, content, options = {}) { if (!element) return null; - + this.destroy(element); if (this.tooltipElementsMap.size > this.maxTooltips * this.cleanupThreshold) { const oldestEntries = Array.from(this.tooltipElementsMap.entries()) .sort((a, b) => a[1].timestamp - b[1].timestamp) .slice(0, 20); - + oldestEntries.forEach(([el]) => { this.destroy(el); }); @@ -199,7 +199,7 @@ const TooltipManager = (function() { instance[0].destroy(); } catch (e) { console.warn('Error destroying tooltip:', e); - + const tippyRoot = document.querySelector(`[data-for-tooltip-id="${id}"]`); if (tippyRoot && tippyRoot.parentNode) { tippyRoot.parentNode.removeChild(tippyRoot); @@ -209,7 +209,7 @@ const TooltipManager = (function() { this.activeTooltips.delete(element); this.tooltipElementsMap.delete(element); - + element.removeAttribute('data-tooltip-trigger-id'); } @@ -355,7 +355,7 @@ const TooltipManager = (function() { this.handleVisibilityChange = () => { if (document.hidden) { this.cleanup(); - + if (window.MemoryManager) { window.MemoryManager.forceCleanup(); } @@ -365,7 +365,7 @@ const TooltipManager = (function() { window.addEventListener('beforeunload', this.boundCleanup); window.addEventListener('unload', this.boundCleanup); document.addEventListener('visibilitychange', this.handleVisibilityChange); - + if (window.CleanupManager) { window.CleanupManager.registerResource('tooltipManager', this, (tm) => tm.dispose()); } @@ -471,7 +471,7 @@ const TooltipManager = (function() { } }); - this.mutationObserver.observe(document.body, { + this.mutationObserver.observe(document.body, { childList: true, subtree: true }); @@ -497,7 +497,7 @@ const TooltipManager = (function() { cancelAnimationFrame(id); }); this.pendingAnimationFrames.clear(); - + if (this.mutationObserver) { this.mutationObserver.disconnect(); this.mutationObserver = null; diff --git a/basicswap/static/js/modules/wallet-manager.js b/basicswap/static/js/modules/wallet-manager.js index ac81e0d..b97f5b6 100644 --- a/basicswap/static/js/modules/wallet-manager.js +++ b/basicswap/static/js/modules/wallet-manager.js @@ -37,7 +37,7 @@ const WalletManager = (function() { 'Decred': 'DCR', 'Bitcoin Cash': 'BCH' }, - + coingeckoIds: { 'BTC': 'btc', 'PART': 'part', @@ -51,7 +51,7 @@ const WalletManager = (function() { 'DCR': 'dcr', 'BCH': 'bch' }, - + shortNames: { 'Bitcoin': 'BTC', 'Particl': 'PART', @@ -99,9 +99,9 @@ const WalletManager = (function() { try { const processedData = {}; const currentSource = config.priceSource.primary; - + const shouldIncludeWow = currentSource === 'coingecko.com'; - + const coinsToFetch = Object.values(coinData.symbols) .filter(symbol => shouldIncludeWow || symbol !== 'WOW') .map(symbol => coinData.coingeckoIds[symbol] || symbol.toLowerCase()) @@ -171,8 +171,8 @@ const WalletManager = (function() { lastError = error; console.error(`Price fetch attempt ${attempt + 1} failed:`, error); - if (attempt === config.maxRetries - 1 && - config.priceSource.fallback && + if (attempt === config.maxRetries - 1 && + config.priceSource.fallback && config.priceSource.fallback !== config.priceSource.primary) { const temp = config.priceSource.primary; config.priceSource.primary = config.priceSource.fallback; @@ -269,14 +269,14 @@ const WalletManager = (function() { } const coinId = coinName.toLowerCase().replace(' ', '-'); - + if (!prices[coinId]) { return; } const price = prices[coinId]?.usd || parseFloat(localStorage.getItem(`${coinId}-price`) || '0'); if (!price) return; - + const usdValue = (amount * price).toFixed(2); if (coinName === 'Particl') { @@ -300,7 +300,7 @@ const WalletManager = (function() { } let usdEl = null; - + const flexContainer = el.closest('.flex'); if (flexContainer) { const nextFlex = flexContainer.nextElementSibling; @@ -384,7 +384,7 @@ const WalletManager = (function() { return false; } } - + function updateTotalValues(totalUsd, btcPrice) { const totalUsdEl = document.getElementById('total-usd-value'); if (totalUsdEl) { @@ -439,7 +439,7 @@ const WalletManager = (function() { const eyeIcon = document.querySelector("#hide-usd-amount-toggle svg"); if (eyeIcon) { - eyeIcon.innerHTML = isVisible ? + eyeIcon.innerHTML = isVisible ? '' : ''; } @@ -500,7 +500,7 @@ const WalletManager = (function() { document.querySelectorAll('.coinname-value').forEach(el => { el.textContent = '****'; }); - + document.querySelectorAll('.usd-value').forEach(el => { el.textContent = '****'; }); @@ -542,7 +542,7 @@ const WalletManager = (function() { } state.lastUpdateTime = parseInt(localStorage.getItem(stateKeys.lastUpdate) || '0'); - state.isWalletsPage = document.querySelector('.wallet-list') !== null || + state.isWalletsPage = document.querySelector('.wallet-list') !== null || window.location.pathname.includes('/wallets'); document.querySelectorAll('.usd-value').forEach(el => { @@ -553,7 +553,7 @@ const WalletManager = (function() { }); storeOriginalValues(); - + if (localStorage.getItem('balancesVisible') === null) { localStorage.setItem('balancesVisible', 'true'); } @@ -581,7 +581,7 @@ const WalletManager = (function() { state.initialized = true; console.log('WalletManager initialized'); - + return this; }, @@ -606,7 +606,7 @@ const WalletManager = (function() { if (fallbackSource) { config.priceSource.fallback = fallbackSource; } - + return this; }, diff --git a/basicswap/static/js/modules/websocket-manager.js b/basicswap/static/js/modules/websocket-manager.js index 1aae881..e823821 100644 --- a/basicswap/static/js/modules/websocket-manager.js +++ b/basicswap/static/js/modules/websocket-manager.js @@ -43,12 +43,12 @@ const WebSocketManager = (function() { wsPort = window.ws_port.toString(); return wsPort; } - + if (typeof getWebSocketConfig === 'function') { const wsConfig = getWebSocketConfig(); wsPort = (wsConfig.port || wsConfig.fallbackPort || '11700').toString(); return wsPort; - } + } wsPort = '11700'; return wsPort; @@ -62,14 +62,14 @@ const WebSocketManager = (function() { startHealthCheck(); log('WebSocketManager initialized with options:', options); - + if (window.CleanupManager) { window.CleanupManager.registerResource('webSocketManager', this, (mgr) => mgr.dispose()); } return this; }, - + connect: function() { if (state.isConnecting || state.isIntentionallyClosed) { log('Connection attempt blocked - already connecting or intentionally closed'); @@ -87,7 +87,7 @@ const WebSocketManager = (function() { try { const wsPort = determineWebSocketPort(); - + if (!wsPort) { state.isConnecting = false; return false; @@ -129,7 +129,7 @@ const WebSocketManager = (function() { log('Cannot send message - not connected'); return false; } - + try { ws.send(JSON.stringify(message)); return true; @@ -143,13 +143,13 @@ const WebSocketManager = (function() { if (!state.messageHandlers[type]) { state.messageHandlers[type] = {}; } - + const handlerId = generateHandlerId(); state.messageHandlers[type][handlerId] = handler; - + return handlerId; }, - + removeMessageHandler: function(type, handlerId) { if (state.messageHandlers[type] && state.messageHandlers[type][handlerId]) { delete state.messageHandlers[type][handlerId]; @@ -158,7 +158,7 @@ const WebSocketManager = (function() { cleanup: function() { log('Cleaning up WebSocket resources'); - + clearTimeout(state.connectTimeout); stopHealthCheck(); @@ -211,11 +211,11 @@ const WebSocketManager = (function() { resume: function() { log('WebSocketManager resumed'); state.isIntentionallyClosed = false; - + if (!this.isConnected()) { this.connect(); } - + startHealthCheck(); } }; @@ -264,16 +264,16 @@ const WebSocketManager = (function() { log('WebSocket closed:', event); state.isConnecting = false; window.ws = null; - + if (typeof updateConnectionStatus === 'function') { updateConnectionStatus('disconnected'); } - notifyHandlers('disconnect', { - code: event.code, - reason: event.reason + notifyHandlers('disconnect', { + code: event.code, + reason: event.reason }); - + if (!state.isIntentionallyClosed) { handleReconnect(); } @@ -292,12 +292,12 @@ const WebSocketManager = (function() { document.addEventListener('visibilitychange', visibilityChangeHandler); state.listeners.visibilityChange = visibilityChangeHandler; } - + function handlePageHidden() { log('Page hidden'); state.isPageHidden = true; stopHealthCheck(); - + if (ws && ws.readyState === WebSocket.OPEN) { state.isIntentionallyClosed = true; ws.close(1000, 'Page hidden'); @@ -308,7 +308,7 @@ const WebSocketManager = (function() { log('Page visible'); state.isPageHidden = false; state.isIntentionallyClosed = false; - + setTimeout(() => { if (!publicAPI.isConnected()) { publicAPI.connect(); @@ -323,7 +323,7 @@ const WebSocketManager = (function() { performHealthCheck(); }, 30000); } - + function stopHealthCheck() { if (state.healthCheckInterval) { clearInterval(state.healthCheckInterval); @@ -340,7 +340,7 @@ const WebSocketManager = (function() { const now = Date.now(); const lastCheck = state.lastHealthCheck; - + if (lastCheck && (now - lastCheck) > 60000) { log('Health check failed - too long since last check'); handleReconnect(); @@ -401,10 +401,10 @@ const WebSocketManager = (function() { function cleanup() { log('Cleaning up WebSocket resources'); - + clearTimeout(state.connectTimeout); stopHealthCheck(); - + if (state.reconnectTimeout) { clearTimeout(state.reconnectTimeout); state.reconnectTimeout = null; diff --git a/basicswap/static/js/new_offer.js b/basicswap/static/js/new_offer.js index c88df43..beaecab 100644 --- a/basicswap/static/js/new_offer.js +++ b/basicswap/static/js/new_offer.js @@ -61,7 +61,7 @@ const Ajax = { if (xhr.status === 200) { if (onSuccess) { try { - const response = xhr.responseText.startsWith('{') ? + const response = xhr.responseText.startsWith('{') ? JSON.parse(xhr.responseText) : xhr.responseText; onSuccess(response); } catch (e) { @@ -153,10 +153,10 @@ const RateManager = { const params = 'coin_from=' + selectedCoin + '&coin_to=' + coinTo; - Ajax.post('/json/rates', params, + Ajax.post('/json/rates', params, (response) => { if (ratesDisplay) { - ratesDisplay.innerHTML = typeof response === 'string' ? + ratesDisplay.innerHTML = typeof response === 'string' ? response : '
' + JSON.stringify(response, null, '  ') + '
'; } }, @@ -167,7 +167,7 @@ const RateManager = { } ); }, - + getRateInferred: (event) => { if (event) event.preventDefault(); @@ -180,12 +180,12 @@ const RateManager = { return; } - const params = 'coin_from=' + encodeURIComponent(coinFrom) + + const params = 'coin_from=' + encodeURIComponent(coinFrom) + '&coin_to=' + encodeURIComponent(coinTo); DOM.setValue('rate', 'Loading...'); - Ajax.post('/json/rates', params, + Ajax.post('/json/rates', params, (response) => { if (response.coingecko && response.coingecko.rate_inferred) { DOM.setValue('rate', response.coingecko.rate_inferred); @@ -213,7 +213,7 @@ const RateManager = { swapType: DOM.get('swap_type') }; - if (!elements.coinFrom || !elements.coinTo || + if (!elements.coinFrom || !elements.coinTo || !elements.amtFrom || !elements.amtTo || !elements.rate) { console.log('Required elements for setRate not found'); return; @@ -225,7 +225,7 @@ const RateManager = { amtFrom: elements.amtFrom.value, amtTo: elements.amtTo.value, rate: elements.rate.value, - lockRate: elements.rate.value == '' ? false : + lockRate: elements.rate.value == '' ? false : (elements.rateLock ? elements.rateLock.checked : false) }; @@ -236,8 +236,8 @@ const RateManager = { if (elements.swapType) { SwapTypeManager.setSwapTypeEnabled( - values.coinFrom, - values.coinTo, + values.coinFrom, + values.coinTo, elements.swapType ); } @@ -248,8 +248,8 @@ const RateManager = { let params = 'coin_from=' + values.coinFrom + '&coin_to=' + values.coinTo; - if (valueChanged == 'rate' || - (values.lockRate && valueChanged == 'amt_from') || + if (valueChanged == 'rate' || + (values.lockRate && valueChanged == 'amt_from') || (values.amtTo == '' && valueChanged == 'amt_from')) { if (values.rate == '' || (values.amtFrom == '' && values.amtTo == '')) { @@ -274,7 +274,7 @@ const RateManager = { params += '&amt_from=' + values.amtFrom + '&amt_to=' + values.amtTo; } - Ajax.post('/json/rate', params, + Ajax.post('/json/rate', params, (response) => { if (response.hasOwnProperty('rate')) { DOM.setValue('rate', response.rate); @@ -314,13 +314,13 @@ const SwapTypeManager = { coinFrom = String(coinFrom); coinTo = String(coinTo); - if (SwapTypeManager.adaptor_sig_only_coins.includes(coinFrom) || + if (SwapTypeManager.adaptor_sig_only_coins.includes(coinFrom) || SwapTypeManager.adaptor_sig_only_coins.includes(coinTo)) { swapTypeElement.disabled = true; swapTypeElement.value = 'xmr_swap'; makeHidden = true; swapTypeElement.classList.add('select-disabled'); - } else if (SwapTypeManager.secret_hash_only_coins.includes(coinFrom) || + } else if (SwapTypeManager.secret_hash_only_coins.includes(coinFrom) || SwapTypeManager.secret_hash_only_coins.includes(coinTo)) { swapTypeElement.disabled = true; swapTypeElement.value = 'seller_first'; @@ -426,7 +426,7 @@ const UIEnhancer = { const name = selectedOption.textContent.trim(); selectCache[select.id] = { image, name }; } - + function setSelectData(select) { if (!select || !select.options || select.selectedIndex === undefined) return; @@ -457,9 +457,9 @@ const UIEnhancer = { const options = select.querySelectorAll('option'); const selectIcon = select.parentElement?.querySelector('.select-icon'); const selectImage = select.parentElement?.querySelector('.select-image'); - + if (!options || !selectIcon || !selectImage) return; - + options.forEach(option => { const image = option.getAttribute('data-image'); if (image) { @@ -476,7 +476,7 @@ const UIEnhancer = { setSelectData(select); Storage.setRaw(select.name, select.value); }); - + setSelectData(select); selectIcon.style.display = 'none'; selectImage.style.display = 'none'; diff --git a/basicswap/static/js/pricechart.js b/basicswap/static/js/pricechart.js index 05e7147..1e785d1 100644 --- a/basicswap/static/js/pricechart.js +++ b/basicswap/static/js/pricechart.js @@ -145,7 +145,7 @@ const api = { const apiResponse = await Api.fetchCoinGeckoData({ coinGecko: window.config.getAPIKeys().coinGecko }); - + if (!apiResponse || !apiResponse.rates) { if (fallbackData) { return fallbackData; @@ -174,7 +174,7 @@ const api = { const wowResponse = await Api.fetchCoinPrices("wownero", { coinGecko: window.config.getAPIKeys().coinGecko }); - + if (wowResponse && wowResponse.rates && wowResponse.rates.wownero) { transformedData['wow'] = { current_price: wowResponse.rates.wownero, @@ -189,9 +189,9 @@ const api = { console.error('Error fetching WOW price:', wowError); } - const missingCoins = window.config.coins.filter(coin => - !transformedData[coin.symbol.toLowerCase()] && - fallbackData && + const missingCoins = window.config.coins.filter(coin => + !transformedData[coin.symbol.toLowerCase()] && + fallbackData && fallbackData[coin.symbol.toLowerCase()] ); @@ -203,11 +203,11 @@ const api = { }); CacheManager.set(cacheKey, transformedData, 'prices'); - + if (NetworkManager.getReconnectAttempts() > 0) { NetworkManager.resetReconnectAttempts(); } - + return transformedData; } catch (error) { console.error('Error fetching coin data:', error); @@ -250,8 +250,8 @@ const api = { } const historicalData = await Api.fetchHistoricalData( - coinSymbols, - window.config.currentResolution, + coinSymbols, + window.config.currentResolution, { cryptoCompare: window.config.getAPIKeys().cryptoCompare } @@ -260,7 +260,7 @@ const api = { Object.keys(historicalData).forEach(coin => { if (historicalData[coin]) { results[coin] = historicalData[coin]; - + const cacheKey = `historical_${coin}_${window.config.currentResolution}`; CacheManager.set(cacheKey, historicalData[coin], 'historical'); } @@ -279,7 +279,7 @@ const api = { results[coin] = cachedData.value; } } - + return results; } }, @@ -317,7 +317,7 @@ const rateLimiter = { try { await this.requestQueue[apiName]; - + const executeRequest = async () => { const waitTime = this.getWaitTime(apiName); if (waitTime > 0) { @@ -335,7 +335,7 @@ const rateLimiter = { return this.queueRequest(apiName, requestFn, retryCount + 1); } - if ((error.message.includes('timeout') || error.name === 'NetworkError') && + if ((error.message.includes('timeout') || error.name === 'NetworkError') && retryCount < this.retryDelays.length) { const delay = this.retryDelays[retryCount]; logger.warn(`Request failed, retrying in ${delay/1000} seconds...`); @@ -350,12 +350,12 @@ const rateLimiter = { this.requestQueue[apiName] = executeRequest(); return await this.requestQueue[apiName]; } catch (error) { - if (error.message.includes('429') || - error.message.includes('timeout') || + if (error.message.includes('429') || + error.message.includes('timeout') || error.name === 'NetworkError') { - + NetworkManager.handleNetworkError(error); - + const cachedData = CacheManager.get(`coinData_${apiName}`); if (cachedData) { return cachedData.value; @@ -375,7 +375,7 @@ const ui = { const volumeElement = document.querySelector(`#${coin.toLowerCase()}-volume-24h`); const btcPriceDiv = document.querySelector(`#${coin.toLowerCase()}-btc-price-div`); const priceBtcElement = document.querySelector(`#${coin.toLowerCase()}-price-btc`); - + if (priceUsdElement) { priceUsdElement.textContent = isError ? 'N/A' : `$ ${ui.formatPrice(coin, priceUSD)}`; } @@ -490,8 +490,8 @@ const ui = { if (priceChange === null || priceChange === undefined) { container.innerHTML = 'N/A'; } else { - container.innerHTML = priceChange >= 0 ? - ui.positivePriceChangeHTML(priceChange) : + container.innerHTML = priceChange >= 0 ? + ui.positivePriceChangeHTML(priceChange) : ui.negativePriceChangeHTML(priceChange); } } @@ -504,7 +504,7 @@ const ui = { lastRefreshedElement.textContent = `Last Refreshed: ${formattedTime}`; } }, - + updateConnectionStatus: () => { const statusElement = document.getElementById('connection-status'); if (statusElement) { @@ -599,12 +599,12 @@ const ui = { NetworkManager.manualReconnect(); }; - const buttonContainer = errorOverlay.querySelector('.button-container') || + const buttonContainer = errorOverlay.querySelector('.button-container') || document.createElement('div'); buttonContainer.className = "button-container mt-4"; buttonContainer.innerHTML = ''; buttonContainer.appendChild(reconnectBtn); - + if (!errorOverlay.querySelector('.button-container')) { errorOverlay.querySelector('div').appendChild(buttonContainer); } @@ -701,7 +701,7 @@ const chartModule = { const gradient = ctx.createLinearGradient(0, 0, 0, 400); gradient.addColorStop(0, 'rgba(77, 132, 240, 0.2)'); gradient.addColorStop(1, 'rgba(77, 132, 240, 0)'); - + chartModule.chart = new Chart(ctx, { type: 'line', data: { @@ -897,7 +897,7 @@ const chartModule = { } rawDataPoints.sort((a, b) => a.time - b.time); - + let preparedData = []; if (window.config.currentResolution === 'day') { @@ -918,7 +918,7 @@ const chartModule = { closestPoint = point; } } - + if (closestPoint) { preparedData.push({ x: hourUnix, @@ -940,7 +940,7 @@ const chartModule = { y: point.close })); } - + if (preparedData.length === 0 && rawDataPoints.length > 0) { preparedData = rawDataPoints.map(point => ({ x: point.time, @@ -965,7 +965,7 @@ const chartModule = { if (data.length > 0) { const closestDataPoint = data.reduce((prev, curr) => - Math.abs(new Date(curr.x).getTime() - targetTime.getTime()) < + Math.abs(new Date(curr.x).getTime() - targetTime.getTime()) < Math.abs(new Date(prev.x).getTime() - targetTime.getTime()) ? curr : prev , data[0]); hourlyData.push({ @@ -989,7 +989,7 @@ const chartModule = { } chartModule.loadStartTime = Date.now(); const cacheKey = `chartData_${coinSymbol}_${window.config.currentResolution}`; - let cachedData = !forceRefresh ? CacheManager.get(cacheKey) : null; + const cachedData = !forceRefresh ? CacheManager.get(cacheKey) : null; let data; if (cachedData && Object.keys(cachedData.value).length > 0) { data = cachedData.value; @@ -1001,7 +1001,7 @@ const chartModule = { const allData = await api.fetchHistoricalDataXHR([coinSymbol]); data = allData[coinSymbol]; - + if (!data || Object.keys(data).length === 0) { throw new Error(`No data returned for ${coinSymbol}`); } @@ -1009,7 +1009,7 @@ const chartModule = { CacheManager.set(cacheKey, data, 'chart'); } catch (error) { NetworkManager.handleNetworkError(error); - + if (error.message.includes('429') && currentChartData.length > 0) { console.warn(`Rate limit hit for ${coinSymbol}, maintaining current chart`); chartModule.hideChartLoader(); @@ -1041,7 +1041,7 @@ const chartModule = { chartModule.chart.options.scales.x.time.unit = 'hour'; } else { const resolution = window.config.chartConfig.resolutions[window.config.currentResolution]; - chartModule.chart.options.scales.x.time.unit = + chartModule.chart.options.scales.x.time.unit = resolution && resolution.interval === 'hourly' ? 'hour' : window.config.currentResolution === 'year' ? 'month' : 'day'; } @@ -1086,7 +1086,7 @@ const chartModule = { loader.classList.add('hidden'); chart.classList.remove('hidden'); }, - + cleanup: function() { this.destroyChart(); this.currentCoin = null; @@ -1181,7 +1181,7 @@ const app = { 0 ); }); - + return app; }, @@ -1223,13 +1223,13 @@ const app = { } } }, - + loadAllCoinData: async function() { try { if (!NetworkManager.isOnline()) { throw new Error('Network is offline'); } - + const allCoinData = await api.fetchCoinGeckoDataXHR(); if (allCoinData.error) { throw new Error(allCoinData.error); @@ -1242,7 +1242,7 @@ const app = { for (const coin of window.config.coins) { const coinData = allCoinData[coin.symbol.toLowerCase()]; - + if (coinData) { coinData.displayName = coin.displayName || coin.symbol; @@ -1409,7 +1409,7 @@ const app = { const lastGeckoRequest = rateLimiter.lastRequestTime['coingecko'] || 0; const timeSinceLastRequest = Date.now() - lastGeckoRequest; const waitTime = Math.max(0, rateLimiter.minRequestInterval.coingecko - timeSinceLastRequest); - + if (waitTime > 0) { const seconds = Math.ceil(waitTime / 1000); ui.displayErrorMessage(`Rate limit: Please wait ${seconds} seconds before refreshing`); @@ -1480,7 +1480,7 @@ const app = { if (cachedData && cachedData.value && cachedData.value.total_volume) { coinData.total_volume = cachedData.value.total_volume; } - if (cachedData && cachedData.value && cachedData.value.price_change_percentage_24h && + if (cachedData && cachedData.value && cachedData.value.price_change_percentage_24h && !coinData.price_change_percentage_24h) { coinData.price_change_percentage_24h = cachedData.value.price_change_percentage_24h; } @@ -1541,7 +1541,7 @@ const app = { let countdown = 10; ui.displayErrorMessage(`Refresh failed: ${error.message}. Please try again later. (${countdown}s)`); - + const countdownInterval = setInterval(() => { countdown--; if (countdown > 0) { @@ -1652,7 +1652,7 @@ const app = { if (!NetworkManager.isOnline()) { throw new Error('Network is offline'); } - + const response = await Api.fetchCoinPrices("bitcoin"); if (response && response.rates && response.rates.bitcoin) { @@ -1715,7 +1715,7 @@ resolutionButtons.forEach(button => { button.addEventListener('click', () => { const resolution = button.id.split('-')[1]; const currentCoin = chartModule.currentCoin; - + if (currentCoin !== 'WOW' || resolution === 'day') { window.config.currentResolution = resolution; chartModule.updateChart(currentCoin, true); @@ -1726,12 +1726,12 @@ resolutionButtons.forEach(button => { function cleanup() { console.log('Starting cleanup process'); - + try { if (window.MemoryManager) { MemoryManager.forceCleanup(); } - + if (chartModule) { CleanupManager.registerResource('chartModule', chartModule, (cm) => { cm.cleanup(); @@ -1752,7 +1752,7 @@ function cleanup() { const cleanupCounts = CleanupManager.clearAll(); console.log('All resources cleaned up:', cleanupCounts); - + } catch (error) { console.error('Error during cleanup:', error); CleanupManager.clearAll(); @@ -1801,7 +1801,7 @@ document.addEventListener('DOMContentLoaded', () => { NetworkManager.initialize({ connectionTestEndpoint: '/json', connectionTestTimeout: 3000, - reconnectDelay: 5000, + reconnectDelay: 5000, maxReconnectAttempts: 5 }); window.networkManagerInitialized = true; diff --git a/basicswap/static/js/swaps_in_progress.js b/basicswap/static/js/swaps_in_progress.js index 6efe3d7..3724bbd 100644 --- a/basicswap/static/js/swaps_in_progress.js +++ b/basicswap/static/js/swaps_in_progress.js @@ -85,7 +85,7 @@ const getStatusClass = (status, tx_a, tx_b) => { const getTxStatusClass = (status) => { if (!status || status === 'None') return 'text-gray-400'; - + if (status.includes('Complete') || status.includes('Confirmed')) { return 'text-green-500'; } @@ -545,12 +545,12 @@ async function updateSwapsTable(options = {}) { const data = await response.json(); //console.log('Received swap data:', data); - state.swapsData = Array.isArray(data) + state.swapsData = Array.isArray(data) ? data.filter(swap => { const isActive = isActiveSwap(swap); //console.log(`Swap ${swap.bid_id}: ${isActive ? 'Active' : 'Inactive'}`, swap.bid_state); return isActive; - }) + }) : []; //console.log('Filtered active swaps:', state.swapsData); @@ -570,7 +570,7 @@ async function updateSwapsTable(options = {}) { } const totalPages = Math.ceil(state.swapsData.length / PAGE_SIZE); - + if (resetPage && state.swapsData.length > 0) { state.currentPage = 1; } @@ -631,18 +631,18 @@ async function updateSwapsTable(options = {}) { function isActiveSwap(swap) { const activeStates = [ - 'InProgress', - 'Accepted', - 'Delaying', + 'InProgress', + 'Accepted', + 'Delaying', 'Auto accept delay', 'Request accepted', //'Received', - 'Script coin locked', + 'Script coin locked', 'Scriptless coin locked', 'Script coin lock released', - - 'SendingInitialTx', + + 'SendingInitialTx', 'SendingPaymentTx', 'Exchanged script lock tx sigs msg', diff --git a/basicswap/static/js/ui/dropdown.js b/basicswap/static/js/ui/dropdown.js index 788382d..ed0ec86 100644 --- a/basicswap/static/js/ui/dropdown.js +++ b/basicswap/static/js/ui/dropdown.js @@ -62,7 +62,7 @@ this._handleOutsideClick = this._handleOutsideClick.bind(this); dropdownInstances.push(this); - + this.init(); } @@ -73,7 +73,7 @@ this._targetEl.style.position = 'fixed'; this._targetEl.style.zIndex = '40'; this._targetEl.classList.add('dropdown-menu'); - + this._setupEventListeners(); this._initialized = true; } @@ -120,8 +120,8 @@ } _handleOutsideClick(e) { - if (this._visible && - !this._targetEl.contains(e.target) && + if (this._visible && + !this._targetEl.contains(e.target) && !this._triggerEl.contains(e.target)) { this.hide(); }