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