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>
46 lines
1.4 KiB
TypeScript
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>
|
|
);
|
|
}
|