Fix updater auto-refresh for all open tabs (related to #671)
Problem: When an update was triggered, only the tab that clicked "Apply Update" would show the progress modal and auto-refresh after completion. Other open tabs would remain on the old version indefinitely. Root cause: The UpdateProgressModal was only shown when explicitly opened via the UpdateBanner component. WebSocket already broadcasts update:progress events, but no global listener existed to show the modal in all tabs. Solution: Added GlobalUpdateProgressWatcher component in App.tsx that: - Listens to WebSocket updateProgress events globally (in all tabs) - Filters to only real update-in-progress states (downloading, verifying, extracting, installing, restarting) to avoid false positives from routine update checks - Auto-opens the progress modal when an update starts - Allows manual dismissal after update completes - Works independently of UpdateBanner visibility (e.g., when banner is dismissed) The modal's existing health-check and auto-reload logic handles the page refresh once the backend is healthy again.
This commit is contained in:
parent
6bf32f98d6
commit
706822ed58
1 changed files with 57 additions and 0 deletions
|
|
@ -40,6 +40,8 @@ import BellIcon from 'lucide-solid/icons/bell';
|
||||||
import SettingsIcon from 'lucide-solid/icons/settings';
|
import SettingsIcon from 'lucide-solid/icons/settings';
|
||||||
import { TokenRevealDialog } from './components/TokenRevealDialog';
|
import { TokenRevealDialog } from './components/TokenRevealDialog';
|
||||||
import { useAlertsActivation } from './stores/alertsActivation';
|
import { useAlertsActivation } from './stores/alertsActivation';
|
||||||
|
import { UpdateProgressModal } from './components/UpdateProgressModal';
|
||||||
|
import type { UpdateStatus } from './api/updates';
|
||||||
|
|
||||||
const Dashboard = lazy(() =>
|
const Dashboard = lazy(() =>
|
||||||
import('./components/Dashboard/Dashboard').then((module) => ({ default: module.Dashboard })),
|
import('./components/Dashboard/Dashboard').then((module) => ({ default: module.Dashboard })),
|
||||||
|
|
@ -106,6 +108,60 @@ function HostsRoute() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to detect if an update is actively in progress (not just checking for updates)
|
||||||
|
function isUpdateInProgress(status: string | undefined): boolean {
|
||||||
|
if (!status) return false;
|
||||||
|
const inProgressStates = ['downloading', 'verifying', 'extracting', 'installing', 'restarting'];
|
||||||
|
return inProgressStates.includes(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global update progress watcher - shows modal in ALL tabs when an update is running
|
||||||
|
function GlobalUpdateProgressWatcher() {
|
||||||
|
const wsContext = useContext(WebSocketContext);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [showProgressModal, setShowProgressModal] = createSignal(false);
|
||||||
|
const [hasAutoOpened, setHasAutoOpened] = createSignal(false);
|
||||||
|
|
||||||
|
// Watch for update progress events from WebSocket
|
||||||
|
createEffect(() => {
|
||||||
|
const progress = wsContext?.updateProgress?.() as UpdateStatus | null;
|
||||||
|
|
||||||
|
if (!progress) {
|
||||||
|
// Reset when no progress data
|
||||||
|
setHasAutoOpened(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inProgress = isUpdateInProgress(progress.status);
|
||||||
|
|
||||||
|
if (inProgress && !showProgressModal() && !hasAutoOpened()) {
|
||||||
|
// Update is starting - auto-open the modal in this tab
|
||||||
|
logger.info('Update in progress detected via WebSocket, showing progress modal', {
|
||||||
|
status: progress.status,
|
||||||
|
message: progress.message,
|
||||||
|
});
|
||||||
|
setShowProgressModal(true);
|
||||||
|
setHasAutoOpened(true);
|
||||||
|
} else if (!inProgress && hasAutoOpened()) {
|
||||||
|
// Update finished - allow the modal to be dismissed
|
||||||
|
setHasAutoOpened(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UpdateProgressModal
|
||||||
|
isOpen={showProgressModal()}
|
||||||
|
onClose={() => setShowProgressModal(false)}
|
||||||
|
onViewHistory={() => {
|
||||||
|
setShowProgressModal(false);
|
||||||
|
navigate('/settings/updates');
|
||||||
|
}}
|
||||||
|
connected={wsContext?.connected}
|
||||||
|
reconnecting={wsContext?.reconnecting}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const TooltipRoot = createTooltipSystem();
|
const TooltipRoot = createTooltipSystem();
|
||||||
const owner = getOwner();
|
const owner = getOwner();
|
||||||
|
|
@ -644,6 +700,7 @@ function App() {
|
||||||
<SecurityWarning />
|
<SecurityWarning />
|
||||||
<DemoBanner />
|
<DemoBanner />
|
||||||
<UpdateBanner />
|
<UpdateBanner />
|
||||||
|
<GlobalUpdateProgressWatcher />
|
||||||
<div class="min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 font-sans py-4 sm:py-6">
|
<div class="min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 font-sans py-4 sm:py-6">
|
||||||
<AppLayout
|
<AppLayout
|
||||||
connected={connected}
|
connected={connected}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue