Bluesky app fork with some witchin' additions 馃挮
at main 119 lines 2.9 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import {useReducedMotion} from 'react-native-reanimated' 4 5import {s} from '#/lib/styles' 6import {useTheme} from '#/alf' 7import { 8 Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, 9 Heart2_Stroke2_Corner0_Rounded as HeartIconOutline, 10} from '#/components/icons/Heart2' 11 12const animationConfig = { 13 duration: 600, 14 easing: 'cubic-bezier(0.4, 0, 0.2, 1)', 15 fill: 'forwards' as FillMode, 16} 17 18const keyframe = [ 19 {transform: 'scale(1)'}, 20 {transform: 'scale(0.7)'}, 21 {transform: 'scale(1.2)'}, 22 {transform: 'scale(1)'}, 23] 24 25const circle1Keyframe = [ 26 {opacity: 0, transform: 'scale(0)'}, 27 {opacity: 0.4}, 28 {transform: 'scale(1.5)'}, 29 {opacity: 0.4}, 30 {opacity: 0, transform: 'scale(1.5)'}, 31] 32 33const circle2Keyframe = [ 34 {opacity: 0, transform: 'scale(0)'}, 35 {opacity: 1}, 36 {transform: 'scale(0)'}, 37 {opacity: 1}, 38 {opacity: 0, transform: 'scale(1.5)'}, 39] 40 41export function AnimatedLikeIcon({ 42 isLiked, 43 big, 44 hasBeenToggled, 45}: { 46 isLiked: boolean 47 big?: boolean 48 hasBeenToggled: boolean 49}) { 50 const t = useTheme() 51 const size = big ? 22 : 18 52 const shouldAnimate = !useReducedMotion() && hasBeenToggled 53 const prevIsLiked = React.useRef(isLiked) 54 55 const likeIconRef = React.useRef<HTMLDivElement>(null) 56 const circle1Ref = React.useRef<HTMLDivElement>(null) 57 const circle2Ref = React.useRef<HTMLDivElement>(null) 58 59 React.useEffect(() => { 60 if (prevIsLiked.current === isLiked) { 61 return 62 } 63 64 if (shouldAnimate && isLiked) { 65 likeIconRef.current?.animate?.(keyframe, animationConfig) 66 circle1Ref.current?.animate?.(circle1Keyframe, animationConfig) 67 circle2Ref.current?.animate?.(circle2Keyframe, animationConfig) 68 } 69 prevIsLiked.current = isLiked 70 }, [shouldAnimate, isLiked]) 71 72 return ( 73 <View> 74 {isLiked ? ( 75 // @ts-expect-error is div 76 <View ref={likeIconRef}> 77 <HeartIconFilled style={s.likeColor} width={size} /> 78 </View> 79 ) : ( 80 <HeartIconOutline 81 style={[{color: t.palette.contrast_500}, {pointerEvents: 'none'}]} 82 width={size} 83 /> 84 )} 85 <View 86 // @ts-expect-error is div 87 ref={circle1Ref} 88 style={{ 89 position: 'absolute', 90 backgroundColor: s.likeColor.color, 91 top: 0, 92 left: 0, 93 width: size, 94 height: size, 95 zIndex: -1, 96 pointerEvents: 'none', 97 borderRadius: size / 2, 98 opacity: 0, 99 }} 100 /> 101 <View 102 // @ts-expect-error is div 103 ref={circle2Ref} 104 style={{ 105 position: 'absolute', 106 backgroundColor: t.atoms.bg.backgroundColor, 107 top: 0, 108 left: 0, 109 width: size, 110 height: size, 111 zIndex: -1, 112 pointerEvents: 'none', 113 borderRadius: size / 2, 114 opacity: 0, 115 }} 116 /> 117 </View> 118 ) 119}