import { useState, useEffect } from 'react'; import { AlertTriangle, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; interface ReconnectingOverlayProps { /** Gateway boot timestamp captured pre-update. Null falls back to offline-then-online detection. */ preUpdateStartedAt: number | null; } export function ReconnectingOverlay({ preUpdateStartedAt }: ReconnectingOverlayProps) { const [elapsed, setElapsed] = useState(0); const timedOut = elapsed >= 300; // 5 minutes useEffect(() => { const timer = setInterval(() => setElapsed(s => s + 1), 1000); return () => clearInterval(timer); }, []); useEffect(() => { if (timedOut) return; let sawOffline = false; const poll = setInterval(async () => { try { const res = await fetch('/api/health'); if (!res.ok) { sawOffline = true; return; } const data = await res.json().catch(() => null) as { startedAt?: number } | null; const currentStartedAt = typeof data?.startedAt === 'number' ? data.startedAt : null; if (preUpdateStartedAt !== null && currentStartedAt !== null) { if (currentStartedAt !== preUpdateStartedAt) { window.location.reload(); } return; } // Fallback when we don't know the original startedAt: require an offline // response first so we don't reload while the old process is still mid-pull. if (sawOffline) { window.location.reload(); } } catch { sawOffline = true; } }, 3000); return () => clearInterval(poll); }, [timedOut, preUpdateStartedAt]); return (
The server has not come back within 5 minutes. Check the Docker host directly.
> ) : ( <>The server is pulling the latest image and restarting. This page will reload automatically.
{elapsed}s elapsed
> )}