From 42fa8304ddb811b0f725f245130f70c0f5e86a6c Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 04 Nov 2025 06:12:02 +0000
Subject: [PATCH] =Refactored LoginManager to be more extensible and configurable, as well as an AjaxRateLimiter
---
assets/js/dash/Integrations.js | 164 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 143 insertions(+), 21 deletions(-)
diff --git a/assets/js/dash/Integrations.js b/assets/js/dash/Integrations.js
index 079afcc..e0d9ed6 100644
--- a/assets/js/dash/Integrations.js
+++ b/assets/js/dash/Integrations.js
@@ -53,11 +53,21 @@
const error = urlParams.get('error');
if (success) {
- this.showNotification(success, 'success');
+ this.showNotification(success, 'success', 5000);
+
// Clean URL without reloading
this.cleanURL();
+
+ // Refresh the integration status display
+ const forms = document.querySelectorAll('form.integration');
+ forms.forEach(form => {
+ // Update UI to show connected state
+ this.updateUI(form, 'connected');
+ });
+
} else if (error) {
- this.showNotification(error, 'error');
+ this.showNotification(error, 'error', 8000);
+
// Clean URL without reloading
this.cleanURL();
}
@@ -76,26 +86,42 @@
/**
* Show notification message
*/
- showNotification(message, type = 'info') {
- // If you have a notification system, use it
+ showNotification(message, type = 'info', duration = 5000) {
+ // Find or create notification container
+ let container = document.querySelector('.integration-status-message');
+
+ if (!container) {
+ // Create notification container
+ container = document.createElement('div');
+ container.className = 'integration-status-message';
+
+ // Insert at top of main content or integrations container
+ const target = document.querySelector('.integration-settings') ||
+ document.querySelector('main') ||
+ document.body;
+ target.insertBefore(container, target.firstChild);
+ }
+
+ // Update content and type
+ container.textContent = message;
+ container.className = `integration-status-message ${type}`;
+
+ // Clear any existing timeout
+ if (this.notificationTimeout) {
+ clearTimeout(this.notificationTimeout);
+ }
+
+ // Auto-hide after duration
+ if (duration > 0) {
+ this.notificationTimeout = setTimeout(() => {
+ container.className = 'integration-status-message';
+ container.textContent = '';
+ }, duration);
+ }
+
+ // Also use popup if available
if (this.popup) {
- this.addPopup(message, type === 'error' ? 5000 : 3000);
- } else {
- // Fallback to console or alert
- console.log(`[${type}]`, message);
-
- // Update any status elements on the page
- const statusElements = document.querySelectorAll('.integration-status-message');
- statusElements.forEach(el => {
- el.textContent = message;
- el.className = `integration-status-message ${type}`;
-
- // Auto-hide after delay
- setTimeout(() => {
- el.textContent = '';
- el.className = 'integration-status-message';
- }, 5000);
- });
+ this.addPopup(message, duration);
}
}
@@ -122,9 +148,21 @@
}
clickHandler(e) {
+ // // Check for OAuth authorization link
+ // if (e.target.classList.contains('jvb-oauth-connect') ||
+ // e.target.closest('.jvb-oauth-connect')) {
+ // e.preventDefault();
+ // const link = e.target.classList.contains('jvb-oauth-connect')
+ // ? e.target
+ // : e.target.closest('.jvb-oauth-connect');
+ // return this.handleOAuthClick(link);
+ // }
+
+ // Existing integration form handling
if (!e.target.closest(this.selectors.form)) {
return;
}
+
console.log('Clicked!');
if (e.target.tagName === 'BUTTON' || e.target.closest('button')) {
e.preventDefault();
@@ -159,6 +197,90 @@
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;
--
Gitblit v1.10.0