about summary refs log tree commit diff
path: root/src/view/com/composer/Composer.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/composer/Composer.tsx')
-rw-r--r--src/view/com/composer/Composer.tsx145
1 files changed, 122 insertions, 23 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index b1c020a10..ad79cdb58 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -9,6 +9,7 @@ import React, {
 import {
   ActivityIndicator,
   Keyboard,
+  LayoutChangeEvent,
   StyleSheet,
   TouchableOpacity,
   View,
@@ -19,6 +20,7 @@ import {
 } from 'react-native-keyboard-controller'
 import Animated, {
   interpolateColor,
+  runOnUI,
   useAnimatedStyle,
   useSharedValue,
   withTiming,
@@ -170,22 +172,6 @@ export const ComposePost = observer(function ComposePost({
     [insets, isKeyboardVisible],
   )
 
-  const hasScrolled = useSharedValue(0)
-  const scrollHandler = useAnimatedScrollHandler({
-    onScroll: event => {
-      hasScrolled.value = withTiming(event.contentOffset.y > 0 ? 1 : 0)
-    },
-  })
-  const topBarAnimatedStyle = useAnimatedStyle(() => {
-    return {
-      borderColor: interpolateColor(
-        hasScrolled.value,
-        [0, 1],
-        ['transparent', t.atoms.border_contrast_medium.borderColor],
-      ),
-    }
-  })
-
   const onPressCancel = useCallback(() => {
     if (graphemeLength > 0 || !gallery.isEmpty) {
       closeAllDialogs()
@@ -395,13 +381,21 @@ export const ComposePost = observer(function ComposePost({
     [setExtLink],
   )
 
+  const {
+    scrollHandler,
+    onScrollViewContentSizeChange,
+    onScrollViewLayout,
+    topBarAnimatedStyle,
+    bottomBarAnimatedStyle,
+  } = useAnimatedBorders()
+
   return (
     <>
       <KeyboardAvoidingView
         testID="composePostView"
         behavior="padding"
         style={a.flex_1}
-        keyboardVerticalOffset={replyTo ? 120 : isAndroid ? 180 : 150}>
+        keyboardVerticalOffset={replyTo ? 110 : isAndroid ? 180 : 140}>
         <View
           style={[a.flex_1, viewStyles]}
           aria-modal
@@ -509,7 +503,9 @@ export const ComposePost = observer(function ComposePost({
           <Animated.ScrollView
             onScroll={scrollHandler}
             style={styles.scrollView}
-            keyboardShouldPersistTaps="always">
+            keyboardShouldPersistTaps="always"
+            onContentSizeChange={onScrollViewContentSizeChange}
+            onLayout={onScrollViewLayout}>
             {replyTo ? <ComposerReplyTo replyTo={replyTo} /> : undefined}
 
             <View
@@ -575,7 +571,11 @@ export const ComposePost = observer(function ComposePost({
       <KeyboardStickyView
         offset={{closed: isIOS ? -insets.bottom : 0, opened: 0}}>
         {replyTo ? null : (
-          <ThreadgateBtn threadgate={threadgate} onChange={setThreadgate} />
+          <ThreadgateBtn
+            threadgate={threadgate}
+            onChange={setThreadgate}
+            style={bottomBarAnimatedStyle}
+          />
         )}
         <View
           style={[
@@ -625,10 +625,108 @@ export function useComposerCancelRef() {
   return useRef<CancelRef>(null)
 }
 
+function useAnimatedBorders() {
+  const t = useTheme()
+  const hasScrolledTop = useSharedValue(0)
+  const hasScrolledBottom = useSharedValue(0)
+  const contentOffset = useSharedValue(0)
+  const scrollViewHeight = useSharedValue(Infinity)
+  const contentHeight = useSharedValue(0)
+
+  /**
+   * Make sure to run this on the UI thread!
+   */
+  const showHideBottomBorder = useCallback(
+    ({
+      newContentHeight,
+      newContentOffset,
+      newScrollViewHeight,
+    }: {
+      newContentHeight?: number
+      newContentOffset?: number
+      newScrollViewHeight?: number
+    }) => {
+      'worklet'
+
+      if (typeof newContentHeight === 'number')
+        contentHeight.value = newContentHeight
+      if (typeof newContentOffset === 'number')
+        contentOffset.value = newContentOffset
+      if (typeof newScrollViewHeight === 'number')
+        scrollViewHeight.value = newScrollViewHeight
+
+      hasScrolledBottom.value = withTiming(
+        contentHeight.value - contentOffset.value >= scrollViewHeight.value
+          ? 1
+          : 0,
+      )
+    },
+    [contentHeight, contentOffset, scrollViewHeight, hasScrolledBottom],
+  )
+
+  const scrollHandler = useAnimatedScrollHandler({
+    onScroll: event => {
+      hasScrolledTop.value = withTiming(event.contentOffset.y > 0 ? 1 : 0)
+
+      // already on UI thread
+      showHideBottomBorder({
+        newContentOffset: event.contentOffset.y,
+        newContentHeight: event.contentSize.height,
+        newScrollViewHeight: event.layoutMeasurement.height,
+      })
+    },
+  })
+
+  const onScrollViewContentSizeChange = useCallback(
+    (_width: number, height: number) => {
+      runOnUI(showHideBottomBorder)({
+        newContentHeight: height,
+      })
+    },
+    [showHideBottomBorder],
+  )
+
+  const onScrollViewLayout = useCallback(
+    (evt: LayoutChangeEvent) => {
+      runOnUI(showHideBottomBorder)({
+        newScrollViewHeight: evt.nativeEvent.layout.height,
+      })
+    },
+    [showHideBottomBorder],
+  )
+
+  const topBarAnimatedStyle = useAnimatedStyle(() => {
+    return {
+      borderBottomWidth: hairlineWidth,
+      borderColor: interpolateColor(
+        hasScrolledTop.value,
+        [0, 1],
+        ['transparent', t.atoms.border_contrast_medium.borderColor],
+      ),
+    }
+  })
+  const bottomBarAnimatedStyle = useAnimatedStyle(() => {
+    return {
+      borderTopWidth: hairlineWidth,
+      borderColor: interpolateColor(
+        hasScrolledBottom.value,
+        [0, 1],
+        ['transparent', t.atoms.border_contrast_medium.borderColor],
+      ),
+    }
+  })
+
+  return {
+    scrollHandler,
+    onScrollViewContentSizeChange,
+    onScrollViewLayout,
+    topBarAnimatedStyle,
+    bottomBarAnimatedStyle,
+  }
+}
+
 const styles = StyleSheet.create({
-  topbar: {
-    borderBottomWidth: StyleSheet.hairlineWidth,
-  },
+  topbar: {},
   topbarDesktop: {
     paddingTop: 10,
     paddingBottom: 10,
@@ -698,7 +796,8 @@ const styles = StyleSheet.create({
   bottomBar: {
     flexDirection: 'row',
     paddingVertical: 4,
-    paddingLeft: 8,
+    // should be 8 but due to visual alignment we have to fudge it
+    paddingLeft: 7,
     paddingRight: 16,
     alignItems: 'center',
     borderTopWidth: hairlineWidth,