diff options
Diffstat (limited to 'src/view/com/composer/Composer.tsx')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 145 |
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, |