Add connection overlay and Gitea CI/CD workflow
Show full-screen overlay with spinner when server is unreachable, with periodic health checks that auto-hide on reconnect. Add Gitea Actions workflow for auto-deploy on release tags. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import { apiKey, setApiKey, refreshInterval } from './core/state.js';
|
||||
import { Modal } from './core/modal.js';
|
||||
|
||||
// Layer 1: api, i18n
|
||||
import { loadServerInfo, loadDisplays, configureApiKey } from './core/api.js';
|
||||
import { loadServerInfo, loadDisplays, configureApiKey, startConnectionMonitor, stopConnectionMonitor } from './core/api.js';
|
||||
import { t, initLocale, changeLocale } from './core/i18n.js';
|
||||
|
||||
// Layer 2: ui
|
||||
@@ -506,6 +506,7 @@ window.addEventListener('beforeunload', () => {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
}
|
||||
stopConnectionMonitor();
|
||||
stopEventsWS();
|
||||
disconnectAllKCWebSockets();
|
||||
disconnectAllLedPreviewWS();
|
||||
@@ -552,6 +553,10 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Setup form handler
|
||||
document.getElementById('add-device-form').addEventListener('submit', handleAddDevice);
|
||||
|
||||
// Always monitor server connection (even before login)
|
||||
loadServerInfo();
|
||||
startConnectionMonitor();
|
||||
|
||||
// Show modal if no API key is stored
|
||||
if (!apiKey) {
|
||||
setTimeout(() => {
|
||||
@@ -563,7 +568,6 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
}
|
||||
|
||||
// User is logged in, load data
|
||||
loadServerInfo();
|
||||
loadDisplays();
|
||||
loadTargetsTab();
|
||||
|
||||
|
||||
@@ -120,17 +120,56 @@ export function handle401Error() {
|
||||
}
|
||||
}
|
||||
|
||||
let _connCheckTimer = null;
|
||||
let _serverOnline = null; // null = unknown, true/false
|
||||
|
||||
function _setConnectionState(online) {
|
||||
const changed = _serverOnline !== online;
|
||||
_serverOnline = online;
|
||||
const banner = document.getElementById('connection-overlay');
|
||||
const badge = document.getElementById('server-status');
|
||||
if (online) {
|
||||
if (banner) banner.style.display = 'none';
|
||||
if (badge) badge.className = 'status-badge online';
|
||||
} else {
|
||||
if (banner) banner.style.display = 'flex';
|
||||
if (badge) badge.className = 'status-badge offline';
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
export async function loadServerInfo() {
|
||||
try {
|
||||
const response = await fetch('/health');
|
||||
const response = await fetch('/health', { signal: AbortSignal.timeout(5000) });
|
||||
const data = await response.json();
|
||||
|
||||
document.getElementById('version-number').textContent = `v${data.version}`;
|
||||
document.getElementById('server-status').textContent = '●';
|
||||
document.getElementById('server-status').className = 'status-badge online';
|
||||
const wasOffline = _serverOnline === false;
|
||||
_setConnectionState(true);
|
||||
if (wasOffline) {
|
||||
// Server came back — reload data
|
||||
window.dispatchEvent(new CustomEvent('server:reconnected'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load server info:', error);
|
||||
document.getElementById('server-status').className = 'status-badge offline';
|
||||
_setConnectionState(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start periodic health checks. Shows/hides the connection banner.
|
||||
* @param {number} interval - Check interval in ms (default 10s)
|
||||
*/
|
||||
export function startConnectionMonitor(interval = 10000) {
|
||||
stopConnectionMonitor();
|
||||
_connCheckTimer = setInterval(loadServerInfo, interval);
|
||||
}
|
||||
|
||||
export function stopConnectionMonitor() {
|
||||
if (_connCheckTimer) {
|
||||
clearInterval(_connCheckTimer);
|
||||
_connCheckTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user