← back to Watches
src/components/OfflineIndicator.jsx
102 lines
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { setupOnlineDetection, getOnlineStatus } from '../utils/pwaUtils';
/**
* Offline Status Indicator Component
* Shows a banner when the app is offline
*/
function OfflineIndicator() {
const [isOnline, setIsOnline] = useState(getOnlineStatus());
const [showToast, setShowToast] = useState(false);
useEffect(() => {
// Setup online/offline detection
setupOnlineDetection((online) => {
const wasOffline = !isOnline;
setIsOnline(online);
// Show toast when status changes
if (online && wasOffline) {
setShowToast(true);
setTimeout(() => setShowToast(false), 3000);
}
});
}, [isOnline]);
// Persistent offline banner at top
if (!isOnline) {
return (
<motion.div
initial={{ y: -50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
className="fixed top-0 left-0 right-0 z-50 bg-yellow-500 text-gray-900"
>
<div className="container mx-auto px-4 py-2">
<div className="flex items-center justify-center gap-3">
<svg
className="w-5 h-5 animate-pulse"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414"
/>
</svg>
<span className="font-semibold text-sm md:text-base">
You're offline - Browsing cached data
</span>
<svg
className="w-4 h-4"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clipRule="evenodd"
/>
</svg>
</div>
</div>
</motion.div>
);
}
// Toast notification when back online
return (
<AnimatePresence>
{showToast && (
<motion.div
initial={{ y: -100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -100, opacity: 0 }}
className="fixed top-4 left-1/2 transform -translate-x-1/2 z-50"
>
<div className="bg-green-500 text-white px-6 py-3 rounded-full shadow-lg flex items-center gap-3">
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
<span className="font-semibold">Back online!</span>
</div>
</motion.div>
)}
</AnimatePresence>
);
}
export default OfflineIndicator;