diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 6c57c73..1055e1c 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -159,14 +159,11 @@ async function ensureFreshBuild() { } } -onMounted(async () => { +onMounted(() => { ensureFreshBuild() - await Promise.all([ - loadStrategies(), - loadPresets(), - api.getStatus().then((s) => (status.value = s)), - refreshAll(), - ]) + + // Set up the socket and the polling fallback first, so a failing initial + // fetch can never prevent them (that left the badge stuck on "connecting"). socket = api.openSocket({ onMessage: handleMessage, onOpen: () => { @@ -185,9 +182,19 @@ onMounted(async () => { graceTimer = setTimeout(() => { if (!connected.value) pollingFallback.value = true }, 6000) + + // Initial loads are best-effort; the intervals below keep retrying. + api.getStatus().then((s) => (status.value = s)).catch(() => {}) + refreshAll().catch(() => {}) + loadStrategies().catch(() => {}) + loadPresets().catch(() => {}) + // Poll fast enough that the UI feels live even when it is the only channel. - pollTimer = setInterval(refreshAll, 3000) - statusTimer = setInterval(() => api.getStatus().then((s) => (status.value = s)), 3000) + pollTimer = setInterval(() => refreshAll().catch(() => {}), 3000) + statusTimer = setInterval( + () => api.getStatus().then((s) => (status.value = s)).catch(() => {}), + 3000, + ) }) onUnmounted(() => { diff --git a/frontend/src/api.js b/frontend/src/api.js index e20c073..80f680e 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -48,6 +48,7 @@ export function openSocket({ onMessage, onOpen, onClose, onError }) { if (closed) return let opened = false let reported = false + console.debug('[ws] connecting to', url) socket = new WebSocket(url) // Report at most one failure per attempt (Safari often fires error *and* @@ -72,11 +73,13 @@ export function openSocket({ onMessage, onOpen, onClose, onError }) { opened = true clearTimeout(openTimeout) attempt = 0 + console.debug('[ws] open') onOpen?.() } socket.onmessage = (event) => onMessage?.(JSON.parse(event.data)) socket.onclose = (event) => { clearTimeout(openTimeout) + console.debug('[ws] close', event.code, event.reason) onClose?.() if (!opened) { // 1008 = policy violation (token rejected); 1006 = abnormal close.