diff --git a/src/screens/Messages/Conversation/MessagesList.tsx b/src/screens/Messages/Conversation/MessagesList.tsx index e00cf11cd8..7068097fcf 100644 --- a/src/screens/Messages/Conversation/MessagesList.tsx +++ b/src/screens/Messages/Conversation/MessagesList.tsx @@ -94,6 +94,9 @@ export function MessagesList() { // the bottom. const isAtBottom = useSharedValue(true) + // This will be used on web to assist in determing if we need to maintain the content offset + const isAtTop = useSharedValue(true) + // Used to keep track of the current content height. We'll need this in `onScroll` so we know when to start allowing // onStartReached to fire. const contentHeight = useSharedValue(0) @@ -116,6 +119,15 @@ export function MessagesList() { // we will not scroll whenever new items get prepended to the top. const onContentSizeChange = useCallback( (_: number, height: number) => { + // Because web does not have `maintainVisibleContentPosition` support, we will need to manually scroll to the + // previous offset whenever we add new content to the previous offset whenever we add new content to the list. + if (isWeb && isAtTop.value && hasInitiallyScrolled) { + flatListRef.current?.scrollToOffset({ + animated: false, + offset: height - contentHeight.value, + }) + } + contentHeight.value = height // This number _must_ be the height of the MaybeLoader component @@ -133,6 +145,7 @@ export function MessagesList() { contentHeight, hasInitiallyScrolled, isAtBottom.value, + isAtTop.value, isMomentumScrolling, ], ) @@ -164,6 +177,7 @@ export function MessagesList() { // Most apps have a little bit of space the user can scroll past while still automatically scrolling ot the bottom // when a new message is added, hence the 100 pixel offset isAtBottom.value = e.contentSize.height - 100 < bottomOffset + isAtTop.value = e.contentOffset.y <= 1 // This number _must_ be the height of the MaybeLoader component. // We don't check for zero, because the `MaybeLoader` component is always present, even when not visible, which @@ -172,7 +186,7 @@ export function MessagesList() { runOnJS(setHasInitiallyScrolled)(true) } }, - [contentHeight.value, hasInitiallyScrolled, isAtBottom], + [contentHeight.value, hasInitiallyScrolled, isAtBottom, isAtTop], ) const onMomentumEnd = React.useCallback(() => { @@ -211,40 +225,37 @@ export function MessagesList() { keyboardVerticalOffset={isIOS ? topInset : 0} behavior="padding" contentContainerStyle={a.flex_1}> - {/* This view keeps the scroll bar and content within the CenterView on web, otherwise the entire window would scroll */} - {/* @ts-expect-error web only */} - - {/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */} - - - } - /> - - + {/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */} + + + } + /> + ) diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx index e5c427f136..73000d95f1 100644 --- a/src/view/com/util/List.web.tsx +++ b/src/view/com/util/List.web.tsx @@ -143,6 +143,7 @@ function ListImpl( scrollToTop() { getScrollableNode()?.scrollTo({top: 0}) }, + scrollToOffset({ animated, offset,