about summary refs log tree commit diff
path: root/src/components/dms/ActionsWrapper.tsx
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-05-02 13:54:17 -0700
committerGitHub <noreply@github.com>2024-05-02 13:54:17 -0700
commit8ba1b10ce0d278a88e37d6b6c277a41673392877 (patch)
tree1e44ec05c427aaa124077b6fb9ce01c831daa8b3 /src/components/dms/ActionsWrapper.tsx
parent6da18e3dcffaf72a03bde8a205a596b4b3366b86 (diff)
downloadvoidsky-8ba1b10ce0d278a88e37d6b6c277a41673392877.tar.zst
[Clipclops] Message actions for native and web (#3807)
* haptic on long press

* add animation to press and hold

* eslint disable for now

* adjust styles

* dont trigger if animation is cancelled

* organize

* add a delete menu

* reset scale automatically

* message actions dialog

cleanup

center the trigger

handle focus/unfocus better

make triggers accessible

weg dropdown menu

add a wep specific wrapper

decrease press delay

add report button

improve shrink logic

use `self_end` instead of `margin: auto`

rm extra `?`

move `MessageItem` to `components`

add delete button

* rm some padding

* update after merge

* fix merge

* web only types

* fix crash

* add an explanation

* fix web types

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Diffstat (limited to 'src/components/dms/ActionsWrapper.tsx')
-rw-r--r--src/components/dms/ActionsWrapper.tsx82
1 files changed, 82 insertions, 0 deletions
diff --git a/src/components/dms/ActionsWrapper.tsx b/src/components/dms/ActionsWrapper.tsx
new file mode 100644
index 000000000..107e5eb8e
--- /dev/null
+++ b/src/components/dms/ActionsWrapper.tsx
@@ -0,0 +1,82 @@
+import React, {useCallback} from 'react'
+import {Pressable, View} from 'react-native'
+import Animated, {
+  cancelAnimation,
+  runOnJS,
+  useAnimatedStyle,
+  useSharedValue,
+  withTiming,
+} from 'react-native-reanimated'
+import {ChatBskyConvoDefs} from '@atproto-labs/api'
+
+import {useHaptics} from 'lib/haptics'
+import {atoms as a} from '#/alf'
+import {MessageMenu} from '#/components/dms/MessageMenu'
+import {useMenuControl} from '#/components/Menu'
+
+const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
+
+export function ActionsWrapper({
+  message,
+  isFromSelf,
+  children,
+}: {
+  message: ChatBskyConvoDefs.MessageView
+  isFromSelf: boolean
+  children: React.ReactNode
+}) {
+  const playHaptic = useHaptics()
+  const menuControl = useMenuControl()
+
+  const scale = useSharedValue(1)
+  const animationDidComplete = useSharedValue(false)
+
+  const animatedStyle = useAnimatedStyle(() => ({
+    transform: [{scale: scale.value}],
+  }))
+
+  // Reanimated's `runOnJS` doesn't like refs, so we can't use `runOnJS(menuControl.open)()`. Instead, we'll use this
+  // function
+  const open = useCallback(() => {
+    menuControl.open()
+  }, [menuControl])
+
+  const shrink = useCallback(() => {
+    'worklet'
+    cancelAnimation(scale)
+    scale.value = withTiming(1, {duration: 200}, () => {
+      animationDidComplete.value = false
+    })
+  }, [animationDidComplete, scale])
+
+  const grow = React.useCallback(() => {
+    'worklet'
+    scale.value = withTiming(1.05, {duration: 750}, finished => {
+      if (!finished) return
+      animationDidComplete.value = true
+      runOnJS(playHaptic)()
+      runOnJS(open)()
+
+      shrink()
+    })
+  }, [scale, animationDidComplete, playHaptic, shrink, open])
+
+  return (
+    <View
+      style={[
+        {
+          maxWidth: '65%',
+        },
+        isFromSelf ? a.self_end : a.self_start,
+      ]}>
+      <AnimatedPressable
+        style={animatedStyle}
+        unstable_pressDelay={200}
+        onPressIn={grow}
+        onTouchEnd={shrink}>
+        {children}
+      </AnimatedPressable>
+      <MessageMenu message={message} control={menuControl} hideTrigger={true} />
+    </View>
+  )
+}