PageSourceSearch

https://playteq.com/js/blazor-reconnect.js

js playteq.com collected 2026-09-27 05:04:05 UTC 11,504 bytes, 298 lines download raw bytes

1// Custom Blazor Reconnection Handler
2// Provides automatic reconnection with custom UI matching the application theme
3
4(function () {
5    const maxRetries = 8;
6    const retryIntervalMilliseconds = 3000;
7    let currentReconnectionAttempt = 0;
8    let reconnectDisplay = null;
9    let reconnectModal = null;
10    let statusText = null;
11    let progressBar = null;
12    let reconnectButton = null;
13    let closeButton = null;
14    let reconnectionStartTime = null;
15
16    function createReconnectUI() {
17        if (reconnectDisplay) return;
18
19        reconnectDisplay = document.createElement('div');
20        reconnectDisplay.id = 'blazor-reconnect-ui';
21        reconnectDisplay.innerHTML = `
22            <div class="blazor-reconnect-overlay"></div>
23            <div class="blazor-reconnect-modal">
24                <div class="blazor-reconnect-header">
25                    <i class="fas fa-wifi"></i>
26                    <h3>Connection Status</h3>
27                </div>
28                <div class="blazor-reconnect-body">
29                    <div class="blazor-reconnect-icon">
30                        <i class="fas fa-circle-notch fa-spin"></i>
31                    </div>
32                    <div class="blazor-reconnect-status">Attempting to reconnect...</div>
33                    <div class="blazor-reconnect-progress">
34                        <div class="blazor-reconnect-progress-bar"></div>
35                    </div>
36                    <div class="blazor-reconnect-attempt">Attempt <span class="attempt-number">1</span> of ${maxRetries}</div>
37                </div>
38                <div class="blazor-reconnect-actions">
39                    <button class="blazor-reconnect-btn blazor-reconnect-btn-primary" id="blazor-reconnect-manual">
40                        <i class="fas fa-redo"></i>
41                        Reconnect Now
42                    </button>
43                    <button class="blazor-reconnect-btn blazor-reconnect-btn-secondary" id="blazor-reconnect-reload">
44                        <i class="fas fa-sync-alt"></i>
45                        Reload Page
46                    </button>
47                </div>
48            </div>
49        `;
50
51        document.body.appendChild(reconnectDisplay);
52
53        reconnectModal = reconnectDisplay.querySelector('.blazor-reconnect-modal');
54        statusText = reconnectDisplay.querySelector('.blazor-reconnect-status');
55        progressBar = reconnectDisplay.querySelector('.blazor-reconnect-progress-bar');
56        reconnectButton = document.getElementById('blazor-reconnect-manual');
57        closeButton = document.getElementById('blazor-reconnect-reload');
58
59        reconnectButton.addEventListener('click', () => {
60            reportReconnectionMetric('manual', true, 0);
61            window.location.reload();
62        });
63
64        closeButton.addEventListener('click', () => {
65            window.location.reload();
66        });
67    }
68
69    function showReconnectUI(show) {
70        if (!reconnectDisplay) {
71            createReconnectUI();
72        }
73
74        if (show) {
75            reconnectDisplay.style.display = 'flex';
76            setTimeout(() => {
77                reconnectDisplay.classList.add('show');
78            }, 10);
79        } else {
80            reconnectDisplay.classList.remove('show');
81            setTimeout(() => {
82                reconnectDisplay.style.display = 'none';
83            }, 300);
84        }
85    }
86
87    function updateReconnectUI(attempt, status) {
88        if (!reconnectDisplay) return;
89
90        const attemptNumber = reconnectDisplay.querySelector('.attempt-number');
91        const icon = reconnectDisplay.querySelector('.blazor-reconnect-icon i');
92
93        if (attemptNumber) {
94            attemptNumber.textContent = attempt;
95        }
96
97        if (progressBar) {
98            const progress = (attempt / maxRetries) * 100;
99            progressBar.style.width = progress + '%';
100        }
101
102        if (status === 'failed') {
103            statusText.textContent = 'Connection failed. Please try again.';
104            icon.className = 'fas fa-exclamation-triangle';
105            icon.style.color = '#e74c3c';
106            reconnectModal.classList.add('failed');
107        } else if (status === 'reconnecting') {
108            statusText.textContent = `Attempting to reconnect... (${attempt}/${maxRetries})`;
109            icon.className = 'fas fa-circle-notch fa-spin';
110            icon.style.color = '#3498db';
111            reconnectModal.classList.remove('failed');
112        } else if (status === 'success') {
113            statusText.textContent = 'Reconnected successfully!';
114            icon.className = 'fas fa-check-circle';
115            icon.style.color = '#27ae60';
116            reconnectModal.classList.remove('failed');
117        }
118    }
119
120    function reportReconnectionMetric(eventType, success, duration) {
121        // Only report if we're connected (to avoid errors during reconnection)
122        if (success) {
123            fetch('/api/metrics/reconnection', {
124                method: 'POST',
125                headers: {
126                    'Content-Type': 'application/json'
127                },
128                body: JSON.stringify({
129                    eventType: eventType,
130                    success: success,
131                    duration: duration
132                })
133            }).catch(err => {
134                console.error('Failed to report reconnection metric:', err);
135            });
136        }
137    }
138
139    // Custom reconnection handler
140    class CustomReconnectionHandler {
141        constructor(maxRetries, retryInterval) {
142            this._maxRetries = maxRetries;
143            this._retryInterval = retryInterval;
144            // Create an array of retry delays - all the same interval
145            this._retryDelays = [];
146            for (let i = 0; i < maxRetries; i++) {
147                this._retryDelays.push(retryInterval);
148            }
149        }
150
151        onConnectionDown(options, error) {
152            console.log('Blazor: Connection down', error);
153            currentReconnectionAttempt = 0;
154            reconnectionStartTime = Date.now();
155            showReconnectUI(true);
156            updateReconnectUI(1, 'reconnecting');
157        }
158
159        onConnectionUp() {
160            console.log('Blazor: Connection up');
161            const duration = reconnectionStartTime ? Date.now() - reconnectionStartTime : 0;
162            updateReconnectUI(currentReconnectionAttempt, 'success');
163            reportReconnectionMetric('automatic', true, duration);
164            setTimeout(() => {
165                showReconnectUI(false);
166                currentReconnectionAttempt = 0;
167                reconnectionStartTime = null;
168            }, 2000);
169        }
170
171        onReconnecting(attempt) {
172            console.log('Blazor: onReconnecting called with attempt', attempt, 'of', this._maxRetries);
173            currentReconnectionAttempt = attempt;
174            updateReconnectUI(attempt, 'reconnecting');
175
176            // attempt is 1-based, array is 0-based
177            const delayIndex = attempt - 1;
178
179            if (delayIndex >= this._retryDelays.length) {
180                console.log('Blazor: Max retries exceeded, stopping');
181                return null; // Stop retrying
182            }
183
184            const delay = this._retryDelays[delayIndex];
185            console.log('Blazor: Will retry in', delay, 'ms (attempt', attempt, 'of', this._maxRetries, ')');
186            return delay;
187        }
188
189        onReconnectFailed() {
190            console.log('Blazor: Reconnection failed after all attempts');
191            updateReconnectUI(currentReconnectionAttempt, 'failed');
192            reconnectionStartTime = null;
193        }
194    }
195
196    // .NET 8+ uses reconnectionHandler with onConnectionDown/onConnectionUp callbacks
197    console.log('Blazor reconnect script loaded');
198
199    let currentReconnectionProcess = null;
200
201    const startReconnectionProcess = () => {
202        if (currentReconnectionProcess) {
203            return currentReconnectionProcess;
204        }
205
206        console.log('Starting reconnection process...');
207        reconnectionStartTime = Date.now();
208        currentReconnectionAttempt = 0;
209
210        createReconnectUI();
211        showReconnectUI();
212
213        let isCanceled = false;
214
215        const reconnectionPromise = (async () => {
216            for (let i = 0; i < maxRetries; i++) {
217                currentReconnectionAttempt = i + 1;
218                updateReconnectUI();
219
220                console.log(`Reconnection attempt ${currentReconnectionAttempt} of ${maxRetries}`);
221
222                await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
223
224                if (isCanceled) {
225                    console.log('Reconnection canceled');
226                    return;
227                }
228
229                try {
230                    const result = await Blazor.reconnect();
231                    if (!result) {
232                        // Server reached but connection rejected - reload page
233                        console.log('Connection rejected by server, reloading page...');
234                        location.reload();
235                        return;
236                    }
237
238                    // Successfully reconnected
239                    console.log('Successfully reconnected!');
240                    return;
241                } catch (error) {
242                    console.error(`Reconnection attempt ${currentReconnectionAttempt} failed:`, error);
243                }
244            }
245
246            // Max retries reached - show failed state
247            console.error('Max reconnection attempts reached');
248            showFailedState();
249        })();
250
251        currentReconnectionProcess = {
252            promise: reconnectionPromise,
253            cancel: () => {
254                isCanceled = true;
255                hideReconnectUI();
256                currentReconnectionProcess = null;
257            }
258        };
259
260        return currentReconnectionProcess;
261    };
262
263    // Start Blazor with .NET 8+ reconnection handler
264    console.log('Starting Blazor with .NET 8+ reconnection handler...');
265    Blazor.start({
266        circuit: {
267            reconnectionHandler: {
268                onConnectionDown: (options, error) => {
269                    console.log('Connection down:', error);
270                    startReconnectionProcess();
271                },
272                onConnectionUp: () => {
273                    console.log('Connection up!');
274                    if (currentReconnectionProcess) {
275                        currentReconnectionProcess.cancel();
276                        currentReconnectionProcess = null;
277                    }
278                }
279            }
280        }
281    }).then(() => {
282        console.log('Blazor started successfully');
283    }).catch(err => {
284        console.error('Error starting Blazor:', err);
285    });
286
287    // Test function to simulate a disconnection
288    window.testReconnection = function() {
289        console.log('Testing reconnection by simulating disconnect...');
290        if (window.Blazor && window.Blazor._internal && window.Blazor._internal.forceCloseConnection) {
291            window.Blazor._internal.forceCloseConnection();
292        } else {
293            console.log('Cannot force disconnect, reloading page instead...');
294            location.reload();
295        }
296    };
297})();
298

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.