nexus/ui/src/components/OfflineBanner.tsx
Nexus Dev 471a9daaa6 feat(26): merge worktree code from plans 26-00, 26-01, 26-03
SW cache-first rewrite, React.lazy code splitting, PWA types/test stubs,
install prompt, offline banner, offline queue, ChatPanel wiring.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:48 +00:00

46 lines
1.4 KiB
TypeScript

import { useEffect, useState } from "react";
import { WifiOff } from "lucide-react";
import { useOnlineStatus } from "../hooks/useOnlineStatus";
interface OfflineBannerProps {
queuedCount?: number;
}
/**
* Amber offline status banner shown at the top of the screen when offline.
* Auto-dismisses 3 seconds after reconnection when the queue is empty.
*/
export function OfflineBanner({ queuedCount = 0 }: OfflineBannerProps) {
const isOnline = useOnlineStatus();
const [visible, setVisible] = useState(!isOnline);
useEffect(() => {
if (!isOnline) {
setVisible(true);
return;
}
// Online transition: hide after 3s if no queued messages
if (queuedCount === 0) {
const timer = setTimeout(() => setVisible(false), 3000);
return () => clearTimeout(timer);
}
}, [isOnline, queuedCount]);
if (!visible) return null;
const text =
queuedCount > 0
? `You're offline — ${queuedCount} message${queuedCount === 1 ? "" : "s"} queued`
: "You're offline — messages will send when you reconnect";
return (
<div
className="fixed top-0 left-0 right-0 z-50 px-4 py-2 text-sm flex items-center gap-2 bg-amber-50 text-amber-800 border-b border-amber-200 dark:bg-amber-900/40 dark:text-amber-200 dark:border-amber-800"
role="status"
aria-live="polite"
>
<WifiOff className="h-4 w-4 flex-shrink-0" />
<span>{text}</span>
</div>
);
}