import React, { useEffect } from 'react';
import { View, Text, StyleSheet, ActivityIndicator, ViewStyle, TextStyle } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import Animated, {
FadeIn,
FadeOut,
useSharedValue,
useAnimatedStyle,
withRepeat,
withTiming,
interpolate,
Easing,
} from 'react-native-reanimated';
import { colors, typography, spacing, radius } from '../../theme';
export type LoadingVariant = 'spinner' | 'shimmer' | 'progress';
export interface LoadingOverlayProps {
/** Whether the loading overlay is visible */
visible: boolean;
/** Visual variant of the loading indicator */
variant?: LoadingVariant;
/** Status text to display */
statusText?: string;
/** Secondary/detail text */
detailText?: string;
/** Progress value (0-100) for 'progress' variant */
progress?: number;
/** Custom spinner color */
spinnerColor?: string;
/** Whether to use full-screen overlay mode */
fullScreen?: boolean;
/** Custom container style */
style?: ViewStyle;
/** Custom text style */
textStyle?: TextStyle;
}
/**
* Flexible loading overlay component supporting multiple variants.
* Used for upload status, loading states, and progress indicators.
*/
export function LoadingOverlay({
visible,
variant = 'spinner',
statusText,
detailText,
progress = 0,
spinnerColor = colors.premium.gold,
fullScreen = false,
style,
textStyle,
}: LoadingOverlayProps) {
const shimmer = useSharedValue(0);
useEffect(() => {
if (visible && variant === 'shimmer') {
shimmer.value = withRepeat(
withTiming(1, { duration: 1500, easing: Easing.inOut(Easing.ease) }),
-1,
false
);
} else {
shimmer.value = 0;
}
}, [visible, variant]);
const shimmerStyle = useAnimatedStyle(() => ({
opacity: interpolate(shimmer.value, [0, 0.5, 1], [0.3, 0.6, 0.3]),
}));
if (!visible) return null;
const renderContent = () => {
switch (variant) {
case 'shimmer':
return (
{statusText && (
{statusText}
)}
{detailText && (
{detailText}
)}
);
case 'progress':
return (
{statusText && (
{statusText}
)}
{detailText && (
{detailText}
)}
);
case 'spinner':
default:
return (
{statusText && (
{statusText}
)}
{detailText && (
{detailText}
)}
);
}
};
if (fullScreen) {
return (
{renderContent()}
);
}
return (
{renderContent()}
);
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
padding: spacing.md,
},
fullScreenContainer: {
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(13, 13, 26, 0.85)',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1000,
},
spinnerContent: {
alignItems: 'center',
justifyContent: 'center',
gap: spacing.sm,
},
shimmerContent: {
alignItems: 'center',
justifyContent: 'center',
gap: spacing.xs,
},
progressContent: {
alignItems: 'center',
justifyContent: 'center',
gap: spacing.sm,
width: '100%',
},
statusText: {
...typography.body,
color: colors.textSecondary,
textAlign: 'center',
marginTop: spacing.xs,
},
detailText: {
...typography.caption1,
color: colors.textTertiary,
textAlign: 'center',
},
progressBarContainer: {
width: '80%',
height: 4,
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: radius.full,
overflow: 'hidden',
},
progressBar: {
height: '100%',
borderRadius: radius.full,
},
});
export default LoadingOverlay;