| | |
| | | async retryWithBackoff(callback) { |
| | | const backoffTime = Math.min(1000 * Math.pow(2, this.retryCount), 10000); |
| | | |
| | | // Display retry notification |
| | | if (this.options.displayNotifications) { |
| | | this.displayRetryNotification(backoffTime); |
| | | } |
| | | |
| | | // Wait before retrying |
| | | await new Promise(resolve => setTimeout(resolve, backoffTime)); |
| | | |
| | |
| | | return callback(); |
| | | } |
| | | |
| | | /** |
| | | * Display retry notification |
| | | */ |
| | | displayRetryNotification(backoffTime) { |
| | | if (window.jvbNotifications) { |
| | | window.jvbNotifications.queuePopupNotification({ |
| | | type: 'info', |
| | | message: `Retrying in ${backoffTime/1000} seconds...`, |
| | | icon: 'refresh', |
| | | priority: 'medium', |
| | | displayDuration: backoffTime |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Reset retry counter |
| | |
| | | resetRetryCount() { |
| | | this.retryCount = 0; |
| | | } |
| | | |
| | | /** |
| | | * Handle user feedback for errors |
| | | */ |
| | | collectUserFeedback(errorInfo) { |
| | | // Create a modal for collecting feedback |
| | | const modal = document.createElement('dialog'); |
| | | modal.className = 'error-feedback-modal'; |
| | | |
| | | modal.innerHTML = ` |
| | | <h2>Help Us Improve</h2> |
| | | <p>We encountered an error. Would you like to tell us what happened?</p> |
| | | <form method="dialog" data-save="error"> |
| | | <textarea placeholder="What were you trying to do when this error occurred?"></textarea> |
| | | <div class="actions"> |
| | | <button value="cancel">Skip</button> |
| | | <button value="submit" class="primary">Send Feedback</button> |
| | | </div> |
| | | </form> |
| | | `; |
| | | |
| | | document.body.appendChild(modal); |
| | | |
| | | return new Promise((resolve) => { |
| | | modal.addEventListener('close', () => { |
| | | const feedback = modal.returnValue === 'submit' |
| | | ? modal.querySelector('textarea').value |
| | | : null; |
| | | |
| | | document.body.removeChild(modal); |
| | | resolve(feedback); |
| | | }); |
| | | |
| | | modal.showModal(); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * Handle global errors |
| | | */ |
| | | setupGlobalErrorHandling() { |
| | | // Handle uncaught errors |
| | | window.addEventListener('error', event => { |
| | | this.log( |
| | | event.error || new Error(event.message), |
| | | { |
| | | message: event.message, |
| | | filename: event.filename, |
| | | lineno: event.lineno, |
| | | colno: event.colno, |
| | | type: 'global_error' |
| | | } |
| | | ); |
| | | |
| | | // Don't prevent default - let browser show its own error if needed |
| | | }); |
| | | |
| | | // Handle unhandled promise rejections |
| | | window.addEventListener('unhandledrejection', event => { |
| | | this.log( |
| | | event.reason, |
| | | { |
| | | type: 'unhandled_promise', |
| | | message: event.reason?.message || 'Unhandled promise rejection' |
| | | } |
| | | ); |
| | | }); |
| | | } |
| | | } |
| | | document.addEventListener('DOMContentLoaded', async function () { |
| | | window.auth.subscribe((event) => { |