iloom-flatten/src/components/NetworkStatusBar.tsx

114 lines
4.0 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Wifi, WifiOff, AlertCircle, Loader2 } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { useNetworkStatus } from '../hooks/useNetworkStatus';
interface NetworkStatusBarProps {
className?: string;
}
export function NetworkStatusBar({ className = '' }: NetworkStatusBarProps) {
const { isOnline, isSlowConnection, queueLength } = useNetworkStatus();
const [showSlowWarning, setShowSlowWarning] = useState(false);
const [dismissed, setDismissed] = useState(false);
// 慢网络警告自动显示
useEffect(() => {
if (isSlowConnection && isOnline) {
setShowSlowWarning(true);
const timer = setTimeout(() => setShowSlowWarning(false), 5000);
return () => clearTimeout(timer);
}
}, [isSlowConnection, isOnline]);
// 离线时重置 dismissed
useEffect(() => {
if (!isOnline) setDismissed(false);
}, [isOnline]);
if (dismissed) return null;
return (
<AnimatePresence>
{!isOnline && (
<motion.div
initial={{ opacity: 0, y: -40 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -40 }}
className={`fixed top-0 left-0 right-0 z-50 bg-red-500 text-white px-4 py-2 ${className}`}
>
<div className="max-w-7xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-2 min-w-0">
<WifiOff className="w-4 h-4 flex-shrink-0" />
<span className="text-xs sm:text-sm font-medium whitespace-nowrap truncate"></span>
{queueLength > 0 && (
<span className="text-[10px] sm:text-xs bg-red-600 px-1.5 sm:px-2 py-0.5 rounded-full whitespace-nowrap flex-shrink-0">
{queueLength}
</span>
)}
</div>
<button
onClick={() => setDismissed(true)}
className="text-[10px] sm:text-xs text-red-100 hover:text-white flex-shrink-0 ml-2"
>
</button>
</div>
</motion.div>
)}
{isOnline && showSlowWarning && (
<motion.div
initial={{ opacity: 0, y: -40 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -40 }}
className={`fixed top-0 left-0 right-0 z-50 bg-yellow-500 text-white px-4 py-2 ${className}`}
>
<div className="max-w-7xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-2 min-w-0">
<AlertCircle className="w-4 h-4 flex-shrink-0" />
<span className="text-xs sm:text-sm font-medium whitespace-nowrap truncate"></span>
</div>
<button
onClick={() => setShowSlowWarning(false)}
className="text-[10px] sm:text-xs text-yellow-100 hover:text-white flex-shrink-0 ml-2"
>
</button>
</div>
</motion.div>
)}
{isOnline && queueLength > 0 && (
<motion.div
initial={{ opacity: 0, y: -40 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -40 }}
className={`fixed top-0 left-0 right-0 z-50 bg-blue-500 text-white px-4 py-2 ${className}`}
>
<div className="max-w-7xl mx-auto flex items-center gap-2">
<Loader2 className="w-4 h-4 animate-spin flex-shrink-0" />
<span className="text-xs sm:text-sm font-medium whitespace-nowrap truncate">
{queueLength}...
</span>
</div>
</motion.div>
)}
</AnimatePresence>
);
}
// 小型网络状态指示器(用于按钮等)
export function NetworkIndicator({ className = '' }: { className?: string }) {
const { isOnline } = useNetworkStatus();
if (isOnline) return null;
return (
<span className={`inline-flex items-center gap-1 text-xs text-red-500 ${className}`}>
<WifiOff className="w-3 h-3" />
线
</span>
);
}