mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {View} from 'react-native'
3import {useReducedMotion} from 'react-native-reanimated'
4import {i18n} from '@lingui/core'
5
6import {decideShouldRoll} from 'lib/custom-animations/util'
7import {s} from 'lib/styles'
8import {formatCount} from 'view/com/util/numeric/format'
9import {Text} from 'view/com/util/text/Text'
10import {atoms as a, useTheme} from '#/alf'
11
12const animationConfig = {
13 duration: 400,
14 easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
15 fill: 'forwards' as FillMode,
16}
17
18const enteringUpKeyframe = [
19 {opacity: 0, transform: 'translateY(18px)'},
20 {opacity: 1, transform: 'translateY(0)'},
21]
22
23const enteringDownKeyframe = [
24 {opacity: 0, transform: 'translateY(-18px)'},
25 {opacity: 1, transform: 'translateY(0)'},
26]
27
28const exitingUpKeyframe = [
29 {opacity: 1, transform: 'translateY(0)'},
30 {opacity: 0, transform: 'translateY(-18px)'},
31]
32
33const exitingDownKeyframe = [
34 {opacity: 1, transform: 'translateY(0)'},
35 {opacity: 0, transform: 'translateY(18px)'},
36]
37
38export function CountWheel({
39 likeCount,
40 big,
41 isLiked,
42 isToggle,
43}: {
44 likeCount: number
45 big?: boolean
46 isLiked: boolean
47 isToggle: boolean
48}) {
49 const t = useTheme()
50 const shouldAnimate = !useReducedMotion() && isToggle
51 const shouldRoll = decideShouldRoll(isLiked, likeCount)
52
53 const countView = React.useRef<HTMLDivElement>(null)
54 const prevCountView = React.useRef<HTMLDivElement>(null)
55
56 const [prevCount, setPrevCount] = React.useState(likeCount)
57 const prevIsLiked = React.useRef(isLiked)
58 const formattedCount = formatCount(i18n, likeCount)
59 const formattedPrevCount = formatCount(i18n, prevCount)
60
61 React.useEffect(() => {
62 if (isLiked === prevIsLiked.current) {
63 return
64 }
65
66 const newPrevCount = isLiked ? likeCount - 1 : likeCount + 1
67 if (shouldAnimate && shouldRoll) {
68 countView.current?.animate?.(
69 isLiked ? enteringUpKeyframe : enteringDownKeyframe,
70 animationConfig,
71 )
72 prevCountView.current?.animate?.(
73 isLiked ? exitingUpKeyframe : exitingDownKeyframe,
74 animationConfig,
75 )
76 setPrevCount(newPrevCount)
77 }
78 prevIsLiked.current = isLiked
79 }, [isLiked, likeCount, shouldAnimate, shouldRoll])
80
81 if (likeCount < 1) {
82 return null
83 }
84
85 return (
86 <View>
87 <View
88 // @ts-expect-error is div
89 ref={countView}>
90 <Text
91 testID="likeCount"
92 style={[
93 big ? a.text_md : {fontSize: 15},
94 a.user_select_none,
95 isLiked
96 ? [a.font_bold, s.likeColor]
97 : {color: t.palette.contrast_500},
98 ]}>
99 {formattedCount}
100 </Text>
101 </View>
102 {shouldAnimate && (likeCount > 1 || !isLiked) ? (
103 <View
104 style={{position: 'absolute', opacity: 0}}
105 aria-disabled={true}
106 // @ts-expect-error is div
107 ref={prevCountView}>
108 <Text
109 style={[
110 big ? a.text_md : {fontSize: 15},
111 a.user_select_none,
112 isLiked
113 ? [a.font_bold, s.likeColor]
114 : {color: t.palette.contrast_500},
115 ]}>
116 {formattedPrevCount}
117 </Text>
118 </View>
119 ) : null}
120 </View>
121 )
122}