| | |
| | | return this.forms.get(service)??false; |
| | | } |
| | | |
| | | /** |
| | | * Handle OAuth authorization link clicks |
| | | * Opens OAuth in a popup window with proper handling |
| | | */ |
| | | handleOAuthClick(link) { |
| | | const service = link.dataset.service; |
| | | const href = link.href; |
| | | |
| | | // Calculate center position for popup |
| | | const width = 600; |
| | | const height = 700; |
| | | const left = (screen.width - width) / 2; |
| | | const top = (screen.height - height) / 2; |
| | | |
| | | // Show loading notification |
| | | this.showNotification('Opening authorization window...', 'info'); |
| | | |
| | | // Add loading state to the link |
| | | link.classList.add('loading'); |
| | | link.setAttribute('aria-busy', 'true'); |
| | | |
| | | // Open OAuth in popup |
| | | const popup = window.open( |
| | | href, |
| | | 'oauth_' + service, |
| | | `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no,location=yes,status=yes,resizable=yes` |
| | | ); |
| | | |
| | | if (!popup) { |
| | | // Popup was blocked |
| | | this.showNotification('Popup was blocked. Please allow popups and try again.', 'error'); |
| | | link.classList.remove('loading'); |
| | | link.removeAttribute('aria-busy'); |
| | | return true; // Allow default behavior as fallback |
| | | } |
| | | |
| | | // Focus the popup |
| | | popup.focus(); |
| | | |
| | | // Update notification |
| | | this.showNotification('Waiting for authorization...', 'info'); |
| | | |
| | | // Poll for popup close |
| | | const pollTimer = setInterval(() => { |
| | | try { |
| | | if (popup.closed) { |
| | | clearInterval(pollTimer); |
| | | |
| | | // Remove loading state |
| | | link.classList.remove('loading'); |
| | | link.removeAttribute('aria-busy'); |
| | | |
| | | // Show checking notification |
| | | this.showNotification('Checking authorization status...', 'info'); |
| | | |
| | | // Wait a moment for redirect to complete, then check for messages |
| | | setTimeout(() => { |
| | | this.checkForOAuthMessages(); |
| | | |
| | | // If no messages found, reload to get updated connection status |
| | | setTimeout(() => { |
| | | const urlParams = new URLSearchParams(window.location.search); |
| | | if (!urlParams.has('success') && !urlParams.has('error')) { |
| | | // No messages in URL, reload to check server-side status |
| | | window.location.reload(); |
| | | } |
| | | }, 500); |
| | | }, 500); |
| | | } |
| | | } catch (error) { |
| | | // Ignore cross-origin errors during polling |
| | | } |
| | | }, 500); |
| | | |
| | | // Safety timeout - stop polling after 5 minutes |
| | | setTimeout(() => { |
| | | clearInterval(pollTimer); |
| | | link.classList.remove('loading'); |
| | | link.removeAttribute('aria-busy'); |
| | | }, 300000); |
| | | |
| | | return false; // Prevent default link behavior |
| | | } |
| | | |
| | | async handleAction(input) { |
| | | const form = input.closest('form'); |
| | | const service = form.dataset.service; |