import {useEffect, useRef, useState} from 'react' import {View} from 'react-native' import {useReducedMotion} from 'react-native-reanimated' import {decideShouldRoll} from '#/lib/custom-animations/util' const animationConfig = { duration: 400, easing: 'cubic-bezier(0.4, 0, 0.2, 1)', fill: 'forwards' as FillMode, } const enteringUpKeyframe = [ {opacity: 0, transform: 'translateY(18px)'}, {opacity: 1, transform: 'translateY(0)'}, ] const enteringDownKeyframe = [ {opacity: 0, transform: 'translateY(-18px)'}, {opacity: 1, transform: 'translateY(0)'}, ] const exitingUpKeyframe = [ {opacity: 1, transform: 'translateY(0)'}, {opacity: 0, transform: 'translateY(-18px)'}, ] const exitingDownKeyframe = [ {opacity: 1, transform: 'translateY(0)'}, {opacity: 0, transform: 'translateY(18px)'}, ] export function CountWheel({ count, isToggled, hasBeenToggled, renderCount, }: { count: number isToggled: boolean hasBeenToggled: boolean renderCount: (props: {count: number}) => React.ReactNode }) { const shouldAnimate = !useReducedMotion() && hasBeenToggled const shouldRoll = decideShouldRoll(isToggled, count) const countView = useRef(null) const prevCountView = useRef(null) const [prevCount, setPrevCount] = useState(count) const prevIsToggled = useRef(isToggled) useEffect(() => { if (isToggled === prevIsToggled.current) { return } const newPrevCount = isToggled ? count - 1 : count + 1 if (shouldAnimate && shouldRoll) { countView.current?.animate?.( isToggled ? enteringUpKeyframe : enteringDownKeyframe, animationConfig, ) prevCountView.current?.animate?.( isToggled ? exitingUpKeyframe : exitingDownKeyframe, animationConfig, ) setPrevCount(newPrevCount) } prevIsToggled.current = isToggled }, [isToggled, count, shouldAnimate, shouldRoll]) if (count < 1) { return null } return ( {renderCount({count})} {shouldAnimate && (count > 1 || !isToggled) ? ( {renderCount({count: prevCount})} ) : null} ) }