/** * NetworkStatusBanner Component * * Shows a minimal, classy popup when the device is offline. * - Shows popup when going offline * - Auto-dismisses when internet comes back * - Shows again on next disconnect */ import { Ionicons } from "@expo/vector-icons"; import NetInfo, { type NetInfoState } from "@react-native-community/netinfo"; import { useCallback, useEffect, useRef, useState } from "react"; import { Animated, Modal, Text, TouchableOpacity, View } from "react-native"; export function NetworkStatusBanner() { // eslint-disable-next-line react-compiler/react-compiler -- Animated.Value ref access in JSX is incompatible with React Compiler "use no memo"; const [showModal, setShowModal] = useState(false); const fadeAnimRef = useRef(new Animated.Value(0)); // Simple refs to track state const previousOfflineRef = useRef(null); const userDismissedRef = useRef(false); const checkIfOffline = useCallback((state: NetInfoState): boolean => { if (state.isConnected === false) return true; if (state.isInternetReachable === false) return true; return false; }, []); useEffect(() => { const handleNetworkChange = (state: NetInfoState) => { const currentlyOffline = checkIfOffline(state); const wasOffline = previousOfflineRef.current; // Transition: Online → Offline (only show if user hasn't dismissed) if (currentlyOffline && wasOffline === false && !userDismissedRef.current) { setShowModal(true); } // Transition: Offline → Online if (!currentlyOffline && wasOffline === true) { setShowModal(false); // Auto-dismiss userDismissedRef.current = false; // Reset for next offline event } previousOfflineRef.current = currentlyOffline; }; // Get initial state NetInfo.fetch().then((state) => { const offline = checkIfOffline(state); previousOfflineRef.current = offline; if (offline) { setShowModal(true); } }); // Listen for changes const unsubscribe = NetInfo.addEventListener(handleNetworkChange); return () => unsubscribe(); }, [checkIfOffline]); useEffect(() => { if (showModal) { fadeAnimRef.current.setValue(0); Animated.timing(fadeAnimRef.current, { toValue: 1, duration: 250, useNativeDriver: true, }).start(); } }, [showModal]); const handleDismiss = () => { userDismissedRef.current = true; Animated.timing(fadeAnimRef.current, { toValue: 0, duration: 150, useNativeDriver: true, }).start(() => { setShowModal(false); }); }; if (!showModal) return null; return ( You're offline The internet left the chat. Don't panic! You can still browse around. Cool, got it ); } export default NetworkStatusBanner;