/ components / SkeletonLoading.tsx
SkeletonLoading.tsx
  1  import React, { useEffect, useRef } from 'react';
  2  import {
  3    View,
  4    Animated,
  5    StyleSheet,
  6    Dimensions,
  7    ViewStyle,
  8  } from 'react-native';
  9  import { useTheme } from '@/constants/ThemeContext';
 10  import { Colors } from '@/constants/Colors';
 11  import { ShimmerEffect, ShimmerCard } from './ShimmerEffect';
 12  
 13  const { width: SCREEN_WIDTH } = Dimensions.get('window');
 14  
 15  interface SkeletonLoadingProps {
 16    width?: number | string;
 17    height?: number;
 18    borderRadius?: number;
 19    style?: ViewStyle;
 20  }
 21  
 22  export const SkeletonItem: React.FC<SkeletonLoadingProps> = ({
 23    width = '100%',
 24    height = 20,
 25    borderRadius = 4,
 26    style,
 27  }) => {
 28    const { actualTheme } = useTheme();
 29    const colors = Colors[actualTheme];
 30    const animatedValue = useRef(new Animated.Value(0)).current;
 31  
 32    useEffect(() => {
 33      const startAnimation = () => {
 34        Animated.loop(
 35          Animated.sequence([
 36            Animated.timing(animatedValue, {
 37              toValue: 1,
 38              duration: 1000,
 39              useNativeDriver: false,
 40            }),
 41            Animated.timing(animatedValue, {
 42              toValue: 0,
 43              duration: 1000,
 44              useNativeDriver: false,
 45            }),
 46          ])
 47        ).start();
 48      };
 49  
 50      startAnimation();
 51    }, [animatedValue]);
 52  
 53    const backgroundColor = animatedValue.interpolate({
 54      inputRange: [0, 1],
 55      outputRange: [colors.card, colors.border],
 56    });
 57  
 58    return (
 59      <Animated.View
 60        style={[
 61          {
 62            width: Number(width) || 100,
 63            height,
 64            borderRadius,
 65            backgroundColor,
 66          },
 67          style,
 68        ]}
 69      />
 70    );
 71  };
 72  
 73  export const MangaCardSkeleton: React.FC = () => {
 74    const { actualTheme } = useTheme();
 75    const colors = Colors[actualTheme];
 76    const styles = getStyles(colors);
 77  
 78    return <ShimmerCard style={styles.cardContainer} />;
 79  };
 80  
 81  export const RecentlyReadSkeleton: React.FC = () => {
 82    const cardWidth = Math.min(160, (SCREEN_WIDTH - 64) / 2);
 83  
 84    return (
 85      <View style={{ flexDirection: 'row', paddingHorizontal: 16 }}>
 86        {Array.from({ length: 3 }).map((_, index) => (
 87          <View key={index} style={{ width: cardWidth, marginRight: 12 }}>
 88            <MangaCardSkeleton />
 89          </View>
 90        ))}
 91      </View>
 92    );
 93  };
 94  
 95  export const TrendingSkeleton: React.FC = () => {
 96    return (
 97      <View style={{ flexDirection: 'row', paddingHorizontal: 16 }}>
 98        {Array.from({ length: 4 }).map((_, index) => (
 99          <View key={index} style={{ width: 200, height: 260, marginRight: 12 }}>
100            <ShimmerEffect width={200} height={260} borderRadius={16} />
101          </View>
102        ))}
103      </View>
104    );
105  };
106  
107  export const NewReleasesSkeleton: React.FC = () => {
108    const { actualTheme } = useTheme();
109    const colors = Colors[actualTheme];
110    const styles = getStyles(colors);
111  
112    return (
113      <View style={styles.gridContainer}>
114        {Array.from({ length: 6 }).map((_, index) => (
115          <View key={index} style={styles.gridItem}>
116            <MangaCardSkeleton />
117          </View>
118        ))}
119      </View>
120    );
121  };
122  
123  export const FeaturedMangaSkeleton: React.FC = () => {
124    return (
125      <View style={{ height: 280, marginHorizontal: 16, marginBottom: 24 }}>
126        <ShimmerEffect width={300} height={280} borderRadius={16} />
127      </View>
128    );
129  };
130  
131  const getStyles = (colors: typeof Colors.light) =>
132    StyleSheet.create({
133      cardContainer: {
134        borderRadius: 12,
135        overflow: 'hidden',
136        backgroundColor: colors.card,
137      },
138      cardInfo: {
139        padding: 8,
140      },
141      gridContainer: {
142        flexDirection: 'row',
143        flexWrap: 'wrap',
144        paddingHorizontal: 16,
145      },
146      gridItem: {
147        width: '50%',
148        padding: 8,
149      },
150    });
151  
152  export default SkeletonItem;