From eabcd9150d3513988f5b3c47b95a601d5f1bf738 Mon Sep 17 00:00:00 2001
From: Caidan <caidan@internet.dev>
Date: Thu, 21 Aug 2025 11:56:17 -0700
Subject: [APP-1357] profile header follow recommendations (#8784)

---
 src/lib/custom-animations/AccordionAnimation.tsx | 77 ++++++++++++++++++++++++
 1 file changed, 77 insertions(+)
 create mode 100644 src/lib/custom-animations/AccordionAnimation.tsx

(limited to 'src/lib/custom-animations/AccordionAnimation.tsx')

diff --git a/src/lib/custom-animations/AccordionAnimation.tsx b/src/lib/custom-animations/AccordionAnimation.tsx
new file mode 100644
index 000000000..146735aa6
--- /dev/null
+++ b/src/lib/custom-animations/AccordionAnimation.tsx
@@ -0,0 +1,77 @@
+import {
+  type LayoutChangeEvent,
+  type StyleProp,
+  View,
+  type ViewStyle,
+} from 'react-native'
+import Animated, {
+  Easing,
+  FadeInUp,
+  FadeOutUp,
+  useAnimatedStyle,
+  useSharedValue,
+  withTiming,
+} from 'react-native-reanimated'
+
+import {isIOS, isWeb} from '#/platform/detection'
+
+type AccordionAnimationProps = React.PropsWithChildren<{
+  isExpanded: boolean
+  duration?: number
+  style?: StyleProp<ViewStyle>
+}>
+
+function WebAccordion({
+  isExpanded,
+  duration = 300,
+  style,
+  children,
+}: AccordionAnimationProps) {
+  const heightValue = useSharedValue(0)
+
+  const animatedStyle = useAnimatedStyle(() => {
+    const targetHeight = isExpanded ? heightValue.get() : 0
+    return {
+      height: withTiming(targetHeight, {
+        duration,
+        easing: Easing.out(Easing.cubic),
+      }),
+      overflow: 'hidden',
+    }
+  })
+
+  const onLayout = (e: LayoutChangeEvent) => {
+    if (heightValue.get() === 0) {
+      heightValue.set(e.nativeEvent.layout.height)
+    }
+  }
+
+  return (
+    <Animated.View style={[animatedStyle, style]}>
+      <View onLayout={onLayout}>{children}</View>
+    </Animated.View>
+  )
+}
+
+function MobileAccordion({
+  isExpanded,
+  duration = 200,
+  style,
+  children,
+}: AccordionAnimationProps) {
+  if (!isExpanded) return null
+
+  return (
+    <Animated.View
+      style={style}
+      entering={FadeInUp.duration(duration)}
+      exiting={FadeOutUp.duration(duration / 2)}
+      pointerEvents={isIOS ? 'auto' : 'box-none'}>
+      {children}
+    </Animated.View>
+  )
+}
+
+export function AccordionAnimation(props: AccordionAnimationProps) {
+  return isWeb ? <WebAccordion {...props} /> : <MobileAccordion {...props} />
+}
-- 
cgit 1.4.1

