Files
calendar/companion/components/NetworkStatusBanner.tsx
T
Dhairyashil ShindeandGitHub 4036fb6d08 feat(companion): react compiler (#26221)
* react compiler

* remove compilation mode 'all', it will use infer by default
2025-12-27 16:06:00 -03:00

113 lines
3.7 KiB
TypeScript

/**
* 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<boolean | null>(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 (
<Modal transparent visible={showModal} animationType="none" onRequestClose={handleDismiss}>
<Animated.View
style={{ opacity: fadeAnimRef.current }}
className="flex-1 items-center justify-center bg-black/40 px-8"
>
<View className="w-full max-w-xs items-center rounded-2xl bg-white px-6 py-8">
<Ionicons name="cloud-offline-outline" size={36} color="#292929" />
<Text className="mt-4 text-lg font-semibold text-gray-900">You're offline</Text>
<Text className="mt-2 text-center text-sm leading-5 text-gray-500">
The internet left the chat. Don't panic! You can still browse around.
</Text>
<TouchableOpacity
onPress={handleDismiss}
className="mt-6 rounded-full bg-gray-900 px-8 py-3 active:bg-gray-700"
>
<Text className="text-sm font-medium text-white">Cool, got it</Text>
</TouchableOpacity>
</View>
</Animated.View>
</Modal>
);
}
export default NetworkStatusBanner;