mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-14 22:38:10 +01:00
AMM (#310)
* AMM * LINT + Fixes * Remove unused global variables. * BLACK * BLACK * AMM - Various Fixes/Features/Bug Fixes. * FLAKE * FLAKE * BLACK * Small fix * Fix * Auto-start option AMM + Various fixes/bugs/styling. * Updated createoffers.py * BLACK * AMM Styling * Update bid_xmr template confirm model. * Fixed bug with Create Default Configuration + Added confirm modal. * Fix: Better redirect. * Fixed adjust_rates_based_on_market + Removed debug / extra logging + Various fixes. * GUI v3.2.2 * Fix sub-header your-offers count when created offers by AMM. * Fix math. * Added USD prices + Add offers/bids checkbox enabled always checked. * Donation page. * Updated header.html + Typo. * Update on createoffer.py + BLACK * AMM: html updates. * AMM: Add all, minrate, and static options. * AMM: Amount step default 0.001 * Fix global settings. * Update createoffers.py * Fixed bug with autostart when save global settings + Various layout fixes. * Fixed bug with autostart with add/edit + Added new option Orderbook (Auto-Accept) * Fixed debug + New feature attempt bids first. * Fix: Orderbook (Auto-Accept) * Added bidding strategy: Only bid on auto-accept offers (best rates from auto-accept only) * Fix: with_extra_info * Small fix automation_strat_id * Various fixes. * Final fixes
This commit is contained in:
255
basicswap/static/js/amm_counter.js
Normal file
255
basicswap/static/js/amm_counter.js
Normal file
@@ -0,0 +1,255 @@
|
||||
const AmmCounterManager = (function() {
|
||||
const config = {
|
||||
refreshInterval: 10000,
|
||||
ammStatusEndpoint: '/amm/status',
|
||||
retryDelay: 5000,
|
||||
maxRetries: 3,
|
||||
debug: false
|
||||
};
|
||||
|
||||
let refreshTimer = null;
|
||||
let fetchRetryCount = 0;
|
||||
let lastAmmStatus = null;
|
||||
|
||||
function isDebugEnabled() {
|
||||
return localStorage.getItem('amm_debug_enabled') === 'true' || config.debug;
|
||||
}
|
||||
|
||||
function debugLog(message, data) {
|
||||
// if (isDebugEnabled()) {
|
||||
// if (data) {
|
||||
// console.log(`[AmmCounter] ${message}`, data);
|
||||
// } else {
|
||||
// console.log(`[AmmCounter] ${message}`);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
function updateAmmCounter(count, status) {
|
||||
const ammCounter = document.getElementById('amm-counter');
|
||||
const ammCounterMobile = document.getElementById('amm-counter-mobile');
|
||||
|
||||
debugLog(`Updating AMM counter: count=${count}, status=${status}`);
|
||||
|
||||
if (ammCounter) {
|
||||
ammCounter.textContent = count;
|
||||
ammCounter.classList.remove('bg-blue-500', 'bg-gray-400');
|
||||
ammCounter.classList.add(status === 'running' && count > 0 ? 'bg-blue-500' : 'bg-gray-400');
|
||||
}
|
||||
|
||||
if (ammCounterMobile) {
|
||||
ammCounterMobile.textContent = count;
|
||||
ammCounterMobile.classList.remove('bg-blue-500', 'bg-gray-400');
|
||||
ammCounterMobile.classList.add(status === 'running' && count > 0 ? 'bg-blue-500' : 'bg-gray-400');
|
||||
}
|
||||
|
||||
updateAmmTooltips(count, status);
|
||||
}
|
||||
|
||||
function updateAmmTooltips(count, status) {
|
||||
debugLog(`updateAmmTooltips called with count=${count}, status=${status}`);
|
||||
|
||||
const subheaderTooltip = document.getElementById('tooltip-amm-subheader');
|
||||
debugLog('Looking for tooltip-amm-subheader element:', subheaderTooltip);
|
||||
|
||||
if (subheaderTooltip) {
|
||||
const statusText = status === 'running' ? 'Active' : 'Inactive';
|
||||
|
||||
const newContent = `
|
||||
<p><b>Status:</b> ${statusText}</p>
|
||||
<p><b>Currently active offers/bids:</b> ${count}</p>
|
||||
`;
|
||||
|
||||
const statusParagraph = subheaderTooltip.querySelector('p:first-child');
|
||||
const countParagraph = subheaderTooltip.querySelector('p:last-child');
|
||||
|
||||
if (statusParagraph && countParagraph) {
|
||||
statusParagraph.innerHTML = `<b>Status:</b> ${statusText}`;
|
||||
countParagraph.innerHTML = `<b>Currently active offers/bids:</b> ${count}`;
|
||||
debugLog(`Updated AMM subheader tooltip paragraphs: status=${statusText}, count=${count}`);
|
||||
} else {
|
||||
subheaderTooltip.innerHTML = newContent;
|
||||
debugLog(`Replaced AMM subheader tooltip content: status=${statusText}, count=${count}`);
|
||||
}
|
||||
|
||||
refreshTooltipInstances('tooltip-amm-subheader', newContent);
|
||||
} else {
|
||||
debugLog('AMM subheader tooltip element not found - checking all tooltip elements');
|
||||
const allTooltips = document.querySelectorAll('[id*="tooltip"]');
|
||||
debugLog('All tooltip elements found:', Array.from(allTooltips).map(el => el.id));
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTooltipInstances(tooltipId, newContent) {
|
||||
const triggers = document.querySelectorAll(`[data-tooltip-target="${tooltipId}"]`);
|
||||
|
||||
triggers.forEach(trigger => {
|
||||
if (trigger._tippy) {
|
||||
trigger._tippy.setContent(newContent);
|
||||
debugLog(`Updated Tippy instance content for ${tooltipId}`);
|
||||
} else {
|
||||
if (window.TooltipManager && typeof window.TooltipManager.create === 'function') {
|
||||
window.TooltipManager.create(trigger, newContent, {
|
||||
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
|
||||
});
|
||||
debugLog(`Created new Tippy instance for ${tooltipId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.refreshTooltip === 'function') {
|
||||
window.TooltipManager.refreshTooltip(tooltipId, newContent);
|
||||
debugLog(`Refreshed tooltip via TooltipManager for ${tooltipId}`);
|
||||
}
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.initializeTooltips === 'function') {
|
||||
setTimeout(() => {
|
||||
window.TooltipManager.initializeTooltips(`[data-tooltip-target="${tooltipId}"]`);
|
||||
debugLog(`Re-initialized tooltips for ${tooltipId}`);
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
function fetchAmmStatus() {
|
||||
debugLog('Fetching AMM status...');
|
||||
|
||||
let url = config.ammStatusEndpoint;
|
||||
if (isDebugEnabled()) {
|
||||
url += '?debug=true';
|
||||
}
|
||||
|
||||
return fetch(url, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Pragma': 'no-cache'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
lastAmmStatus = data;
|
||||
debugLog('AMM status data received:', data);
|
||||
updateAmmCounter(data.amm_active_count, data.status);
|
||||
fetchRetryCount = 0;
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
if (isDebugEnabled()) {
|
||||
console.error('[AmmCounter] AMM status fetch error:', error);
|
||||
}
|
||||
|
||||
if (fetchRetryCount < config.maxRetries) {
|
||||
fetchRetryCount++;
|
||||
debugLog(`Retrying AMM status fetch (${fetchRetryCount}/${config.maxRetries}) in ${config.retryDelay/1000}s`);
|
||||
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(fetchAmmStatus());
|
||||
}, config.retryDelay);
|
||||
});
|
||||
} else {
|
||||
fetchRetryCount = 0;
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startRefreshTimer() {
|
||||
stopRefreshTimer();
|
||||
|
||||
debugLog('Starting AMM status refresh timer');
|
||||
|
||||
fetchAmmStatus()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
|
||||
refreshTimer = setInterval(() => {
|
||||
fetchAmmStatus()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
}, config.refreshInterval);
|
||||
}
|
||||
|
||||
function stopRefreshTimer() {
|
||||
if (refreshTimer) {
|
||||
debugLog('Stopping AMM status refresh timer');
|
||||
clearInterval(refreshTimer);
|
||||
refreshTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function setupWebSocketHandler() {
|
||||
if (window.WebSocketManager && typeof window.WebSocketManager.addMessageHandler === 'function') {
|
||||
debugLog('Setting up WebSocket handler for AMM status updates');
|
||||
window.WebSocketManager.addMessageHandler('message', (data) => {
|
||||
if (data && data.event) {
|
||||
debugLog('WebSocket event received, refreshing AMM status');
|
||||
fetchAmmStatus()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setupDebugListener() {
|
||||
const debugCheckbox = document.getElementById('amm_debug');
|
||||
if (debugCheckbox) {
|
||||
debugLog('Found AMM debug checkbox, setting up listener');
|
||||
|
||||
localStorage.setItem('amm_debug_enabled', debugCheckbox.checked ? 'true' : 'false');
|
||||
|
||||
debugCheckbox.addEventListener('change', function() {
|
||||
localStorage.setItem('amm_debug_enabled', this.checked ? 'true' : 'false');
|
||||
debugLog(`Debug mode ${this.checked ? 'enabled' : 'disabled'}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const publicAPI = {
|
||||
initialize: function(options = {}) {
|
||||
Object.assign(config, options);
|
||||
|
||||
setupWebSocketHandler();
|
||||
setupDebugListener();
|
||||
startRefreshTimer();
|
||||
|
||||
debugLog('AMM Counter Manager initialized');
|
||||
|
||||
if (window.CleanupManager) {
|
||||
window.CleanupManager.registerResource('ammCounterManager', this, (mgr) => mgr.dispose());
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
fetchAmmStatus: fetchAmmStatus,
|
||||
|
||||
updateCounter: updateAmmCounter,
|
||||
|
||||
updateTooltips: updateAmmTooltips,
|
||||
|
||||
startRefreshTimer: startRefreshTimer,
|
||||
|
||||
stopRefreshTimer: stopRefreshTimer,
|
||||
|
||||
dispose: function() {
|
||||
debugLog('Disposing AMM Counter Manager');
|
||||
stopRefreshTimer();
|
||||
}
|
||||
};
|
||||
|
||||
return publicAPI;
|
||||
})();
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (!window.ammCounterManagerInitialized) {
|
||||
window.AmmCounterManager = AmmCounterManager.initialize();
|
||||
window.ammCounterManagerInitialized = true;
|
||||
}
|
||||
});
|
||||
2367
basicswap/static/js/amm_tables.js
Normal file
2367
basicswap/static/js/amm_tables.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ const state = {
|
||||
|
||||
document.addEventListener('tabactivated', function(event) {
|
||||
if (event.detail && event.detail.tabId) {
|
||||
const tabType = event.detail.type || (event.detail.tabId === '#all' ? 'all' :
|
||||
const tabType = event.detail.type || (event.detail.tabId === '#all' ? 'all' :
|
||||
(event.detail.tabId === '#sent' ? 'sent' : 'received'));
|
||||
//console.log('Tab activation event received for:', tabType);
|
||||
state.currentTab = tabType;
|
||||
@@ -555,7 +555,7 @@ function filterAndSortData(bids) {
|
||||
const coinName = selectedOption?.textContent.trim();
|
||||
|
||||
if (coinName) {
|
||||
const coinToMatch = state.currentTab === 'all'
|
||||
const coinToMatch = state.currentTab === 'all'
|
||||
? (bid.source === 'sent' ? bid.coin_to : bid.coin_from)
|
||||
: (state.currentTab === 'sent' ? bid.coin_to : bid.coin_from);
|
||||
if (!coinMatches(coinToMatch, coinName)) {
|
||||
@@ -614,7 +614,7 @@ function filterAndSortData(bids) {
|
||||
let matchesDisplayedLabel = false;
|
||||
if (!matchesLabel && document) {
|
||||
try {
|
||||
const tableId = state.currentTab === 'sent' ? 'sent' :
|
||||
const tableId = state.currentTab === 'sent' ? 'sent' :
|
||||
(state.currentTab === 'received' ? 'received' : 'all');
|
||||
const cells = document.querySelectorAll(`#${tableId} a[href^="/identity/"]`);
|
||||
|
||||
@@ -1056,6 +1056,24 @@ async function fetchAllBids() {
|
||||
receivedResponse.json()
|
||||
]);
|
||||
|
||||
if (sentData.error || receivedData.error) {
|
||||
const errorData = sentData.error ? sentData : receivedData;
|
||||
if (errorData.locked) {
|
||||
const tbody = elements.allBidsBody;
|
||||
if (tbody) {
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-yellow-600 dark:text-yellow-400">
|
||||
${errorData.error}
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
return [];
|
||||
} else {
|
||||
throw new Error(errorData.error);
|
||||
}
|
||||
}
|
||||
|
||||
const filteredSentData = filterAndSortData(sentData).map(bid => ({ ...bid, source: 'sent' }));
|
||||
const filteredReceivedData = filterAndSortData(receivedData).map(bid => ({ ...bid, source: 'received' }));
|
||||
|
||||
@@ -1090,7 +1108,7 @@ const createTableRow = async (bid) => {
|
||||
const timeColor = getTimeStrokeColor(bid.expire_at);
|
||||
const currentTabIsAll = state.currentTab === 'all';
|
||||
const isSent = currentTabIsAll ? (bid.source === 'sent') : (state.currentTab === 'sent');
|
||||
const sourceIndicator = currentTabIsAll ?
|
||||
const sourceIndicator = currentTabIsAll ?
|
||||
`<span class="ml-1 px-1.5 py-0.5 text-xs font-medium ${isSent ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' : 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'} rounded">
|
||||
${isSent ? 'Sent' : 'Received'}
|
||||
</span>` : '';
|
||||
@@ -1321,6 +1339,24 @@ async function fetchBids(type = state.currentTab) {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
if (data.locked) {
|
||||
const tbody = elements[`${type}BidsBody`];
|
||||
if (tbody) {
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-yellow-600 dark:text-yellow-400">
|
||||
${data.error}
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
return [];
|
||||
} else {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(`Received raw ${type} data:`, data.length, 'bids');
|
||||
|
||||
state.filters.with_expired = includeExpired;
|
||||
@@ -1509,14 +1545,13 @@ const updateBidsTable = async () => {
|
||||
updateLoadingState(true);
|
||||
|
||||
let bids;
|
||||
|
||||
|
||||
if (state.currentTab === 'all') {
|
||||
bids = await fetchAllBids();
|
||||
} else {
|
||||
bids = await fetchBids();
|
||||
}
|
||||
|
||||
// Add identity preloading if we're searching
|
||||
if (state.filters.searchQuery && state.filters.searchQuery.length > 0) {
|
||||
await preloadIdentitiesForSearch(bids);
|
||||
}
|
||||
@@ -1774,7 +1809,7 @@ const setupRefreshButtons = () => {
|
||||
|
||||
state.data[lowerType] = data;
|
||||
}
|
||||
|
||||
|
||||
await updateTableContent(lowerType);
|
||||
updatePaginationControls(lowerType);
|
||||
|
||||
@@ -1802,7 +1837,7 @@ const switchTab = (tabId) => {
|
||||
|
||||
tooltipIdsToCleanup.clear();
|
||||
|
||||
state.currentTab = tabId === '#all' ? 'all' :
|
||||
state.currentTab = tabId === '#all' ? 'all' :
|
||||
(tabId === '#sent' ? 'sent' : 'received');
|
||||
|
||||
elements.allContent.classList.add('hidden');
|
||||
@@ -1888,7 +1923,7 @@ const setupEventListeners = () => {
|
||||
elements.sentContent.classList.toggle('hidden', targetId !== '#sent');
|
||||
elements.receivedContent.classList.toggle('hidden', targetId !== '#received');
|
||||
|
||||
state.currentTab = targetId === '#all' ? 'all' :
|
||||
state.currentTab = targetId === '#all' ? 'all' :
|
||||
(targetId === '#sent' ? 'sent' : 'received');
|
||||
state.currentPage[state.currentTab] = 1;
|
||||
|
||||
@@ -2101,7 +2136,7 @@ function initialize() {
|
||||
}, 100);
|
||||
|
||||
setupMemoryMonitoring();
|
||||
|
||||
|
||||
window.cleanupBidsTable = cleanup;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ const SummaryManager = (function() {
|
||||
summaryEndpoint: '/json',
|
||||
retryDelay: 5000,
|
||||
maxRetries: 3,
|
||||
requestTimeout: 15000
|
||||
requestTimeout: 15000,
|
||||
debug: false
|
||||
};
|
||||
|
||||
let refreshTimer = null;
|
||||
@@ -60,12 +61,15 @@ const SummaryManager = (function() {
|
||||
|
||||
updateElement('network-offers-counter', data.num_network_offers);
|
||||
updateElement('offers-counter', data.num_sent_active_offers);
|
||||
updateElement('offers-counter-mobile', data.num_sent_active_offers);
|
||||
updateElement('sent-bids-counter', data.num_sent_active_bids);
|
||||
updateElement('recv-bids-counter', data.num_recv_active_bids);
|
||||
updateElement('bid-requests-counter', data.num_available_bids);
|
||||
updateElement('swaps-counter', data.num_swapping);
|
||||
updateElement('watched-outputs-counter', data.num_watched_outputs);
|
||||
|
||||
updateTooltips(data);
|
||||
|
||||
const shutdownButtons = document.querySelectorAll('.shutdown-button');
|
||||
shutdownButtons.forEach(button => {
|
||||
button.setAttribute('data-active-swaps', data.num_swapping);
|
||||
@@ -81,6 +85,100 @@ const SummaryManager = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateTooltips(data) {
|
||||
debugLog(`updateTooltips called with data:`, data);
|
||||
|
||||
const yourOffersTooltip = document.getElementById('tooltip-your-offers');
|
||||
debugLog('Looking for tooltip-your-offers element:', yourOffersTooltip);
|
||||
|
||||
if (yourOffersTooltip) {
|
||||
const newContent = `
|
||||
<p><b>Total offers:</b> ${data.num_sent_offers || 0}</p>
|
||||
<p><b>Active offers:</b> ${data.num_sent_active_offers || 0}</p>
|
||||
`;
|
||||
|
||||
const totalParagraph = yourOffersTooltip.querySelector('p:first-child');
|
||||
const activeParagraph = yourOffersTooltip.querySelector('p:last-child');
|
||||
|
||||
debugLog('Found paragraphs:', { totalParagraph, activeParagraph });
|
||||
|
||||
if (totalParagraph && activeParagraph) {
|
||||
totalParagraph.innerHTML = `<b>Total offers:</b> ${data.num_sent_offers || 0}`;
|
||||
activeParagraph.innerHTML = `<b>Active offers:</b> ${data.num_sent_active_offers || 0}`;
|
||||
debugLog(`Updated Your Offers tooltip paragraphs: total=${data.num_sent_offers}, active=${data.num_sent_active_offers}`);
|
||||
} else {
|
||||
yourOffersTooltip.innerHTML = newContent;
|
||||
debugLog(`Replaced Your Offers tooltip content: total=${data.num_sent_offers}, active=${data.num_sent_active_offers}`);
|
||||
}
|
||||
|
||||
refreshTooltipInstances('tooltip-your-offers', newContent);
|
||||
} else {
|
||||
debugLog('Your Offers tooltip element not found - checking all tooltip elements');
|
||||
const allTooltips = document.querySelectorAll('[id*="tooltip"]');
|
||||
debugLog('All tooltip elements found:', Array.from(allTooltips).map(el => el.id));
|
||||
}
|
||||
|
||||
const bidsTooltip = document.getElementById('tooltip-bids');
|
||||
if (bidsTooltip) {
|
||||
const newBidsContent = `
|
||||
<p><b>Sent bids:</b> ${data.num_sent_bids || 0} (${data.num_sent_active_bids || 0} active)</p>
|
||||
<p><b>Received bids:</b> ${data.num_recv_bids || 0} (${data.num_recv_active_bids || 0} active)</p>
|
||||
`;
|
||||
|
||||
const sentParagraph = bidsTooltip.querySelector('p:first-child');
|
||||
const recvParagraph = bidsTooltip.querySelector('p:last-child');
|
||||
|
||||
if (sentParagraph && recvParagraph) {
|
||||
sentParagraph.innerHTML = `<b>Sent bids:</b> ${data.num_sent_bids || 0} (${data.num_sent_active_bids || 0} active)`;
|
||||
recvParagraph.innerHTML = `<b>Received bids:</b> ${data.num_recv_bids || 0} (${data.num_recv_active_bids || 0} active)`;
|
||||
debugLog(`Updated Bids tooltip: sent=${data.num_sent_bids}(${data.num_sent_active_bids}), recv=${data.num_recv_bids}(${data.num_recv_active_bids})`);
|
||||
} else {
|
||||
bidsTooltip.innerHTML = newBidsContent;
|
||||
debugLog(`Replaced Bids tooltip content: sent=${data.num_sent_bids}(${data.num_sent_active_bids}), recv=${data.num_recv_bids}(${data.num_recv_active_bids})`);
|
||||
}
|
||||
|
||||
refreshTooltipInstances('tooltip-bids', newBidsContent);
|
||||
} else {
|
||||
debugLog('Bids tooltip element not found');
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTooltipInstances(tooltipId, newContent) {
|
||||
const triggers = document.querySelectorAll(`[data-tooltip-target="${tooltipId}"]`);
|
||||
|
||||
triggers.forEach(trigger => {
|
||||
if (trigger._tippy) {
|
||||
trigger._tippy.setContent(newContent);
|
||||
debugLog(`Updated Tippy instance content for ${tooltipId}`);
|
||||
} else {
|
||||
if (window.TooltipManager && typeof window.TooltipManager.create === 'function') {
|
||||
window.TooltipManager.create(trigger, newContent, {
|
||||
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
|
||||
});
|
||||
debugLog(`Created new Tippy instance for ${tooltipId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.refreshTooltip === 'function') {
|
||||
window.TooltipManager.refreshTooltip(tooltipId, newContent);
|
||||
debugLog(`Refreshed tooltip via TooltipManager for ${tooltipId}`);
|
||||
}
|
||||
|
||||
if (window.TooltipManager && typeof window.TooltipManager.initializeTooltips === 'function') {
|
||||
setTimeout(() => {
|
||||
window.TooltipManager.initializeTooltips(`[data-tooltip-target="${tooltipId}"]`);
|
||||
debugLog(`Re-initialized tooltips for ${tooltipId}`);
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
function debugLog(message) {
|
||||
if (config.debug && console && console.log) {
|
||||
console.log(`[SummaryManager] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function cacheSummaryData(data) {
|
||||
if (!data) return;
|
||||
|
||||
@@ -303,6 +401,14 @@ const SummaryManager = (function() {
|
||||
});
|
||||
},
|
||||
|
||||
updateTooltips: function(data) {
|
||||
updateTooltips(data || lastSuccessfulData);
|
||||
},
|
||||
|
||||
updateUI: function(data) {
|
||||
updateUIFromData(data || lastSuccessfulData);
|
||||
},
|
||||
|
||||
startRefreshTimer: function() {
|
||||
startRefreshTimer();
|
||||
},
|
||||
|
||||
@@ -217,8 +217,8 @@ function filterAndSortData() {
|
||||
|
||||
const sentFromFilter = filters.sent_from || 'any';
|
||||
filteredData = filteredData.filter(offer => {
|
||||
const isMatch = sentFromFilter === 'public' ? offer.is_public :
|
||||
sentFromFilter === 'private' ? !offer.is_public :
|
||||
const isMatch = sentFromFilter === 'public' ? offer.is_public :
|
||||
sentFromFilter === 'private' ? !offer.is_public :
|
||||
true;
|
||||
return isMatch;
|
||||
});
|
||||
@@ -232,7 +232,7 @@ function filterAndSortData() {
|
||||
const coinToSelect = document.getElementById('coin_to');
|
||||
const selectedOption = coinToSelect?.querySelector(`option[value="${filters.coin_to}"]`);
|
||||
const coinName = selectedOption?.textContent.trim();
|
||||
|
||||
|
||||
if (coinName && !coinMatches(offer.coin_to, coinName)) {
|
||||
return false;
|
||||
}
|
||||
@@ -254,13 +254,13 @@ function filterAndSortData() {
|
||||
|
||||
let statusMatch = false;
|
||||
switch (filters.status) {
|
||||
case 'active':
|
||||
case 'active':
|
||||
statusMatch = !isExpired && !isRevoked;
|
||||
break;
|
||||
case 'expired':
|
||||
case 'expired':
|
||||
statusMatch = isExpired && !isRevoked;
|
||||
break;
|
||||
case 'revoked':
|
||||
case 'revoked':
|
||||
statusMatch = isRevoked;
|
||||
break;
|
||||
}
|
||||
@@ -275,7 +275,7 @@ function filterAndSortData() {
|
||||
|
||||
if (currentSortColumn === 7) {
|
||||
const offersWithPercentages = [];
|
||||
|
||||
|
||||
for (const offer of filteredData) {
|
||||
const fromAmount = parseFloat(offer.amount_from) || 0;
|
||||
const toAmount = parseFloat(offer.amount_to) || 0;
|
||||
@@ -293,7 +293,7 @@ function filterAndSortData() {
|
||||
if (fromPriceUSD && toPriceUSD && !isNaN(fromPriceUSD) && !isNaN(toPriceUSD)) {
|
||||
const fromValueUSD = fromAmount * fromPriceUSD;
|
||||
const toValueUSD = toAmount * toPriceUSD;
|
||||
|
||||
|
||||
if (fromValueUSD && toValueUSD) {
|
||||
if (offer.is_own_offer || isSentOffers) {
|
||||
percentDiff = ((toValueUSD / fromValueUSD) - 1) * 100;
|
||||
@@ -302,7 +302,7 @@ function filterAndSortData() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
offersWithPercentages.push({
|
||||
offer: offer,
|
||||
percentDiff: percentDiff
|
||||
@@ -353,7 +353,7 @@ function filterAndSortData() {
|
||||
const bRate = parseFloat(b.rate) || 0;
|
||||
const aPriceUSD = latestPrices && aSymbol ? latestPrices[aSymbol]?.usd : null;
|
||||
const bPriceUSD = latestPrices && bSymbol ? latestPrices[bSymbol]?.usd : null;
|
||||
|
||||
|
||||
aValue = aPriceUSD && !isNaN(aPriceUSD) ? aRate * aPriceUSD : 0;
|
||||
bValue = bPriceUSD && !isNaN(bPriceUSD) ? bRate * bPriceUSD : 0;
|
||||
break;
|
||||
@@ -367,8 +367,8 @@ function filterAndSortData() {
|
||||
}
|
||||
|
||||
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||
return currentSortDirection === 'asc'
|
||||
? aValue.localeCompare(bValue)
|
||||
return currentSortDirection === 'asc'
|
||||
? aValue.localeCompare(bValue)
|
||||
: bValue.localeCompare(aValue);
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwn
|
||||
normalizedCoin = window.CoinManager.getPriceKey(coin) || normalizedCoin;
|
||||
} else {
|
||||
if (normalizedCoin === 'zcoin') normalizedCoin = 'firo';
|
||||
if (normalizedCoin === 'bitcoincash' || normalizedCoin === 'bitcoin cash')
|
||||
if (normalizedCoin === 'bitcoincash' || normalizedCoin === 'bitcoin cash')
|
||||
normalizedCoin = 'bitcoin-cash';
|
||||
if (normalizedCoin.includes('particl')) normalizedCoin = 'particl';
|
||||
}
|
||||
@@ -481,6 +481,29 @@ async function fetchOffers() {
|
||||
}
|
||||
|
||||
const data = await offersResponse.json();
|
||||
|
||||
if (data.error) {
|
||||
if (data.locked) {
|
||||
if (typeof ui !== 'undefined' && ui.displayErrorMessage) {
|
||||
ui.displayErrorMessage(data.error);
|
||||
} else {
|
||||
offersBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="9" class="text-center py-8">
|
||||
<div class="flex flex-col items-center justify-center text-yellow-600 dark:text-yellow-400">
|
||||
<svg class="w-8 h-8 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
||||
</svg>
|
||||
<span class="font-medium">${data.error}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
}
|
||||
const processedData = Array.isArray(data) ? data : Object.values(data);
|
||||
|
||||
jsonData = formatInitialData(processedData);
|
||||
@@ -980,7 +1003,7 @@ function createTableRow(offer, identity = null) {
|
||||
} = offer;
|
||||
|
||||
let coinFromSymbol, coinToSymbol;
|
||||
|
||||
|
||||
if (window.CoinManager) {
|
||||
coinFromSymbol = window.CoinManager.getSymbol(coinFrom) || coinFrom.toLowerCase();
|
||||
coinToSymbol = window.CoinManager.getSymbol(coinTo) || coinTo.toLowerCase();
|
||||
@@ -1572,7 +1595,7 @@ function createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer) {
|
||||
|
||||
const getPriceKey = (coin) => {
|
||||
if (!coin) return null;
|
||||
|
||||
|
||||
const lowerCoin = coin.toLowerCase();
|
||||
|
||||
if (lowerCoin === 'zcoin') return 'firo';
|
||||
@@ -1807,7 +1830,7 @@ function getPriceKey(coin) {
|
||||
}
|
||||
|
||||
if (!coin) return null;
|
||||
|
||||
|
||||
const lowerCoin = coin.toLowerCase();
|
||||
|
||||
if (lowerCoin === 'zcoin') {
|
||||
@@ -1818,7 +1841,7 @@ function getPriceKey(coin) {
|
||||
return 'bitcoin-cash';
|
||||
}
|
||||
|
||||
if (lowerCoin === 'part' || lowerCoin === 'particl' ||
|
||||
if (lowerCoin === 'part' || lowerCoin === 'particl' ||
|
||||
lowerCoin.includes('particl')) {
|
||||
return 'particl';
|
||||
}
|
||||
@@ -2221,7 +2244,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
}
|
||||
|
||||
await updateOffersTable();
|
||||
|
||||
|
||||
updateProfitLossDisplays();
|
||||
|
||||
document.querySelectorAll('.usd-value').forEach(usdValue => {
|
||||
|
||||
Reference in New Issue
Block a user