11import { FlashList } from '@shopify/flash-list' ;
22import { useLocalSearchParams } from 'expo-router' ;
33import { delay } from 'lodash-es' ;
4- import React , { ReactElement , useCallback , useRef , useState } from 'react' ;
5- import { NativeScrollEvent , NativeSyntheticEvent } from 'react-native' ;
4+ import React , { ReactElement , useCallback , useRef } from 'react' ;
5+ import { GestureResponderEvent , NativeScrollEvent , NativeSyntheticEvent } from 'react-native' ;
66import { useSharedValue , withTiming } from 'react-native-reanimated' ;
77import { AiMessageActions } from '@open-webui-react-native/mobile/chat/features/ai-message-actions' ;
88import { useManageMessageSiblings } from '@open-webui-react-native/mobile/chat/features/use-manage-messages-siblings' ;
@@ -62,7 +62,8 @@ export default function ChatMessagesList({
6262 const isScrollToBottomAvailableTimeout = useRef < NodeJS . Timeout | null | number > ( null ) ; //NOTE: number needs to fix pipeline lint error
6363 const isScrollToBottomVisible = useSharedValue ( 0 ) ;
6464 const previousScrollY = useRef ( 0 ) ;
65- const [ autoscrollToBottomThreshold , setAutoscrollToBottomThreshold ] = useState < number | undefined > ( 1 ) ;
65+ const shouldAutoscrollToBottomRef = useRef ( true ) ;
66+ const previousTouchY = useRef ( 0 ) ;
6667
6768 const { showPreviousSibling, showNextSibling, getSiblingsInfo } = useManageMessageSiblings ( chatId , history ) ;
6869 const { mutate : completeChat } = chatApi . useCompleteChat ( ) ;
@@ -80,6 +81,12 @@ export default function ChatMessagesList({
8081 isScrollToBottomAvailable . current = true ;
8182 } , 500 ) ;
8283
84+ if ( shouldAutoscrollToBottomRef . current ) {
85+ requestAnimationFrame ( ( ) => {
86+ listRef . current ?. scrollToEnd ( { animated : true } ) ;
87+ } ) ;
88+ }
89+
8390 if ( ! isMessagesListLoaded && listRef . current && messages ?. length > 0 ) {
8491 delay ( ( ) => {
8592 listRef . current ?. scrollToIndex ( {
@@ -120,6 +127,7 @@ export default function ChatMessagesList({
120127 //NOTE: Needs to hide scroll to bottom button to avoid its jumping while scrolling to bottom
121128 animateScrollToBottom ( 0 ) ;
122129 isScrollToBottomAvailable . current = false ;
130+
123131 delay ( ( ) => {
124132 isScrollToBottomAvailable . current = true ;
125133 } , 1000 ) ;
@@ -128,7 +136,6 @@ export default function ChatMessagesList({
128136 } ;
129137
130138 const handleEditPress = ( index : number , messageId : string , content : string ) : void => {
131- setAutoscrollToBottomThreshold ( undefined ) ;
132139 onEditPress ( messageId , content ) ;
133140 delay ( ( ) => {
134141 listRef . current ?. scrollToIndex ( {
@@ -137,9 +144,6 @@ export default function ChatMessagesList({
137144 animated : true ,
138145 } ) ;
139146 } , 500 ) ;
140- delay ( ( ) => {
141- setAutoscrollToBottomThreshold ( 1 ) ;
142- } , 1000 ) ;
143147 } ;
144148
145149 const handleContinueResponsePress = ( messageId : string ) : void => {
@@ -171,6 +175,23 @@ export default function ChatMessagesList({
171175 onFollowUpPress ( text ) ;
172176 } ;
173177
178+ const handleTouchStart = ( e : GestureResponderEvent ) : void => {
179+ if ( ! isResponseGenerating ) return ;
180+
181+ shouldAutoscrollToBottomRef . current = false ;
182+ previousTouchY . current = e . nativeEvent . pageY ;
183+ } ;
184+
185+ const handleTouchMove = ( e : GestureResponderEvent ) : void => {
186+ if ( ! isResponseGenerating ) return ;
187+
188+ const { pageY } = e . nativeEvent ;
189+ const deltaY = pageY - previousTouchY . current ;
190+
191+ previousTouchY . current = pageY ;
192+ shouldAutoscrollToBottomRef . current = deltaY < 0 ;
193+ } ;
194+
174195 const renderItem = useCallback (
175196 ( { item, index } : { item : Message ; index : number } ) => {
176197 const message = history ?. messages [ item . id ] ;
@@ -191,6 +212,7 @@ export default function ChatMessagesList({
191212 onTryAgain = { onTryAgain }
192213 onAddDetails = { onAddDetails }
193214 onMoreConcise = { onMoreConcise }
215+ isResponseGenerating = { isResponseGenerating }
194216 isLast = { isLast } >
195217 < ChatAiMessage
196218 message = { message }
@@ -243,14 +265,13 @@ export default function ChatMessagesList({
243265 ItemSeparatorComponent = { ( ) => < View className = 'h-20' /> }
244266 data = { messages }
245267 renderItem = { renderItem }
246- // TODO: Add autoscrollToBottom logic when it implemented in lib
247268 maintainVisibleContentPosition = { {
248269 startRenderingFromBottom : true ,
249- animateAutoScrollToBottom : true ,
250- autoscrollToBottomThreshold,
251270 } }
252271 onContentSizeChange = { handleContentSizeChange }
253272 onScroll = { handleScroll }
273+ onTouchStart = { handleTouchStart }
274+ onTouchMove = { handleTouchMove }
254275 scrollEventThrottle = { 16 }
255276 />
256277 < ChatBottomButton isVisible = { isScrollToBottomVisible } onPress = { scrollToBottom } />
0 commit comments