From ee96d111941ba518816a2c0b8987b15890ee9957 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 12 Oct 2025 18:25:38 +0000 Subject: [PATCH] fix: Update modal now properly detects when update completes The update progress modal was stuck showing 'initializing' even after the backend restarted and websocket reconnected. Users could see the connection status badge reconnecting behind the modal, but the modal never cleared. Now the modal: - Watches websocket connection status during update - Detects when backend disconnects and reconnects - Verifies health after reconnection - Automatically reloads the page when update is complete - Shows clearer messaging about restart progress --- .../src/components/UpdateBanner.tsx | 6 ++- .../src/components/UpdateProgressModal.tsx | 41 ++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/frontend-modern/src/components/UpdateBanner.tsx b/frontend-modern/src/components/UpdateBanner.tsx index 22bb85daa..ca76975fc 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 cf2826cf6..ca90c358a 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. +