import { createContext, useContext, useEffect } from "react" import { StyleProp, View, ViewStyle } from "react-native" import Animated, { SharedValue, useAnimatedStyle, useSharedValue, withRepeat, withTiming, Easing, interpolate, } from "react-native-reanimated" import { useAppTheme } from "@/theme/context" import { s } from "@/utils/responsive" // Shared animation context for synchronized pulse const PulseContext = createContext | null>(null) export interface SkeletonProps { /** * Width of the skeleton. Can be number or percentage string. * @default "100%" */ width?: number | `${number}%` /** * Height of the skeleton. * @default 16 */ height?: number /** * Border radius. Use "round" for circle. * @default 4 */ radius?: number | "round" /** * Style overrides */ style?: StyleProp } /** * A skeleton placeholder with pulse animation. * When used inside SkeletonContainer, all skeletons animate in sync. */ export function Skeleton(props: SkeletonProps) { const { width = "100%", height = s(16), radius = s(4), style } = props const { theme } = useAppTheme() // Use shared animation from context, or create own const sharedProgress = useContext(PulseContext) const localProgress = useSharedValue(0) const progress = sharedProgress || localProgress // Only start local animation if not using shared context useEffect(() => { if (!sharedProgress) { localProgress.value = withRepeat( withTiming(1, { duration: 1000, easing: Easing.inOut(Easing.ease) }), -1, true, ) } }, [sharedProgress, localProgress]) const animatedStyle = useAnimatedStyle(() => ({ opacity: interpolate(progress.value, [0, 1], [0.4, 1]), })) const borderRadius = radius === "round" ? (typeof height === "number" ? height / 2 : 999) : radius return ( ) } export interface SkeletonContainerProps { /** * Children (Skeleton components) */ children: React.ReactNode /** * Style overrides for container */ style?: StyleProp } /** * Container that synchronizes pulse animation across all child Skeletons. * * @example * * * * */ export function SkeletonContainer(props: SkeletonContainerProps) { const { children, style } = props const progress = useSharedValue(0) useEffect(() => { progress.value = withRepeat( withTiming(1, { duration: 1000, easing: Easing.inOut(Easing.ease) }), -1, true, ) }, [progress]) return ( {children} ) }