mirror of
https://github.com/basicswap/basicswap.git
synced 2025-01-22 10:34:34 +00:00
Fix potential sources of mem leaks.
This commit is contained in:
parent
65fbcda556
commit
b70e46ffc1
1 changed files with 281 additions and 185 deletions
|
@ -1,3 +1,66 @@
|
||||||
|
const EventManager = {
|
||||||
|
listeners: new Map(),
|
||||||
|
|
||||||
|
add(element, type, handler, options = false) {
|
||||||
|
if (!this.listeners.has(element)) {
|
||||||
|
this.listeners.set(element, new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
const elementListeners = this.listeners.get(element);
|
||||||
|
if (!elementListeners.has(type)) {
|
||||||
|
elementListeners.set(type, new Set());
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlerInfo = { handler, options };
|
||||||
|
elementListeners.get(type).add(handlerInfo);
|
||||||
|
element.addEventListener(type, handler, options);
|
||||||
|
|
||||||
|
return handlerInfo;
|
||||||
|
},
|
||||||
|
|
||||||
|
remove(element, type, handler, options = false) {
|
||||||
|
const elementListeners = this.listeners.get(element);
|
||||||
|
if (!elementListeners) return;
|
||||||
|
|
||||||
|
const typeListeners = elementListeners.get(type);
|
||||||
|
if (!typeListeners) return;
|
||||||
|
|
||||||
|
typeListeners.forEach(info => {
|
||||||
|
if (info.handler === handler) {
|
||||||
|
element.removeEventListener(type, handler, options);
|
||||||
|
typeListeners.delete(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeListeners.size === 0) {
|
||||||
|
elementListeners.delete(type);
|
||||||
|
}
|
||||||
|
if (elementListeners.size === 0) {
|
||||||
|
this.listeners.delete(element);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAll(element) {
|
||||||
|
const elementListeners = this.listeners.get(element);
|
||||||
|
if (!elementListeners) return;
|
||||||
|
|
||||||
|
elementListeners.forEach((typeListeners, type) => {
|
||||||
|
typeListeners.forEach(info => {
|
||||||
|
element.removeEventListener(type, info.handler, info.options);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.listeners.delete(element);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearAll() {
|
||||||
|
this.listeners.forEach((elementListeners, element) => {
|
||||||
|
this.removeAll(element);
|
||||||
|
});
|
||||||
|
this.listeners.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// GLOBAL STATE VARIABLES
|
// GLOBAL STATE VARIABLES
|
||||||
let latestPrices = null;
|
let latestPrices = null;
|
||||||
let lastRefreshTime = null;
|
let lastRefreshTime = null;
|
||||||
|
@ -168,6 +231,7 @@ const WebSocketManager = {
|
||||||
reconnectDelay: 5000,
|
reconnectDelay: 5000,
|
||||||
maxQueueSize: 1000,
|
maxQueueSize: 1000,
|
||||||
isIntentionallyClosed: false,
|
isIntentionallyClosed: false,
|
||||||
|
handlers: {},
|
||||||
|
|
||||||
connectionState: {
|
connectionState: {
|
||||||
isConnecting: false,
|
isConnecting: false,
|
||||||
|
@ -185,13 +249,15 @@ const WebSocketManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
setupPageVisibilityHandler() {
|
setupPageVisibilityHandler() {
|
||||||
document.addEventListener('visibilitychange', () => {
|
this.handlers.visibilityChange = () => {
|
||||||
if (document.hidden) {
|
if (document.hidden) {
|
||||||
this.handlePageHidden();
|
this.handlePageHidden();
|
||||||
} else {
|
} else {
|
||||||
this.handlePageVisible();
|
this.handlePageVisible();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', this.handlers.visibilityChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
handlePageHidden() {
|
handlePageHidden() {
|
||||||
|
@ -287,7 +353,7 @@ const WebSocketManager = {
|
||||||
setupEventHandlers() {
|
setupEventHandlers() {
|
||||||
if (!this.ws) return;
|
if (!this.ws) return;
|
||||||
|
|
||||||
this.ws.onopen = () => {
|
this.handlers.open = () => {
|
||||||
console.log('🟢 WebSocket connected successfully');
|
console.log('🟢 WebSocket connected successfully');
|
||||||
this.connectionState.isConnecting = false;
|
this.connectionState.isConnecting = false;
|
||||||
this.reconnectAttempts = 0;
|
this.reconnectAttempts = 0;
|
||||||
|
@ -297,7 +363,7 @@ const WebSocketManager = {
|
||||||
updateConnectionStatus('connected');
|
updateConnectionStatus('connected');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onmessage = (event) => {
|
this.handlers.message = (event) => {
|
||||||
try {
|
try {
|
||||||
const message = JSON.parse(event.data);
|
const message = JSON.parse(event.data);
|
||||||
this.handleMessage(message);
|
this.handleMessage(message);
|
||||||
|
@ -307,12 +373,12 @@ const WebSocketManager = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onerror = (error) => {
|
this.handlers.error = (error) => {
|
||||||
console.error('WebSocket error:', error);
|
console.error('WebSocket error:', error);
|
||||||
updateConnectionStatus('error');
|
updateConnectionStatus('error');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onclose = (event) => {
|
this.handlers.close = (event) => {
|
||||||
console.log('🔴 WebSocket closed:', event.code, event.reason);
|
console.log('🔴 WebSocket closed:', event.code, event.reason);
|
||||||
this.connectionState.isConnecting = false;
|
this.connectionState.isConnecting = false;
|
||||||
window.ws = null;
|
window.ws = null;
|
||||||
|
@ -322,7 +388,12 @@ const WebSocketManager = {
|
||||||
this.handleReconnect();
|
this.handleReconnect();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
this.ws.onopen = this.handlers.open;
|
||||||
|
this.ws.onmessage = this.handlers.message;
|
||||||
|
this.ws.onerror = this.handlers.error;
|
||||||
|
this.ws.onclose = this.handlers.close;
|
||||||
|
},
|
||||||
|
|
||||||
handleMessage(message) {
|
handleMessage(message) {
|
||||||
if (this.messageQueue.length >= this.maxQueueSize) {
|
if (this.messageQueue.length >= this.maxQueueSize) {
|
||||||
|
@ -330,7 +401,10 @@ const WebSocketManager = {
|
||||||
this.messageQueue.shift();
|
this.messageQueue.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.debounceTimeout) {
|
||||||
clearTimeout(this.debounceTimeout);
|
clearTimeout(this.debounceTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
this.messageQueue.push(message);
|
this.messageQueue.push(message);
|
||||||
|
|
||||||
this.debounceTimeout = setTimeout(() => {
|
this.debounceTimeout = setTimeout(() => {
|
||||||
|
@ -404,6 +478,7 @@ const WebSocketManager = {
|
||||||
clearTimeout(this.debounceTimeout);
|
clearTimeout(this.debounceTimeout);
|
||||||
clearTimeout(this.reconnectTimeout);
|
clearTimeout(this.reconnectTimeout);
|
||||||
clearTimeout(this.connectionState.connectTimeout);
|
clearTimeout(this.connectionState.connectTimeout);
|
||||||
|
this.stopHealthCheck();
|
||||||
|
|
||||||
this.messageQueue = [];
|
this.messageQueue = [];
|
||||||
this.processingQueue = false;
|
this.processingQueue = false;
|
||||||
|
@ -418,22 +493,31 @@ const WebSocketManager = {
|
||||||
if (this.ws.readyState === WebSocket.OPEN) {
|
if (this.ws.readyState === WebSocket.OPEN) {
|
||||||
this.ws.close(1000, 'Cleanup');
|
this.ws.close(1000, 'Cleanup');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ws = null;
|
this.ws = null;
|
||||||
window.ws = null;
|
window.ws = null;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
isConnected() {
|
if (this.handlers.visibilityChange) {
|
||||||
return this.ws && this.ws.readyState === WebSocket.OPEN;
|
document.removeEventListener('visibilitychange', this.handlers.visibilityChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handlers = {};
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this.isIntentionallyClosed = true;
|
this.isIntentionallyClosed = true;
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
this.stopHealthCheck();
|
this.stopHealthCheck();
|
||||||
|
},
|
||||||
|
|
||||||
|
isConnected() {
|
||||||
|
return this.ws && this.ws.readyState === WebSocket.OPEN;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.WebSocketManager = WebSocketManager;
|
||||||
|
|
||||||
const CacheManager = {
|
const CacheManager = {
|
||||||
maxItems: 100,
|
maxItems: 100,
|
||||||
maxSize: 5 * 1024 * 1024, // 5MB
|
maxSize: 5 * 1024 * 1024, // 5MB
|
||||||
|
@ -1405,8 +1489,9 @@ function updateClearFiltersButton() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupRow(row) {
|
function cleanupRow(row) {
|
||||||
|
EventManager.removeAll(row);
|
||||||
|
|
||||||
const tooltips = row.querySelectorAll('[data-tooltip-target]');
|
const tooltips = row.querySelectorAll('[data-tooltip-target]');
|
||||||
const count = tooltips.length;
|
|
||||||
tooltips.forEach(tooltip => {
|
tooltips.forEach(tooltip => {
|
||||||
const tooltipId = tooltip.getAttribute('data-tooltip-target');
|
const tooltipId = tooltip.getAttribute('data-tooltip-target');
|
||||||
const tooltipElement = document.getElementById(tooltipId);
|
const tooltipElement = document.getElementById(tooltipId);
|
||||||
|
@ -1414,7 +1499,18 @@ function cleanupRow(row) {
|
||||||
tooltipElement.remove();
|
tooltipElement.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//console.log(`Cleaned up ${count} tooltips from row`);
|
}
|
||||||
|
|
||||||
|
function cleanupTable() {
|
||||||
|
EventManager.clearAll();
|
||||||
|
|
||||||
|
if (offersBody) {
|
||||||
|
const existingRows = offersBody.querySelectorAll('tr');
|
||||||
|
existingRows.forEach(row => {
|
||||||
|
cleanupRow(row);
|
||||||
|
});
|
||||||
|
offersBody.innerHTML = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNoOffersScenario() {
|
function handleNoOffersScenario() {
|
||||||
|
@ -2436,46 +2532,74 @@ function getCoinSymbol(fullName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EVENT LISTENERS
|
// EVENT LISTENERS
|
||||||
document.querySelectorAll('th[data-sortable="true"]').forEach(header => {
|
function initializeTableEvents() {
|
||||||
header.addEventListener('click', () => {
|
const filterForm = document.getElementById('filterForm');
|
||||||
const columnIndex = parseInt(header.getAttribute('data-column-index'));
|
if (filterForm) {
|
||||||
|
EventManager.add(filterForm, 'submit', (e) => {
|
||||||
if (currentSortColumn === columnIndex) {
|
e.preventDefault();
|
||||||
currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
|
|
||||||
} else {
|
|
||||||
|
|
||||||
currentSortColumn = columnIndex;
|
|
||||||
currentSortDirection = 'desc';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelectorAll('.sort-icon').forEach(icon => {
|
|
||||||
icon.classList.remove('text-blue-500');
|
|
||||||
icon.textContent = '↓';
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortIcon = document.getElementById(`sort-icon-${columnIndex}`);
|
|
||||||
if (sortIcon) {
|
|
||||||
sortIcon.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
|
|
||||||
sortIcon.classList.add('text-blue-500');
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelectorAll('th[data-sortable="true"]').forEach(th => {
|
|
||||||
const thColumnIndex = parseInt(th.getAttribute('data-column-index'));
|
|
||||||
if (thColumnIndex === columnIndex) {
|
|
||||||
th.classList.add('text-blue-500');
|
|
||||||
} else {
|
|
||||||
th.classList.remove('text-blue-500');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
localStorage.setItem('tableSortColumn', currentSortColumn);
|
|
||||||
localStorage.setItem('tableSortDirection', currentSortDirection);
|
|
||||||
|
|
||||||
applyFilters();
|
applyFilters();
|
||||||
});
|
});
|
||||||
|
|
||||||
header.classList.add('cursor-pointer', 'hover:bg-gray-100', 'dark:hover:bg-gray-700');
|
EventManager.add(filterForm, 'change', () => {
|
||||||
});
|
applyFilters();
|
||||||
|
updateClearFiltersButton();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const coinToSelect = document.getElementById('coin_to');
|
||||||
|
const coinFromSelect = document.getElementById('coin_from');
|
||||||
|
|
||||||
|
if (coinToSelect) {
|
||||||
|
EventManager.add(coinToSelect, 'change', () => {
|
||||||
|
applyFilters();
|
||||||
|
updateCoinFilterImages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coinFromSelect) {
|
||||||
|
EventManager.add(coinFromSelect, 'change', () => {
|
||||||
|
applyFilters();
|
||||||
|
updateCoinFilterImages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearFiltersBtn = document.getElementById('clearFilters');
|
||||||
|
if (clearFiltersBtn) {
|
||||||
|
EventManager.add(clearFiltersBtn, 'click', () => {
|
||||||
|
clearFilters();
|
||||||
|
updateCoinFilterImages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('th[data-sortable="true"]').forEach(header => {
|
||||||
|
EventManager.add(header, 'click', () => {
|
||||||
|
const columnIndex = parseInt(header.getAttribute('data-column-index'));
|
||||||
|
handleTableSort(columnIndex, header);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const prevPageButton = document.getElementById('prevPage');
|
||||||
|
const nextPageButton = document.getElementById('nextPage');
|
||||||
|
|
||||||
|
if (prevPageButton) {
|
||||||
|
EventManager.add(prevPageButton, 'click', () => {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--;
|
||||||
|
updateOffersTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextPageButton) {
|
||||||
|
EventManager.add(nextPageButton, 'click', () => {
|
||||||
|
const totalPages = Math.ceil(jsonData.length / itemsPerPage);
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
currentPage++;
|
||||||
|
updateOffersTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const eventListeners = {
|
const eventListeners = {
|
||||||
listeners: [],
|
listeners: [],
|
||||||
|
@ -2518,6 +2642,60 @@ const eventListeners = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function handleTableSort(columnIndex, header) {
|
||||||
|
if (currentSortColumn === columnIndex) {
|
||||||
|
currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
|
||||||
|
} else {
|
||||||
|
currentSortColumn = columnIndex;
|
||||||
|
currentSortDirection = 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.sort-icon').forEach(icon => {
|
||||||
|
icon.classList.remove('text-blue-500');
|
||||||
|
icon.textContent = '↓';
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortIcon = document.getElementById(`sort-icon-${columnIndex}`);
|
||||||
|
if (sortIcon) {
|
||||||
|
sortIcon.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
|
||||||
|
sortIcon.classList.add('text-blue-500');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('th[data-sortable="true"]').forEach(th => {
|
||||||
|
if (th === header) {
|
||||||
|
th.classList.add('text-blue-500');
|
||||||
|
} else {
|
||||||
|
th.classList.remove('text-blue-500');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
localStorage.setItem('tableSortColumn', currentSortColumn);
|
||||||
|
localStorage.setItem('tableSortDirection', currentSortDirection);
|
||||||
|
|
||||||
|
applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupRowEventListeners(row, offer) {
|
||||||
|
const tooltipTriggers = row.querySelectorAll('[data-tooltip-target]');
|
||||||
|
tooltipTriggers.forEach(trigger => {
|
||||||
|
EventManager.add(trigger, 'mouseenter', () => {
|
||||||
|
const tooltipId = trigger.getAttribute('data-tooltip-target');
|
||||||
|
const tooltip = document.getElementById(tooltipId);
|
||||||
|
if (tooltip) {
|
||||||
|
tooltip.classList.remove('invisible', 'opacity-0');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
EventManager.add(trigger, 'mouseleave', () => {
|
||||||
|
const tooltipId = trigger.getAttribute('data-tooltip-target');
|
||||||
|
const tooltip = document.getElementById(tooltipId);
|
||||||
|
if (tooltip) {
|
||||||
|
tooltip.classList.add('invisible', 'opacity-0');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TIMER MANAGEMENT
|
// TIMER MANAGEMENT
|
||||||
const timerManager = {
|
const timerManager = {
|
||||||
intervals: [],
|
intervals: [],
|
||||||
|
@ -2553,23 +2731,11 @@ const timerManager = {
|
||||||
|
|
||||||
// INITIALIZATION AND EVENT BINDING
|
// INITIALIZATION AND EVENT BINDING
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
//console.log('DOM content loaded, initializing...');
|
console.log('DOM content loaded, initializing...');
|
||||||
console.log('View type:', isSentOffers ? 'sent offers' : 'received offers');
|
console.log('View type:', isSentOffers ? 'sent offers' : 'received offers');
|
||||||
|
|
||||||
updateClearFiltersButton();
|
updateClearFiltersButton();
|
||||||
|
initializeTableEvents();
|
||||||
// Add event listeners for filter controls
|
|
||||||
const selectElements = filterForm.querySelectorAll('select');
|
|
||||||
selectElements.forEach(select => {
|
|
||||||
select.addEventListener('change', () => {
|
|
||||||
updateClearFiltersButton();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
filterForm.addEventListener('change', () => {
|
|
||||||
applyFilters();
|
|
||||||
updateClearFiltersButton();
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('Starting WebSocket initialization...');
|
console.log('Starting WebSocket initialization...');
|
||||||
|
@ -2587,108 +2753,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
clearInterval(retryInterval);
|
clearInterval(retryInterval);
|
||||||
continueInitialization();
|
continueInitialization();
|
||||||
} else if (retryCount >= maxRetries) {
|
} else if (retryCount >= maxRetries) {
|
||||||
//console.error('Failed to load tableRateModule after multiple attempts');
|
|
||||||
clearInterval(retryInterval);
|
clearInterval(retryInterval);
|
||||||
continueInitialization();
|
continueInitialization();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
eventListeners.add(filterForm, 'submit', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
applyFilters();
|
|
||||||
});
|
|
||||||
|
|
||||||
eventListeners.add(filterForm, 'change', applyFilters);
|
|
||||||
|
|
||||||
const coinToSelect = document.getElementById('coin_to');
|
|
||||||
const coinFromSelect = document.getElementById('coin_from');
|
|
||||||
|
|
||||||
eventListeners.add(coinToSelect, 'change', () => {
|
|
||||||
applyFilters();
|
|
||||||
updateCoinFilterImages();
|
|
||||||
});
|
|
||||||
|
|
||||||
eventListeners.add(coinFromSelect, 'change', () => {
|
|
||||||
applyFilters();
|
|
||||||
updateCoinFilterImages();
|
|
||||||
});
|
|
||||||
|
|
||||||
eventListeners.add(document.getElementById('clearFilters'), 'click', () => {
|
|
||||||
filterForm.reset();
|
|
||||||
const statusSelect = document.getElementById('status');
|
|
||||||
if (statusSelect) {
|
|
||||||
statusSelect.value = 'any';
|
|
||||||
}
|
|
||||||
jsonData = [...originalJsonData];
|
|
||||||
currentPage = 1;
|
|
||||||
applyFilters();
|
|
||||||
updateCoinFilterImages();
|
|
||||||
});
|
|
||||||
|
|
||||||
eventListeners.add(document.getElementById('refreshOffers'), 'click', async () => {
|
|
||||||
console.log('Manual refresh initiated');
|
|
||||||
|
|
||||||
const refreshButton = document.getElementById('refreshOffers');
|
|
||||||
const refreshIcon = document.getElementById('refreshIcon');
|
|
||||||
const refreshText = document.getElementById('refreshText');
|
|
||||||
|
|
||||||
refreshButton.disabled = true;
|
|
||||||
refreshIcon.classList.add('animate-spin');
|
|
||||||
refreshText.textContent = 'Refreshing...';
|
|
||||||
refreshButton.classList.add('opacity-75', 'cursor-wait');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const endpoint = isSentOffers ? '/json/sentoffers' : '/json/offers';
|
|
||||||
const response = await fetch(endpoint);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
}
|
|
||||||
const newData = await response.json();
|
|
||||||
|
|
||||||
const processedNewData = Array.isArray(newData) ? newData : Object.values(newData);
|
|
||||||
console.log('Fetched offers:', processedNewData.length);
|
|
||||||
|
|
||||||
jsonData = formatInitialData(processedNewData);
|
|
||||||
originalJsonData = [...jsonData];
|
|
||||||
|
|
||||||
await updateOffersTable();
|
|
||||||
updateJsonView();
|
|
||||||
updatePaginationInfo();
|
|
||||||
|
|
||||||
console.log(' Manual refresh completed successfully');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error during manual refresh:', error);
|
|
||||||
ui.displayErrorMessage('Failed to refresh offers. Please try again later.');
|
|
||||||
} finally {
|
|
||||||
refreshButton.disabled = false;
|
|
||||||
refreshIcon.classList.remove('animate-spin');
|
|
||||||
refreshText.textContent = 'Refresh';
|
|
||||||
refreshButton.classList.remove('opacity-75', 'cursor-wait');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
eventListeners.add(prevPageButton, 'click', () => {
|
|
||||||
if (currentPage > 1) {
|
|
||||||
currentPage--;
|
|
||||||
const validOffers = getValidOffers();
|
|
||||||
const totalPages = Math.ceil(validOffers.length / itemsPerPage);
|
|
||||||
updateOffersTable();
|
|
||||||
updatePaginationControls(totalPages);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
eventListeners.add(nextPageButton, 'click', () => {
|
|
||||||
const validOffers = getValidOffers();
|
|
||||||
const totalPages = Math.ceil(validOffers.length / itemsPerPage);
|
|
||||||
if (currentPage < totalPages) {
|
|
||||||
currentPage++;
|
|
||||||
updateOffersTable();
|
|
||||||
updatePaginationControls(totalPages);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
timerManager.addInterval(() => {
|
timerManager.addInterval(() => {
|
||||||
if (WebSocketManager.isConnected()) {
|
if (WebSocketManager.isConnected()) {
|
||||||
console.log('WebSocket Status: Connected');
|
console.log('WebSocket Status: Connected');
|
||||||
|
@ -2701,7 +2771,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
updateCoinFilterImages();
|
updateCoinFilterImages();
|
||||||
fetchOffers().then(() => {
|
fetchOffers().then(() => {
|
||||||
//console.log('Initial offers fetched');
|
|
||||||
applyFilters();
|
applyFilters();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('Error fetching initial offers:', error);
|
console.error('Error fetching initial offers:', error);
|
||||||
|
@ -2714,7 +2783,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
timerManager.addInterval(updateRowTimes, 900000);
|
timerManager.addInterval(updateRowTimes, 900000);
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', () => {
|
EventManager.add(document, 'visibilitychange', () => {
|
||||||
if (!document.hidden) {
|
if (!document.hidden) {
|
||||||
console.log('Page became visible, checking WebSocket connection');
|
console.log('Page became visible, checking WebSocket connection');
|
||||||
if (!WebSocketManager.isConnected()) {
|
if (!WebSocketManager.isConnected()) {
|
||||||
|
@ -2723,7 +2792,34 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EventManager.add(window, 'beforeunload', () => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
console.log('Initialization completed');
|
console.log('Initialization completed');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
console.log('Cleaning up resources...');
|
||||||
|
|
||||||
|
EventManager.clearAll();
|
||||||
|
|
||||||
|
WebSocketManager.cleanup();
|
||||||
|
|
||||||
|
timerManager.clearAll();
|
||||||
|
|
||||||
|
cleanupTable();
|
||||||
|
|
||||||
|
CacheManager.clear();
|
||||||
|
|
||||||
|
currentPage = 1;
|
||||||
|
jsonData = [];
|
||||||
|
originalJsonData = [];
|
||||||
|
currentSortColumn = 0;
|
||||||
|
currentSortDirection = 'desc';
|
||||||
|
|
||||||
|
console.log('Cleanup completed');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.cleanup = cleanup;
|
||||||
console.log('Offers Table Module fully initialized');
|
console.log('Offers Table Module fully initialized');
|
||||||
|
|
Loading…
Reference in a new issue