From 4e1446346544f1ef6c65e24abfd019e2dcadd4e9 Mon Sep 17 00:00:00 2001 From: rob Date: Sun, 1 Mar 2026 17:34:27 -0400 Subject: [PATCH] Add user feedback for exchange configuration - Show loading state while connecting to exchange - Display success/error messages in the form - Prevent double-submission with disabled button - Add 30-second timeout with error message - Register handler for Exchange_connection_result message - Reload page after successful connection (with delay to show message) Co-Authored-By: Claude Opus 4.5 --- src/static/exchanges.js | 112 ++++++++++++++++++++--- src/templates/exchange_config_popup.html | 7 +- 2 files changed, 103 insertions(+), 16 deletions(-) diff --git a/src/static/exchanges.js b/src/static/exchanges.js index a0032bc..99edfc1 100644 --- a/src/static/exchanges.js +++ b/src/static/exchanges.js @@ -3,6 +3,7 @@ class Exchanges { this.exchanges = {}; this.balances = {}; this.connected_exchanges = []; + this.isSubmitting = false; } initialize() { @@ -12,36 +13,100 @@ class Exchanges { // Extract the text content from each span and store it in the connected_exchanges array this.connected_exchanges = Array.from(spans).map(span => span.textContent.trim()); + + // Register handler for exchange connection results + if (window.UI && window.UI.data && window.UI.data.comms) { + window.UI.data.comms.on('Exchange_connection_result', this.handleConnectionResult.bind(this)); + } } - status() { + // Reset form state when opening + this.resetFormState(); document.getElementById('exchanges_config_form').style.display = "grid"; } closeForm() { + this.resetFormState(); document.getElementById('exchanges_config_form').style.display = "none"; } + resetFormState() { + this.isSubmitting = false; + const statusEl = document.getElementById('exchange_status'); + const connectBtn = document.getElementById('exchange_connect_btn'); + + if (statusEl) { + statusEl.style.display = 'none'; + document.getElementById('exchange_status_text').textContent = ''; + } + if (connectBtn) { + connectBtn.disabled = false; + connectBtn.textContent = 'Connect'; + } + } + + showStatus(message, type) { + const statusEl = document.getElementById('exchange_status'); + const statusText = document.getElementById('exchange_status_text'); + + if (statusEl && statusText) { + statusText.textContent = message; + statusEl.style.display = 'block'; + + // Set color based on type + if (type === 'loading') { + statusEl.style.color = '#666'; + statusEl.style.backgroundColor = '#f0f0f0'; + } else if (type === 'success') { + statusEl.style.color = '#155724'; + statusEl.style.backgroundColor = '#d4edda'; + } else if (type === 'error') { + statusEl.style.color = '#721c24'; + statusEl.style.backgroundColor = '#f8d7da'; + } + } + } + validateApiKey(data) { return !(data === undefined || data === null || data === ""); } - postConnection(data) { + handleConnectionResult(data) { + this.isSubmitting = false; + const connectBtn = document.getElementById('exchange_connect_btn'); + + if (connectBtn) { + connectBtn.disabled = false; + connectBtn.textContent = 'Connect'; + } + if (data.status === 'success') { - // Trigger a page reload - location.reload(); - } - else if (data.status === 'already_connected') { - alert(data.message); - } - else if (data.status === 'failure') { - alert(data.message); + this.showStatus('Exchange connected successfully! Reloading...', 'success'); + // Reload after a brief delay so user sees the success message + setTimeout(() => { + location.reload(); + }, 1500); + } else if (data.status === 'already_connected') { + this.showStatus(data.message || 'Exchange is already connected.', 'error'); + } else if (data.status === 'failure' || data.status === 'error') { + this.showStatus(data.message || 'Failed to connect exchange.', 'error'); + } else { + this.showStatus(data.message || 'Unknown response from server.', 'error'); } } + // Legacy handler for backwards compatibility + postConnection(data) { + this.handleConnectionResult(data); + } submitApi() { + // Prevent double submission + if (this.isSubmitting) { + return; + } + // Collect the data to submit. let exchange = document.getElementById('c_exchanges').value; let user = window.UI.data.user_name; @@ -59,16 +124,33 @@ class Exchanges { keys = {}; // Clear keys for public exchanges } else if (!isKeyValid || !isSecretKeyValid) { // Validate keys for non-public exchanges - alert('Enter a valid API key and secret key to register.'); + this.showStatus('Please enter a valid API key and secret key.', 'error'); return; // Exit early if validation fails } - // Send the data if validation passes or no validation is required. + // Show loading state + this.isSubmitting = true; + const connectBtn = document.getElementById('exchange_connect_btn'); + if (connectBtn) { + connectBtn.disabled = true; + connectBtn.textContent = 'Connecting...'; + } + this.showStatus('Connecting to exchange...', 'loading'); + + // Send the data let payload = { 'user': user, 'exch': exchange, 'keys': keys }; window.UI.data.comms.sendToApp("config_exchange", payload); - this.closeForm(); - // Refreshes the current page - // setTimeout(function() { location.reload(); }, 200); + // Set a timeout in case we don't get a response + setTimeout(() => { + if (this.isSubmitting) { + this.isSubmitting = false; + if (connectBtn) { + connectBtn.disabled = false; + connectBtn.textContent = 'Connect'; + } + this.showStatus('Connection timed out. Please try again.', 'error'); + } + }, 30000); // 30 second timeout } } diff --git a/src/templates/exchange_config_popup.html b/src/templates/exchange_config_popup.html index d47b143..ed7e433 100644 --- a/src/templates/exchange_config_popup.html +++ b/src/templates/exchange_config_popup.html @@ -17,10 +17,15 @@ + + +
- +