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