diff --git a/frontend-modern/src/components/UpdateBanner.tsx b/frontend-modern/src/components/UpdateBanner.tsx index 22bb85d..ca76975 100644 --- a/frontend-modern/src/components/UpdateBanner.tsx +++ b/frontend-modern/src/components/UpdateBanner.tsx @@ -1,12 +1,14 @@ -import { Show, createSignal, createEffect, For } from 'solid-js'; +import { Show, createSignal, createEffect, For, useContext } from 'solid-js'; import { useNavigate } from '@solidjs/router'; import { updateStore } from '@/stores/updates'; import { UpdatesAPI, type UpdatePlan } from '@/api/updates'; import { UpdateConfirmationModal } from './UpdateConfirmationModal'; import { UpdateProgressModal } from './UpdateProgressModal'; +import { WebSocketContext } from '@/App'; export function UpdateBanner() { const navigate = useNavigate(); + const wsContext = useContext(WebSocketContext); const [isExpanded, setIsExpanded] = createSignal(false); const [updatePlan, setUpdatePlan] = createSignal(null); const [showConfirmModal, setShowConfirmModal] = createSignal(false); @@ -300,6 +302,8 @@ export function UpdateBanner() { setShowProgressModal(false); navigate('/settings/updates'); }} + connected={wsContext?.connected} + reconnecting={wsContext?.reconnecting} /> ); diff --git a/frontend-modern/src/components/UpdateProgressModal.tsx b/frontend-modern/src/components/UpdateProgressModal.tsx index cf2826c..ca90c35 100644 --- a/frontend-modern/src/components/UpdateProgressModal.tsx +++ b/frontend-modern/src/components/UpdateProgressModal.tsx @@ -1,10 +1,12 @@ -import { createSignal, Show, onMount, onCleanup } from 'solid-js'; +import { createSignal, Show, onMount, onCleanup, createEffect } from 'solid-js'; import { UpdatesAPI, type UpdateStatus } from '@/api/updates'; interface UpdateProgressModalProps { isOpen: boolean; onClose: () => void; onViewHistory: () => void; + connected?: () => boolean; + reconnecting?: () => boolean; } export function UpdateProgressModal(props: UpdateProgressModalProps) { @@ -12,6 +14,7 @@ export function UpdateProgressModal(props: UpdateProgressModalProps) { const [isComplete, setIsComplete] = createSignal(false); const [hasError, setHasError] = createSignal(false); const [isRestarting, setIsRestarting] = createSignal(false); + const [wsDisconnected, setWsDisconnected] = createSignal(false); let pollInterval: number | undefined; let healthCheckTimer: number | undefined; let healthCheckAttempts = 0; @@ -113,6 +116,36 @@ export function UpdateProgressModal(props: UpdateProgressModalProps) { healthCheckTimer = window.setTimeout(checkHealth, 0); }; + // Watch websocket status during restart + createEffect(() => { + if (!isRestarting()) return; + + const connected = props.connected?.(); + const reconnecting = props.reconnecting?.(); + + // Track if websocket disconnected during restart + if (connected === false && !reconnecting) { + setWsDisconnected(true); + } + + // If websocket reconnected after being disconnected, the backend is likely back + if (wsDisconnected() && connected === true && !reconnecting) { + console.log('WebSocket reconnected after restart, verifying health...'); + // Give it a moment for the backend to fully initialize + setTimeout(async () => { + try { + const response = await fetch('/api/health', { cache: 'no-store' }); + if (response.ok) { + console.log('Backend healthy after websocket reconnect, reloading...'); + window.location.reload(); + } + } catch (error) { + console.warn('Health check failed after websocket reconnect, will keep trying'); + } + }, 1000); + } + }); + onMount(() => { if (props.isOpen) { // Start polling immediately @@ -252,7 +285,11 @@ export function UpdateProgressModal(props: UpdateProgressModalProps) {
- Pulse is restarting with the new version. This page will reload automatically when ready. + Pulse is restarting with the new version... + }> + Waiting for Pulse to complete restart. This page will reload automatically. +