about summary refs log tree commit diff
path: root/src/view/com/util/post-ctrls/PostCtrls.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util/post-ctrls/PostCtrls.tsx')
-rw-r--r--src/view/com/util/post-ctrls/PostCtrls.tsx254
1 files changed, 7 insertions, 247 deletions
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index 49c9229a6..6a58a5624 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -6,14 +6,6 @@ import {
   View,
   type ViewStyle,
 } from 'react-native'
-import Animated, {
-  Easing,
-  interpolate,
-  SharedValue,
-  useAnimatedStyle,
-  useSharedValue,
-  withTiming,
-} from 'react-native-reanimated'
 import * as Clipboard from 'expo-clipboard'
 import {
   AppBskyFeedDefs,
@@ -31,8 +23,6 @@ import {makeProfileLink} from '#/lib/routes/links'
 import {shareUrl} from '#/lib/sharing'
 import {useGate} from '#/lib/statsig/statsig'
 import {toShareUrl} from '#/lib/strings/url-helpers'
-import {s} from '#/lib/styles'
-import {isWeb} from '#/platform/detection'
 import {Shadow} from '#/state/cache/types'
 import {useFeedFeedbackContext} from '#/state/feed-feedback'
 import {
@@ -45,16 +35,13 @@ import {
   ProgressGuideAction,
   useProgressGuideControls,
 } from '#/state/shell/progress-guide'
+import {CountWheel} from 'lib/custom-animations/CountWheel'
+import {AnimatedLikeIcon} from 'lib/custom-animations/LikeIcon'
 import {atoms as a, useTheme} from '#/alf'
 import {useDialogControl} from '#/components/Dialog'
 import {ArrowOutOfBox_Stroke2_Corner0_Rounded as ArrowOutOfBox} from '#/components/icons/ArrowOutOfBox'
 import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble'
-import {
-  Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled,
-  Heart2_Stroke2_Corner0_Rounded as HeartIconOutline,
-} from '#/components/icons/Heart2'
 import * as Prompt from '#/components/Prompt'
-import {PlatformInfo} from '../../../../../modules/expo-bluesky-swiss-army'
 import {PostDropdownBtn} from '../forms/PostDropdownBtn'
 import {formatCount} from '../numeric/format'
 import {Text} from '../text/Text'
@@ -120,17 +107,7 @@ let PostCtrls = ({
   ) as StyleProp<ViewStyle>
 
   const likeValue = post.viewer?.like ? 1 : 0
-  const likeIconAnimValue = useSharedValue(likeValue)
-  const likeTextAnimValue = useSharedValue(likeValue)
   const nextExpectedLikeValue = React.useRef(likeValue)
-  React.useEffect(() => {
-    // Catch nonlocal changes (e.g. shadow update) and always reflect them.
-    if (likeValue !== nextExpectedLikeValue.current) {
-      nextExpectedLikeValue.current = likeValue
-      likeIconAnimValue.value = likeValue
-      likeTextAnimValue.value = likeValue
-    }
-  }, [likeValue, likeIconAnimValue, likeTextAnimValue])
 
   const onPressToggleLike = React.useCallback(async () => {
     if (isBlocked) {
@@ -144,19 +121,6 @@ let PostCtrls = ({
     try {
       if (!post.viewer?.like) {
         nextExpectedLikeValue.current = 1
-        if (PlatformInfo.getIsReducedMotionEnabled()) {
-          likeIconAnimValue.value = 1
-          likeTextAnimValue.value = 1
-        } else {
-          likeIconAnimValue.value = withTiming(1, {
-            duration: 400,
-            easing: Easing.out(Easing.cubic),
-          })
-          likeTextAnimValue.value = withTiming(1, {
-            duration: 400,
-            easing: Easing.out(Easing.cubic),
-          })
-        }
         playHaptic()
         sendInteraction({
           item: post.uri,
@@ -167,15 +131,6 @@ let PostCtrls = ({
         await queueLike()
       } else {
         nextExpectedLikeValue.current = 0
-        likeIconAnimValue.value = 0 // Intentionally not animated
-        if (PlatformInfo.getIsReducedMotionEnabled()) {
-          likeTextAnimValue.value = 0
-        } else {
-          likeTextAnimValue.value = withTiming(0, {
-            duration: 400,
-            easing: Easing.out(Easing.cubic),
-          })
-        }
         await queueUnlike()
       }
     } catch (e: any) {
@@ -185,8 +140,6 @@ let PostCtrls = ({
     }
   }, [
     _,
-    likeIconAnimValue,
-    likeTextAnimValue,
     playHaptic,
     post.uri,
     post.viewer?.like,
@@ -291,8 +244,8 @@ let PostCtrls = ({
       a.gap_xs,
       a.rounded_full,
       a.flex_row,
-      a.align_center,
       a.justify_center,
+      a.align_center,
       {padding: 5},
       (pressed || hovered) && t.atoms.bg_contrast_25,
     ],
@@ -364,13 +317,11 @@ let PostCtrls = ({
           }
           accessibilityHint=""
           hitSlop={POST_CTRL_HITSLOP}>
-          <AnimatedLikeIcon
-            big={big ?? false}
-            likeIconAnimValue={likeIconAnimValue}
-            likeTextAnimValue={likeTextAnimValue}
-            defaultCtrlColor={defaultCtrlColor}
-            isLiked={Boolean(post.viewer?.like)}
+          <AnimatedLikeIcon isLiked={Boolean(post.viewer?.like)} big={big} />
+          <CountWheel
             likeCount={post.likeCount ?? 0}
+            big={big}
+            isLiked={Boolean(post.viewer?.like)}
           />
         </Pressable>
       </View>
@@ -450,194 +401,3 @@ let PostCtrls = ({
 }
 PostCtrls = memo(PostCtrls)
 export {PostCtrls}
-
-function AnimatedLikeIcon({
-  big,
-  likeIconAnimValue,
-  likeTextAnimValue,
-  defaultCtrlColor,
-  isLiked,
-  likeCount,
-}: {
-  big: boolean
-  likeIconAnimValue: SharedValue<number>
-  likeTextAnimValue: SharedValue<number>
-  defaultCtrlColor: StyleProp<ViewStyle>
-  isLiked: boolean
-  likeCount: number
-}) {
-  const t = useTheme()
-  const {i18n} = useLingui()
-  const likeStyle = useAnimatedStyle(() => ({
-    transform: [
-      {
-        scale: interpolate(
-          likeIconAnimValue.value,
-          [0, 0.1, 0.4, 1],
-          [1, 0.7, 1.2, 1],
-          'clamp',
-        ),
-      },
-    ],
-  }))
-  const circle1Style = useAnimatedStyle(() => ({
-    opacity: interpolate(
-      likeIconAnimValue.value,
-      [0, 0.1, 0.95, 1],
-      [0, 0.4, 0.4, 0],
-      'clamp',
-    ),
-    transform: [
-      {
-        scale: interpolate(
-          likeIconAnimValue.value,
-          [0, 0.4, 1],
-          [0, 1.5, 1.5],
-          'clamp',
-        ),
-      },
-    ],
-  }))
-  const circle2Style = useAnimatedStyle(() => ({
-    opacity: interpolate(
-      likeIconAnimValue.value,
-      [0, 0.1, 0.95, 1],
-      [0, 1, 1, 0],
-      'clamp',
-    ),
-    transform: [
-      {
-        scale: interpolate(
-          likeIconAnimValue.value,
-          [0, 0.4, 1],
-          [0, 0, 1.5],
-          'clamp',
-        ),
-      },
-    ],
-  }))
-  const countStyle = useAnimatedStyle(() => ({
-    transform: [
-      {
-        translateY: interpolate(
-          likeTextAnimValue.value,
-          [0, 1],
-          [0, big ? -22 : -18],
-          'clamp',
-        ),
-      },
-    ],
-  }))
-
-  const prevFormattedCount = formatCount(
-    i18n,
-    isLiked ? likeCount - 1 : likeCount,
-  )
-  const nextFormattedCount = formatCount(
-    i18n,
-    isLiked ? likeCount : likeCount + 1,
-  )
-  const shouldRollLike =
-    prevFormattedCount !== nextFormattedCount && prevFormattedCount !== '0'
-
-  return (
-    <>
-      <View>
-        <Animated.View
-          style={[
-            {
-              position: 'absolute',
-              backgroundColor: s.likeColor.color,
-              top: 0,
-              left: 0,
-              width: big ? 22 : 18,
-              height: big ? 22 : 18,
-              zIndex: -1,
-              pointerEvents: 'none',
-              borderRadius: (big ? 22 : 18) / 2,
-            },
-            circle1Style,
-          ]}
-        />
-        <Animated.View
-          style={[
-            {
-              position: 'absolute',
-              backgroundColor: isWeb
-                ? t.atoms.bg_contrast_25.backgroundColor
-                : t.atoms.bg.backgroundColor,
-              top: 0,
-              left: 0,
-              width: big ? 22 : 18,
-              height: big ? 22 : 18,
-              zIndex: -1,
-              pointerEvents: 'none',
-              borderRadius: (big ? 22 : 18) / 2,
-            },
-            circle2Style,
-          ]}
-        />
-        <Animated.View style={likeStyle}>
-          {isLiked ? (
-            <HeartIconFilled style={s.likeColor} width={big ? 22 : 18} />
-          ) : (
-            <HeartIconOutline
-              style={[defaultCtrlColor, {pointerEvents: 'none'}]}
-              width={big ? 22 : 18}
-            />
-          )}
-        </Animated.View>
-      </View>
-      <View style={{overflow: 'hidden'}}>
-        <Text
-          testID="likeCount"
-          style={[
-            [
-              big ? a.text_md : {fontSize: 15},
-              a.user_select_none,
-              isLiked ? [a.font_bold, s.likeColor] : defaultCtrlColor,
-              {opacity: shouldRollLike ? 0 : 1},
-            ],
-          ]}>
-          {likeCount > 0 ? formatCount(i18n, likeCount) : ''}
-        </Text>
-        <Animated.View
-          aria-hidden={true}
-          style={[
-            countStyle,
-            {
-              position: 'absolute',
-              top: 0,
-              left: 0,
-              opacity: shouldRollLike ? 1 : 0,
-            },
-          ]}>
-          <Text
-            testID="likeCount"
-            style={[
-              [
-                big ? a.text_md : {fontSize: 15},
-                a.user_select_none,
-                isLiked ? [a.font_bold, s.likeColor] : defaultCtrlColor,
-                {height: big ? 22 : 18},
-              ],
-            ]}>
-            {prevFormattedCount}
-          </Text>
-          <Text
-            testID="likeCount"
-            style={[
-              [
-                big ? a.text_md : {fontSize: 15},
-                a.user_select_none,
-                isLiked ? [a.font_bold, s.likeColor] : defaultCtrlColor,
-                {height: big ? 22 : 18},
-              ],
-            ]}>
-            {nextFormattedCount}
-          </Text>
-        </Animated.View>
-      </View>
-    </>
-  )
-}