Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | /**
* Offline Banner Component
*
* Displays a banner at the top of the screen when the device is offline.
* Automatically shows/hides based on network connectivity.
*/
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Animated, {
useAnimatedStyle,
withTiming,
useSharedValue,
withSequence,
} from 'react-native-reanimated';
import { useNetworkStatus } from '@/utils/networkStatus';
interface OfflineBannerProps {
/**
* Custom message to display when offline
* Default: "No internet connection"
*/
message?: string;
/**
* Position of the banner
* Default: "top"
*/
position?: 'top' | 'bottom';
/**
* Background color
* Default: "#FF3B30" (red)
*/
backgroundColor?: string;
/**
* Text color
* Default: "#FFFFFF" (white)
*/
textColor?: string;
/**
* Height of the banner
* Default: 40
*/
height?: number;
}
export function OfflineBanner({
message = 'No internet connection',
position = 'top',
backgroundColor = '#FF3B30',
textColor = '#FFFFFF',
height = 40,
}: OfflineBannerProps) {
const { isOffline } = useNetworkStatus();
const translateY = useSharedValue(position === 'top' ? -height : height);
// Animate banner in/out based on network status
React.useEffect(() => {
if (isOffline) {
// Slide in with slight bounce
translateY.value = withSequence(
withTiming(0, { duration: 300 }),
withTiming(-2, { duration: 100 }),
withTiming(0, { duration: 100 })
);
} else {
// Slide out
translateY.value = withTiming(position === 'top' ? -height : height, {
duration: 200,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOffline, position, height]);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateY: translateY.value }],
}));
return (
<Animated.View
style={[
styles.container,
{
backgroundColor,
height,
[position]: 0,
},
animatedStyle,
]}
>
<Text style={[styles.text, { color: textColor }]}>{message}</Text>
</Animated.View>
);
}
/**
* Inline Offline Banner
* Displays within the component tree (not positioned absolutely)
* Use this for inline offline indicators
*/
export function InlineOfflineBanner({
message = 'No internet connection',
backgroundColor = '#FF3B30',
textColor = '#FFFFFF',
}: Omit<OfflineBannerProps, 'position' | 'height'>) {
const { isOffline } = useNetworkStatus();
if (!isOffline) {
return null;
}
return (
<View style={[styles.inlineContainer, { backgroundColor }]}>
<Text style={[styles.text, { color: textColor }]}>{message}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
left: 0,
right: 0,
justifyContent: 'center',
alignItems: 'center',
zIndex: 9999,
paddingHorizontal: 16,
},
inlineContainer: {
paddingVertical: 12,
paddingHorizontal: 16,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 16,
borderRadius: 8,
},
text: {
fontSize: 14,
fontWeight: '600',
textAlign: 'center',
},
});
|