Skip to content

Commit 73d42e8

Browse files
committed
Refine loading screen logic for Blazor Server & WASM
Standardize loading message to "Starting..." across WASM and Server. Enhance Blazor Server loader with robust readiness detection using DOM observation, console monitoring, and render stability checks. Reduce fallback timeout to 8s and expose new ElsaStudio API methods for manual loading control. Improves accuracy and responsiveness of loading screen removal.
1 parent 44f7261 commit 73d42e8

File tree

3 files changed

+175
-8
lines changed

3 files changed

+175
-8
lines changed

src/framework/Elsa.Studio.Shared/wwwroot/js/elsa-studio-init.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
if (!blazorStartAttempted) {
4545
blazorStartAttempted = true;
46-
updateLoadingText('Starting Blazor WASM...');
46+
updateLoadingText('Starting...');
4747

4848
Blazor.start(config || {}).then(() => {
4949
updateLoadingText('Loading application...');

src/framework/Elsa.Studio.Shared/wwwroot/js/elsa-studio-loader-hosted-wasm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
return;
110110
}
111111

112-
updateLoadingText('Starting Blazor WASM...');
112+
updateLoadingText('Starting...');
113113

114114
const config = {
115115
loadBootResource: function (type, name, defaultUri, integrity) {

src/framework/Elsa.Studio.Shared/wwwroot/js/elsa-studio-loader-server.js

Lines changed: 173 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
(function() {
88
'use strict';
99

10+
let loadingHidden = false;
11+
let blazorReady = false;
12+
let initialRenderComplete = false;
13+
1014
// Load required stylesheets
1115
function loadStyles() {
1216
const styles = [
@@ -79,11 +83,170 @@
7983
document.head.insertAdjacentHTML('beforeend', loadingStyle);
8084
}
8185

82-
// Initialize Blazor Server with timeout fallback
86+
// Update loading text (minimal usage)
87+
function updateLoadingText(text) {
88+
const loadingTextEl = document.getElementById('elsa-loading-text');
89+
if (loadingTextEl) {
90+
loadingTextEl.textContent = text;
91+
}
92+
}
93+
94+
// Hide loading screen when actually ready
95+
function hideLoadingScreen() {
96+
if (!loadingHidden) {
97+
loadingHidden = true;
98+
console.log('Blazor Server ready - hiding loading screen');
99+
document.body.classList.add('blazor-ready');
100+
}
101+
}
102+
103+
// Check if we should hide loading screen
104+
function checkReadiness() {
105+
if (blazorReady && initialRenderComplete && !loadingHidden) {
106+
hideLoadingScreen();
107+
}
108+
}
109+
110+
// Detect when Blazor Server connection is established
111+
function detectBlazorConnection() {
112+
// Check for Blazor global object and connection
113+
if (typeof window.Blazor !== 'undefined') {
114+
// Monitor for SignalR connection
115+
const originalLog = console.log;
116+
console.log = function(...args) {
117+
const message = args.join(' ');
118+
if (message.includes('SignalR') || message.includes('connected') || message.includes('circuit')) {
119+
blazorReady = true;
120+
checkReadiness();
121+
}
122+
originalLog.apply(console, args);
123+
};
124+
}
125+
126+
// Check for Blazor Server specific DOM changes
127+
const observer = new MutationObserver(function(mutations) {
128+
mutations.forEach(function(mutation) {
129+
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
130+
for (let node of mutation.addedNodes) {
131+
if (node.nodeType === Node.ELEMENT_NODE) {
132+
// Check for Blazor component markers
133+
if (node.hasAttribute && (
134+
node.hasAttribute('_bl_') ||
135+
node.querySelector && node.querySelector('[_bl_]') ||
136+
node.classList && node.classList.contains('mud-main-content') ||
137+
node.tagName === 'APP'
138+
)) {
139+
if (!blazorReady) {
140+
blazorReady = true;
141+
}
142+
}
143+
}
144+
}
145+
}
146+
});
147+
});
148+
149+
observer.observe(document.body, {
150+
childList: true,
151+
subtree: true,
152+
attributes: true
153+
});
154+
155+
// Stop observing after readiness is detected
156+
setTimeout(() => {
157+
if (blazorReady || loadingHidden) {
158+
observer.disconnect();
159+
}
160+
}, 10000);
161+
}
162+
163+
// Detect when initial render is complete
164+
function detectRenderCompletion() {
165+
// Check for common Blazor/MudBlazor elements
166+
function checkForMainContent() {
167+
const indicators = [
168+
'.mud-main-content',
169+
'.mud-layout',
170+
'[role="main"]',
171+
'.elsa-main-layout',
172+
'.mud-drawer',
173+
'.mud-appbar'
174+
];
175+
176+
for (let selector of indicators) {
177+
const element = document.querySelector(selector);
178+
if (element && element.offsetHeight > 0) {
179+
initialRenderComplete = true;
180+
checkReadiness();
181+
return true;
182+
}
183+
}
184+
return false;
185+
}
186+
187+
// Use requestAnimationFrame to detect when rendering settles
188+
let frameCount = 0;
189+
let lastBodyHeight = 0;
190+
let stableFrames = 0;
191+
192+
function checkRenderStability() {
193+
frameCount++;
194+
const currentHeight = document.body.offsetHeight;
195+
196+
if (currentHeight === lastBodyHeight && currentHeight > 100) {
197+
stableFrames++;
198+
if (stableFrames >= 5) { // 5 stable frames
199+
if (!initialRenderComplete && checkForMainContent()) {
200+
return;
201+
} else if (!initialRenderComplete && frameCount > 30) {
202+
initialRenderComplete = true;
203+
checkReadiness();
204+
return;
205+
}
206+
}
207+
} else {
208+
stableFrames = 0;
209+
lastBodyHeight = currentHeight;
210+
}
211+
212+
if (frameCount < 100 && !initialRenderComplete) {
213+
requestAnimationFrame(checkRenderStability);
214+
} else if (!initialRenderComplete) {
215+
// Fallback after 100 frames
216+
initialRenderComplete = true;
217+
checkReadiness();
218+
}
219+
}
220+
221+
// Start checking on next frame
222+
requestAnimationFrame(checkRenderStability);
223+
224+
// Also check periodically
225+
const intervalCheck = setInterval(() => {
226+
if (checkForMainContent()) {
227+
clearInterval(intervalCheck);
228+
} else if (frameCount > 100) {
229+
clearInterval(intervalCheck);
230+
}
231+
}, 200);
232+
}
233+
234+
// Enhanced Blazor Server initialization
83235
function initializeBlazorServer(maxWaitMs) {
84-
maxWaitMs = maxWaitMs || 10000;
236+
maxWaitMs = maxWaitMs || 8000; // Fallback timeout
237+
238+
// Start detection methods
239+
setTimeout(() => detectBlazorConnection(), 100);
240+
setTimeout(() => detectRenderCompletion(), 500);
241+
242+
// Ultimate fallback timeout
85243
setTimeout(function() {
86-
document.body.classList.add('blazor-ready');
244+
if (!loadingHidden) {
245+
console.warn('Fallback timeout reached - forcing loading screen to hide');
246+
blazorReady = true;
247+
initialRenderComplete = true;
248+
hideLoadingScreen();
249+
}
87250
}, maxWaitMs);
88251
}
89252

@@ -92,7 +255,7 @@
92255
loadStyles();
93256
injectLoadingScreen();
94257
loadScripts();
95-
initializeBlazorServer(10000);
258+
initializeBlazorServer(8000);
96259
}
97260

98261
// Run initialization
@@ -104,8 +267,12 @@
104267

105268
// Expose API for manual control if needed
106269
window.ElsaStudio = window.ElsaStudio || {};
107-
window.ElsaStudio.hideLoading = function() {
108-
document.body.classList.add('blazor-ready');
270+
window.ElsaStudio.hideLoading = hideLoadingScreen;
271+
window.ElsaStudio.updateLoadingText = updateLoadingText;
272+
window.ElsaStudio.forceReady = function() {
273+
blazorReady = true;
274+
initialRenderComplete = true;
275+
checkReadiness();
109276
};
110277

111278
})();

0 commit comments

Comments
 (0)