Skip to content

Commit 3f989cc

Browse files
authored
Display errors on failed connection (#694)
1 parent 0329664 commit 3f989cc

1 file changed

Lines changed: 66 additions & 5 deletions

File tree

src/panel_material_ui/_templates/base.html

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@
5858
justify-content: center;
5959
align-items: center;
6060
z-index: 9999;
61+
flex-direction: column;
62+
gap: 16px;
63+
}
64+
#loader.error {
65+
background: none;
6166
}
6267
.spinner {
6368
width: 50px;
@@ -67,6 +72,17 @@
6772
border-radius: 50%;
6873
animation: spin 1s linear infinite;
6974
}
75+
#loader-error {
76+
display: none;
77+
color: #d32f2f;
78+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
79+
font-size: 14px;
80+
text-align: center;
81+
padding: 12px 20px;
82+
background: rgba(211, 47, 47, 0.08);
83+
border: 1px solid rgba(211, 47, 47, 0.3);
84+
border-radius: 8px;
85+
}
7086
@keyframes spin {
7187
0% { transform: rotate(0deg); }
7288
100% { transform: rotate(360deg); }
@@ -128,6 +144,7 @@
128144
{% block loader %}
129145
<div id="loader" style="display: none;">
130146
<div class="spinner"></div>
147+
<div id="loader-error"></div>
131148
</div>
132149
{% endblock %}
133150
{% block contents %}
@@ -140,18 +157,62 @@
140157
{% endfor %}
141158
{% endfor %}
142159
{% endblock contents %}
160+
<script>
161+
const _origWebSocket = WebSocket;
162+
let _wsError = false;
163+
WebSocket = function(...args) {
164+
const ws = new _origWebSocket(...args);
165+
ws.addEventListener('error', function() {
166+
_wsError = true;
167+
});
168+
ws.addEventListener('close', function(e) {
169+
if (e.code !== 1000) {
170+
_wsError = true;
171+
}
172+
});
173+
return ws;
174+
};
175+
WebSocket.prototype = _origWebSocket.prototype;
176+
Object.defineProperty(WebSocket, 'CONNECTING', {value: _origWebSocket.CONNECTING});
177+
Object.defineProperty(WebSocket, 'OPEN', {value: _origWebSocket.OPEN});
178+
Object.defineProperty(WebSocket, 'CLOSING', {value: _origWebSocket.CLOSING});
179+
Object.defineProperty(WebSocket, 'CLOSED', {value: _origWebSocket.CLOSED});
180+
</script>
143181
{{ plot_script | indent(4) }}
144182
{% block loader_script %}
145183
<script>
146184
let timeout = 0;
185+
186+
function showError(message) {
187+
const loader = document.getElementById('loader');
188+
const spinner = loader.querySelector('.spinner');
189+
const errorEl = document.getElementById('loader-error');
190+
loader.style.display = 'flex';
191+
loader.classList.add('error');
192+
spinner.style.display = 'none';
193+
errorEl.style.display = 'block';
194+
errorEl.textContent = message;
195+
}
196+
147197
function checkIdle() {
148-
if (timeout > 30000 || (Bokeh.index && Bokeh.index.roots[0] && Bokeh.index.roots[0].is_idle)) {
198+
if (typeof Bokeh === 'undefined') {
199+
if (timeout > 10000) {
200+
showError('Resources could not be loaded.');
201+
return;
202+
}
203+
} else if (_wsError) {
204+
showError('Connection with the server could not be established.');
205+
return;
206+
} else if (Bokeh.index && Bokeh.index.roots[0] && Bokeh.index.roots[0].is_idle) {
207+
document.getElementById('loader').style.display = 'none';
208+
return;
209+
} else if (timeout > 30000) {
149210
document.getElementById('loader').style.display = 'none';
150-
} else {
151-
timeout += 100;
152-
document.getElementById('loader').style.display = 'flex';
153-
setTimeout(checkIdle, 100);
211+
return;
154212
}
213+
timeout += 100;
214+
document.getElementById('loader').style.display = 'flex';
215+
setTimeout(checkIdle, 100);
155216
}
156217
setTimeout(checkIdle, 100)
157218
</script>

0 commit comments

Comments
 (0)