import React, { useEffect, useRef } from 'react'; import { View, StyleSheet, Animated, ViewStyle, Easing, DimensionValue, } from 'react-native'; import { AppColors, BorderRadius, Spacing } from '@/constants/theme'; interface SkeletonProps { width?: DimensionValue; height?: DimensionValue; borderRadius?: number; style?: ViewStyle; } /** * Animated skeleton loader for placeholder content * Uses a shimmer effect to indicate loading state */ export function Skeleton({ width = '100%', height = 20, borderRadius = BorderRadius.sm, style, }: SkeletonProps) { const shimmerAnim = useRef(new Animated.Value(0)).current; useEffect(() => { const animation = Animated.loop( Animated.sequence([ Animated.timing(shimmerAnim, { toValue: 1, duration: 1000, easing: Easing.ease, useNativeDriver: true, }), Animated.timing(shimmerAnim, { toValue: 0, duration: 1000, easing: Easing.ease, useNativeDriver: true, }), ]) ); animation.start(); return () => animation.stop(); }, [shimmerAnim]); const animatedOpacity = shimmerAnim.interpolate({ inputRange: [0, 1], outputRange: [0.3, 0.7], }); return ( ); } /** * Skeleton for text lines - common use case */ export function SkeletonText({ lines = 1, lineHeight = 16, spacing = Spacing.sm, lastLineWidth = '60%', style, }: { lines?: number; lineHeight?: number; spacing?: number; lastLineWidth?: DimensionValue; style?: ViewStyle; }) { return ( {Array.from({ length: lines }).map((_, index) => ( 1 ? lastLineWidth : '100%'} style={index < lines - 1 ? { marginBottom: spacing } : undefined} /> ))} ); } /** * Skeleton for avatar/circular elements */ export function SkeletonAvatar({ size = 48, style, }: { size?: number; style?: ViewStyle; }) { return ( ); } /** * Skeleton for a card-like element */ export function SkeletonCard({ height = 100, style, }: { height?: DimensionValue; style?: ViewStyle; }) { return ( ); } /** * Skeleton for list items with avatar and text */ export function SkeletonListItem({ avatarSize = 48, lines = 2, style, }: { avatarSize?: number; lines?: number; style?: ViewStyle; }) { return ( ); } /** * Skeleton for beneficiary cards */ export function SkeletonBeneficiaryCard({ style }: { style?: ViewStyle }) { return ( ); } /** * Skeleton for sensor health cards */ export function SkeletonSensorCard({ style }: { style?: ViewStyle }) { return ( ); } const styles = StyleSheet.create({ skeleton: { backgroundColor: AppColors.surfaceSecondary, }, card: { marginBottom: Spacing.md, }, listItem: { flexDirection: 'row', alignItems: 'center', paddingVertical: Spacing.md, paddingHorizontal: Spacing.md, backgroundColor: AppColors.surface, borderRadius: BorderRadius.lg, marginBottom: Spacing.sm, }, listItemContent: { flex: 1, marginLeft: Spacing.md, }, beneficiaryCard: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.lg, marginBottom: Spacing.md, }, beneficiaryHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: Spacing.md, }, beneficiaryInfo: { flex: 1, marginLeft: Spacing.md, }, beneficiaryActions: { flexDirection: 'row', gap: Spacing.sm, }, sensorCard: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.lg, padding: Spacing.md, marginBottom: Spacing.sm, }, sensorHeader: { flexDirection: 'row', alignItems: 'center', gap: Spacing.sm, }, });